Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2 | pmbaty | 1 | // |
2 | // GCDAsyncSocket.m |
||
3 | // |
||
4 | // This class is in the public domain. |
||
5 | // Originally created by Robbie Hanson in Q4 2010. |
||
6 | // Updated and maintained by Deusty LLC and the Apple development community. |
||
7 | // |
||
8 | // https://github.com/robbiehanson/CocoaAsyncSocket |
||
9 | // |
||
10 | |||
11 | #import "GCDAsyncSocket.h" |
||
12 | |||
13 | #if TARGET_OS_IPHONE |
||
14 | #import <CFNetwork/CFNetwork.h> |
||
15 | #endif |
||
16 | |||
17 | #import <TargetConditionals.h> |
||
18 | #import <arpa/inet.h> |
||
19 | #import <fcntl.h> |
||
20 | #import <ifaddrs.h> |
||
21 | #import <netdb.h> |
||
22 | #import <netinet/in.h> |
||
23 | #import <net/if.h> |
||
24 | #import <sys/socket.h> |
||
25 | #import <sys/types.h> |
||
26 | #import <sys/ioctl.h> |
||
27 | #import <sys/poll.h> |
||
28 | #import <sys/uio.h> |
||
29 | #import <sys/un.h> |
||
30 | #import <unistd.h> |
||
31 | |||
32 | #if ! __has_feature(objc_arc) |
||
33 | #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). |
||
34 | // For more information see: https://github.com/robbiehanson/CocoaAsyncSocket/wiki/ARC |
||
35 | #endif |
||
36 | |||
37 | |||
38 | #ifndef GCDAsyncSocketLoggingEnabled |
||
39 | #define GCDAsyncSocketLoggingEnabled 0 |
||
40 | #endif |
||
41 | |||
42 | #if GCDAsyncSocketLoggingEnabled |
||
43 | |||
44 | // Logging Enabled - See log level below |
||
45 | |||
46 | // Logging uses the CocoaLumberjack framework (which is also GCD based). |
||
47 | // https://github.com/robbiehanson/CocoaLumberjack |
||
48 | // |
||
49 | // It allows us to do a lot of logging without significantly slowing down the code. |
||
50 | #import "DDLog.h" |
||
51 | |||
52 | #define LogAsync YES |
||
53 | #define LogContext GCDAsyncSocketLoggingContext |
||
54 | |||
55 | #define LogObjc(flg, frmt, ...) LOG_OBJC_MAYBE(LogAsync, logLevel, flg, LogContext, frmt, ##__VA_ARGS__) |
||
56 | #define LogC(flg, frmt, ...) LOG_C_MAYBE(LogAsync, logLevel, flg, LogContext, frmt, ##__VA_ARGS__) |
||
57 | |||
58 | #define LogError(frmt, ...) LogObjc(LOG_FLAG_ERROR, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) |
||
59 | #define LogWarn(frmt, ...) LogObjc(LOG_FLAG_WARN, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) |
||
60 | #define LogInfo(frmt, ...) LogObjc(LOG_FLAG_INFO, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) |
||
61 | #define LogVerbose(frmt, ...) LogObjc(LOG_FLAG_VERBOSE, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) |
||
62 | |||
63 | #define LogCError(frmt, ...) LogC(LOG_FLAG_ERROR, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) |
||
64 | #define LogCWarn(frmt, ...) LogC(LOG_FLAG_WARN, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) |
||
65 | #define LogCInfo(frmt, ...) LogC(LOG_FLAG_INFO, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) |
||
66 | #define LogCVerbose(frmt, ...) LogC(LOG_FLAG_VERBOSE, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__) |
||
67 | |||
68 | #define LogTrace() LogObjc(LOG_FLAG_VERBOSE, @"%@: %@", THIS_FILE, THIS_METHOD) |
||
69 | #define LogCTrace() LogC(LOG_FLAG_VERBOSE, @"%@: %s", THIS_FILE, __FUNCTION__) |
||
70 | |||
71 | #ifndef GCDAsyncSocketLogLevel |
||
72 | #define GCDAsyncSocketLogLevel LOG_LEVEL_VERBOSE |
||
73 | #endif |
||
74 | |||
75 | // Log levels : off, error, warn, info, verbose |
||
76 | static const int logLevel = GCDAsyncSocketLogLevel; |
||
77 | |||
78 | #else |
||
79 | |||
80 | // Logging Disabled |
||
81 | |||
82 | #define LogError(frmt, ...) {} |
||
83 | #define LogWarn(frmt, ...) {} |
||
84 | #define LogInfo(frmt, ...) {} |
||
85 | #define LogVerbose(frmt, ...) {} |
||
86 | |||
87 | #define LogCError(frmt, ...) {} |
||
88 | #define LogCWarn(frmt, ...) {} |
||
89 | #define LogCInfo(frmt, ...) {} |
||
90 | #define LogCVerbose(frmt, ...) {} |
||
91 | |||
92 | #define LogTrace() {} |
||
93 | #define LogCTrace(frmt, ...) {} |
||
94 | |||
95 | #endif |
||
96 | |||
97 | /** |
||
98 | * Seeing a return statements within an inner block |
||
99 | * can sometimes be mistaken for a return point of the enclosing method. |
||
100 | * This makes inline blocks a bit easier to read. |
||
101 | **/ |
||
102 | #define return_from_block return |
||
103 | |||
104 | /** |
||
105 | * A socket file descriptor is really just an integer. |
||
106 | * It represents the index of the socket within the kernel. |
||
107 | * This makes invalid file descriptor comparisons easier to read. |
||
108 | **/ |
||
109 | #define SOCKET_NULL -1 |
||
110 | |||
111 | |||
112 | NSString *const GCDAsyncSocketException = @"GCDAsyncSocketException"; |
||
113 | NSString *const GCDAsyncSocketErrorDomain = @"GCDAsyncSocketErrorDomain"; |
||
114 | |||
115 | NSString *const GCDAsyncSocketQueueName = @"GCDAsyncSocket"; |
||
116 | NSString *const GCDAsyncSocketThreadName = @"GCDAsyncSocket-CFStream"; |
||
117 | |||
118 | NSString *const GCDAsyncSocketManuallyEvaluateTrust = @"GCDAsyncSocketManuallyEvaluateTrust"; |
||
119 | #if TARGET_OS_IPHONE |
||
120 | NSString *const GCDAsyncSocketUseCFStreamForTLS = @"GCDAsyncSocketUseCFStreamForTLS"; |
||
121 | #endif |
||
122 | NSString *const GCDAsyncSocketSSLPeerID = @"GCDAsyncSocketSSLPeerID"; |
||
123 | NSString *const GCDAsyncSocketSSLProtocolVersionMin = @"GCDAsyncSocketSSLProtocolVersionMin"; |
||
124 | NSString *const GCDAsyncSocketSSLProtocolVersionMax = @"GCDAsyncSocketSSLProtocolVersionMax"; |
||
125 | NSString *const GCDAsyncSocketSSLSessionOptionFalseStart = @"GCDAsyncSocketSSLSessionOptionFalseStart"; |
||
126 | NSString *const GCDAsyncSocketSSLSessionOptionSendOneByteRecord = @"GCDAsyncSocketSSLSessionOptionSendOneByteRecord"; |
||
127 | NSString *const GCDAsyncSocketSSLCipherSuites = @"GCDAsyncSocketSSLCipherSuites"; |
||
128 | NSString *const GCDAsyncSocketSSLALPN = @"GCDAsyncSocketSSLALPN"; |
||
129 | #if !TARGET_OS_IPHONE |
||
130 | NSString *const GCDAsyncSocketSSLDiffieHellmanParameters = @"GCDAsyncSocketSSLDiffieHellmanParameters"; |
||
131 | #endif |
||
132 | |||
133 | enum GCDAsyncSocketFlags |
||
134 | { |
||
135 | kSocketStarted = 1 << 0, // If set, socket has been started (accepting/connecting) |
||
136 | kConnected = 1 << 1, // If set, the socket is connected |
||
137 | kForbidReadsWrites = 1 << 2, // If set, no new reads or writes are allowed |
||
138 | kReadsPaused = 1 << 3, // If set, reads are paused due to possible timeout |
||
139 | kWritesPaused = 1 << 4, // If set, writes are paused due to possible timeout |
||
140 | kDisconnectAfterReads = 1 << 5, // If set, disconnect after no more reads are queued |
||
141 | kDisconnectAfterWrites = 1 << 6, // If set, disconnect after no more writes are queued |
||
142 | kSocketCanAcceptBytes = 1 << 7, // If set, we know socket can accept bytes. If unset, it's unknown. |
||
143 | kReadSourceSuspended = 1 << 8, // If set, the read source is suspended |
||
144 | kWriteSourceSuspended = 1 << 9, // If set, the write source is suspended |
||
145 | kQueuedTLS = 1 << 10, // If set, we've queued an upgrade to TLS |
||
146 | kStartingReadTLS = 1 << 11, // If set, we're waiting for TLS negotiation to complete |
||
147 | kStartingWriteTLS = 1 << 12, // If set, we're waiting for TLS negotiation to complete |
||
148 | kSocketSecure = 1 << 13, // If set, socket is using secure communication via SSL/TLS |
||
149 | kSocketHasReadEOF = 1 << 14, // If set, we have read EOF from socket |
||
150 | kReadStreamClosed = 1 << 15, // If set, we've read EOF plus prebuffer has been drained |
||
151 | kDealloc = 1 << 16, // If set, the socket is being deallocated |
||
152 | #if TARGET_OS_IPHONE |
||
153 | kAddedStreamsToRunLoop = 1 << 17, // If set, CFStreams have been added to listener thread |
||
154 | kUsingCFStreamForTLS = 1 << 18, // If set, we're forced to use CFStream instead of SecureTransport |
||
155 | kSecureSocketHasBytesAvailable = 1 << 19, // If set, CFReadStream has notified us of bytes available |
||
156 | #endif |
||
157 | }; |
||
158 | |||
159 | enum GCDAsyncSocketConfig |
||
160 | { |
||
161 | kIPv4Disabled = 1 << 0, // If set, IPv4 is disabled |
||
162 | kIPv6Disabled = 1 << 1, // If set, IPv6 is disabled |
||
163 | kPreferIPv6 = 1 << 2, // If set, IPv6 is preferred over IPv4 |
||
164 | kAllowHalfDuplexConnection = 1 << 3, // If set, the socket will stay open even if the read stream closes |
||
165 | }; |
||
166 | |||
167 | #if TARGET_OS_IPHONE |
||
168 | static NSThread *cfstreamThread; // Used for CFStreams |
||
169 | |||
170 | |||
171 | static uint64_t cfstreamThreadRetainCount; // setup & teardown |
||
172 | static dispatch_queue_t cfstreamThreadSetupQueue; // setup & teardown |
||
173 | #endif |
||
174 | |||
175 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
176 | #pragma mark - |
||
177 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
178 | |||
179 | /** |
||
180 | * A PreBuffer is used when there is more data available on the socket |
||
181 | * than is being requested by current read request. |
||
182 | * In this case we slurp up all data from the socket (to minimize sys calls), |
||
183 | * and store additional yet unread data in a "prebuffer". |
||
184 | * |
||
185 | * The prebuffer is entirely drained before we read from the socket again. |
||
186 | * In other words, a large chunk of data is written is written to the prebuffer. |
||
187 | * The prebuffer is then drained via a series of one or more reads (for subsequent read request(s)). |
||
188 | * |
||
189 | * A ring buffer was once used for this purpose. |
||
190 | * But a ring buffer takes up twice as much memory as needed (double the size for mirroring). |
||
191 | * In fact, it generally takes up more than twice the needed size as everything has to be rounded up to vm_page_size. |
||
192 | * And since the prebuffer is always completely drained after being written to, a full ring buffer isn't needed. |
||
193 | * |
||
194 | * The current design is very simple and straight-forward, while also keeping memory requirements lower. |
||
195 | **/ |
||
196 | |||
197 | @interface GCDAsyncSocketPreBuffer : NSObject |
||
198 | { |
||
199 | uint8_t *preBuffer; |
||
200 | size_t preBufferSize; |
||
201 | |||
202 | uint8_t *readPointer; |
||
203 | uint8_t *writePointer; |
||
204 | } |
||
205 | |||
206 | - (instancetype)initWithCapacity:(size_t)numBytes NS_DESIGNATED_INITIALIZER; |
||
207 | |||
208 | - (void)ensureCapacityForWrite:(size_t)numBytes; |
||
209 | |||
210 | - (size_t)availableBytes; |
||
211 | - (uint8_t *)readBuffer; |
||
212 | |||
213 | - (void)getReadBuffer:(uint8_t **)bufferPtr availableBytes:(size_t *)availableBytesPtr; |
||
214 | |||
215 | - (size_t)availableSpace; |
||
216 | - (uint8_t *)writeBuffer; |
||
217 | |||
218 | - (void)getWriteBuffer:(uint8_t **)bufferPtr availableSpace:(size_t *)availableSpacePtr; |
||
219 | |||
220 | - (void)didRead:(size_t)bytesRead; |
||
221 | - (void)didWrite:(size_t)bytesWritten; |
||
222 | |||
223 | - (void)reset; |
||
224 | |||
225 | @end |
||
226 | |||
227 | @implementation GCDAsyncSocketPreBuffer |
||
228 | |||
229 | // Cover the superclass' designated initializer |
||
230 | - (instancetype)init NS_UNAVAILABLE |
||
231 | { |
||
232 | NSAssert(0, @"Use the designated initializer"); |
||
233 | return nil; |
||
234 | } |
||
235 | |||
236 | - (instancetype)initWithCapacity:(size_t)numBytes |
||
237 | { |
||
238 | if ((self = [super init])) |
||
239 | { |
||
240 | preBufferSize = numBytes; |
||
241 | preBuffer = malloc(preBufferSize); |
||
242 | |||
243 | readPointer = preBuffer; |
||
244 | writePointer = preBuffer; |
||
245 | } |
||
246 | return self; |
||
247 | } |
||
248 | |||
249 | - (void)dealloc |
||
250 | { |
||
251 | if (preBuffer) |
||
252 | free(preBuffer); |
||
253 | } |
||
254 | |||
255 | - (void)ensureCapacityForWrite:(size_t)numBytes |
||
256 | { |
||
257 | size_t availableSpace = [self availableSpace]; |
||
258 | |||
259 | if (numBytes > availableSpace) |
||
260 | { |
||
261 | size_t additionalBytes = numBytes - availableSpace; |
||
262 | |||
263 | size_t newPreBufferSize = preBufferSize + additionalBytes; |
||
264 | uint8_t *newPreBuffer = realloc(preBuffer, newPreBufferSize); |
||
265 | |||
266 | size_t readPointerOffset = readPointer - preBuffer; |
||
267 | size_t writePointerOffset = writePointer - preBuffer; |
||
268 | |||
269 | preBuffer = newPreBuffer; |
||
270 | preBufferSize = newPreBufferSize; |
||
271 | |||
272 | readPointer = preBuffer + readPointerOffset; |
||
273 | writePointer = preBuffer + writePointerOffset; |
||
274 | } |
||
275 | } |
||
276 | |||
277 | - (size_t)availableBytes |
||
278 | { |
||
279 | return writePointer - readPointer; |
||
280 | } |
||
281 | |||
282 | - (uint8_t *)readBuffer |
||
283 | { |
||
284 | return readPointer; |
||
285 | } |
||
286 | |||
287 | - (void)getReadBuffer:(uint8_t **)bufferPtr availableBytes:(size_t *)availableBytesPtr |
||
288 | { |
||
289 | if (bufferPtr) *bufferPtr = readPointer; |
||
290 | if (availableBytesPtr) *availableBytesPtr = [self availableBytes]; |
||
291 | } |
||
292 | |||
293 | - (void)didRead:(size_t)bytesRead |
||
294 | { |
||
295 | readPointer += bytesRead; |
||
296 | |||
297 | if (readPointer == writePointer) |
||
298 | { |
||
299 | // The prebuffer has been drained. Reset pointers. |
||
300 | readPointer = preBuffer; |
||
301 | writePointer = preBuffer; |
||
302 | } |
||
303 | } |
||
304 | |||
305 | - (size_t)availableSpace |
||
306 | { |
||
307 | return preBufferSize - (writePointer - preBuffer); |
||
308 | } |
||
309 | |||
310 | - (uint8_t *)writeBuffer |
||
311 | { |
||
312 | return writePointer; |
||
313 | } |
||
314 | |||
315 | - (void)getWriteBuffer:(uint8_t **)bufferPtr availableSpace:(size_t *)availableSpacePtr |
||
316 | { |
||
317 | if (bufferPtr) *bufferPtr = writePointer; |
||
318 | if (availableSpacePtr) *availableSpacePtr = [self availableSpace]; |
||
319 | } |
||
320 | |||
321 | - (void)didWrite:(size_t)bytesWritten |
||
322 | { |
||
323 | writePointer += bytesWritten; |
||
324 | } |
||
325 | |||
326 | - (void)reset |
||
327 | { |
||
328 | readPointer = preBuffer; |
||
329 | writePointer = preBuffer; |
||
330 | } |
||
331 | |||
332 | @end |
||
333 | |||
334 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
335 | #pragma mark - |
||
336 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
337 | |||
338 | /** |
||
339 | * The GCDAsyncReadPacket encompasses the instructions for any given read. |
||
340 | * The content of a read packet allows the code to determine if we're: |
||
341 | * - reading to a certain length |
||
342 | * - reading to a certain separator |
||
343 | * - or simply reading the first chunk of available data |
||
344 | **/ |
||
345 | @interface GCDAsyncReadPacket : NSObject |
||
346 | { |
||
347 | @public |
||
348 | NSMutableData *buffer; |
||
349 | NSUInteger startOffset; |
||
350 | NSUInteger bytesDone; |
||
351 | NSUInteger maxLength; |
||
352 | NSTimeInterval timeout; |
||
353 | NSUInteger readLength; |
||
354 | NSData *term; |
||
355 | BOOL bufferOwner; |
||
356 | NSUInteger originalBufferLength; |
||
357 | long tag; |
||
358 | } |
||
359 | - (instancetype)initWithData:(NSMutableData *)d |
||
360 | startOffset:(NSUInteger)s |
||
361 | maxLength:(NSUInteger)m |
||
362 | timeout:(NSTimeInterval)t |
||
363 | readLength:(NSUInteger)l |
||
364 | terminator:(NSData *)e |
||
365 | tag:(long)i NS_DESIGNATED_INITIALIZER; |
||
366 | |||
367 | - (void)ensureCapacityForAdditionalDataOfLength:(NSUInteger)bytesToRead; |
||
368 | |||
369 | - (NSUInteger)optimalReadLengthWithDefault:(NSUInteger)defaultValue shouldPreBuffer:(BOOL *)shouldPreBufferPtr; |
||
370 | |||
371 | - (NSUInteger)readLengthForNonTermWithHint:(NSUInteger)bytesAvailable; |
||
372 | - (NSUInteger)readLengthForTermWithHint:(NSUInteger)bytesAvailable shouldPreBuffer:(BOOL *)shouldPreBufferPtr; |
||
373 | - (NSUInteger)readLengthForTermWithPreBuffer:(GCDAsyncSocketPreBuffer *)preBuffer found:(BOOL *)foundPtr; |
||
374 | |||
375 | - (NSInteger)searchForTermAfterPreBuffering:(ssize_t)numBytes; |
||
376 | |||
377 | @end |
||
378 | |||
379 | @implementation GCDAsyncReadPacket |
||
380 | |||
381 | // Cover the superclass' designated initializer |
||
382 | - (instancetype)init NS_UNAVAILABLE |
||
383 | { |
||
384 | NSAssert(0, @"Use the designated initializer"); |
||
385 | return nil; |
||
386 | } |
||
387 | |||
388 | - (instancetype)initWithData:(NSMutableData *)d |
||
389 | startOffset:(NSUInteger)s |
||
390 | maxLength:(NSUInteger)m |
||
391 | timeout:(NSTimeInterval)t |
||
392 | readLength:(NSUInteger)l |
||
393 | terminator:(NSData *)e |
||
394 | tag:(long)i |
||
395 | { |
||
396 | if((self = [super init])) |
||
397 | { |
||
398 | bytesDone = 0; |
||
399 | maxLength = m; |
||
400 | timeout = t; |
||
401 | readLength = l; |
||
402 | term = [e copy]; |
||
403 | tag = i; |
||
404 | |||
405 | if (d) |
||
406 | { |
||
407 | buffer = d; |
||
408 | startOffset = s; |
||
409 | bufferOwner = NO; |
||
410 | originalBufferLength = [d length]; |
||
411 | } |
||
412 | else |
||
413 | { |
||
414 | if (readLength > 0) |
||
415 | buffer = [[NSMutableData alloc] initWithLength:readLength]; |
||
416 | else |
||
417 | buffer = [[NSMutableData alloc] initWithLength:0]; |
||
418 | |||
419 | startOffset = 0; |
||
420 | bufferOwner = YES; |
||
421 | originalBufferLength = 0; |
||
422 | } |
||
423 | } |
||
424 | return self; |
||
425 | } |
||
426 | |||
427 | /** |
||
428 | * Increases the length of the buffer (if needed) to ensure a read of the given size will fit. |
||
429 | **/ |
||
430 | - (void)ensureCapacityForAdditionalDataOfLength:(NSUInteger)bytesToRead |
||
431 | { |
||
432 | NSUInteger buffSize = [buffer length]; |
||
433 | NSUInteger buffUsed = startOffset + bytesDone; |
||
434 | |||
435 | NSUInteger buffSpace = buffSize - buffUsed; |
||
436 | |||
437 | if (bytesToRead > buffSpace) |
||
438 | { |
||
439 | NSUInteger buffInc = bytesToRead - buffSpace; |
||
440 | |||
441 | [buffer increaseLengthBy:buffInc]; |
||
442 | } |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * This method is used when we do NOT know how much data is available to be read from the socket. |
||
447 | * This method returns the default value unless it exceeds the specified readLength or maxLength. |
||
448 | * |
||
449 | * Furthermore, the shouldPreBuffer decision is based upon the packet type, |
||
450 | * and whether the returned value would fit in the current buffer without requiring a resize of the buffer. |
||
451 | **/ |
||
452 | - (NSUInteger)optimalReadLengthWithDefault:(NSUInteger)defaultValue shouldPreBuffer:(BOOL *)shouldPreBufferPtr |
||
453 | { |
||
454 | NSUInteger result; |
||
455 | |||
456 | if (readLength > 0) |
||
457 | { |
||
458 | // Read a specific length of data |
||
459 | result = readLength - bytesDone; |
||
460 | |||
461 | // There is no need to prebuffer since we know exactly how much data we need to read. |
||
462 | // Even if the buffer isn't currently big enough to fit this amount of data, |
||
463 | // it would have to be resized eventually anyway. |
||
464 | |||
465 | if (shouldPreBufferPtr) |
||
466 | *shouldPreBufferPtr = NO; |
||
467 | } |
||
468 | else |
||
469 | { |
||
470 | // Either reading until we find a specified terminator, |
||
471 | // or we're simply reading all available data. |
||
472 | // |
||
473 | // In other words, one of: |
||
474 | // |
||
475 | // - readDataToData packet |
||
476 | // - readDataWithTimeout packet |
||
477 | |||
478 | if (maxLength > 0) |
||
479 | result = MIN(defaultValue, (maxLength - bytesDone)); |
||
480 | else |
||
481 | result = defaultValue; |
||
482 | |||
483 | // Since we don't know the size of the read in advance, |
||
484 | // the shouldPreBuffer decision is based upon whether the returned value would fit |
||
485 | // in the current buffer without requiring a resize of the buffer. |
||
486 | // |
||
487 | // This is because, in all likelyhood, the amount read from the socket will be less than the default value. |
||
488 | // Thus we should avoid over-allocating the read buffer when we can simply use the pre-buffer instead. |
||
489 | |||
490 | if (shouldPreBufferPtr) |
||
491 | { |
||
492 | NSUInteger buffSize = [buffer length]; |
||
493 | NSUInteger buffUsed = startOffset + bytesDone; |
||
494 | |||
495 | NSUInteger buffSpace = buffSize - buffUsed; |
||
496 | |||
497 | if (buffSpace >= result) |
||
498 | *shouldPreBufferPtr = NO; |
||
499 | else |
||
500 | *shouldPreBufferPtr = YES; |
||
501 | } |
||
502 | } |
||
503 | |||
504 | return result; |
||
505 | } |
||
506 | |||
507 | /** |
||
508 | * For read packets without a set terminator, returns the amount of data |
||
509 | * that can be read without exceeding the readLength or maxLength. |
||
510 | * |
||
511 | * The given parameter indicates the number of bytes estimated to be available on the socket, |
||
512 | * which is taken into consideration during the calculation. |
||
513 | * |
||
514 | * The given hint MUST be greater than zero. |
||
515 | **/ |
||
516 | - (NSUInteger)readLengthForNonTermWithHint:(NSUInteger)bytesAvailable |
||
517 | { |
||
518 | NSAssert(term == nil, @"This method does not apply to term reads"); |
||
519 | NSAssert(bytesAvailable > 0, @"Invalid parameter: bytesAvailable"); |
||
520 | |||
521 | if (readLength > 0) |
||
522 | { |
||
523 | // Read a specific length of data |
||
524 | |||
525 | return MIN(bytesAvailable, (readLength - bytesDone)); |
||
526 | |||
527 | // No need to avoid resizing the buffer. |
||
528 | // If the user provided their own buffer, |
||
529 | // and told us to read a certain length of data that exceeds the size of the buffer, |
||
530 | // then it is clear that our code will resize the buffer during the read operation. |
||
531 | // |
||
532 | // This method does not actually do any resizing. |
||
533 | // The resizing will happen elsewhere if needed. |
||
534 | } |
||
535 | else |
||
536 | { |
||
537 | // Read all available data |
||
538 | |||
539 | NSUInteger result = bytesAvailable; |
||
540 | |||
541 | if (maxLength > 0) |
||
542 | { |
||
543 | result = MIN(result, (maxLength - bytesDone)); |
||
544 | } |
||
545 | |||
546 | // No need to avoid resizing the buffer. |
||
547 | // If the user provided their own buffer, |
||
548 | // and told us to read all available data without giving us a maxLength, |
||
549 | // then it is clear that our code might resize the buffer during the read operation. |
||
550 | // |
||
551 | // This method does not actually do any resizing. |
||
552 | // The resizing will happen elsewhere if needed. |
||
553 | |||
554 | return result; |
||
555 | } |
||
556 | } |
||
557 | |||
558 | /** |
||
559 | * For read packets with a set terminator, returns the amount of data |
||
560 | * that can be read without exceeding the maxLength. |
||
561 | * |
||
562 | * The given parameter indicates the number of bytes estimated to be available on the socket, |
||
563 | * which is taken into consideration during the calculation. |
||
564 | * |
||
565 | * To optimize memory allocations, mem copies, and mem moves |
||
566 | * the shouldPreBuffer boolean value will indicate if the data should be read into a prebuffer first, |
||
567 | * or if the data can be read directly into the read packet's buffer. |
||
568 | **/ |
||
569 | - (NSUInteger)readLengthForTermWithHint:(NSUInteger)bytesAvailable shouldPreBuffer:(BOOL *)shouldPreBufferPtr |
||
570 | { |
||
571 | NSAssert(term != nil, @"This method does not apply to non-term reads"); |
||
572 | NSAssert(bytesAvailable > 0, @"Invalid parameter: bytesAvailable"); |
||
573 | |||
574 | |||
575 | NSUInteger result = bytesAvailable; |
||
576 | |||
577 | if (maxLength > 0) |
||
578 | { |
||
579 | result = MIN(result, (maxLength - bytesDone)); |
||
580 | } |
||
581 | |||
582 | // Should the data be read into the read packet's buffer, or into a pre-buffer first? |
||
583 | // |
||
584 | // One would imagine the preferred option is the faster one. |
||
585 | // So which one is faster? |
||
586 | // |
||
587 | // Reading directly into the packet's buffer requires: |
||
588 | // 1. Possibly resizing packet buffer (malloc/realloc) |
||
589 | // 2. Filling buffer (read) |
||
590 | // 3. Searching for term (memcmp) |
||
591 | // 4. Possibly copying overflow into prebuffer (malloc/realloc, memcpy) |
||
592 | // |
||
593 | // Reading into prebuffer first: |
||
594 | // 1. Possibly resizing prebuffer (malloc/realloc) |
||
595 | // 2. Filling buffer (read) |
||
596 | // 3. Searching for term (memcmp) |
||
597 | // 4. Copying underflow into packet buffer (malloc/realloc, memcpy) |
||
598 | // 5. Removing underflow from prebuffer (memmove) |
||
599 | // |
||
600 | // Comparing the performance of the two we can see that reading |
||
601 | // data into the prebuffer first is slower due to the extra memove. |
||
602 | // |
||
603 | // However: |
||
604 | // The implementation of NSMutableData is open source via core foundation's CFMutableData. |
||
605 | // Decreasing the length of a mutable data object doesn't cause a realloc. |
||
606 | // In other words, the capacity of a mutable data object can grow, but doesn't shrink. |
||
607 | // |
||
608 | // This means the prebuffer will rarely need a realloc. |
||
609 | // The packet buffer, on the other hand, may often need a realloc. |
||
610 | // This is especially true if we are the buffer owner. |
||
611 | // Furthermore, if we are constantly realloc'ing the packet buffer, |
||
612 | // and then moving the overflow into the prebuffer, |
||
613 | // then we're consistently over-allocating memory for each term read. |
||
614 | // And now we get into a bit of a tradeoff between speed and memory utilization. |
||
615 | // |
||
616 | // The end result is that the two perform very similarly. |
||
617 | // And we can answer the original question very simply by another means. |
||
618 | // |
||
619 | // If we can read all the data directly into the packet's buffer without resizing it first, |
||
620 | // then we do so. Otherwise we use the prebuffer. |
||
621 | |||
622 | if (shouldPreBufferPtr) |
||
623 | { |
||
624 | NSUInteger buffSize = [buffer length]; |
||
625 | NSUInteger buffUsed = startOffset + bytesDone; |
||
626 | |||
627 | if ((buffSize - buffUsed) >= result) |
||
628 | *shouldPreBufferPtr = NO; |
||
629 | else |
||
630 | *shouldPreBufferPtr = YES; |
||
631 | } |
||
632 | |||
633 | return result; |
||
634 | } |
||
635 | |||
636 | /** |
||
637 | * For read packets with a set terminator, |
||
638 | * returns the amount of data that can be read from the given preBuffer, |
||
639 | * without going over a terminator or the maxLength. |
||
640 | * |
||
641 | * It is assumed the terminator has not already been read. |
||
642 | **/ |
||
643 | - (NSUInteger)readLengthForTermWithPreBuffer:(GCDAsyncSocketPreBuffer *)preBuffer found:(BOOL *)foundPtr |
||
644 | { |
||
645 | NSAssert(term != nil, @"This method does not apply to non-term reads"); |
||
646 | NSAssert([preBuffer availableBytes] > 0, @"Invoked with empty pre buffer!"); |
||
647 | |||
648 | // We know that the terminator, as a whole, doesn't exist in our own buffer. |
||
649 | // But it is possible that a _portion_ of it exists in our buffer. |
||
650 | // So we're going to look for the terminator starting with a portion of our own buffer. |
||
651 | // |
||
652 | // Example: |
||
653 | // |
||
654 | // term length = 3 bytes |
||
655 | // bytesDone = 5 bytes |
||
656 | // preBuffer length = 5 bytes |
||
657 | // |
||
658 | // If we append the preBuffer to our buffer, |
||
659 | // it would look like this: |
||
660 | // |
||
661 | // --------------------- |
||
662 | // |B|B|B|B|B|P|P|P|P|P| |
||
663 | // --------------------- |
||
664 | // |
||
665 | // So we start our search here: |
||
666 | // |
||
667 | // --------------------- |
||
668 | // |B|B|B|B|B|P|P|P|P|P| |
||
669 | // -------^-^-^--------- |
||
670 | // |
||
671 | // And move forwards... |
||
672 | // |
||
673 | // --------------------- |
||
674 | // |B|B|B|B|B|P|P|P|P|P| |
||
675 | // ---------^-^-^------- |
||
676 | // |
||
677 | // Until we find the terminator or reach the end. |
||
678 | // |
||
679 | // --------------------- |
||
680 | // |B|B|B|B|B|P|P|P|P|P| |
||
681 | // ---------------^-^-^- |
||
682 | |||
683 | BOOL found = NO; |
||
684 | |||
685 | NSUInteger termLength = [term length]; |
||
686 | NSUInteger preBufferLength = [preBuffer availableBytes]; |
||
687 | |||
688 | if ((bytesDone + preBufferLength) < termLength) |
||
689 | { |
||
690 | // Not enough data for a full term sequence yet |
||
691 | return preBufferLength; |
||
692 | } |
||
693 | |||
694 | NSUInteger maxPreBufferLength; |
||
695 | if (maxLength > 0) { |
||
696 | maxPreBufferLength = MIN(preBufferLength, (maxLength - bytesDone)); |
||
697 | |||
698 | // Note: maxLength >= termLength |
||
699 | } |
||
700 | else { |
||
701 | maxPreBufferLength = preBufferLength; |
||
702 | } |
||
703 | |||
704 | uint8_t seq[termLength]; |
||
705 | const void *termBuf = [term bytes]; |
||
706 | |||
707 | NSUInteger bufLen = MIN(bytesDone, (termLength - 1)); |
||
708 | uint8_t *buf = (uint8_t *)[buffer mutableBytes] + startOffset + bytesDone - bufLen; |
||
709 | |||
710 | NSUInteger preLen = termLength - bufLen; |
||
711 | const uint8_t *pre = [preBuffer readBuffer]; |
||
712 | |||
713 | NSUInteger loopCount = bufLen + maxPreBufferLength - termLength + 1; // Plus one. See example above. |
||
714 | |||
715 | NSUInteger result = maxPreBufferLength; |
||
716 | |||
717 | NSUInteger i; |
||
718 | for (i = 0; i < loopCount; i++) |
||
719 | { |
||
720 | if (bufLen > 0) |
||
721 | { |
||
722 | // Combining bytes from buffer and preBuffer |
||
723 | |||
724 | memcpy(seq, buf, bufLen); |
||
725 | memcpy(seq + bufLen, pre, preLen); |
||
726 | |||
727 | if (memcmp(seq, termBuf, termLength) == 0) |
||
728 | { |
||
729 | result = preLen; |
||
730 | found = YES; |
||
731 | break; |
||
732 | } |
||
733 | |||
734 | buf++; |
||
735 | bufLen--; |
||
736 | preLen++; |
||
737 | } |
||
738 | else |
||
739 | { |
||
740 | // Comparing directly from preBuffer |
||
741 | |||
742 | if (memcmp(pre, termBuf, termLength) == 0) |
||
743 | { |
||
744 | NSUInteger preOffset = pre - [preBuffer readBuffer]; // pointer arithmetic |
||
745 | |||
746 | result = preOffset + termLength; |
||
747 | found = YES; |
||
748 | break; |
||
749 | } |
||
750 | |||
751 | pre++; |
||
752 | } |
||
753 | } |
||
754 | |||
755 | // There is no need to avoid resizing the buffer in this particular situation. |
||
756 | |||
757 | if (foundPtr) *foundPtr = found; |
||
758 | return result; |
||
759 | } |
||
760 | |||
761 | /** |
||
762 | * For read packets with a set terminator, scans the packet buffer for the term. |
||
763 | * It is assumed the terminator had not been fully read prior to the new bytes. |
||
764 | * |
||
765 | * If the term is found, the number of excess bytes after the term are returned. |
||
766 | * If the term is not found, this method will return -1. |
||
767 | * |
||
768 | * Note: A return value of zero means the term was found at the very end. |
||
769 | * |
||
770 | * Prerequisites: |
||
771 | * The given number of bytes have been added to the end of our buffer. |
||
772 | * Our bytesDone variable has NOT been changed due to the prebuffered bytes. |
||
773 | **/ |
||
774 | - (NSInteger)searchForTermAfterPreBuffering:(ssize_t)numBytes |
||
775 | { |
||
776 | NSAssert(term != nil, @"This method does not apply to non-term reads"); |
||
777 | |||
778 | // The implementation of this method is very similar to the above method. |
||
779 | // See the above method for a discussion of the algorithm used here. |
||
780 | |||
781 | uint8_t *buff = [buffer mutableBytes]; |
||
782 | NSUInteger buffLength = bytesDone + numBytes; |
||
783 | |||
784 | const void *termBuff = [term bytes]; |
||
785 | NSUInteger termLength = [term length]; |
||
786 | |||
787 | // Note: We are dealing with unsigned integers, |
||
788 | // so make sure the math doesn't go below zero. |
||
789 | |||
790 | NSUInteger i = ((buffLength - numBytes) >= termLength) ? (buffLength - numBytes - termLength + 1) : 0; |
||
791 | |||
792 | while (i + termLength <= buffLength) |
||
793 | { |
||
794 | uint8_t *subBuffer = buff + startOffset + i; |
||
795 | |||
796 | if (memcmp(subBuffer, termBuff, termLength) == 0) |
||
797 | { |
||
798 | return buffLength - (i + termLength); |
||
799 | } |
||
800 | |||
801 | i++; |
||
802 | } |
||
803 | |||
804 | return -1; |
||
805 | } |
||
806 | |||
807 | |||
808 | @end |
||
809 | |||
810 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
811 | #pragma mark - |
||
812 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
813 | |||
814 | /** |
||
815 | * The GCDAsyncWritePacket encompasses the instructions for any given write. |
||
816 | **/ |
||
817 | @interface GCDAsyncWritePacket : NSObject |
||
818 | { |
||
819 | @public |
||
820 | NSData *buffer; |
||
821 | NSUInteger bytesDone; |
||
822 | long tag; |
||
823 | NSTimeInterval timeout; |
||
824 | } |
||
825 | - (instancetype)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i NS_DESIGNATED_INITIALIZER; |
||
826 | @end |
||
827 | |||
828 | @implementation GCDAsyncWritePacket |
||
829 | |||
830 | // Cover the superclass' designated initializer |
||
831 | - (instancetype)init NS_UNAVAILABLE |
||
832 | { |
||
833 | NSAssert(0, @"Use the designated initializer"); |
||
834 | return nil; |
||
835 | } |
||
836 | |||
837 | - (instancetype)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i |
||
838 | { |
||
839 | if((self = [super init])) |
||
840 | { |
||
841 | buffer = d; // Retain not copy. For performance as documented in header file. |
||
842 | bytesDone = 0; |
||
843 | timeout = t; |
||
844 | tag = i; |
||
845 | } |
||
846 | return self; |
||
847 | } |
||
848 | |||
849 | |||
850 | @end |
||
851 | |||
852 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
853 | #pragma mark - |
||
854 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
855 | |||
856 | /** |
||
857 | * The GCDAsyncSpecialPacket encompasses special instructions for interruptions in the read/write queues. |
||
858 | * This class my be altered to support more than just TLS in the future. |
||
859 | **/ |
||
860 | @interface GCDAsyncSpecialPacket : NSObject |
||
861 | { |
||
862 | @public |
||
863 | NSDictionary *tlsSettings; |
||
864 | } |
||
865 | - (instancetype)initWithTLSSettings:(NSDictionary <NSString*,NSObject*>*)settings NS_DESIGNATED_INITIALIZER; |
||
866 | @end |
||
867 | |||
868 | @implementation GCDAsyncSpecialPacket |
||
869 | |||
870 | // Cover the superclass' designated initializer |
||
871 | - (instancetype)init NS_UNAVAILABLE |
||
872 | { |
||
873 | NSAssert(0, @"Use the designated initializer"); |
||
874 | return nil; |
||
875 | } |
||
876 | |||
877 | - (instancetype)initWithTLSSettings:(NSDictionary <NSString*,NSObject*>*)settings |
||
878 | { |
||
879 | if((self = [super init])) |
||
880 | { |
||
881 | tlsSettings = [settings copy]; |
||
882 | } |
||
883 | return self; |
||
884 | } |
||
885 | |||
886 | |||
887 | @end |
||
888 | |||
889 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
890 | #pragma mark - |
||
891 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
892 | |||
893 | @implementation GCDAsyncSocket |
||
894 | { |
||
895 | uint32_t flags; |
||
896 | uint16_t config; |
||
897 | |||
898 | __weak id<GCDAsyncSocketDelegate> delegate; |
||
899 | dispatch_queue_t delegateQueue; |
||
900 | |||
901 | int socket4FD; |
||
902 | int socket6FD; |
||
903 | int socketUN; |
||
904 | NSURL *socketUrl; |
||
905 | int stateIndex; |
||
906 | NSData * connectInterface4; |
||
907 | NSData * connectInterface6; |
||
908 | NSData * connectInterfaceUN; |
||
909 | |||
910 | dispatch_queue_t socketQueue; |
||
911 | |||
912 | dispatch_source_t accept4Source; |
||
913 | dispatch_source_t accept6Source; |
||
914 | dispatch_source_t acceptUNSource; |
||
915 | dispatch_source_t connectTimer; |
||
916 | dispatch_source_t readSource; |
||
917 | dispatch_source_t writeSource; |
||
918 | dispatch_source_t readTimer; |
||
919 | dispatch_source_t writeTimer; |
||
920 | |||
921 | NSMutableArray *readQueue; |
||
922 | NSMutableArray *writeQueue; |
||
923 | |||
924 | GCDAsyncReadPacket *currentRead; |
||
925 | GCDAsyncWritePacket *currentWrite; |
||
926 | |||
927 | unsigned long socketFDBytesAvailable; |
||
928 | |||
929 | GCDAsyncSocketPreBuffer *preBuffer; |
||
930 | |||
931 | #if TARGET_OS_IPHONE |
||
932 | CFStreamClientContext streamContext; |
||
933 | CFReadStreamRef readStream; |
||
934 | CFWriteStreamRef writeStream; |
||
935 | #endif |
||
936 | SSLContextRef sslContext; |
||
937 | GCDAsyncSocketPreBuffer *sslPreBuffer; |
||
938 | size_t sslWriteCachedLength; |
||
939 | OSStatus sslErrCode; |
||
940 | OSStatus lastSSLHandshakeError; |
||
941 | |||
942 | void *IsOnSocketQueueOrTargetQueueKey; |
||
943 | |||
944 | id userData; |
||
945 | NSTimeInterval alternateAddressDelay; |
||
946 | } |
||
947 | |||
948 | - (instancetype)init |
||
949 | { |
||
950 | return [self initWithDelegate:nil delegateQueue:NULL socketQueue:NULL]; |
||
951 | } |
||
952 | |||
953 | - (instancetype)initWithSocketQueue:(dispatch_queue_t)sq |
||
954 | { |
||
955 | return [self initWithDelegate:nil delegateQueue:NULL socketQueue:sq]; |
||
956 | } |
||
957 | |||
958 | - (instancetype)initWithDelegate:(id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(dispatch_queue_t)dq |
||
959 | { |
||
960 | return [self initWithDelegate:aDelegate delegateQueue:dq socketQueue:NULL]; |
||
961 | } |
||
962 | |||
963 | - (instancetype)initWithDelegate:(id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq |
||
964 | { |
||
965 | if((self = [super init])) |
||
966 | { |
||
967 | delegate = aDelegate; |
||
968 | delegateQueue = dq; |
||
969 | |||
970 | #if !OS_OBJECT_USE_OBJC |
||
971 | if (dq) dispatch_retain(dq); |
||
972 | #endif |
||
973 | |||
974 | socket4FD = SOCKET_NULL; |
||
975 | socket6FD = SOCKET_NULL; |
||
976 | socketUN = SOCKET_NULL; |
||
977 | socketUrl = nil; |
||
978 | stateIndex = 0; |
||
979 | |||
980 | if (sq) |
||
981 | { |
||
982 | NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), |
||
983 | @"The given socketQueue parameter must not be a concurrent queue."); |
||
984 | NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), |
||
985 | @"The given socketQueue parameter must not be a concurrent queue."); |
||
986 | NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), |
||
987 | @"The given socketQueue parameter must not be a concurrent queue."); |
||
988 | |||
989 | socketQueue = sq; |
||
990 | #if !OS_OBJECT_USE_OBJC |
||
991 | dispatch_retain(sq); |
||
992 | #endif |
||
993 | } |
||
994 | else |
||
995 | { |
||
996 | socketQueue = dispatch_queue_create([GCDAsyncSocketQueueName UTF8String], NULL); |
||
997 | } |
||
998 | |||
999 | // The dispatch_queue_set_specific() and dispatch_get_specific() functions take a "void *key" parameter. |
||
1000 | // From the documentation: |
||
1001 | // |
||
1002 | // > Keys are only compared as pointers and are never dereferenced. |
||
1003 | // > Thus, you can use a pointer to a static variable for a specific subsystem or |
||
1004 | // > any other value that allows you to identify the value uniquely. |
||
1005 | // |
||
1006 | // We're just going to use the memory address of an ivar. |
||
1007 | // Specifically an ivar that is explicitly named for our purpose to make the code more readable. |
||
1008 | // |
||
1009 | // However, it feels tedious (and less readable) to include the "&" all the time: |
||
1010 | // dispatch_get_specific(&IsOnSocketQueueOrTargetQueueKey) |
||
1011 | // |
||
1012 | // So we're going to make it so it doesn't matter if we use the '&' or not, |
||
1013 | // by assigning the value of the ivar to the address of the ivar. |
||
1014 | // Thus: IsOnSocketQueueOrTargetQueueKey == &IsOnSocketQueueOrTargetQueueKey; |
||
1015 | |||
1016 | IsOnSocketQueueOrTargetQueueKey = &IsOnSocketQueueOrTargetQueueKey; |
||
1017 | |||
1018 | void *nonNullUnusedPointer = (__bridge void *)self; |
||
1019 | dispatch_queue_set_specific(socketQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL); |
||
1020 | |||
1021 | readQueue = [[NSMutableArray alloc] initWithCapacity:5]; |
||
1022 | currentRead = nil; |
||
1023 | |||
1024 | writeQueue = [[NSMutableArray alloc] initWithCapacity:5]; |
||
1025 | currentWrite = nil; |
||
1026 | |||
1027 | preBuffer = [[GCDAsyncSocketPreBuffer alloc] initWithCapacity:(1024 * 4)]; |
||
1028 | alternateAddressDelay = 0.3; |
||
1029 | } |
||
1030 | return self; |
||
1031 | } |
||
1032 | |||
1033 | - (void)dealloc |
||
1034 | { |
||
1035 | LogInfo(@"%@ - %@ (start)", THIS_METHOD, self); |
||
1036 | |||
1037 | // Set dealloc flag. |
||
1038 | // This is used by closeWithError to ensure we don't accidentally retain ourself. |
||
1039 | flags |= kDealloc; |
||
1040 | |||
1041 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1042 | { |
||
1043 | [self closeWithError:nil]; |
||
1044 | } |
||
1045 | else |
||
1046 | { |
||
1047 | dispatch_sync(socketQueue, ^{ |
||
1048 | [self closeWithError:nil]; |
||
1049 | }); |
||
1050 | } |
||
1051 | |||
1052 | delegate = nil; |
||
1053 | |||
1054 | #if !OS_OBJECT_USE_OBJC |
||
1055 | if (delegateQueue) dispatch_release(delegateQueue); |
||
1056 | #endif |
||
1057 | delegateQueue = NULL; |
||
1058 | |||
1059 | #if !OS_OBJECT_USE_OBJC |
||
1060 | if (socketQueue) dispatch_release(socketQueue); |
||
1061 | #endif |
||
1062 | socketQueue = NULL; |
||
1063 | |||
1064 | LogInfo(@"%@ - %@ (finish)", THIS_METHOD, self); |
||
1065 | } |
||
1066 | |||
1067 | #pragma mark - |
||
1068 | |||
1069 | + (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD socketQueue:(nullable dispatch_queue_t)sq error:(NSError**)error { |
||
1070 | return [self socketFromConnectedSocketFD:socketFD delegate:nil delegateQueue:NULL socketQueue:sq error:error]; |
||
1071 | } |
||
1072 | |||
1073 | + (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD delegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq error:(NSError**)error { |
||
1074 | return [self socketFromConnectedSocketFD:socketFD delegate:aDelegate delegateQueue:dq socketQueue:NULL error:error]; |
||
1075 | } |
||
1076 | |||
1077 | + (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD delegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq error:(NSError* __autoreleasing *)error |
||
1078 | { |
||
1079 | __block BOOL errorOccured = NO; |
||
1080 | |||
1081 | GCDAsyncSocket *socket = [[[self class] alloc] initWithDelegate:aDelegate delegateQueue:dq socketQueue:sq]; |
||
1082 | |||
1083 | dispatch_sync(socket->socketQueue, ^{ @autoreleasepool { |
||
1084 | struct sockaddr addr; |
||
1085 | socklen_t addr_size = sizeof(struct sockaddr); |
||
1086 | int retVal = getpeername(socketFD, (struct sockaddr *)&addr, &addr_size); |
||
1087 | if (retVal) |
||
1088 | { |
||
1089 | NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketOtherError", |
||
1090 | @"GCDAsyncSocket", [NSBundle mainBundle], |
||
1091 | @"Attempt to create socket from socket FD failed. getpeername() failed", nil); |
||
1092 | |||
1093 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; |
||
1094 | |||
1095 | errorOccured = YES; |
||
1096 | if (error) |
||
1097 | *error = [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketOtherError userInfo:userInfo]; |
||
1098 | return; |
||
1099 | } |
||
1100 | |||
1101 | if (addr.sa_family == AF_INET) |
||
1102 | { |
||
1103 | socket->socket4FD = socketFD; |
||
1104 | } |
||
1105 | else if (addr.sa_family == AF_INET6) |
||
1106 | { |
||
1107 | socket->socket6FD = socketFD; |
||
1108 | } |
||
1109 | else |
||
1110 | { |
||
1111 | NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketOtherError", |
||
1112 | @"GCDAsyncSocket", [NSBundle mainBundle], |
||
1113 | @"Attempt to create socket from socket FD failed. socket FD is neither IPv4 nor IPv6", nil); |
||
1114 | |||
1115 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; |
||
1116 | |||
1117 | errorOccured = YES; |
||
1118 | if (error) |
||
1119 | *error = [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketOtherError userInfo:userInfo]; |
||
1120 | return; |
||
1121 | } |
||
1122 | |||
1123 | socket->flags = kSocketStarted; |
||
1124 | [socket didConnect:socket->stateIndex]; |
||
1125 | }}); |
||
1126 | |||
1127 | return errorOccured? nil: socket; |
||
1128 | } |
||
1129 | |||
1130 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
1131 | #pragma mark Configuration |
||
1132 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
1133 | |||
1134 | - (id)delegate |
||
1135 | { |
||
1136 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1137 | { |
||
1138 | return delegate; |
||
1139 | } |
||
1140 | else |
||
1141 | { |
||
1142 | __block id result; |
||
1143 | |||
1144 | dispatch_sync(socketQueue, ^{ |
||
1145 | result = self->delegate; |
||
1146 | }); |
||
1147 | |||
1148 | return result; |
||
1149 | } |
||
1150 | } |
||
1151 | |||
1152 | - (void)setDelegate:(id)newDelegate synchronously:(BOOL)synchronously |
||
1153 | { |
||
1154 | dispatch_block_t block = ^{ |
||
1155 | self->delegate = newDelegate; |
||
1156 | }; |
||
1157 | |||
1158 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { |
||
1159 | block(); |
||
1160 | } |
||
1161 | else { |
||
1162 | if (synchronously) |
||
1163 | dispatch_sync(socketQueue, block); |
||
1164 | else |
||
1165 | dispatch_async(socketQueue, block); |
||
1166 | } |
||
1167 | } |
||
1168 | |||
1169 | - (void)setDelegate:(id<GCDAsyncSocketDelegate>)newDelegate |
||
1170 | { |
||
1171 | [self setDelegate:newDelegate synchronously:NO]; |
||
1172 | } |
||
1173 | |||
1174 | - (void)synchronouslySetDelegate:(id<GCDAsyncSocketDelegate>)newDelegate |
||
1175 | { |
||
1176 | [self setDelegate:newDelegate synchronously:YES]; |
||
1177 | } |
||
1178 | |||
1179 | - (dispatch_queue_t)delegateQueue |
||
1180 | { |
||
1181 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1182 | { |
||
1183 | return delegateQueue; |
||
1184 | } |
||
1185 | else |
||
1186 | { |
||
1187 | __block dispatch_queue_t result; |
||
1188 | |||
1189 | dispatch_sync(socketQueue, ^{ |
||
1190 | result = self->delegateQueue; |
||
1191 | }); |
||
1192 | |||
1193 | return result; |
||
1194 | } |
||
1195 | } |
||
1196 | |||
1197 | - (void)setDelegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously |
||
1198 | { |
||
1199 | dispatch_block_t block = ^{ |
||
1200 | |||
1201 | #if !OS_OBJECT_USE_OBJC |
||
1202 | if (self->delegateQueue) dispatch_release(self->delegateQueue); |
||
1203 | if (newDelegateQueue) dispatch_retain(newDelegateQueue); |
||
1204 | #endif |
||
1205 | |||
1206 | self->delegateQueue = newDelegateQueue; |
||
1207 | }; |
||
1208 | |||
1209 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { |
||
1210 | block(); |
||
1211 | } |
||
1212 | else { |
||
1213 | if (synchronously) |
||
1214 | dispatch_sync(socketQueue, block); |
||
1215 | else |
||
1216 | dispatch_async(socketQueue, block); |
||
1217 | } |
||
1218 | } |
||
1219 | |||
1220 | - (void)setDelegateQueue:(dispatch_queue_t)newDelegateQueue |
||
1221 | { |
||
1222 | [self setDelegateQueue:newDelegateQueue synchronously:NO]; |
||
1223 | } |
||
1224 | |||
1225 | - (void)synchronouslySetDelegateQueue:(dispatch_queue_t)newDelegateQueue |
||
1226 | { |
||
1227 | [self setDelegateQueue:newDelegateQueue synchronously:YES]; |
||
1228 | } |
||
1229 | |||
1230 | - (void)getDelegate:(id<GCDAsyncSocketDelegate> *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr |
||
1231 | { |
||
1232 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1233 | { |
||
1234 | if (delegatePtr) *delegatePtr = delegate; |
||
1235 | if (delegateQueuePtr) *delegateQueuePtr = delegateQueue; |
||
1236 | } |
||
1237 | else |
||
1238 | { |
||
1239 | __block id dPtr = NULL; |
||
1240 | __block dispatch_queue_t dqPtr = NULL; |
||
1241 | |||
1242 | dispatch_sync(socketQueue, ^{ |
||
1243 | dPtr = self->delegate; |
||
1244 | dqPtr = self->delegateQueue; |
||
1245 | }); |
||
1246 | |||
1247 | if (delegatePtr) *delegatePtr = dPtr; |
||
1248 | if (delegateQueuePtr) *delegateQueuePtr = dqPtr; |
||
1249 | } |
||
1250 | } |
||
1251 | |||
1252 | - (void)setDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously |
||
1253 | { |
||
1254 | dispatch_block_t block = ^{ |
||
1255 | |||
1256 | self->delegate = newDelegate; |
||
1257 | |||
1258 | #if !OS_OBJECT_USE_OBJC |
||
1259 | if (self->delegateQueue) dispatch_release(self->delegateQueue); |
||
1260 | if (newDelegateQueue) dispatch_retain(newDelegateQueue); |
||
1261 | #endif |
||
1262 | |||
1263 | self->delegateQueue = newDelegateQueue; |
||
1264 | }; |
||
1265 | |||
1266 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { |
||
1267 | block(); |
||
1268 | } |
||
1269 | else { |
||
1270 | if (synchronously) |
||
1271 | dispatch_sync(socketQueue, block); |
||
1272 | else |
||
1273 | dispatch_async(socketQueue, block); |
||
1274 | } |
||
1275 | } |
||
1276 | |||
1277 | - (void)setDelegate:(id<GCDAsyncSocketDelegate>)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue |
||
1278 | { |
||
1279 | [self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:NO]; |
||
1280 | } |
||
1281 | |||
1282 | - (void)synchronouslySetDelegate:(id<GCDAsyncSocketDelegate>)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue |
||
1283 | { |
||
1284 | [self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:YES]; |
||
1285 | } |
||
1286 | |||
1287 | - (BOOL)isIPv4Enabled |
||
1288 | { |
||
1289 | // Note: YES means kIPv4Disabled is OFF |
||
1290 | |||
1291 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1292 | { |
||
1293 | return ((config & kIPv4Disabled) == 0); |
||
1294 | } |
||
1295 | else |
||
1296 | { |
||
1297 | __block BOOL result; |
||
1298 | |||
1299 | dispatch_sync(socketQueue, ^{ |
||
1300 | result = ((self->config & kIPv4Disabled) == 0); |
||
1301 | }); |
||
1302 | |||
1303 | return result; |
||
1304 | } |
||
1305 | } |
||
1306 | |||
1307 | - (void)setIPv4Enabled:(BOOL)flag |
||
1308 | { |
||
1309 | // Note: YES means kIPv4Disabled is OFF |
||
1310 | |||
1311 | dispatch_block_t block = ^{ |
||
1312 | |||
1313 | if (flag) |
||
1314 | self->config &= ~kIPv4Disabled; |
||
1315 | else |
||
1316 | self->config |= kIPv4Disabled; |
||
1317 | }; |
||
1318 | |||
1319 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1320 | block(); |
||
1321 | else |
||
1322 | dispatch_async(socketQueue, block); |
||
1323 | } |
||
1324 | |||
1325 | - (BOOL)isIPv6Enabled |
||
1326 | { |
||
1327 | // Note: YES means kIPv6Disabled is OFF |
||
1328 | |||
1329 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1330 | { |
||
1331 | return ((config & kIPv6Disabled) == 0); |
||
1332 | } |
||
1333 | else |
||
1334 | { |
||
1335 | __block BOOL result; |
||
1336 | |||
1337 | dispatch_sync(socketQueue, ^{ |
||
1338 | result = ((self->config & kIPv6Disabled) == 0); |
||
1339 | }); |
||
1340 | |||
1341 | return result; |
||
1342 | } |
||
1343 | } |
||
1344 | |||
1345 | - (void)setIPv6Enabled:(BOOL)flag |
||
1346 | { |
||
1347 | // Note: YES means kIPv6Disabled is OFF |
||
1348 | |||
1349 | dispatch_block_t block = ^{ |
||
1350 | |||
1351 | if (flag) |
||
1352 | self->config &= ~kIPv6Disabled; |
||
1353 | else |
||
1354 | self->config |= kIPv6Disabled; |
||
1355 | }; |
||
1356 | |||
1357 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1358 | block(); |
||
1359 | else |
||
1360 | dispatch_async(socketQueue, block); |
||
1361 | } |
||
1362 | |||
1363 | - (BOOL)isIPv4PreferredOverIPv6 |
||
1364 | { |
||
1365 | // Note: YES means kPreferIPv6 is OFF |
||
1366 | |||
1367 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1368 | { |
||
1369 | return ((config & kPreferIPv6) == 0); |
||
1370 | } |
||
1371 | else |
||
1372 | { |
||
1373 | __block BOOL result; |
||
1374 | |||
1375 | dispatch_sync(socketQueue, ^{ |
||
1376 | result = ((self->config & kPreferIPv6) == 0); |
||
1377 | }); |
||
1378 | |||
1379 | return result; |
||
1380 | } |
||
1381 | } |
||
1382 | |||
1383 | - (void)setIPv4PreferredOverIPv6:(BOOL)flag |
||
1384 | { |
||
1385 | // Note: YES means kPreferIPv6 is OFF |
||
1386 | |||
1387 | dispatch_block_t block = ^{ |
||
1388 | |||
1389 | if (flag) |
||
1390 | self->config &= ~kPreferIPv6; |
||
1391 | else |
||
1392 | self->config |= kPreferIPv6; |
||
1393 | }; |
||
1394 | |||
1395 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1396 | block(); |
||
1397 | else |
||
1398 | dispatch_async(socketQueue, block); |
||
1399 | } |
||
1400 | |||
1401 | - (NSTimeInterval) alternateAddressDelay { |
||
1402 | __block NSTimeInterval delay; |
||
1403 | dispatch_block_t block = ^{ |
||
1404 | delay = self->alternateAddressDelay; |
||
1405 | }; |
||
1406 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1407 | block(); |
||
1408 | else |
||
1409 | dispatch_sync(socketQueue, block); |
||
1410 | return delay; |
||
1411 | } |
||
1412 | |||
1413 | - (void) setAlternateAddressDelay:(NSTimeInterval)delay { |
||
1414 | dispatch_block_t block = ^{ |
||
1415 | self->alternateAddressDelay = delay; |
||
1416 | }; |
||
1417 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1418 | block(); |
||
1419 | else |
||
1420 | dispatch_async(socketQueue, block); |
||
1421 | } |
||
1422 | |||
1423 | - (id)userData |
||
1424 | { |
||
1425 | __block id result = nil; |
||
1426 | |||
1427 | dispatch_block_t block = ^{ |
||
1428 | |||
1429 | result = self->userData; |
||
1430 | }; |
||
1431 | |||
1432 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1433 | block(); |
||
1434 | else |
||
1435 | dispatch_sync(socketQueue, block); |
||
1436 | |||
1437 | return result; |
||
1438 | } |
||
1439 | |||
1440 | - (void)setUserData:(id)arbitraryUserData |
||
1441 | { |
||
1442 | dispatch_block_t block = ^{ |
||
1443 | |||
1444 | if (self->userData != arbitraryUserData) |
||
1445 | { |
||
1446 | self->userData = arbitraryUserData; |
||
1447 | } |
||
1448 | }; |
||
1449 | |||
1450 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1451 | block(); |
||
1452 | else |
||
1453 | dispatch_async(socketQueue, block); |
||
1454 | } |
||
1455 | |||
1456 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
1457 | #pragma mark Accepting |
||
1458 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
1459 | |||
1460 | - (BOOL)acceptOnPort:(uint16_t)port error:(NSError **)errPtr |
||
1461 | { |
||
1462 | return [self acceptOnInterface:nil port:port error:errPtr]; |
||
1463 | } |
||
1464 | |||
1465 | - (BOOL)acceptOnInterface:(NSString *)inInterface port:(uint16_t)port error:(NSError **)errPtr |
||
1466 | { |
||
1467 | LogTrace(); |
||
1468 | |||
1469 | // Just in-case interface parameter is immutable. |
||
1470 | NSString *interface = [inInterface copy]; |
||
1471 | |||
1472 | __block BOOL result = NO; |
||
1473 | __block NSError *err = nil; |
||
1474 | |||
1475 | // CreateSocket Block |
||
1476 | // This block will be invoked within the dispatch block below. |
||
1477 | |||
1478 | int(^createSocket)(int, NSData*) = ^int (int domain, NSData *interfaceAddr) { |
||
1479 | |||
1480 | int socketFD = socket(domain, SOCK_STREAM, 0); |
||
1481 | |||
1482 | if (socketFD == SOCKET_NULL) |
||
1483 | { |
||
1484 | NSString *reason = @"Error in socket() function"; |
||
1485 | err = [self errorWithErrno:errno reason:reason]; |
||
1486 | |||
1487 | return SOCKET_NULL; |
||
1488 | } |
||
1489 | |||
1490 | int status; |
||
1491 | |||
1492 | // Set socket options |
||
1493 | |||
1494 | status = fcntl(socketFD, F_SETFL, O_NONBLOCK); |
||
1495 | if (status == -1) |
||
1496 | { |
||
1497 | NSString *reason = @"Error enabling non-blocking IO on socket (fcntl)"; |
||
1498 | err = [self errorWithErrno:errno reason:reason]; |
||
1499 | |||
1500 | LogVerbose(@"close(socketFD)"); |
||
1501 | close(socketFD); |
||
1502 | return SOCKET_NULL; |
||
1503 | } |
||
1504 | |||
1505 | int reuseOn = 1; |
||
1506 | status = setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); |
||
1507 | if (status == -1) |
||
1508 | { |
||
1509 | NSString *reason = @"Error enabling address reuse (setsockopt)"; |
||
1510 | err = [self errorWithErrno:errno reason:reason]; |
||
1511 | |||
1512 | LogVerbose(@"close(socketFD)"); |
||
1513 | close(socketFD); |
||
1514 | return SOCKET_NULL; |
||
1515 | } |
||
1516 | |||
1517 | // Bind socket |
||
1518 | |||
1519 | status = bind(socketFD, (const struct sockaddr *)[interfaceAddr bytes], (socklen_t)[interfaceAddr length]); |
||
1520 | if (status == -1) |
||
1521 | { |
||
1522 | NSString *reason = @"Error in bind() function"; |
||
1523 | err = [self errorWithErrno:errno reason:reason]; |
||
1524 | |||
1525 | LogVerbose(@"close(socketFD)"); |
||
1526 | close(socketFD); |
||
1527 | return SOCKET_NULL; |
||
1528 | } |
||
1529 | |||
1530 | // Listen |
||
1531 | |||
1532 | status = listen(socketFD, 1024); |
||
1533 | if (status == -1) |
||
1534 | { |
||
1535 | NSString *reason = @"Error in listen() function"; |
||
1536 | err = [self errorWithErrno:errno reason:reason]; |
||
1537 | |||
1538 | LogVerbose(@"close(socketFD)"); |
||
1539 | close(socketFD); |
||
1540 | return SOCKET_NULL; |
||
1541 | } |
||
1542 | |||
1543 | return socketFD; |
||
1544 | }; |
||
1545 | |||
1546 | // Create dispatch block and run on socketQueue |
||
1547 | |||
1548 | dispatch_block_t block = ^{ @autoreleasepool { |
||
1549 | |||
1550 | if (self->delegate == nil) // Must have delegate set |
||
1551 | { |
||
1552 | NSString *msg = @"Attempting to accept without a delegate. Set a delegate first."; |
||
1553 | err = [self badConfigError:msg]; |
||
1554 | |||
1555 | return_from_block; |
||
1556 | } |
||
1557 | |||
1558 | if (self->delegateQueue == NULL) // Must have delegate queue set |
||
1559 | { |
||
1560 | NSString *msg = @"Attempting to accept without a delegate queue. Set a delegate queue first."; |
||
1561 | err = [self badConfigError:msg]; |
||
1562 | |||
1563 | return_from_block; |
||
1564 | } |
||
1565 | |||
1566 | BOOL isIPv4Disabled = (self->config & kIPv4Disabled) ? YES : NO; |
||
1567 | BOOL isIPv6Disabled = (self->config & kIPv6Disabled) ? YES : NO; |
||
1568 | |||
1569 | if (isIPv4Disabled && isIPv6Disabled) // Must have IPv4 or IPv6 enabled |
||
1570 | { |
||
1571 | NSString *msg = @"Both IPv4 and IPv6 have been disabled. Must enable at least one protocol first."; |
||
1572 | err = [self badConfigError:msg]; |
||
1573 | |||
1574 | return_from_block; |
||
1575 | } |
||
1576 | |||
1577 | if (![self isDisconnected]) // Must be disconnected |
||
1578 | { |
||
1579 | NSString *msg = @"Attempting to accept while connected or accepting connections. Disconnect first."; |
||
1580 | err = [self badConfigError:msg]; |
||
1581 | |||
1582 | return_from_block; |
||
1583 | } |
||
1584 | |||
1585 | // Clear queues (spurious read/write requests post disconnect) |
||
1586 | [self->readQueue removeAllObjects]; |
||
1587 | [self->writeQueue removeAllObjects]; |
||
1588 | |||
1589 | // Resolve interface from description |
||
1590 | |||
1591 | NSMutableData *interface4 = nil; |
||
1592 | NSMutableData *interface6 = nil; |
||
1593 | |||
1594 | [self getInterfaceAddress4:&interface4 address6:&interface6 fromDescription:interface port:port]; |
||
1595 | |||
1596 | if ((interface4 == nil) && (interface6 == nil)) |
||
1597 | { |
||
1598 | NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; |
||
1599 | err = [self badParamError:msg]; |
||
1600 | |||
1601 | return_from_block; |
||
1602 | } |
||
1603 | |||
1604 | if (isIPv4Disabled && (interface6 == nil)) |
||
1605 | { |
||
1606 | NSString *msg = @"IPv4 has been disabled and specified interface doesn't support IPv6."; |
||
1607 | err = [self badParamError:msg]; |
||
1608 | |||
1609 | return_from_block; |
||
1610 | } |
||
1611 | |||
1612 | if (isIPv6Disabled && (interface4 == nil)) |
||
1613 | { |
||
1614 | NSString *msg = @"IPv6 has been disabled and specified interface doesn't support IPv4."; |
||
1615 | err = [self badParamError:msg]; |
||
1616 | |||
1617 | return_from_block; |
||
1618 | } |
||
1619 | |||
1620 | BOOL enableIPv4 = !isIPv4Disabled && (interface4 != nil); |
||
1621 | BOOL enableIPv6 = !isIPv6Disabled && (interface6 != nil); |
||
1622 | |||
1623 | // Create sockets, configure, bind, and listen |
||
1624 | |||
1625 | if (enableIPv4) |
||
1626 | { |
||
1627 | LogVerbose(@"Creating IPv4 socket"); |
||
1628 | self->socket4FD = createSocket(AF_INET, interface4); |
||
1629 | |||
1630 | if (self->socket4FD == SOCKET_NULL) |
||
1631 | { |
||
1632 | return_from_block; |
||
1633 | } |
||
1634 | } |
||
1635 | |||
1636 | if (enableIPv6) |
||
1637 | { |
||
1638 | LogVerbose(@"Creating IPv6 socket"); |
||
1639 | |||
1640 | if (enableIPv4 && (port == 0)) |
||
1641 | { |
||
1642 | // No specific port was specified, so we allowed the OS to pick an available port for us. |
||
1643 | // Now we need to make sure the IPv6 socket listens on the same port as the IPv4 socket. |
||
1644 | |||
1645 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)[interface6 mutableBytes]; |
||
1646 | addr6->sin6_port = htons([self localPort4]); |
||
1647 | } |
||
1648 | |||
1649 | self->socket6FD = createSocket(AF_INET6, interface6); |
||
1650 | |||
1651 | if (self->socket6FD == SOCKET_NULL) |
||
1652 | { |
||
1653 | if (self->socket4FD != SOCKET_NULL) |
||
1654 | { |
||
1655 | LogVerbose(@"close(socket4FD)"); |
||
1656 | close(self->socket4FD); |
||
1657 | self->socket4FD = SOCKET_NULL; |
||
1658 | } |
||
1659 | |||
1660 | return_from_block; |
||
1661 | } |
||
1662 | } |
||
1663 | |||
1664 | // Create accept sources |
||
1665 | |||
1666 | if (enableIPv4) |
||
1667 | { |
||
1668 | self->accept4Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, self->socket4FD, 0, self->socketQueue); |
||
1669 | |||
1670 | int socketFD = self->socket4FD; |
||
1671 | dispatch_source_t acceptSource = self->accept4Source; |
||
1672 | |||
1673 | __weak GCDAsyncSocket *weakSelf = self; |
||
1674 | |||
1675 | dispatch_source_set_event_handler(self->accept4Source, ^{ @autoreleasepool { |
||
1676 | #pragma clang diagnostic push |
||
1677 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
1678 | |||
1679 | __strong GCDAsyncSocket *strongSelf = weakSelf; |
||
1680 | if (strongSelf == nil) return_from_block; |
||
1681 | |||
1682 | LogVerbose(@"event4Block"); |
||
1683 | |||
1684 | unsigned long i = 0; |
||
1685 | unsigned long numPendingConnections = dispatch_source_get_data(acceptSource); |
||
1686 | |||
1687 | LogVerbose(@"numPendingConnections: %lu", numPendingConnections); |
||
1688 | |||
1689 | while ([strongSelf doAccept:socketFD] && (++i < numPendingConnections)); |
||
1690 | |||
1691 | #pragma clang diagnostic pop |
||
1692 | }}); |
||
1693 | |||
1694 | |||
1695 | dispatch_source_set_cancel_handler(self->accept4Source, ^{ |
||
1696 | #pragma clang diagnostic push |
||
1697 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
1698 | |||
1699 | #if !OS_OBJECT_USE_OBJC |
||
1700 | LogVerbose(@"dispatch_release(accept4Source)"); |
||
1701 | dispatch_release(acceptSource); |
||
1702 | #endif |
||
1703 | |||
1704 | LogVerbose(@"close(socket4FD)"); |
||
1705 | close(socketFD); |
||
1706 | |||
1707 | #pragma clang diagnostic pop |
||
1708 | }); |
||
1709 | |||
1710 | LogVerbose(@"dispatch_resume(accept4Source)"); |
||
1711 | dispatch_resume(self->accept4Source); |
||
1712 | } |
||
1713 | |||
1714 | if (enableIPv6) |
||
1715 | { |
||
1716 | self->accept6Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, self->socket6FD, 0, self->socketQueue); |
||
1717 | |||
1718 | int socketFD = self->socket6FD; |
||
1719 | dispatch_source_t acceptSource = self->accept6Source; |
||
1720 | |||
1721 | __weak GCDAsyncSocket *weakSelf = self; |
||
1722 | |||
1723 | dispatch_source_set_event_handler(self->accept6Source, ^{ @autoreleasepool { |
||
1724 | #pragma clang diagnostic push |
||
1725 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
1726 | |||
1727 | __strong GCDAsyncSocket *strongSelf = weakSelf; |
||
1728 | if (strongSelf == nil) return_from_block; |
||
1729 | |||
1730 | LogVerbose(@"event6Block"); |
||
1731 | |||
1732 | unsigned long i = 0; |
||
1733 | unsigned long numPendingConnections = dispatch_source_get_data(acceptSource); |
||
1734 | |||
1735 | LogVerbose(@"numPendingConnections: %lu", numPendingConnections); |
||
1736 | |||
1737 | while ([strongSelf doAccept:socketFD] && (++i < numPendingConnections)); |
||
1738 | |||
1739 | #pragma clang diagnostic pop |
||
1740 | }}); |
||
1741 | |||
1742 | dispatch_source_set_cancel_handler(self->accept6Source, ^{ |
||
1743 | #pragma clang diagnostic push |
||
1744 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
1745 | |||
1746 | #if !OS_OBJECT_USE_OBJC |
||
1747 | LogVerbose(@"dispatch_release(accept6Source)"); |
||
1748 | dispatch_release(acceptSource); |
||
1749 | #endif |
||
1750 | |||
1751 | LogVerbose(@"close(socket6FD)"); |
||
1752 | close(socketFD); |
||
1753 | |||
1754 | #pragma clang diagnostic pop |
||
1755 | }); |
||
1756 | |||
1757 | LogVerbose(@"dispatch_resume(accept6Source)"); |
||
1758 | dispatch_resume(self->accept6Source); |
||
1759 | } |
||
1760 | |||
1761 | self->flags |= kSocketStarted; |
||
1762 | |||
1763 | result = YES; |
||
1764 | }}; |
||
1765 | |||
1766 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1767 | block(); |
||
1768 | else |
||
1769 | dispatch_sync(socketQueue, block); |
||
1770 | |||
1771 | if (result == NO) |
||
1772 | { |
||
1773 | LogInfo(@"Error in accept: %@", err); |
||
1774 | |||
1775 | if (errPtr) |
||
1776 | *errPtr = err; |
||
1777 | } |
||
1778 | |||
1779 | return result; |
||
1780 | } |
||
1781 | |||
1782 | - (BOOL)acceptOnUrl:(NSURL *)url error:(NSError **)errPtr |
||
1783 | { |
||
1784 | LogTrace(); |
||
1785 | |||
1786 | __block BOOL result = NO; |
||
1787 | __block NSError *err = nil; |
||
1788 | |||
1789 | // CreateSocket Block |
||
1790 | // This block will be invoked within the dispatch block below. |
||
1791 | |||
1792 | int(^createSocket)(int, NSData*) = ^int (int domain, NSData *interfaceAddr) { |
||
1793 | |||
1794 | int socketFD = socket(domain, SOCK_STREAM, 0); |
||
1795 | |||
1796 | if (socketFD == SOCKET_NULL) |
||
1797 | { |
||
1798 | NSString *reason = @"Error in socket() function"; |
||
1799 | err = [self errorWithErrno:errno reason:reason]; |
||
1800 | |||
1801 | return SOCKET_NULL; |
||
1802 | } |
||
1803 | |||
1804 | int status; |
||
1805 | |||
1806 | // Set socket options |
||
1807 | |||
1808 | status = fcntl(socketFD, F_SETFL, O_NONBLOCK); |
||
1809 | if (status == -1) |
||
1810 | { |
||
1811 | NSString *reason = @"Error enabling non-blocking IO on socket (fcntl)"; |
||
1812 | err = [self errorWithErrno:errno reason:reason]; |
||
1813 | |||
1814 | LogVerbose(@"close(socketFD)"); |
||
1815 | close(socketFD); |
||
1816 | return SOCKET_NULL; |
||
1817 | } |
||
1818 | |||
1819 | int reuseOn = 1; |
||
1820 | status = setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); |
||
1821 | if (status == -1) |
||
1822 | { |
||
1823 | NSString *reason = @"Error enabling address reuse (setsockopt)"; |
||
1824 | err = [self errorWithErrno:errno reason:reason]; |
||
1825 | |||
1826 | LogVerbose(@"close(socketFD)"); |
||
1827 | close(socketFD); |
||
1828 | return SOCKET_NULL; |
||
1829 | } |
||
1830 | |||
1831 | // Bind socket |
||
1832 | |||
1833 | status = bind(socketFD, (const struct sockaddr *)[interfaceAddr bytes], (socklen_t)[interfaceAddr length]); |
||
1834 | if (status == -1) |
||
1835 | { |
||
1836 | NSString *reason = @"Error in bind() function"; |
||
1837 | err = [self errorWithErrno:errno reason:reason]; |
||
1838 | |||
1839 | LogVerbose(@"close(socketFD)"); |
||
1840 | close(socketFD); |
||
1841 | return SOCKET_NULL; |
||
1842 | } |
||
1843 | |||
1844 | // Listen |
||
1845 | |||
1846 | status = listen(socketFD, 1024); |
||
1847 | if (status == -1) |
||
1848 | { |
||
1849 | NSString *reason = @"Error in listen() function"; |
||
1850 | err = [self errorWithErrno:errno reason:reason]; |
||
1851 | |||
1852 | LogVerbose(@"close(socketFD)"); |
||
1853 | close(socketFD); |
||
1854 | return SOCKET_NULL; |
||
1855 | } |
||
1856 | |||
1857 | return socketFD; |
||
1858 | }; |
||
1859 | |||
1860 | // Create dispatch block and run on socketQueue |
||
1861 | |||
1862 | dispatch_block_t block = ^{ @autoreleasepool { |
||
1863 | |||
1864 | if (self->delegate == nil) // Must have delegate set |
||
1865 | { |
||
1866 | NSString *msg = @"Attempting to accept without a delegate. Set a delegate first."; |
||
1867 | err = [self badConfigError:msg]; |
||
1868 | |||
1869 | return_from_block; |
||
1870 | } |
||
1871 | |||
1872 | if (self->delegateQueue == NULL) // Must have delegate queue set |
||
1873 | { |
||
1874 | NSString *msg = @"Attempting to accept without a delegate queue. Set a delegate queue first."; |
||
1875 | err = [self badConfigError:msg]; |
||
1876 | |||
1877 | return_from_block; |
||
1878 | } |
||
1879 | |||
1880 | if (![self isDisconnected]) // Must be disconnected |
||
1881 | { |
||
1882 | NSString *msg = @"Attempting to accept while connected or accepting connections. Disconnect first."; |
||
1883 | err = [self badConfigError:msg]; |
||
1884 | |||
1885 | return_from_block; |
||
1886 | } |
||
1887 | |||
1888 | // Clear queues (spurious read/write requests post disconnect) |
||
1889 | [self->readQueue removeAllObjects]; |
||
1890 | [self->writeQueue removeAllObjects]; |
||
1891 | |||
1892 | // Remove a previous socket |
||
1893 | |||
1894 | NSError *error = nil; |
||
1895 | NSFileManager *fileManager = [NSFileManager defaultManager]; |
||
1896 | NSString *urlPath = url.path; |
||
1897 | if (urlPath && [fileManager fileExistsAtPath:urlPath]) { |
||
1898 | if (![fileManager removeItemAtURL:url error:&error]) { |
||
1899 | NSString *msg = @"Could not remove previous unix domain socket at given url."; |
||
1900 | err = [self otherError:msg]; |
||
1901 | |||
1902 | return_from_block; |
||
1903 | } |
||
1904 | } |
||
1905 | |||
1906 | // Resolve interface from description |
||
1907 | |||
1908 | NSData *interface = [self getInterfaceAddressFromUrl:url]; |
||
1909 | |||
1910 | if (interface == nil) |
||
1911 | { |
||
1912 | NSString *msg = @"Invalid unix domain url. Specify a valid file url that does not exist (e.g. \"file:///tmp/socket\")"; |
||
1913 | err = [self badParamError:msg]; |
||
1914 | |||
1915 | return_from_block; |
||
1916 | } |
||
1917 | |||
1918 | // Create sockets, configure, bind, and listen |
||
1919 | |||
1920 | LogVerbose(@"Creating unix domain socket"); |
||
1921 | self->socketUN = createSocket(AF_UNIX, interface); |
||
1922 | |||
1923 | if (self->socketUN == SOCKET_NULL) |
||
1924 | { |
||
1925 | return_from_block; |
||
1926 | } |
||
1927 | |||
1928 | self->socketUrl = url; |
||
1929 | |||
1930 | // Create accept sources |
||
1931 | |||
1932 | self->acceptUNSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, self->socketUN, 0, self->socketQueue); |
||
1933 | |||
1934 | int socketFD = self->socketUN; |
||
1935 | dispatch_source_t acceptSource = self->acceptUNSource; |
||
1936 | |||
1937 | __weak GCDAsyncSocket *weakSelf = self; |
||
1938 | |||
1939 | dispatch_source_set_event_handler(self->acceptUNSource, ^{ @autoreleasepool { |
||
1940 | |||
1941 | __strong GCDAsyncSocket *strongSelf = weakSelf; |
||
1942 | |||
1943 | LogVerbose(@"eventUNBlock"); |
||
1944 | |||
1945 | unsigned long i = 0; |
||
1946 | unsigned long numPendingConnections = dispatch_source_get_data(acceptSource); |
||
1947 | |||
1948 | LogVerbose(@"numPendingConnections: %lu", numPendingConnections); |
||
1949 | |||
1950 | while ([strongSelf doAccept:socketFD] && (++i < numPendingConnections)); |
||
1951 | }}); |
||
1952 | |||
1953 | dispatch_source_set_cancel_handler(self->acceptUNSource, ^{ |
||
1954 | |||
1955 | #if !OS_OBJECT_USE_OBJC |
||
1956 | LogVerbose(@"dispatch_release(acceptUNSource)"); |
||
1957 | dispatch_release(acceptSource); |
||
1958 | #endif |
||
1959 | |||
1960 | LogVerbose(@"close(socketUN)"); |
||
1961 | close(socketFD); |
||
1962 | }); |
||
1963 | |||
1964 | LogVerbose(@"dispatch_resume(acceptUNSource)"); |
||
1965 | dispatch_resume(self->acceptUNSource); |
||
1966 | |||
1967 | self->flags |= kSocketStarted; |
||
1968 | |||
1969 | result = YES; |
||
1970 | }}; |
||
1971 | |||
1972 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
1973 | block(); |
||
1974 | else |
||
1975 | dispatch_sync(socketQueue, block); |
||
1976 | |||
1977 | if (result == NO) |
||
1978 | { |
||
1979 | LogInfo(@"Error in accept: %@", err); |
||
1980 | |||
1981 | if (errPtr) |
||
1982 | *errPtr = err; |
||
1983 | } |
||
1984 | |||
1985 | return result; |
||
1986 | } |
||
1987 | |||
1988 | - (BOOL)doAccept:(int)parentSocketFD |
||
1989 | { |
||
1990 | LogTrace(); |
||
1991 | |||
1992 | int socketType; |
||
1993 | int childSocketFD; |
||
1994 | NSData *childSocketAddress; |
||
1995 | |||
1996 | if (parentSocketFD == socket4FD) |
||
1997 | { |
||
1998 | socketType = 0; |
||
1999 | |||
2000 | struct sockaddr_in addr; |
||
2001 | socklen_t addrLen = sizeof(addr); |
||
2002 | |||
2003 | childSocketFD = accept(parentSocketFD, (struct sockaddr *)&addr, &addrLen); |
||
2004 | |||
2005 | if (childSocketFD == -1) |
||
2006 | { |
||
2007 | LogWarn(@"Accept failed with error: %@", [self errnoError]); |
||
2008 | return NO; |
||
2009 | } |
||
2010 | |||
2011 | childSocketAddress = [NSData dataWithBytes:&addr length:addrLen]; |
||
2012 | } |
||
2013 | else if (parentSocketFD == socket6FD) |
||
2014 | { |
||
2015 | socketType = 1; |
||
2016 | |||
2017 | struct sockaddr_in6 addr; |
||
2018 | socklen_t addrLen = sizeof(addr); |
||
2019 | |||
2020 | childSocketFD = accept(parentSocketFD, (struct sockaddr *)&addr, &addrLen); |
||
2021 | |||
2022 | if (childSocketFD == -1) |
||
2023 | { |
||
2024 | LogWarn(@"Accept failed with error: %@", [self errnoError]); |
||
2025 | return NO; |
||
2026 | } |
||
2027 | |||
2028 | childSocketAddress = [NSData dataWithBytes:&addr length:addrLen]; |
||
2029 | } |
||
2030 | else // if (parentSocketFD == socketUN) |
||
2031 | { |
||
2032 | socketType = 2; |
||
2033 | |||
2034 | struct sockaddr_un addr; |
||
2035 | socklen_t addrLen = sizeof(addr); |
||
2036 | |||
2037 | childSocketFD = accept(parentSocketFD, (struct sockaddr *)&addr, &addrLen); |
||
2038 | |||
2039 | if (childSocketFD == -1) |
||
2040 | { |
||
2041 | LogWarn(@"Accept failed with error: %@", [self errnoError]); |
||
2042 | return NO; |
||
2043 | } |
||
2044 | |||
2045 | childSocketAddress = [NSData dataWithBytes:&addr length:addrLen]; |
||
2046 | } |
||
2047 | |||
2048 | // Enable non-blocking IO on the socket |
||
2049 | |||
2050 | int result = fcntl(childSocketFD, F_SETFL, O_NONBLOCK); |
||
2051 | if (result == -1) |
||
2052 | { |
||
2053 | LogWarn(@"Error enabling non-blocking IO on accepted socket (fcntl)"); |
||
2054 | LogVerbose(@"close(childSocketFD)"); |
||
2055 | close(childSocketFD); |
||
2056 | return NO; |
||
2057 | } |
||
2058 | |||
2059 | // Prevent SIGPIPE signals |
||
2060 | |||
2061 | int nosigpipe = 1; |
||
2062 | setsockopt(childSocketFD, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); |
||
2063 | |||
2064 | // Notify delegate |
||
2065 | |||
2066 | if (delegateQueue) |
||
2067 | { |
||
2068 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
2069 | |||
2070 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
2071 | |||
2072 | // Query delegate for custom socket queue |
||
2073 | |||
2074 | dispatch_queue_t childSocketQueue = NULL; |
||
2075 | |||
2076 | if ([theDelegate respondsToSelector:@selector(newSocketQueueForConnectionFromAddress:onSocket:)]) |
||
2077 | { |
||
2078 | childSocketQueue = [theDelegate newSocketQueueForConnectionFromAddress:childSocketAddress |
||
2079 | onSocket:self]; |
||
2080 | } |
||
2081 | |||
2082 | // Create GCDAsyncSocket instance for accepted socket |
||
2083 | |||
2084 | GCDAsyncSocket *acceptedSocket = [[[self class] alloc] initWithDelegate:theDelegate |
||
2085 | delegateQueue:self->delegateQueue |
||
2086 | socketQueue:childSocketQueue]; |
||
2087 | |||
2088 | if (socketType == 0) |
||
2089 | acceptedSocket->socket4FD = childSocketFD; |
||
2090 | else if (socketType == 1) |
||
2091 | acceptedSocket->socket6FD = childSocketFD; |
||
2092 | else |
||
2093 | acceptedSocket->socketUN = childSocketFD; |
||
2094 | |||
2095 | acceptedSocket->flags = (kSocketStarted | kConnected); |
||
2096 | |||
2097 | // Setup read and write sources for accepted socket |
||
2098 | |||
2099 | dispatch_async(acceptedSocket->socketQueue, ^{ @autoreleasepool { |
||
2100 | |||
2101 | [acceptedSocket setupReadAndWriteSourcesForNewlyConnectedSocket:childSocketFD]; |
||
2102 | }}); |
||
2103 | |||
2104 | // Notify delegate |
||
2105 | |||
2106 | if ([theDelegate respondsToSelector:@selector(socket:didAcceptNewSocket:)]) |
||
2107 | { |
||
2108 | [theDelegate socket:self didAcceptNewSocket:acceptedSocket]; |
||
2109 | } |
||
2110 | |||
2111 | // Release the socket queue returned from the delegate (it was retained by acceptedSocket) |
||
2112 | #if !OS_OBJECT_USE_OBJC |
||
2113 | if (childSocketQueue) dispatch_release(childSocketQueue); |
||
2114 | #endif |
||
2115 | |||
2116 | // The accepted socket should have been retained by the delegate. |
||
2117 | // Otherwise it gets properly released when exiting the block. |
||
2118 | }}); |
||
2119 | } |
||
2120 | |||
2121 | return YES; |
||
2122 | } |
||
2123 | |||
2124 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
2125 | #pragma mark Connecting |
||
2126 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
2127 | |||
2128 | /** |
||
2129 | * This method runs through the various checks required prior to a connection attempt. |
||
2130 | * It is shared between the connectToHost and connectToAddress methods. |
||
2131 | * |
||
2132 | **/ |
||
2133 | - (BOOL)preConnectWithInterface:(NSString *)interface error:(NSError **)errPtr |
||
2134 | { |
||
2135 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
2136 | |||
2137 | if (delegate == nil) // Must have delegate set |
||
2138 | { |
||
2139 | if (errPtr) |
||
2140 | { |
||
2141 | NSString *msg = @"Attempting to connect without a delegate. Set a delegate first."; |
||
2142 | *errPtr = [self badConfigError:msg]; |
||
2143 | } |
||
2144 | return NO; |
||
2145 | } |
||
2146 | |||
2147 | if (delegateQueue == NULL) // Must have delegate queue set |
||
2148 | { |
||
2149 | if (errPtr) |
||
2150 | { |
||
2151 | NSString *msg = @"Attempting to connect without a delegate queue. Set a delegate queue first."; |
||
2152 | *errPtr = [self badConfigError:msg]; |
||
2153 | } |
||
2154 | return NO; |
||
2155 | } |
||
2156 | |||
2157 | if (![self isDisconnected]) // Must be disconnected |
||
2158 | { |
||
2159 | if (errPtr) |
||
2160 | { |
||
2161 | NSString *msg = @"Attempting to connect while connected or accepting connections. Disconnect first."; |
||
2162 | *errPtr = [self badConfigError:msg]; |
||
2163 | } |
||
2164 | return NO; |
||
2165 | } |
||
2166 | |||
2167 | BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; |
||
2168 | BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; |
||
2169 | |||
2170 | if (isIPv4Disabled && isIPv6Disabled) // Must have IPv4 or IPv6 enabled |
||
2171 | { |
||
2172 | if (errPtr) |
||
2173 | { |
||
2174 | NSString *msg = @"Both IPv4 and IPv6 have been disabled. Must enable at least one protocol first."; |
||
2175 | *errPtr = [self badConfigError:msg]; |
||
2176 | } |
||
2177 | return NO; |
||
2178 | } |
||
2179 | |||
2180 | if (interface) |
||
2181 | { |
||
2182 | NSMutableData *interface4 = nil; |
||
2183 | NSMutableData *interface6 = nil; |
||
2184 | |||
2185 | [self getInterfaceAddress4:&interface4 address6:&interface6 fromDescription:interface port:0]; |
||
2186 | |||
2187 | if ((interface4 == nil) && (interface6 == nil)) |
||
2188 | { |
||
2189 | if (errPtr) |
||
2190 | { |
||
2191 | NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; |
||
2192 | *errPtr = [self badParamError:msg]; |
||
2193 | } |
||
2194 | return NO; |
||
2195 | } |
||
2196 | |||
2197 | if (isIPv4Disabled && (interface6 == nil)) |
||
2198 | { |
||
2199 | if (errPtr) |
||
2200 | { |
||
2201 | NSString *msg = @"IPv4 has been disabled and specified interface doesn't support IPv6."; |
||
2202 | *errPtr = [self badParamError:msg]; |
||
2203 | } |
||
2204 | return NO; |
||
2205 | } |
||
2206 | |||
2207 | if (isIPv6Disabled && (interface4 == nil)) |
||
2208 | { |
||
2209 | if (errPtr) |
||
2210 | { |
||
2211 | NSString *msg = @"IPv6 has been disabled and specified interface doesn't support IPv4."; |
||
2212 | *errPtr = [self badParamError:msg]; |
||
2213 | } |
||
2214 | return NO; |
||
2215 | } |
||
2216 | |||
2217 | connectInterface4 = interface4; |
||
2218 | connectInterface6 = interface6; |
||
2219 | } |
||
2220 | |||
2221 | // Clear queues (spurious read/write requests post disconnect) |
||
2222 | [readQueue removeAllObjects]; |
||
2223 | [writeQueue removeAllObjects]; |
||
2224 | |||
2225 | return YES; |
||
2226 | } |
||
2227 | |||
2228 | - (BOOL)preConnectWithUrl:(NSURL *)url error:(NSError **)errPtr |
||
2229 | { |
||
2230 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
2231 | |||
2232 | if (delegate == nil) // Must have delegate set |
||
2233 | { |
||
2234 | if (errPtr) |
||
2235 | { |
||
2236 | NSString *msg = @"Attempting to connect without a delegate. Set a delegate first."; |
||
2237 | *errPtr = [self badConfigError:msg]; |
||
2238 | } |
||
2239 | return NO; |
||
2240 | } |
||
2241 | |||
2242 | if (delegateQueue == NULL) // Must have delegate queue set |
||
2243 | { |
||
2244 | if (errPtr) |
||
2245 | { |
||
2246 | NSString *msg = @"Attempting to connect without a delegate queue. Set a delegate queue first."; |
||
2247 | *errPtr = [self badConfigError:msg]; |
||
2248 | } |
||
2249 | return NO; |
||
2250 | } |
||
2251 | |||
2252 | if (![self isDisconnected]) // Must be disconnected |
||
2253 | { |
||
2254 | if (errPtr) |
||
2255 | { |
||
2256 | NSString *msg = @"Attempting to connect while connected or accepting connections. Disconnect first."; |
||
2257 | *errPtr = [self badConfigError:msg]; |
||
2258 | } |
||
2259 | return NO; |
||
2260 | } |
||
2261 | |||
2262 | NSData *interface = [self getInterfaceAddressFromUrl:url]; |
||
2263 | |||
2264 | if (interface == nil) |
||
2265 | { |
||
2266 | if (errPtr) |
||
2267 | { |
||
2268 | NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; |
||
2269 | *errPtr = [self badParamError:msg]; |
||
2270 | } |
||
2271 | return NO; |
||
2272 | } |
||
2273 | |||
2274 | connectInterfaceUN = interface; |
||
2275 | |||
2276 | // Clear queues (spurious read/write requests post disconnect) |
||
2277 | [readQueue removeAllObjects]; |
||
2278 | [writeQueue removeAllObjects]; |
||
2279 | |||
2280 | return YES; |
||
2281 | } |
||
2282 | |||
2283 | - (BOOL)connectToHost:(NSString*)host onPort:(uint16_t)port error:(NSError **)errPtr |
||
2284 | { |
||
2285 | return [self connectToHost:host onPort:port withTimeout:-1 error:errPtr]; |
||
2286 | } |
||
2287 | |||
2288 | - (BOOL)connectToHost:(NSString *)host |
||
2289 | onPort:(uint16_t)port |
||
2290 | withTimeout:(NSTimeInterval)timeout |
||
2291 | error:(NSError **)errPtr |
||
2292 | { |
||
2293 | return [self connectToHost:host onPort:port viaInterface:nil withTimeout:timeout error:errPtr]; |
||
2294 | } |
||
2295 | |||
2296 | - (BOOL)connectToHost:(NSString *)inHost |
||
2297 | onPort:(uint16_t)port |
||
2298 | viaInterface:(NSString *)inInterface |
||
2299 | withTimeout:(NSTimeInterval)timeout |
||
2300 | error:(NSError **)errPtr |
||
2301 | { |
||
2302 | LogTrace(); |
||
2303 | |||
2304 | // Just in case immutable objects were passed |
||
2305 | NSString *host = [inHost copy]; |
||
2306 | NSString *interface = [inInterface copy]; |
||
2307 | |||
2308 | __block BOOL result = NO; |
||
2309 | __block NSError *preConnectErr = nil; |
||
2310 | |||
2311 | dispatch_block_t block = ^{ @autoreleasepool { |
||
2312 | |||
2313 | // Check for problems with host parameter |
||
2314 | |||
2315 | if ([host length] == 0) |
||
2316 | { |
||
2317 | NSString *msg = @"Invalid host parameter (nil or \"\"). Should be a domain name or IP address string."; |
||
2318 | preConnectErr = [self badParamError:msg]; |
||
2319 | |||
2320 | return_from_block; |
||
2321 | } |
||
2322 | |||
2323 | // Run through standard pre-connect checks |
||
2324 | |||
2325 | if (![self preConnectWithInterface:interface error:&preConnectErr]) |
||
2326 | { |
||
2327 | return_from_block; |
||
2328 | } |
||
2329 | |||
2330 | // We've made it past all the checks. |
||
2331 | // It's time to start the connection process. |
||
2332 | |||
2333 | self->flags |= kSocketStarted; |
||
2334 | |||
2335 | LogVerbose(@"Dispatching DNS lookup..."); |
||
2336 | |||
2337 | // It's possible that the given host parameter is actually a NSMutableString. |
||
2338 | // So we want to copy it now, within this block that will be executed synchronously. |
||
2339 | // This way the asynchronous lookup block below doesn't have to worry about it changing. |
||
2340 | |||
2341 | NSString *hostCpy = [host copy]; |
||
2342 | |||
2343 | int aStateIndex = self->stateIndex; |
||
2344 | __weak GCDAsyncSocket *weakSelf = self; |
||
2345 | |||
2346 | dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
||
2347 | dispatch_async(globalConcurrentQueue, ^{ @autoreleasepool { |
||
2348 | #pragma clang diagnostic push |
||
2349 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
2350 | |||
2351 | NSError *lookupErr = nil; |
||
2352 | NSMutableArray *addresses = [[self class] lookupHost:hostCpy port:port error:&lookupErr]; |
||
2353 | |||
2354 | __strong GCDAsyncSocket *strongSelf = weakSelf; |
||
2355 | if (strongSelf == nil) return_from_block; |
||
2356 | |||
2357 | if (lookupErr) |
||
2358 | { |
||
2359 | dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool { |
||
2360 | |||
2361 | [strongSelf lookup:aStateIndex didFail:lookupErr]; |
||
2362 | }}); |
||
2363 | } |
||
2364 | else |
||
2365 | { |
||
2366 | NSData *address4 = nil; |
||
2367 | NSData *address6 = nil; |
||
2368 | |||
2369 | for (NSData *address in addresses) |
||
2370 | { |
||
2371 | if (!address4 && [[self class] isIPv4Address:address]) |
||
2372 | { |
||
2373 | address4 = address; |
||
2374 | } |
||
2375 | else if (!address6 && [[self class] isIPv6Address:address]) |
||
2376 | { |
||
2377 | address6 = address; |
||
2378 | } |
||
2379 | } |
||
2380 | |||
2381 | dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool { |
||
2382 | |||
2383 | [strongSelf lookup:aStateIndex didSucceedWithAddress4:address4 address6:address6]; |
||
2384 | }}); |
||
2385 | } |
||
2386 | |||
2387 | #pragma clang diagnostic pop |
||
2388 | }}); |
||
2389 | |||
2390 | [self startConnectTimeout:timeout]; |
||
2391 | |||
2392 | result = YES; |
||
2393 | }}; |
||
2394 | |||
2395 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
2396 | block(); |
||
2397 | else |
||
2398 | dispatch_sync(socketQueue, block); |
||
2399 | |||
2400 | |||
2401 | if (errPtr) *errPtr = preConnectErr; |
||
2402 | return result; |
||
2403 | } |
||
2404 | |||
2405 | - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr |
||
2406 | { |
||
2407 | return [self connectToAddress:remoteAddr viaInterface:nil withTimeout:-1 error:errPtr]; |
||
2408 | } |
||
2409 | |||
2410 | - (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr |
||
2411 | { |
||
2412 | return [self connectToAddress:remoteAddr viaInterface:nil withTimeout:timeout error:errPtr]; |
||
2413 | } |
||
2414 | |||
2415 | - (BOOL)connectToAddress:(NSData *)inRemoteAddr |
||
2416 | viaInterface:(NSString *)inInterface |
||
2417 | withTimeout:(NSTimeInterval)timeout |
||
2418 | error:(NSError **)errPtr |
||
2419 | { |
||
2420 | LogTrace(); |
||
2421 | |||
2422 | // Just in case immutable objects were passed |
||
2423 | NSData *remoteAddr = [inRemoteAddr copy]; |
||
2424 | NSString *interface = [inInterface copy]; |
||
2425 | |||
2426 | __block BOOL result = NO; |
||
2427 | __block NSError *err = nil; |
||
2428 | |||
2429 | dispatch_block_t block = ^{ @autoreleasepool { |
||
2430 | |||
2431 | // Check for problems with remoteAddr parameter |
||
2432 | |||
2433 | NSData *address4 = nil; |
||
2434 | NSData *address6 = nil; |
||
2435 | |||
2436 | if ([remoteAddr length] >= sizeof(struct sockaddr)) |
||
2437 | { |
||
2438 | const struct sockaddr *sockaddr = (const struct sockaddr *)[remoteAddr bytes]; |
||
2439 | |||
2440 | if (sockaddr->sa_family == AF_INET) |
||
2441 | { |
||
2442 | if ([remoteAddr length] == sizeof(struct sockaddr_in)) |
||
2443 | { |
||
2444 | address4 = remoteAddr; |
||
2445 | } |
||
2446 | } |
||
2447 | else if (sockaddr->sa_family == AF_INET6) |
||
2448 | { |
||
2449 | if ([remoteAddr length] == sizeof(struct sockaddr_in6)) |
||
2450 | { |
||
2451 | address6 = remoteAddr; |
||
2452 | } |
||
2453 | } |
||
2454 | } |
||
2455 | |||
2456 | if ((address4 == nil) && (address6 == nil)) |
||
2457 | { |
||
2458 | NSString *msg = @"A valid IPv4 or IPv6 address was not given"; |
||
2459 | err = [self badParamError:msg]; |
||
2460 | |||
2461 | return_from_block; |
||
2462 | } |
||
2463 | |||
2464 | BOOL isIPv4Disabled = (self->config & kIPv4Disabled) ? YES : NO; |
||
2465 | BOOL isIPv6Disabled = (self->config & kIPv6Disabled) ? YES : NO; |
||
2466 | |||
2467 | if (isIPv4Disabled && (address4 != nil)) |
||
2468 | { |
||
2469 | NSString *msg = @"IPv4 has been disabled and an IPv4 address was passed."; |
||
2470 | err = [self badParamError:msg]; |
||
2471 | |||
2472 | return_from_block; |
||
2473 | } |
||
2474 | |||
2475 | if (isIPv6Disabled && (address6 != nil)) |
||
2476 | { |
||
2477 | NSString *msg = @"IPv6 has been disabled and an IPv6 address was passed."; |
||
2478 | err = [self badParamError:msg]; |
||
2479 | |||
2480 | return_from_block; |
||
2481 | } |
||
2482 | |||
2483 | // Run through standard pre-connect checks |
||
2484 | |||
2485 | if (![self preConnectWithInterface:interface error:&err]) |
||
2486 | { |
||
2487 | return_from_block; |
||
2488 | } |
||
2489 | |||
2490 | // We've made it past all the checks. |
||
2491 | // It's time to start the connection process. |
||
2492 | |||
2493 | if (![self connectWithAddress4:address4 address6:address6 error:&err]) |
||
2494 | { |
||
2495 | return_from_block; |
||
2496 | } |
||
2497 | |||
2498 | self->flags |= kSocketStarted; |
||
2499 | |||
2500 | [self startConnectTimeout:timeout]; |
||
2501 | |||
2502 | result = YES; |
||
2503 | }}; |
||
2504 | |||
2505 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
2506 | block(); |
||
2507 | else |
||
2508 | dispatch_sync(socketQueue, block); |
||
2509 | |||
2510 | if (result == NO) |
||
2511 | { |
||
2512 | if (errPtr) |
||
2513 | *errPtr = err; |
||
2514 | } |
||
2515 | |||
2516 | return result; |
||
2517 | } |
||
2518 | |||
2519 | - (BOOL)connectToUrl:(NSURL *)url withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr |
||
2520 | { |
||
2521 | LogTrace(); |
||
2522 | |||
2523 | __block BOOL result = NO; |
||
2524 | __block NSError *err = nil; |
||
2525 | |||
2526 | dispatch_block_t block = ^{ @autoreleasepool { |
||
2527 | |||
2528 | // Check for problems with host parameter |
||
2529 | |||
2530 | if ([url.path length] == 0) |
||
2531 | { |
||
2532 | NSString *msg = @"Invalid unix domain socket url."; |
||
2533 | err = [self badParamError:msg]; |
||
2534 | |||
2535 | return_from_block; |
||
2536 | } |
||
2537 | |||
2538 | // Run through standard pre-connect checks |
||
2539 | |||
2540 | if (![self preConnectWithUrl:url error:&err]) |
||
2541 | { |
||
2542 | return_from_block; |
||
2543 | } |
||
2544 | |||
2545 | // We've made it past all the checks. |
||
2546 | // It's time to start the connection process. |
||
2547 | |||
2548 | self->flags |= kSocketStarted; |
||
2549 | |||
2550 | // Start the normal connection process |
||
2551 | |||
2552 | NSError *connectError = nil; |
||
2553 | if (![self connectWithAddressUN:self->connectInterfaceUN error:&connectError]) |
||
2554 | { |
||
2555 | [self closeWithError:connectError]; |
||
2556 | |||
2557 | return_from_block; |
||
2558 | } |
||
2559 | |||
2560 | [self startConnectTimeout:timeout]; |
||
2561 | |||
2562 | result = YES; |
||
2563 | }}; |
||
2564 | |||
2565 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
2566 | block(); |
||
2567 | else |
||
2568 | dispatch_sync(socketQueue, block); |
||
2569 | |||
2570 | if (result == NO) |
||
2571 | { |
||
2572 | if (errPtr) |
||
2573 | *errPtr = err; |
||
2574 | } |
||
2575 | |||
2576 | return result; |
||
2577 | } |
||
2578 | |||
2579 | - (BOOL)connectToNetService:(NSNetService *)netService error:(NSError **)errPtr |
||
2580 | { |
||
2581 | NSArray* addresses = [netService addresses]; |
||
2582 | for (NSData* address in addresses) |
||
2583 | { |
||
2584 | BOOL result = [self connectToAddress:address error:errPtr]; |
||
2585 | if (result) |
||
2586 | { |
||
2587 | return YES; |
||
2588 | } |
||
2589 | } |
||
2590 | |||
2591 | return NO; |
||
2592 | } |
||
2593 | |||
2594 | - (void)lookup:(int)aStateIndex didSucceedWithAddress4:(NSData *)address4 address6:(NSData *)address6 |
||
2595 | { |
||
2596 | LogTrace(); |
||
2597 | |||
2598 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
2599 | NSAssert(address4 || address6, @"Expected at least one valid address"); |
||
2600 | |||
2601 | if (aStateIndex != stateIndex) |
||
2602 | { |
||
2603 | LogInfo(@"Ignoring lookupDidSucceed, already disconnected"); |
||
2604 | |||
2605 | // The connect operation has been cancelled. |
||
2606 | // That is, socket was disconnected, or connection has already timed out. |
||
2607 | return; |
||
2608 | } |
||
2609 | |||
2610 | // Check for problems |
||
2611 | |||
2612 | BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; |
||
2613 | BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; |
||
2614 | |||
2615 | if (isIPv4Disabled && (address6 == nil)) |
||
2616 | { |
||
2617 | NSString *msg = @"IPv4 has been disabled and DNS lookup found no IPv6 address."; |
||
2618 | |||
2619 | [self closeWithError:[self otherError:msg]]; |
||
2620 | return; |
||
2621 | } |
||
2622 | |||
2623 | if (isIPv6Disabled && (address4 == nil)) |
||
2624 | { |
||
2625 | NSString *msg = @"IPv6 has been disabled and DNS lookup found no IPv4 address."; |
||
2626 | |||
2627 | [self closeWithError:[self otherError:msg]]; |
||
2628 | return; |
||
2629 | } |
||
2630 | |||
2631 | // Start the normal connection process |
||
2632 | |||
2633 | NSError *err = nil; |
||
2634 | if (![self connectWithAddress4:address4 address6:address6 error:&err]) |
||
2635 | { |
||
2636 | [self closeWithError:err]; |
||
2637 | } |
||
2638 | } |
||
2639 | |||
2640 | /** |
||
2641 | * This method is called if the DNS lookup fails. |
||
2642 | * This method is executed on the socketQueue. |
||
2643 | * |
||
2644 | * Since the DNS lookup executed synchronously on a global concurrent queue, |
||
2645 | * the original connection request may have already been cancelled or timed-out by the time this method is invoked. |
||
2646 | * The lookupIndex tells us whether the lookup is still valid or not. |
||
2647 | **/ |
||
2648 | - (void)lookup:(int)aStateIndex didFail:(NSError *)error |
||
2649 | { |
||
2650 | LogTrace(); |
||
2651 | |||
2652 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
2653 | |||
2654 | |||
2655 | if (aStateIndex != stateIndex) |
||
2656 | { |
||
2657 | LogInfo(@"Ignoring lookup:didFail: - already disconnected"); |
||
2658 | |||
2659 | // The connect operation has been cancelled. |
||
2660 | // That is, socket was disconnected, or connection has already timed out. |
||
2661 | return; |
||
2662 | } |
||
2663 | |||
2664 | [self endConnectTimeout]; |
||
2665 | [self closeWithError:error]; |
||
2666 | } |
||
2667 | |||
2668 | - (BOOL)bindSocket:(int)socketFD toInterface:(NSData *)connectInterface error:(NSError **)errPtr |
||
2669 | { |
||
2670 | // Bind the socket to the desired interface (if needed) |
||
2671 | |||
2672 | if (connectInterface) |
||
2673 | { |
||
2674 | LogVerbose(@"Binding socket..."); |
||
2675 | |||
2676 | if ([[self class] portFromAddress:connectInterface] > 0) |
||
2677 | { |
||
2678 | // Since we're going to be binding to a specific port, |
||
2679 | // we should turn on reuseaddr to allow us to override sockets in time_wait. |
||
2680 | |||
2681 | int reuseOn = 1; |
||
2682 | setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); |
||
2683 | } |
||
2684 | |||
2685 | const struct sockaddr *interfaceAddr = (const struct sockaddr *)[connectInterface bytes]; |
||
2686 | |||
2687 | int result = bind(socketFD, interfaceAddr, (socklen_t)[connectInterface length]); |
||
2688 | if (result != 0) |
||
2689 | { |
||
2690 | if (errPtr) |
||
2691 | *errPtr = [self errorWithErrno:errno reason:@"Error in bind() function"]; |
||
2692 | |||
2693 | return NO; |
||
2694 | } |
||
2695 | } |
||
2696 | |||
2697 | return YES; |
||
2698 | } |
||
2699 | |||
2700 | - (int)createSocket:(int)family connectInterface:(NSData *)connectInterface errPtr:(NSError **)errPtr |
||
2701 | { |
||
2702 | int socketFD = socket(family, SOCK_STREAM, 0); |
||
2703 | |||
2704 | if (socketFD == SOCKET_NULL) |
||
2705 | { |
||
2706 | if (errPtr) |
||
2707 | *errPtr = [self errorWithErrno:errno reason:@"Error in socket() function"]; |
||
2708 | |||
2709 | return socketFD; |
||
2710 | } |
||
2711 | |||
2712 | if (![self bindSocket:socketFD toInterface:connectInterface error:errPtr]) |
||
2713 | { |
||
2714 | [self closeSocket:socketFD]; |
||
2715 | |||
2716 | return SOCKET_NULL; |
||
2717 | } |
||
2718 | |||
2719 | // Prevent SIGPIPE signals |
||
2720 | |||
2721 | int nosigpipe = 1; |
||
2722 | setsockopt(socketFD, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); |
||
2723 | |||
2724 | return socketFD; |
||
2725 | } |
||
2726 | |||
2727 | - (void)connectSocket:(int)socketFD address:(NSData *)address stateIndex:(int)aStateIndex |
||
2728 | { |
||
2729 | // If there already is a socket connected, we close socketFD and return |
||
2730 | if (self.isConnected) |
||
2731 | { |
||
2732 | [self closeSocket:socketFD]; |
||
2733 | return; |
||
2734 | } |
||
2735 | |||
2736 | // Start the connection process in a background queue |
||
2737 | |||
2738 | __weak GCDAsyncSocket *weakSelf = self; |
||
2739 | |||
2740 | dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
||
2741 | dispatch_async(globalConcurrentQueue, ^{ |
||
2742 | #pragma clang diagnostic push |
||
2743 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
2744 | |||
2745 | int result = connect(socketFD, (const struct sockaddr *)[address bytes], (socklen_t)[address length]); |
||
2746 | int err = errno; |
||
2747 | |||
2748 | __strong GCDAsyncSocket *strongSelf = weakSelf; |
||
2749 | if (strongSelf == nil) return_from_block; |
||
2750 | |||
2751 | dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool { |
||
2752 | |||
2753 | if (strongSelf.isConnected) |
||
2754 | { |
||
2755 | [strongSelf closeSocket:socketFD]; |
||
2756 | return_from_block; |
||
2757 | } |
||
2758 | |||
2759 | if (result == 0) |
||
2760 | { |
||
2761 | [self closeUnusedSocket:socketFD]; |
||
2762 | |||
2763 | [strongSelf didConnect:aStateIndex]; |
||
2764 | } |
||
2765 | else |
||
2766 | { |
||
2767 | [strongSelf closeSocket:socketFD]; |
||
2768 | |||
2769 | // If there are no more sockets trying to connect, we inform the error to the delegate |
||
2770 | if (strongSelf.socket4FD == SOCKET_NULL && strongSelf.socket6FD == SOCKET_NULL) |
||
2771 | { |
||
2772 | NSError *error = [strongSelf errorWithErrno:err reason:@"Error in connect() function"]; |
||
2773 | [strongSelf didNotConnect:aStateIndex error:error]; |
||
2774 | } |
||
2775 | } |
||
2776 | }}); |
||
2777 | |||
2778 | #pragma clang diagnostic pop |
||
2779 | }); |
||
2780 | |||
2781 | LogVerbose(@"Connecting..."); |
||
2782 | } |
||
2783 | |||
2784 | - (void)closeSocket:(int)socketFD |
||
2785 | { |
||
2786 | if (socketFD != SOCKET_NULL && |
||
2787 | (socketFD == socket6FD || socketFD == socket4FD)) |
||
2788 | { |
||
2789 | close(socketFD); |
||
2790 | |||
2791 | if (socketFD == socket4FD) |
||
2792 | { |
||
2793 | LogVerbose(@"close(socket4FD)"); |
||
2794 | socket4FD = SOCKET_NULL; |
||
2795 | } |
||
2796 | else if (socketFD == socket6FD) |
||
2797 | { |
||
2798 | LogVerbose(@"close(socket6FD)"); |
||
2799 | socket6FD = SOCKET_NULL; |
||
2800 | } |
||
2801 | } |
||
2802 | } |
||
2803 | |||
2804 | - (void)closeUnusedSocket:(int)usedSocketFD |
||
2805 | { |
||
2806 | if (usedSocketFD != socket4FD) |
||
2807 | { |
||
2808 | [self closeSocket:socket4FD]; |
||
2809 | } |
||
2810 | else if (usedSocketFD != socket6FD) |
||
2811 | { |
||
2812 | [self closeSocket:socket6FD]; |
||
2813 | } |
||
2814 | } |
||
2815 | |||
2816 | - (BOOL)connectWithAddress4:(NSData *)address4 address6:(NSData *)address6 error:(NSError **)errPtr |
||
2817 | { |
||
2818 | LogTrace(); |
||
2819 | |||
2820 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
2821 | |||
2822 | LogVerbose(@"IPv4: %@:%hu", [[self class] hostFromAddress:address4], [[self class] portFromAddress:address4]); |
||
2823 | LogVerbose(@"IPv6: %@:%hu", [[self class] hostFromAddress:address6], [[self class] portFromAddress:address6]); |
||
2824 | |||
2825 | // Determine socket type |
||
2826 | |||
2827 | BOOL preferIPv6 = (config & kPreferIPv6) ? YES : NO; |
||
2828 | |||
2829 | // Create and bind the sockets |
||
2830 | |||
2831 | if (address4) |
||
2832 | { |
||
2833 | LogVerbose(@"Creating IPv4 socket"); |
||
2834 | |||
2835 | socket4FD = [self createSocket:AF_INET connectInterface:connectInterface4 errPtr:errPtr]; |
||
2836 | } |
||
2837 | |||
2838 | if (address6) |
||
2839 | { |
||
2840 | LogVerbose(@"Creating IPv6 socket"); |
||
2841 | |||
2842 | socket6FD = [self createSocket:AF_INET6 connectInterface:connectInterface6 errPtr:errPtr]; |
||
2843 | } |
||
2844 | |||
2845 | if (socket4FD == SOCKET_NULL && socket6FD == SOCKET_NULL) |
||
2846 | { |
||
2847 | return NO; |
||
2848 | } |
||
2849 | |||
2850 | int socketFD, alternateSocketFD; |
||
2851 | NSData *address, *alternateAddress; |
||
2852 | |||
2853 | if ((preferIPv6 && socket6FD != SOCKET_NULL) || socket4FD == SOCKET_NULL) |
||
2854 | { |
||
2855 | socketFD = socket6FD; |
||
2856 | alternateSocketFD = socket4FD; |
||
2857 | address = address6; |
||
2858 | alternateAddress = address4; |
||
2859 | } |
||
2860 | else |
||
2861 | { |
||
2862 | socketFD = socket4FD; |
||
2863 | alternateSocketFD = socket6FD; |
||
2864 | address = address4; |
||
2865 | alternateAddress = address6; |
||
2866 | } |
||
2867 | |||
2868 | int aStateIndex = stateIndex; |
||
2869 | |||
2870 | [self connectSocket:socketFD address:address stateIndex:aStateIndex]; |
||
2871 | |||
2872 | if (alternateAddress) |
||
2873 | { |
||
2874 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(alternateAddressDelay * NSEC_PER_SEC)), socketQueue, ^{ |
||
2875 | [self connectSocket:alternateSocketFD address:alternateAddress stateIndex:aStateIndex]; |
||
2876 | }); |
||
2877 | } |
||
2878 | |||
2879 | return YES; |
||
2880 | } |
||
2881 | |||
2882 | - (BOOL)connectWithAddressUN:(NSData *)address error:(NSError **)errPtr |
||
2883 | { |
||
2884 | LogTrace(); |
||
2885 | |||
2886 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
2887 | |||
2888 | // Create the socket |
||
2889 | |||
2890 | int socketFD; |
||
2891 | |||
2892 | LogVerbose(@"Creating unix domain socket"); |
||
2893 | |||
2894 | socketUN = socket(AF_UNIX, SOCK_STREAM, 0); |
||
2895 | |||
2896 | socketFD = socketUN; |
||
2897 | |||
2898 | if (socketFD == SOCKET_NULL) |
||
2899 | { |
||
2900 | if (errPtr) |
||
2901 | *errPtr = [self errorWithErrno:errno reason:@"Error in socket() function"]; |
||
2902 | |||
2903 | return NO; |
||
2904 | } |
||
2905 | |||
2906 | // Bind the socket to the desired interface (if needed) |
||
2907 | |||
2908 | LogVerbose(@"Binding socket..."); |
||
2909 | |||
2910 | int reuseOn = 1; |
||
2911 | setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn)); |
||
2912 | |||
2913 | // const struct sockaddr *interfaceAddr = (const struct sockaddr *)[address bytes]; |
||
2914 | // |
||
2915 | // int result = bind(socketFD, interfaceAddr, (socklen_t)[address length]); |
||
2916 | // if (result != 0) |
||
2917 | // { |
||
2918 | // if (errPtr) |
||
2919 | // *errPtr = [self errnoErrorWithReason:@"Error in bind() function"]; |
||
2920 | // |
||
2921 | // return NO; |
||
2922 | // } |
||
2923 | |||
2924 | // Prevent SIGPIPE signals |
||
2925 | |||
2926 | int nosigpipe = 1; |
||
2927 | setsockopt(socketFD, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); |
||
2928 | |||
2929 | // Start the connection process in a background queue |
||
2930 | |||
2931 | int aStateIndex = stateIndex; |
||
2932 | |||
2933 | dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
||
2934 | dispatch_async(globalConcurrentQueue, ^{ |
||
2935 | |||
2936 | const struct sockaddr *addr = (const struct sockaddr *)[address bytes]; |
||
2937 | int result = connect(socketFD, addr, addr->sa_len); |
||
2938 | if (result == 0) |
||
2939 | { |
||
2940 | dispatch_async(self->socketQueue, ^{ @autoreleasepool { |
||
2941 | |||
2942 | [self didConnect:aStateIndex]; |
||
2943 | }}); |
||
2944 | } |
||
2945 | else |
||
2946 | { |
||
2947 | // TODO: Bad file descriptor |
||
2948 | perror("connect"); |
||
2949 | NSError *error = [self errorWithErrno:errno reason:@"Error in connect() function"]; |
||
2950 | |||
2951 | dispatch_async(self->socketQueue, ^{ @autoreleasepool { |
||
2952 | |||
2953 | [self didNotConnect:aStateIndex error:error]; |
||
2954 | }}); |
||
2955 | } |
||
2956 | }); |
||
2957 | |||
2958 | LogVerbose(@"Connecting..."); |
||
2959 | |||
2960 | return YES; |
||
2961 | } |
||
2962 | |||
2963 | - (void)didConnect:(int)aStateIndex |
||
2964 | { |
||
2965 | LogTrace(); |
||
2966 | |||
2967 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
2968 | |||
2969 | |||
2970 | if (aStateIndex != stateIndex) |
||
2971 | { |
||
2972 | LogInfo(@"Ignoring didConnect, already disconnected"); |
||
2973 | |||
2974 | // The connect operation has been cancelled. |
||
2975 | // That is, socket was disconnected, or connection has already timed out. |
||
2976 | return; |
||
2977 | } |
||
2978 | |||
2979 | flags |= kConnected; |
||
2980 | |||
2981 | [self endConnectTimeout]; |
||
2982 | |||
2983 | #if TARGET_OS_IPHONE |
||
2984 | // The endConnectTimeout method executed above incremented the stateIndex. |
||
2985 | aStateIndex = stateIndex; |
||
2986 | #endif |
||
2987 | |||
2988 | // Setup read/write streams (as workaround for specific shortcomings in the iOS platform) |
||
2989 | // |
||
2990 | // Note: |
||
2991 | // There may be configuration options that must be set by the delegate before opening the streams. |
||
2992 | // The primary example is the kCFStreamNetworkServiceTypeVoIP flag, which only works on an unopened stream. |
||
2993 | // |
||
2994 | // Thus we wait until after the socket:didConnectToHost:port: delegate method has completed. |
||
2995 | // This gives the delegate time to properly configure the streams if needed. |
||
2996 | |||
2997 | dispatch_block_t SetupStreamsPart1 = ^{ |
||
2998 | #if TARGET_OS_IPHONE |
||
2999 | |||
3000 | if (![self createReadAndWriteStream]) |
||
3001 | { |
||
3002 | [self closeWithError:[self otherError:@"Error creating CFStreams"]]; |
||
3003 | return; |
||
3004 | } |
||
3005 | |||
3006 | if (![self registerForStreamCallbacksIncludingReadWrite:NO]) |
||
3007 | { |
||
3008 | [self closeWithError:[self otherError:@"Error in CFStreamSetClient"]]; |
||
3009 | return; |
||
3010 | } |
||
3011 | |||
3012 | #endif |
||
3013 | }; |
||
3014 | dispatch_block_t SetupStreamsPart2 = ^{ |
||
3015 | #if TARGET_OS_IPHONE |
||
3016 | |||
3017 | if (aStateIndex != self->stateIndex) |
||
3018 | { |
||
3019 | // The socket has been disconnected. |
||
3020 | return; |
||
3021 | } |
||
3022 | |||
3023 | if (![self addStreamsToRunLoop]) |
||
3024 | { |
||
3025 | [self closeWithError:[self otherError:@"Error in CFStreamScheduleWithRunLoop"]]; |
||
3026 | return; |
||
3027 | } |
||
3028 | |||
3029 | if (![self openStreams]) |
||
3030 | { |
||
3031 | [self closeWithError:[self otherError:@"Error creating CFStreams"]]; |
||
3032 | return; |
||
3033 | } |
||
3034 | |||
3035 | #endif |
||
3036 | }; |
||
3037 | |||
3038 | // Notify delegate |
||
3039 | |||
3040 | NSString *host = [self connectedHost]; |
||
3041 | uint16_t port = [self connectedPort]; |
||
3042 | NSURL *url = [self connectedUrl]; |
||
3043 | |||
3044 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
3045 | |||
3046 | if (delegateQueue && host != nil && [theDelegate respondsToSelector:@selector(socket:didConnectToHost:port:)]) |
||
3047 | { |
||
3048 | SetupStreamsPart1(); |
||
3049 | |||
3050 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
3051 | |||
3052 | [theDelegate socket:self didConnectToHost:host port:port]; |
||
3053 | |||
3054 | dispatch_async(self->socketQueue, ^{ @autoreleasepool { |
||
3055 | |||
3056 | SetupStreamsPart2(); |
||
3057 | }}); |
||
3058 | }}); |
||
3059 | } |
||
3060 | else if (delegateQueue && url != nil && [theDelegate respondsToSelector:@selector(socket:didConnectToUrl:)]) |
||
3061 | { |
||
3062 | SetupStreamsPart1(); |
||
3063 | |||
3064 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
3065 | |||
3066 | [theDelegate socket:self didConnectToUrl:url]; |
||
3067 | |||
3068 | dispatch_async(self->socketQueue, ^{ @autoreleasepool { |
||
3069 | |||
3070 | SetupStreamsPart2(); |
||
3071 | }}); |
||
3072 | }}); |
||
3073 | } |
||
3074 | else |
||
3075 | { |
||
3076 | SetupStreamsPart1(); |
||
3077 | SetupStreamsPart2(); |
||
3078 | } |
||
3079 | |||
3080 | // Get the connected socket |
||
3081 | |||
3082 | int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; |
||
3083 | |||
3084 | // Enable non-blocking IO on the socket |
||
3085 | |||
3086 | int result = fcntl(socketFD, F_SETFL, O_NONBLOCK); |
||
3087 | if (result == -1) |
||
3088 | { |
||
3089 | NSString *errMsg = @"Error enabling non-blocking IO on socket (fcntl)"; |
||
3090 | [self closeWithError:[self otherError:errMsg]]; |
||
3091 | |||
3092 | return; |
||
3093 | } |
||
3094 | |||
3095 | // Setup our read/write sources |
||
3096 | |||
3097 | [self setupReadAndWriteSourcesForNewlyConnectedSocket:socketFD]; |
||
3098 | |||
3099 | // Dequeue any pending read/write requests |
||
3100 | |||
3101 | [self maybeDequeueRead]; |
||
3102 | [self maybeDequeueWrite]; |
||
3103 | } |
||
3104 | |||
3105 | - (void)didNotConnect:(int)aStateIndex error:(NSError *)error |
||
3106 | { |
||
3107 | LogTrace(); |
||
3108 | |||
3109 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
3110 | |||
3111 | |||
3112 | if (aStateIndex != stateIndex) |
||
3113 | { |
||
3114 | LogInfo(@"Ignoring didNotConnect, already disconnected"); |
||
3115 | |||
3116 | // The connect operation has been cancelled. |
||
3117 | // That is, socket was disconnected, or connection has already timed out. |
||
3118 | return; |
||
3119 | } |
||
3120 | |||
3121 | [self closeWithError:error]; |
||
3122 | } |
||
3123 | |||
3124 | - (void)startConnectTimeout:(NSTimeInterval)timeout |
||
3125 | { |
||
3126 | if (timeout >= 0.0) |
||
3127 | { |
||
3128 | connectTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, socketQueue); |
||
3129 | |||
3130 | __weak GCDAsyncSocket *weakSelf = self; |
||
3131 | |||
3132 | dispatch_source_set_event_handler(connectTimer, ^{ @autoreleasepool { |
||
3133 | #pragma clang diagnostic push |
||
3134 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
3135 | |||
3136 | __strong GCDAsyncSocket *strongSelf = weakSelf; |
||
3137 | if (strongSelf == nil) return_from_block; |
||
3138 | |||
3139 | [strongSelf doConnectTimeout]; |
||
3140 | |||
3141 | #pragma clang diagnostic pop |
||
3142 | }}); |
||
3143 | |||
3144 | #if !OS_OBJECT_USE_OBJC |
||
3145 | dispatch_source_t theConnectTimer = connectTimer; |
||
3146 | dispatch_source_set_cancel_handler(connectTimer, ^{ |
||
3147 | #pragma clang diagnostic push |
||
3148 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
3149 | |||
3150 | LogVerbose(@"dispatch_release(connectTimer)"); |
||
3151 | dispatch_release(theConnectTimer); |
||
3152 | |||
3153 | #pragma clang diagnostic pop |
||
3154 | }); |
||
3155 | #endif |
||
3156 | |||
3157 | dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)); |
||
3158 | dispatch_source_set_timer(connectTimer, tt, DISPATCH_TIME_FOREVER, 0); |
||
3159 | |||
3160 | dispatch_resume(connectTimer); |
||
3161 | } |
||
3162 | } |
||
3163 | |||
3164 | - (void)endConnectTimeout |
||
3165 | { |
||
3166 | LogTrace(); |
||
3167 | |||
3168 | if (connectTimer) |
||
3169 | { |
||
3170 | dispatch_source_cancel(connectTimer); |
||
3171 | connectTimer = NULL; |
||
3172 | } |
||
3173 | |||
3174 | // Increment stateIndex. |
||
3175 | // This will prevent us from processing results from any related background asynchronous operations. |
||
3176 | // |
||
3177 | // Note: This should be called from close method even if connectTimer is NULL. |
||
3178 | // This is because one might disconnect a socket prior to a successful connection which had no timeout. |
||
3179 | |||
3180 | stateIndex++; |
||
3181 | |||
3182 | if (connectInterface4) |
||
3183 | { |
||
3184 | connectInterface4 = nil; |
||
3185 | } |
||
3186 | if (connectInterface6) |
||
3187 | { |
||
3188 | connectInterface6 = nil; |
||
3189 | } |
||
3190 | } |
||
3191 | |||
3192 | - (void)doConnectTimeout |
||
3193 | { |
||
3194 | LogTrace(); |
||
3195 | |||
3196 | [self endConnectTimeout]; |
||
3197 | [self closeWithError:[self connectTimeoutError]]; |
||
3198 | } |
||
3199 | |||
3200 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
3201 | #pragma mark Disconnecting |
||
3202 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
3203 | |||
3204 | - (void)closeWithError:(NSError *)error |
||
3205 | { |
||
3206 | LogTrace(); |
||
3207 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
3208 | |||
3209 | [self endConnectTimeout]; |
||
3210 | |||
3211 | if (currentRead != nil) [self endCurrentRead]; |
||
3212 | if (currentWrite != nil) [self endCurrentWrite]; |
||
3213 | |||
3214 | [readQueue removeAllObjects]; |
||
3215 | [writeQueue removeAllObjects]; |
||
3216 | |||
3217 | [preBuffer reset]; |
||
3218 | |||
3219 | #if TARGET_OS_IPHONE |
||
3220 | { |
||
3221 | if (readStream || writeStream) |
||
3222 | { |
||
3223 | [self removeStreamsFromRunLoop]; |
||
3224 | |||
3225 | if (readStream) |
||
3226 | { |
||
3227 | CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL); |
||
3228 | CFReadStreamClose(readStream); |
||
3229 | CFRelease(readStream); |
||
3230 | readStream = NULL; |
||
3231 | } |
||
3232 | if (writeStream) |
||
3233 | { |
||
3234 | CFWriteStreamSetClient(writeStream, kCFStreamEventNone, NULL, NULL); |
||
3235 | CFWriteStreamClose(writeStream); |
||
3236 | CFRelease(writeStream); |
||
3237 | writeStream = NULL; |
||
3238 | } |
||
3239 | } |
||
3240 | } |
||
3241 | #endif |
||
3242 | |||
3243 | [sslPreBuffer reset]; |
||
3244 | sslErrCode = lastSSLHandshakeError = noErr; |
||
3245 | |||
3246 | if (sslContext) |
||
3247 | { |
||
3248 | // Getting a linker error here about the SSLx() functions? |
||
3249 | // You need to add the Security Framework to your application. |
||
3250 | |||
3251 | SSLClose(sslContext); |
||
3252 | |||
3253 | #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) |
||
3254 | CFRelease(sslContext); |
||
3255 | #else |
||
3256 | SSLDisposeContext(sslContext); |
||
3257 | #endif |
||
3258 | |||
3259 | sslContext = NULL; |
||
3260 | } |
||
3261 | |||
3262 | // For some crazy reason (in my opinion), cancelling a dispatch source doesn't |
||
3263 | // invoke the cancel handler if the dispatch source is paused. |
||
3264 | // So we have to unpause the source if needed. |
||
3265 | // This allows the cancel handler to be run, which in turn releases the source and closes the socket. |
||
3266 | |||
3267 | if (!accept4Source && !accept6Source && !acceptUNSource && !readSource && !writeSource) |
||
3268 | { |
||
3269 | LogVerbose(@"manually closing close"); |
||
3270 | |||
3271 | if (socket4FD != SOCKET_NULL) |
||
3272 | { |
||
3273 | LogVerbose(@"close(socket4FD)"); |
||
3274 | close(socket4FD); |
||
3275 | socket4FD = SOCKET_NULL; |
||
3276 | } |
||
3277 | |||
3278 | if (socket6FD != SOCKET_NULL) |
||
3279 | { |
||
3280 | LogVerbose(@"close(socket6FD)"); |
||
3281 | close(socket6FD); |
||
3282 | socket6FD = SOCKET_NULL; |
||
3283 | } |
||
3284 | |||
3285 | if (socketUN != SOCKET_NULL) |
||
3286 | { |
||
3287 | LogVerbose(@"close(socketUN)"); |
||
3288 | close(socketUN); |
||
3289 | socketUN = SOCKET_NULL; |
||
3290 | unlink(socketUrl.path.fileSystemRepresentation); |
||
3291 | socketUrl = nil; |
||
3292 | } |
||
3293 | } |
||
3294 | else |
||
3295 | { |
||
3296 | if (accept4Source) |
||
3297 | { |
||
3298 | LogVerbose(@"dispatch_source_cancel(accept4Source)"); |
||
3299 | dispatch_source_cancel(accept4Source); |
||
3300 | |||
3301 | // We never suspend accept4Source |
||
3302 | |||
3303 | accept4Source = NULL; |
||
3304 | } |
||
3305 | |||
3306 | if (accept6Source) |
||
3307 | { |
||
3308 | LogVerbose(@"dispatch_source_cancel(accept6Source)"); |
||
3309 | dispatch_source_cancel(accept6Source); |
||
3310 | |||
3311 | // We never suspend accept6Source |
||
3312 | |||
3313 | accept6Source = NULL; |
||
3314 | } |
||
3315 | |||
3316 | if (acceptUNSource) |
||
3317 | { |
||
3318 | LogVerbose(@"dispatch_source_cancel(acceptUNSource)"); |
||
3319 | dispatch_source_cancel(acceptUNSource); |
||
3320 | |||
3321 | // We never suspend acceptUNSource |
||
3322 | |||
3323 | acceptUNSource = NULL; |
||
3324 | } |
||
3325 | |||
3326 | if (readSource) |
||
3327 | { |
||
3328 | LogVerbose(@"dispatch_source_cancel(readSource)"); |
||
3329 | dispatch_source_cancel(readSource); |
||
3330 | |||
3331 | [self resumeReadSource]; |
||
3332 | |||
3333 | readSource = NULL; |
||
3334 | } |
||
3335 | |||
3336 | if (writeSource) |
||
3337 | { |
||
3338 | LogVerbose(@"dispatch_source_cancel(writeSource)"); |
||
3339 | dispatch_source_cancel(writeSource); |
||
3340 | |||
3341 | [self resumeWriteSource]; |
||
3342 | |||
3343 | writeSource = NULL; |
||
3344 | } |
||
3345 | |||
3346 | // The sockets will be closed by the cancel handlers of the corresponding source |
||
3347 | |||
3348 | socket4FD = SOCKET_NULL; |
||
3349 | socket6FD = SOCKET_NULL; |
||
3350 | socketUN = SOCKET_NULL; |
||
3351 | } |
||
3352 | |||
3353 | // If the client has passed the connect/accept method, then the connection has at least begun. |
||
3354 | // Notify delegate that it is now ending. |
||
3355 | BOOL shouldCallDelegate = (flags & kSocketStarted) ? YES : NO; |
||
3356 | BOOL isDeallocating = (flags & kDealloc) ? YES : NO; |
||
3357 | |||
3358 | // Clear stored socket info and all flags (config remains as is) |
||
3359 | socketFDBytesAvailable = 0; |
||
3360 | flags = 0; |
||
3361 | sslWriteCachedLength = 0; |
||
3362 | |||
3363 | if (shouldCallDelegate) |
||
3364 | { |
||
3365 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
3366 | __strong id theSelf = isDeallocating ? nil : self; |
||
3367 | |||
3368 | if (delegateQueue && [theDelegate respondsToSelector: @selector(socketDidDisconnect:withError:)]) |
||
3369 | { |
||
3370 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
3371 | |||
3372 | [theDelegate socketDidDisconnect:theSelf withError:error]; |
||
3373 | }}); |
||
3374 | } |
||
3375 | } |
||
3376 | } |
||
3377 | |||
3378 | - (void)disconnect |
||
3379 | { |
||
3380 | dispatch_block_t block = ^{ @autoreleasepool { |
||
3381 | |||
3382 | if (self->flags & kSocketStarted) |
||
3383 | { |
||
3384 | [self closeWithError:nil]; |
||
3385 | } |
||
3386 | }}; |
||
3387 | |||
3388 | // Synchronous disconnection, as documented in the header file |
||
3389 | |||
3390 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
3391 | block(); |
||
3392 | else |
||
3393 | dispatch_sync(socketQueue, block); |
||
3394 | } |
||
3395 | |||
3396 | - (void)disconnectAfterReading |
||
3397 | { |
||
3398 | dispatch_async(socketQueue, ^{ @autoreleasepool { |
||
3399 | |||
3400 | if (self->flags & kSocketStarted) |
||
3401 | { |
||
3402 | self->flags |= (kForbidReadsWrites | kDisconnectAfterReads); |
||
3403 | [self maybeClose]; |
||
3404 | } |
||
3405 | }}); |
||
3406 | } |
||
3407 | |||
3408 | - (void)disconnectAfterWriting |
||
3409 | { |
||
3410 | dispatch_async(socketQueue, ^{ @autoreleasepool { |
||
3411 | |||
3412 | if (self->flags & kSocketStarted) |
||
3413 | { |
||
3414 | self->flags |= (kForbidReadsWrites | kDisconnectAfterWrites); |
||
3415 | [self maybeClose]; |
||
3416 | } |
||
3417 | }}); |
||
3418 | } |
||
3419 | |||
3420 | - (void)disconnectAfterReadingAndWriting |
||
3421 | { |
||
3422 | dispatch_async(socketQueue, ^{ @autoreleasepool { |
||
3423 | |||
3424 | if (self->flags & kSocketStarted) |
||
3425 | { |
||
3426 | self->flags |= (kForbidReadsWrites | kDisconnectAfterReads | kDisconnectAfterWrites); |
||
3427 | [self maybeClose]; |
||
3428 | } |
||
3429 | }}); |
||
3430 | } |
||
3431 | |||
3432 | /** |
||
3433 | * Closes the socket if possible. |
||
3434 | * That is, if all writes have completed, and we're set to disconnect after writing, |
||
3435 | * or if all reads have completed, and we're set to disconnect after reading. |
||
3436 | **/ |
||
3437 | - (void)maybeClose |
||
3438 | { |
||
3439 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
3440 | |||
3441 | BOOL shouldClose = NO; |
||
3442 | |||
3443 | if (flags & kDisconnectAfterReads) |
||
3444 | { |
||
3445 | if (([readQueue count] == 0) && (currentRead == nil)) |
||
3446 | { |
||
3447 | if (flags & kDisconnectAfterWrites) |
||
3448 | { |
||
3449 | if (([writeQueue count] == 0) && (currentWrite == nil)) |
||
3450 | { |
||
3451 | shouldClose = YES; |
||
3452 | } |
||
3453 | } |
||
3454 | else |
||
3455 | { |
||
3456 | shouldClose = YES; |
||
3457 | } |
||
3458 | } |
||
3459 | } |
||
3460 | else if (flags & kDisconnectAfterWrites) |
||
3461 | { |
||
3462 | if (([writeQueue count] == 0) && (currentWrite == nil)) |
||
3463 | { |
||
3464 | shouldClose = YES; |
||
3465 | } |
||
3466 | } |
||
3467 | |||
3468 | if (shouldClose) |
||
3469 | { |
||
3470 | [self closeWithError:nil]; |
||
3471 | } |
||
3472 | } |
||
3473 | |||
3474 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
3475 | #pragma mark Errors |
||
3476 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
3477 | |||
3478 | - (NSError *)badConfigError:(NSString *)errMsg |
||
3479 | { |
||
3480 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; |
||
3481 | |||
3482 | return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketBadConfigError userInfo:userInfo]; |
||
3483 | } |
||
3484 | |||
3485 | - (NSError *)badParamError:(NSString *)errMsg |
||
3486 | { |
||
3487 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; |
||
3488 | |||
3489 | return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketBadParamError userInfo:userInfo]; |
||
3490 | } |
||
3491 | |||
3492 | + (NSError *)gaiError:(int)gai_error |
||
3493 | { |
||
3494 | NSString *errMsg = [NSString stringWithCString:gai_strerror(gai_error) encoding:NSASCIIStringEncoding]; |
||
3495 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; |
||
3496 | |||
3497 | return [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:gai_error userInfo:userInfo]; |
||
3498 | } |
||
3499 | |||
3500 | - (NSError *)errorWithErrno:(int)err reason:(NSString *)reason |
||
3501 | { |
||
3502 | NSString *errMsg = [NSString stringWithUTF8String:strerror(err)]; |
||
3503 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg, |
||
3504 | NSLocalizedFailureReasonErrorKey : reason}; |
||
3505 | |||
3506 | return [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:userInfo]; |
||
3507 | } |
||
3508 | |||
3509 | - (NSError *)errnoError |
||
3510 | { |
||
3511 | NSString *errMsg = [NSString stringWithUTF8String:strerror(errno)]; |
||
3512 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; |
||
3513 | |||
3514 | return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo]; |
||
3515 | } |
||
3516 | |||
3517 | - (NSError *)sslError:(OSStatus)ssl_error |
||
3518 | { |
||
3519 | NSString *msg = @"Error code definition can be found in Apple's SecureTransport.h"; |
||
3520 | NSDictionary *userInfo = @{NSLocalizedRecoverySuggestionErrorKey : msg}; |
||
3521 | |||
3522 | return [NSError errorWithDomain:@"kCFStreamErrorDomainSSL" code:ssl_error userInfo:userInfo]; |
||
3523 | } |
||
3524 | |||
3525 | - (NSError *)connectTimeoutError |
||
3526 | { |
||
3527 | NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketConnectTimeoutError", |
||
3528 | @"GCDAsyncSocket", [NSBundle mainBundle], |
||
3529 | @"Attempt to connect to host timed out", nil); |
||
3530 | |||
3531 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; |
||
3532 | |||
3533 | return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketConnectTimeoutError userInfo:userInfo]; |
||
3534 | } |
||
3535 | |||
3536 | /** |
||
3537 | * Returns a standard AsyncSocket maxed out error. |
||
3538 | **/ |
||
3539 | - (NSError *)readMaxedOutError |
||
3540 | { |
||
3541 | NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketReadMaxedOutError", |
||
3542 | @"GCDAsyncSocket", [NSBundle mainBundle], |
||
3543 | @"Read operation reached set maximum length", nil); |
||
3544 | |||
3545 | NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg}; |
||
3546 | |||
3547 | return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketReadMaxedOutError userInfo:info]; |
||
3548 | } |
||
3549 | |||
3550 | /** |
||
3551 | * Returns a standard AsyncSocket write timeout error. |
||
3552 | **/ |
||
3553 | - (NSError *)readTimeoutError |
||
3554 | { |
||
3555 | NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketReadTimeoutError", |
||
3556 | @"GCDAsyncSocket", [NSBundle mainBundle], |
||
3557 | @"Read operation timed out", nil); |
||
3558 | |||
3559 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; |
||
3560 | |||
3561 | return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketReadTimeoutError userInfo:userInfo]; |
||
3562 | } |
||
3563 | |||
3564 | /** |
||
3565 | * Returns a standard AsyncSocket write timeout error. |
||
3566 | **/ |
||
3567 | - (NSError *)writeTimeoutError |
||
3568 | { |
||
3569 | NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketWriteTimeoutError", |
||
3570 | @"GCDAsyncSocket", [NSBundle mainBundle], |
||
3571 | @"Write operation timed out", nil); |
||
3572 | |||
3573 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; |
||
3574 | |||
3575 | return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketWriteTimeoutError userInfo:userInfo]; |
||
3576 | } |
||
3577 | |||
3578 | - (NSError *)connectionClosedError |
||
3579 | { |
||
3580 | NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncSocketClosedError", |
||
3581 | @"GCDAsyncSocket", [NSBundle mainBundle], |
||
3582 | @"Socket closed by remote peer", nil); |
||
3583 | |||
3584 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; |
||
3585 | |||
3586 | return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketClosedError userInfo:userInfo]; |
||
3587 | } |
||
3588 | |||
3589 | - (NSError *)otherError:(NSString *)errMsg |
||
3590 | { |
||
3591 | NSDictionary *userInfo = @{NSLocalizedDescriptionKey : errMsg}; |
||
3592 | |||
3593 | return [NSError errorWithDomain:GCDAsyncSocketErrorDomain code:GCDAsyncSocketOtherError userInfo:userInfo]; |
||
3594 | } |
||
3595 | |||
3596 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
3597 | #pragma mark Diagnostics |
||
3598 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
3599 | |||
3600 | - (BOOL)isDisconnected |
||
3601 | { |
||
3602 | __block BOOL result = NO; |
||
3603 | |||
3604 | dispatch_block_t block = ^{ |
||
3605 | result = (self->flags & kSocketStarted) ? NO : YES; |
||
3606 | }; |
||
3607 | |||
3608 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
3609 | block(); |
||
3610 | else |
||
3611 | dispatch_sync(socketQueue, block); |
||
3612 | |||
3613 | return result; |
||
3614 | } |
||
3615 | |||
3616 | - (BOOL)isConnected |
||
3617 | { |
||
3618 | __block BOOL result = NO; |
||
3619 | |||
3620 | dispatch_block_t block = ^{ |
||
3621 | result = (self->flags & kConnected) ? YES : NO; |
||
3622 | }; |
||
3623 | |||
3624 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
3625 | block(); |
||
3626 | else |
||
3627 | dispatch_sync(socketQueue, block); |
||
3628 | |||
3629 | return result; |
||
3630 | } |
||
3631 | |||
3632 | - (NSString *)connectedHost |
||
3633 | { |
||
3634 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
3635 | { |
||
3636 | if (socket4FD != SOCKET_NULL) |
||
3637 | return [self connectedHostFromSocket4:socket4FD]; |
||
3638 | if (socket6FD != SOCKET_NULL) |
||
3639 | return [self connectedHostFromSocket6:socket6FD]; |
||
3640 | |||
3641 | return nil; |
||
3642 | } |
||
3643 | else |
||
3644 | { |
||
3645 | __block NSString *result = nil; |
||
3646 | |||
3647 | dispatch_sync(socketQueue, ^{ @autoreleasepool { |
||
3648 | |||
3649 | if (self->socket4FD != SOCKET_NULL) |
||
3650 | result = [self connectedHostFromSocket4:self->socket4FD]; |
||
3651 | else if (self->socket6FD != SOCKET_NULL) |
||
3652 | result = [self connectedHostFromSocket6:self->socket6FD]; |
||
3653 | }}); |
||
3654 | |||
3655 | return result; |
||
3656 | } |
||
3657 | } |
||
3658 | |||
3659 | - (uint16_t)connectedPort |
||
3660 | { |
||
3661 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
3662 | { |
||
3663 | if (socket4FD != SOCKET_NULL) |
||
3664 | return [self connectedPortFromSocket4:socket4FD]; |
||
3665 | if (socket6FD != SOCKET_NULL) |
||
3666 | return [self connectedPortFromSocket6:socket6FD]; |
||
3667 | |||
3668 | return 0; |
||
3669 | } |
||
3670 | else |
||
3671 | { |
||
3672 | __block uint16_t result = 0; |
||
3673 | |||
3674 | dispatch_sync(socketQueue, ^{ |
||
3675 | // No need for autorelease pool |
||
3676 | |||
3677 | if (self->socket4FD != SOCKET_NULL) |
||
3678 | result = [self connectedPortFromSocket4:self->socket4FD]; |
||
3679 | else if (self->socket6FD != SOCKET_NULL) |
||
3680 | result = [self connectedPortFromSocket6:self->socket6FD]; |
||
3681 | }); |
||
3682 | |||
3683 | return result; |
||
3684 | } |
||
3685 | } |
||
3686 | |||
3687 | - (NSURL *)connectedUrl |
||
3688 | { |
||
3689 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
3690 | { |
||
3691 | if (socketUN != SOCKET_NULL) |
||
3692 | return [self connectedUrlFromSocketUN:socketUN]; |
||
3693 | |||
3694 | return nil; |
||
3695 | } |
||
3696 | else |
||
3697 | { |
||
3698 | __block NSURL *result = nil; |
||
3699 | |||
3700 | dispatch_sync(socketQueue, ^{ @autoreleasepool { |
||
3701 | |||
3702 | if (self->socketUN != SOCKET_NULL) |
||
3703 | result = [self connectedUrlFromSocketUN:self->socketUN]; |
||
3704 | }}); |
||
3705 | |||
3706 | return result; |
||
3707 | } |
||
3708 | } |
||
3709 | |||
3710 | - (NSString *)localHost |
||
3711 | { |
||
3712 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
3713 | { |
||
3714 | if (socket4FD != SOCKET_NULL) |
||
3715 | return [self localHostFromSocket4:socket4FD]; |
||
3716 | if (socket6FD != SOCKET_NULL) |
||
3717 | return [self localHostFromSocket6:socket6FD]; |
||
3718 | |||
3719 | return nil; |
||
3720 | } |
||
3721 | else |
||
3722 | { |
||
3723 | __block NSString *result = nil; |
||
3724 | |||
3725 | dispatch_sync(socketQueue, ^{ @autoreleasepool { |
||
3726 | |||
3727 | if (self->socket4FD != SOCKET_NULL) |
||
3728 | result = [self localHostFromSocket4:self->socket4FD]; |
||
3729 | else if (self->socket6FD != SOCKET_NULL) |
||
3730 | result = [self localHostFromSocket6:self->socket6FD]; |
||
3731 | }}); |
||
3732 | |||
3733 | return result; |
||
3734 | } |
||
3735 | } |
||
3736 | |||
3737 | - (uint16_t)localPort |
||
3738 | { |
||
3739 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
3740 | { |
||
3741 | if (socket4FD != SOCKET_NULL) |
||
3742 | return [self localPortFromSocket4:socket4FD]; |
||
3743 | if (socket6FD != SOCKET_NULL) |
||
3744 | return [self localPortFromSocket6:socket6FD]; |
||
3745 | |||
3746 | return 0; |
||
3747 | } |
||
3748 | else |
||
3749 | { |
||
3750 | __block uint16_t result = 0; |
||
3751 | |||
3752 | dispatch_sync(socketQueue, ^{ |
||
3753 | // No need for autorelease pool |
||
3754 | |||
3755 | if (self->socket4FD != SOCKET_NULL) |
||
3756 | result = [self localPortFromSocket4:self->socket4FD]; |
||
3757 | else if (self->socket6FD != SOCKET_NULL) |
||
3758 | result = [self localPortFromSocket6:self->socket6FD]; |
||
3759 | }); |
||
3760 | |||
3761 | return result; |
||
3762 | } |
||
3763 | } |
||
3764 | |||
3765 | - (NSString *)connectedHost4 |
||
3766 | { |
||
3767 | if (socket4FD != SOCKET_NULL) |
||
3768 | return [self connectedHostFromSocket4:socket4FD]; |
||
3769 | |||
3770 | return nil; |
||
3771 | } |
||
3772 | |||
3773 | - (NSString *)connectedHost6 |
||
3774 | { |
||
3775 | if (socket6FD != SOCKET_NULL) |
||
3776 | return [self connectedHostFromSocket6:socket6FD]; |
||
3777 | |||
3778 | return nil; |
||
3779 | } |
||
3780 | |||
3781 | - (uint16_t)connectedPort4 |
||
3782 | { |
||
3783 | if (socket4FD != SOCKET_NULL) |
||
3784 | return [self connectedPortFromSocket4:socket4FD]; |
||
3785 | |||
3786 | return 0; |
||
3787 | } |
||
3788 | |||
3789 | - (uint16_t)connectedPort6 |
||
3790 | { |
||
3791 | if (socket6FD != SOCKET_NULL) |
||
3792 | return [self connectedPortFromSocket6:socket6FD]; |
||
3793 | |||
3794 | return 0; |
||
3795 | } |
||
3796 | |||
3797 | - (NSString *)localHost4 |
||
3798 | { |
||
3799 | if (socket4FD != SOCKET_NULL) |
||
3800 | return [self localHostFromSocket4:socket4FD]; |
||
3801 | |||
3802 | return nil; |
||
3803 | } |
||
3804 | |||
3805 | - (NSString *)localHost6 |
||
3806 | { |
||
3807 | if (socket6FD != SOCKET_NULL) |
||
3808 | return [self localHostFromSocket6:socket6FD]; |
||
3809 | |||
3810 | return nil; |
||
3811 | } |
||
3812 | |||
3813 | - (uint16_t)localPort4 |
||
3814 | { |
||
3815 | if (socket4FD != SOCKET_NULL) |
||
3816 | return [self localPortFromSocket4:socket4FD]; |
||
3817 | |||
3818 | return 0; |
||
3819 | } |
||
3820 | |||
3821 | - (uint16_t)localPort6 |
||
3822 | { |
||
3823 | if (socket6FD != SOCKET_NULL) |
||
3824 | return [self localPortFromSocket6:socket6FD]; |
||
3825 | |||
3826 | return 0; |
||
3827 | } |
||
3828 | |||
3829 | - (NSString *)connectedHostFromSocket4:(int)socketFD |
||
3830 | { |
||
3831 | struct sockaddr_in sockaddr4; |
||
3832 | socklen_t sockaddr4len = sizeof(sockaddr4); |
||
3833 | |||
3834 | if (getpeername(socketFD, (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) |
||
3835 | { |
||
3836 | return nil; |
||
3837 | } |
||
3838 | return [[self class] hostFromSockaddr4:&sockaddr4]; |
||
3839 | } |
||
3840 | |||
3841 | - (NSString *)connectedHostFromSocket6:(int)socketFD |
||
3842 | { |
||
3843 | struct sockaddr_in6 sockaddr6; |
||
3844 | socklen_t sockaddr6len = sizeof(sockaddr6); |
||
3845 | |||
3846 | if (getpeername(socketFD, (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) |
||
3847 | { |
||
3848 | return nil; |
||
3849 | } |
||
3850 | return [[self class] hostFromSockaddr6:&sockaddr6]; |
||
3851 | } |
||
3852 | |||
3853 | - (uint16_t)connectedPortFromSocket4:(int)socketFD |
||
3854 | { |
||
3855 | struct sockaddr_in sockaddr4; |
||
3856 | socklen_t sockaddr4len = sizeof(sockaddr4); |
||
3857 | |||
3858 | if (getpeername(socketFD, (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) |
||
3859 | { |
||
3860 | return 0; |
||
3861 | } |
||
3862 | return [[self class] portFromSockaddr4:&sockaddr4]; |
||
3863 | } |
||
3864 | |||
3865 | - (uint16_t)connectedPortFromSocket6:(int)socketFD |
||
3866 | { |
||
3867 | struct sockaddr_in6 sockaddr6; |
||
3868 | socklen_t sockaddr6len = sizeof(sockaddr6); |
||
3869 | |||
3870 | if (getpeername(socketFD, (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) |
||
3871 | { |
||
3872 | return 0; |
||
3873 | } |
||
3874 | return [[self class] portFromSockaddr6:&sockaddr6]; |
||
3875 | } |
||
3876 | |||
3877 | - (NSURL *)connectedUrlFromSocketUN:(int)socketFD |
||
3878 | { |
||
3879 | struct sockaddr_un sockaddr; |
||
3880 | socklen_t sockaddrlen = sizeof(sockaddr); |
||
3881 | |||
3882 | if (getpeername(socketFD, (struct sockaddr *)&sockaddr, &sockaddrlen) < 0) |
||
3883 | { |
||
3884 | return 0; |
||
3885 | } |
||
3886 | return [[self class] urlFromSockaddrUN:&sockaddr]; |
||
3887 | } |
||
3888 | |||
3889 | - (NSString *)localHostFromSocket4:(int)socketFD |
||
3890 | { |
||
3891 | struct sockaddr_in sockaddr4; |
||
3892 | socklen_t sockaddr4len = sizeof(sockaddr4); |
||
3893 | |||
3894 | if (getsockname(socketFD, (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) |
||
3895 | { |
||
3896 | return nil; |
||
3897 | } |
||
3898 | return [[self class] hostFromSockaddr4:&sockaddr4]; |
||
3899 | } |
||
3900 | |||
3901 | - (NSString *)localHostFromSocket6:(int)socketFD |
||
3902 | { |
||
3903 | struct sockaddr_in6 sockaddr6; |
||
3904 | socklen_t sockaddr6len = sizeof(sockaddr6); |
||
3905 | |||
3906 | if (getsockname(socketFD, (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) |
||
3907 | { |
||
3908 | return nil; |
||
3909 | } |
||
3910 | return [[self class] hostFromSockaddr6:&sockaddr6]; |
||
3911 | } |
||
3912 | |||
3913 | - (uint16_t)localPortFromSocket4:(int)socketFD |
||
3914 | { |
||
3915 | struct sockaddr_in sockaddr4; |
||
3916 | socklen_t sockaddr4len = sizeof(sockaddr4); |
||
3917 | |||
3918 | if (getsockname(socketFD, (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0) |
||
3919 | { |
||
3920 | return 0; |
||
3921 | } |
||
3922 | return [[self class] portFromSockaddr4:&sockaddr4]; |
||
3923 | } |
||
3924 | |||
3925 | - (uint16_t)localPortFromSocket6:(int)socketFD |
||
3926 | { |
||
3927 | struct sockaddr_in6 sockaddr6; |
||
3928 | socklen_t sockaddr6len = sizeof(sockaddr6); |
||
3929 | |||
3930 | if (getsockname(socketFD, (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0) |
||
3931 | { |
||
3932 | return 0; |
||
3933 | } |
||
3934 | return [[self class] portFromSockaddr6:&sockaddr6]; |
||
3935 | } |
||
3936 | |||
3937 | - (NSData *)connectedAddress |
||
3938 | { |
||
3939 | __block NSData *result = nil; |
||
3940 | |||
3941 | dispatch_block_t block = ^{ |
||
3942 | if (self->socket4FD != SOCKET_NULL) |
||
3943 | { |
||
3944 | struct sockaddr_in sockaddr4; |
||
3945 | socklen_t sockaddr4len = sizeof(sockaddr4); |
||
3946 | |||
3947 | if (getpeername(self->socket4FD, (struct sockaddr *)&sockaddr4, &sockaddr4len) == 0) |
||
3948 | { |
||
3949 | result = [[NSData alloc] initWithBytes:&sockaddr4 length:sockaddr4len]; |
||
3950 | } |
||
3951 | } |
||
3952 | |||
3953 | if (self->socket6FD != SOCKET_NULL) |
||
3954 | { |
||
3955 | struct sockaddr_in6 sockaddr6; |
||
3956 | socklen_t sockaddr6len = sizeof(sockaddr6); |
||
3957 | |||
3958 | if (getpeername(self->socket6FD, (struct sockaddr *)&sockaddr6, &sockaddr6len) == 0) |
||
3959 | { |
||
3960 | result = [[NSData alloc] initWithBytes:&sockaddr6 length:sockaddr6len]; |
||
3961 | } |
||
3962 | } |
||
3963 | }; |
||
3964 | |||
3965 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
3966 | block(); |
||
3967 | else |
||
3968 | dispatch_sync(socketQueue, block); |
||
3969 | |||
3970 | return result; |
||
3971 | } |
||
3972 | |||
3973 | - (NSData *)localAddress |
||
3974 | { |
||
3975 | __block NSData *result = nil; |
||
3976 | |||
3977 | dispatch_block_t block = ^{ |
||
3978 | if (self->socket4FD != SOCKET_NULL) |
||
3979 | { |
||
3980 | struct sockaddr_in sockaddr4; |
||
3981 | socklen_t sockaddr4len = sizeof(sockaddr4); |
||
3982 | |||
3983 | if (getsockname(self->socket4FD, (struct sockaddr *)&sockaddr4, &sockaddr4len) == 0) |
||
3984 | { |
||
3985 | result = [[NSData alloc] initWithBytes:&sockaddr4 length:sockaddr4len]; |
||
3986 | } |
||
3987 | } |
||
3988 | |||
3989 | if (self->socket6FD != SOCKET_NULL) |
||
3990 | { |
||
3991 | struct sockaddr_in6 sockaddr6; |
||
3992 | socklen_t sockaddr6len = sizeof(sockaddr6); |
||
3993 | |||
3994 | if (getsockname(self->socket6FD, (struct sockaddr *)&sockaddr6, &sockaddr6len) == 0) |
||
3995 | { |
||
3996 | result = [[NSData alloc] initWithBytes:&sockaddr6 length:sockaddr6len]; |
||
3997 | } |
||
3998 | } |
||
3999 | }; |
||
4000 | |||
4001 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
4002 | block(); |
||
4003 | else |
||
4004 | dispatch_sync(socketQueue, block); |
||
4005 | |||
4006 | return result; |
||
4007 | } |
||
4008 | |||
4009 | - (BOOL)isIPv4 |
||
4010 | { |
||
4011 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
4012 | { |
||
4013 | return (socket4FD != SOCKET_NULL); |
||
4014 | } |
||
4015 | else |
||
4016 | { |
||
4017 | __block BOOL result = NO; |
||
4018 | |||
4019 | dispatch_sync(socketQueue, ^{ |
||
4020 | result = (self->socket4FD != SOCKET_NULL); |
||
4021 | }); |
||
4022 | |||
4023 | return result; |
||
4024 | } |
||
4025 | } |
||
4026 | |||
4027 | - (BOOL)isIPv6 |
||
4028 | { |
||
4029 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
4030 | { |
||
4031 | return (socket6FD != SOCKET_NULL); |
||
4032 | } |
||
4033 | else |
||
4034 | { |
||
4035 | __block BOOL result = NO; |
||
4036 | |||
4037 | dispatch_sync(socketQueue, ^{ |
||
4038 | result = (self->socket6FD != SOCKET_NULL); |
||
4039 | }); |
||
4040 | |||
4041 | return result; |
||
4042 | } |
||
4043 | } |
||
4044 | |||
4045 | - (BOOL)isSecure |
||
4046 | { |
||
4047 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
4048 | { |
||
4049 | return (flags & kSocketSecure) ? YES : NO; |
||
4050 | } |
||
4051 | else |
||
4052 | { |
||
4053 | __block BOOL result; |
||
4054 | |||
4055 | dispatch_sync(socketQueue, ^{ |
||
4056 | result = (self->flags & kSocketSecure) ? YES : NO; |
||
4057 | }); |
||
4058 | |||
4059 | return result; |
||
4060 | } |
||
4061 | } |
||
4062 | |||
4063 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
4064 | #pragma mark Utilities |
||
4065 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
4066 | |||
4067 | /** |
||
4068 | * Finds the address of an interface description. |
||
4069 | * An inteface description may be an interface name (en0, en1, lo0) or corresponding IP (192.168.4.34). |
||
4070 | * |
||
4071 | * The interface description may optionally contain a port number at the end, separated by a colon. |
||
4072 | * If a non-zero port parameter is provided, any port number in the interface description is ignored. |
||
4073 | * |
||
4074 | * The returned value is a 'struct sockaddr' wrapped in an NSMutableData object. |
||
4075 | **/ |
||
4076 | - (void)getInterfaceAddress4:(NSMutableData **)interfaceAddr4Ptr |
||
4077 | address6:(NSMutableData **)interfaceAddr6Ptr |
||
4078 | fromDescription:(NSString *)interfaceDescription |
||
4079 | port:(uint16_t)port |
||
4080 | { |
||
4081 | NSMutableData *addr4 = nil; |
||
4082 | NSMutableData *addr6 = nil; |
||
4083 | |||
4084 | NSString *interface = nil; |
||
4085 | |||
4086 | NSArray *components = [interfaceDescription componentsSeparatedByString:@":"]; |
||
4087 | if ([components count] > 0) |
||
4088 | { |
||
4089 | NSString *temp = [components objectAtIndex:0]; |
||
4090 | if ([temp length] > 0) |
||
4091 | { |
||
4092 | interface = temp; |
||
4093 | } |
||
4094 | } |
||
4095 | if ([components count] > 1 && port == 0) |
||
4096 | { |
||
4097 | NSString *temp = [components objectAtIndex:1]; |
||
4098 | long portL = strtol([temp UTF8String], NULL, 10); |
||
4099 | |||
4100 | if (portL > 0 && portL <= UINT16_MAX) |
||
4101 | { |
||
4102 | port = (uint16_t)portL; |
||
4103 | } |
||
4104 | } |
||
4105 | |||
4106 | if (interface == nil) |
||
4107 | { |
||
4108 | // ANY address |
||
4109 | |||
4110 | struct sockaddr_in sockaddr4; |
||
4111 | memset(&sockaddr4, 0, sizeof(sockaddr4)); |
||
4112 | |||
4113 | sockaddr4.sin_len = sizeof(sockaddr4); |
||
4114 | sockaddr4.sin_family = AF_INET; |
||
4115 | sockaddr4.sin_port = htons(port); |
||
4116 | sockaddr4.sin_addr.s_addr = htonl(INADDR_ANY); |
||
4117 | |||
4118 | struct sockaddr_in6 sockaddr6; |
||
4119 | memset(&sockaddr6, 0, sizeof(sockaddr6)); |
||
4120 | |||
4121 | sockaddr6.sin6_len = sizeof(sockaddr6); |
||
4122 | sockaddr6.sin6_family = AF_INET6; |
||
4123 | sockaddr6.sin6_port = htons(port); |
||
4124 | sockaddr6.sin6_addr = in6addr_any; |
||
4125 | |||
4126 | addr4 = [NSMutableData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)]; |
||
4127 | addr6 = [NSMutableData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)]; |
||
4128 | } |
||
4129 | else if ([interface isEqualToString:@"localhost"] || [interface isEqualToString:@"loopback"]) |
||
4130 | { |
||
4131 | // LOOPBACK address |
||
4132 | |||
4133 | struct sockaddr_in sockaddr4; |
||
4134 | memset(&sockaddr4, 0, sizeof(sockaddr4)); |
||
4135 | |||
4136 | sockaddr4.sin_len = sizeof(sockaddr4); |
||
4137 | sockaddr4.sin_family = AF_INET; |
||
4138 | sockaddr4.sin_port = htons(port); |
||
4139 | sockaddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
||
4140 | |||
4141 | struct sockaddr_in6 sockaddr6; |
||
4142 | memset(&sockaddr6, 0, sizeof(sockaddr6)); |
||
4143 | |||
4144 | sockaddr6.sin6_len = sizeof(sockaddr6); |
||
4145 | sockaddr6.sin6_family = AF_INET6; |
||
4146 | sockaddr6.sin6_port = htons(port); |
||
4147 | sockaddr6.sin6_addr = in6addr_loopback; |
||
4148 | |||
4149 | addr4 = [NSMutableData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)]; |
||
4150 | addr6 = [NSMutableData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)]; |
||
4151 | } |
||
4152 | else |
||
4153 | { |
||
4154 | const char *iface = [interface UTF8String]; |
||
4155 | |||
4156 | struct ifaddrs *addrs; |
||
4157 | const struct ifaddrs *cursor; |
||
4158 | |||
4159 | if ((getifaddrs(&addrs) == 0)) |
||
4160 | { |
||
4161 | cursor = addrs; |
||
4162 | while (cursor != NULL) |
||
4163 | { |
||
4164 | if ((addr4 == nil) && (cursor->ifa_addr->sa_family == AF_INET)) |
||
4165 | { |
||
4166 | // IPv4 |
||
4167 | |||
4168 | struct sockaddr_in nativeAddr4; |
||
4169 | memcpy(&nativeAddr4, cursor->ifa_addr, sizeof(nativeAddr4)); |
||
4170 | |||
4171 | if (strcmp(cursor->ifa_name, iface) == 0) |
||
4172 | { |
||
4173 | // Name match |
||
4174 | |||
4175 | nativeAddr4.sin_port = htons(port); |
||
4176 | |||
4177 | addr4 = [NSMutableData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; |
||
4178 | } |
||
4179 | else |
||
4180 | { |
||
4181 | char ip[INET_ADDRSTRLEN]; |
||
4182 | |||
4183 | const char *conversion = inet_ntop(AF_INET, &nativeAddr4.sin_addr, ip, sizeof(ip)); |
||
4184 | |||
4185 | if ((conversion != NULL) && (strcmp(ip, iface) == 0)) |
||
4186 | { |
||
4187 | // IP match |
||
4188 | |||
4189 | nativeAddr4.sin_port = htons(port); |
||
4190 | |||
4191 | addr4 = [NSMutableData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; |
||
4192 | } |
||
4193 | } |
||
4194 | } |
||
4195 | else if ((addr6 == nil) && (cursor->ifa_addr->sa_family == AF_INET6)) |
||
4196 | { |
||
4197 | // IPv6 |
||
4198 | |||
4199 | struct sockaddr_in6 nativeAddr6; |
||
4200 | memcpy(&nativeAddr6, cursor->ifa_addr, sizeof(nativeAddr6)); |
||
4201 | |||
4202 | if (strcmp(cursor->ifa_name, iface) == 0) |
||
4203 | { |
||
4204 | // Name match |
||
4205 | |||
4206 | nativeAddr6.sin6_port = htons(port); |
||
4207 | |||
4208 | addr6 = [NSMutableData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; |
||
4209 | } |
||
4210 | else |
||
4211 | { |
||
4212 | char ip[INET6_ADDRSTRLEN]; |
||
4213 | |||
4214 | const char *conversion = inet_ntop(AF_INET6, &nativeAddr6.sin6_addr, ip, sizeof(ip)); |
||
4215 | |||
4216 | if ((conversion != NULL) && (strcmp(ip, iface) == 0)) |
||
4217 | { |
||
4218 | // IP match |
||
4219 | |||
4220 | nativeAddr6.sin6_port = htons(port); |
||
4221 | |||
4222 | addr6 = [NSMutableData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; |
||
4223 | } |
||
4224 | } |
||
4225 | } |
||
4226 | |||
4227 | cursor = cursor->ifa_next; |
||
4228 | } |
||
4229 | |||
4230 | freeifaddrs(addrs); |
||
4231 | } |
||
4232 | } |
||
4233 | |||
4234 | if (interfaceAddr4Ptr) *interfaceAddr4Ptr = addr4; |
||
4235 | if (interfaceAddr6Ptr) *interfaceAddr6Ptr = addr6; |
||
4236 | } |
||
4237 | |||
4238 | - (NSData *)getInterfaceAddressFromUrl:(NSURL *)url |
||
4239 | { |
||
4240 | NSString *path = url.path; |
||
4241 | if (path.length == 0) { |
||
4242 | return nil; |
||
4243 | } |
||
4244 | |||
4245 | struct sockaddr_un nativeAddr; |
||
4246 | nativeAddr.sun_family = AF_UNIX; |
||
4247 | strlcpy(nativeAddr.sun_path, path.fileSystemRepresentation, sizeof(nativeAddr.sun_path)); |
||
4248 | nativeAddr.sun_len = (unsigned char)SUN_LEN(&nativeAddr); |
||
4249 | NSData *interface = [NSData dataWithBytes:&nativeAddr length:sizeof(struct sockaddr_un)]; |
||
4250 | |||
4251 | return interface; |
||
4252 | } |
||
4253 | |||
4254 | - (void)setupReadAndWriteSourcesForNewlyConnectedSocket:(int)socketFD |
||
4255 | { |
||
4256 | readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socketFD, 0, socketQueue); |
||
4257 | writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socketFD, 0, socketQueue); |
||
4258 | |||
4259 | // Setup event handlers |
||
4260 | |||
4261 | __weak GCDAsyncSocket *weakSelf = self; |
||
4262 | |||
4263 | dispatch_source_set_event_handler(readSource, ^{ @autoreleasepool { |
||
4264 | #pragma clang diagnostic push |
||
4265 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
4266 | |||
4267 | __strong GCDAsyncSocket *strongSelf = weakSelf; |
||
4268 | if (strongSelf == nil) return_from_block; |
||
4269 | |||
4270 | LogVerbose(@"readEventBlock"); |
||
4271 | |||
4272 | strongSelf->socketFDBytesAvailable = dispatch_source_get_data(strongSelf->readSource); |
||
4273 | LogVerbose(@"socketFDBytesAvailable: %lu", strongSelf->socketFDBytesAvailable); |
||
4274 | |||
4275 | if (strongSelf->socketFDBytesAvailable > 0) |
||
4276 | [strongSelf doReadData]; |
||
4277 | else |
||
4278 | [strongSelf doReadEOF]; |
||
4279 | |||
4280 | #pragma clang diagnostic pop |
||
4281 | }}); |
||
4282 | |||
4283 | dispatch_source_set_event_handler(writeSource, ^{ @autoreleasepool { |
||
4284 | #pragma clang diagnostic push |
||
4285 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
4286 | |||
4287 | __strong GCDAsyncSocket *strongSelf = weakSelf; |
||
4288 | if (strongSelf == nil) return_from_block; |
||
4289 | |||
4290 | LogVerbose(@"writeEventBlock"); |
||
4291 | |||
4292 | strongSelf->flags |= kSocketCanAcceptBytes; |
||
4293 | [strongSelf doWriteData]; |
||
4294 | |||
4295 | #pragma clang diagnostic pop |
||
4296 | }}); |
||
4297 | |||
4298 | // Setup cancel handlers |
||
4299 | |||
4300 | __block int socketFDRefCount = 2; |
||
4301 | |||
4302 | #if !OS_OBJECT_USE_OBJC |
||
4303 | dispatch_source_t theReadSource = readSource; |
||
4304 | dispatch_source_t theWriteSource = writeSource; |
||
4305 | #endif |
||
4306 | |||
4307 | dispatch_source_set_cancel_handler(readSource, ^{ |
||
4308 | #pragma clang diagnostic push |
||
4309 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
4310 | |||
4311 | LogVerbose(@"readCancelBlock"); |
||
4312 | |||
4313 | #if !OS_OBJECT_USE_OBJC |
||
4314 | LogVerbose(@"dispatch_release(readSource)"); |
||
4315 | dispatch_release(theReadSource); |
||
4316 | #endif |
||
4317 | |||
4318 | if (--socketFDRefCount == 0) |
||
4319 | { |
||
4320 | LogVerbose(@"close(socketFD)"); |
||
4321 | close(socketFD); |
||
4322 | } |
||
4323 | |||
4324 | #pragma clang diagnostic pop |
||
4325 | }); |
||
4326 | |||
4327 | dispatch_source_set_cancel_handler(writeSource, ^{ |
||
4328 | #pragma clang diagnostic push |
||
4329 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
4330 | |||
4331 | LogVerbose(@"writeCancelBlock"); |
||
4332 | |||
4333 | #if !OS_OBJECT_USE_OBJC |
||
4334 | LogVerbose(@"dispatch_release(writeSource)"); |
||
4335 | dispatch_release(theWriteSource); |
||
4336 | #endif |
||
4337 | |||
4338 | if (--socketFDRefCount == 0) |
||
4339 | { |
||
4340 | LogVerbose(@"close(socketFD)"); |
||
4341 | close(socketFD); |
||
4342 | } |
||
4343 | |||
4344 | #pragma clang diagnostic pop |
||
4345 | }); |
||
4346 | |||
4347 | // We will not be able to read until data arrives. |
||
4348 | // But we should be able to write immediately. |
||
4349 | |||
4350 | socketFDBytesAvailable = 0; |
||
4351 | flags &= ~kReadSourceSuspended; |
||
4352 | |||
4353 | LogVerbose(@"dispatch_resume(readSource)"); |
||
4354 | dispatch_resume(readSource); |
||
4355 | |||
4356 | flags |= kSocketCanAcceptBytes; |
||
4357 | flags |= kWriteSourceSuspended; |
||
4358 | } |
||
4359 | |||
4360 | - (BOOL)usingCFStreamForTLS |
||
4361 | { |
||
4362 | #if TARGET_OS_IPHONE |
||
4363 | |||
4364 | if ((flags & kSocketSecure) && (flags & kUsingCFStreamForTLS)) |
||
4365 | { |
||
4366 | // The startTLS method was given the GCDAsyncSocketUseCFStreamForTLS flag. |
||
4367 | |||
4368 | return YES; |
||
4369 | } |
||
4370 | |||
4371 | #endif |
||
4372 | |||
4373 | return NO; |
||
4374 | } |
||
4375 | |||
4376 | - (BOOL)usingSecureTransportForTLS |
||
4377 | { |
||
4378 | // Invoking this method is equivalent to ![self usingCFStreamForTLS] (just more readable) |
||
4379 | |||
4380 | #if TARGET_OS_IPHONE |
||
4381 | |||
4382 | if ((flags & kSocketSecure) && (flags & kUsingCFStreamForTLS)) |
||
4383 | { |
||
4384 | // The startTLS method was given the GCDAsyncSocketUseCFStreamForTLS flag. |
||
4385 | |||
4386 | return NO; |
||
4387 | } |
||
4388 | |||
4389 | #endif |
||
4390 | |||
4391 | return YES; |
||
4392 | } |
||
4393 | |||
4394 | - (void)suspendReadSource |
||
4395 | { |
||
4396 | if (!(flags & kReadSourceSuspended)) |
||
4397 | { |
||
4398 | LogVerbose(@"dispatch_suspend(readSource)"); |
||
4399 | |||
4400 | dispatch_suspend(readSource); |
||
4401 | flags |= kReadSourceSuspended; |
||
4402 | } |
||
4403 | } |
||
4404 | |||
4405 | - (void)resumeReadSource |
||
4406 | { |
||
4407 | if (flags & kReadSourceSuspended) |
||
4408 | { |
||
4409 | LogVerbose(@"dispatch_resume(readSource)"); |
||
4410 | |||
4411 | dispatch_resume(readSource); |
||
4412 | flags &= ~kReadSourceSuspended; |
||
4413 | } |
||
4414 | } |
||
4415 | |||
4416 | - (void)suspendWriteSource |
||
4417 | { |
||
4418 | if (!(flags & kWriteSourceSuspended)) |
||
4419 | { |
||
4420 | LogVerbose(@"dispatch_suspend(writeSource)"); |
||
4421 | |||
4422 | dispatch_suspend(writeSource); |
||
4423 | flags |= kWriteSourceSuspended; |
||
4424 | } |
||
4425 | } |
||
4426 | |||
4427 | - (void)resumeWriteSource |
||
4428 | { |
||
4429 | if (flags & kWriteSourceSuspended) |
||
4430 | { |
||
4431 | LogVerbose(@"dispatch_resume(writeSource)"); |
||
4432 | |||
4433 | dispatch_resume(writeSource); |
||
4434 | flags &= ~kWriteSourceSuspended; |
||
4435 | } |
||
4436 | } |
||
4437 | |||
4438 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
4439 | #pragma mark Reading |
||
4440 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
4441 | |||
4442 | - (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag |
||
4443 | { |
||
4444 | [self readDataWithTimeout:timeout buffer:nil bufferOffset:0 maxLength:0 tag:tag]; |
||
4445 | } |
||
4446 | |||
4447 | - (void)readDataWithTimeout:(NSTimeInterval)timeout |
||
4448 | buffer:(NSMutableData *)buffer |
||
4449 | bufferOffset:(NSUInteger)offset |
||
4450 | tag:(long)tag |
||
4451 | { |
||
4452 | [self readDataWithTimeout:timeout buffer:buffer bufferOffset:offset maxLength:0 tag:tag]; |
||
4453 | } |
||
4454 | |||
4455 | - (void)readDataWithTimeout:(NSTimeInterval)timeout |
||
4456 | buffer:(NSMutableData *)buffer |
||
4457 | bufferOffset:(NSUInteger)offset |
||
4458 | maxLength:(NSUInteger)length |
||
4459 | tag:(long)tag |
||
4460 | { |
||
4461 | if (offset > [buffer length]) { |
||
4462 | LogWarn(@"Cannot read: offset > [buffer length]"); |
||
4463 | return; |
||
4464 | } |
||
4465 | |||
4466 | GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer |
||
4467 | startOffset:offset |
||
4468 | maxLength:length |
||
4469 | timeout:timeout |
||
4470 | readLength:0 |
||
4471 | terminator:nil |
||
4472 | tag:tag]; |
||
4473 | |||
4474 | dispatch_async(socketQueue, ^{ @autoreleasepool { |
||
4475 | |||
4476 | LogTrace(); |
||
4477 | |||
4478 | if ((self->flags & kSocketStarted) && !(self->flags & kForbidReadsWrites)) |
||
4479 | { |
||
4480 | [self->readQueue addObject:packet]; |
||
4481 | [self maybeDequeueRead]; |
||
4482 | } |
||
4483 | }}); |
||
4484 | |||
4485 | // Do not rely on the block being run in order to release the packet, |
||
4486 | // as the queue might get released without the block completing. |
||
4487 | } |
||
4488 | |||
4489 | - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag |
||
4490 | { |
||
4491 | [self readDataToLength:length withTimeout:timeout buffer:nil bufferOffset:0 tag:tag]; |
||
4492 | } |
||
4493 | |||
4494 | - (void)readDataToLength:(NSUInteger)length |
||
4495 | withTimeout:(NSTimeInterval)timeout |
||
4496 | buffer:(NSMutableData *)buffer |
||
4497 | bufferOffset:(NSUInteger)offset |
||
4498 | tag:(long)tag |
||
4499 | { |
||
4500 | if (length == 0) { |
||
4501 | LogWarn(@"Cannot read: length == 0"); |
||
4502 | return; |
||
4503 | } |
||
4504 | if (offset > [buffer length]) { |
||
4505 | LogWarn(@"Cannot read: offset > [buffer length]"); |
||
4506 | return; |
||
4507 | } |
||
4508 | |||
4509 | GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer |
||
4510 | startOffset:offset |
||
4511 | maxLength:0 |
||
4512 | timeout:timeout |
||
4513 | readLength:length |
||
4514 | terminator:nil |
||
4515 | tag:tag]; |
||
4516 | |||
4517 | dispatch_async(socketQueue, ^{ @autoreleasepool { |
||
4518 | |||
4519 | LogTrace(); |
||
4520 | |||
4521 | if ((self->flags & kSocketStarted) && !(self->flags & kForbidReadsWrites)) |
||
4522 | { |
||
4523 | [self->readQueue addObject:packet]; |
||
4524 | [self maybeDequeueRead]; |
||
4525 | } |
||
4526 | }}); |
||
4527 | |||
4528 | // Do not rely on the block being run in order to release the packet, |
||
4529 | // as the queue might get released without the block completing. |
||
4530 | } |
||
4531 | |||
4532 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag |
||
4533 | { |
||
4534 | [self readDataToData:data withTimeout:timeout buffer:nil bufferOffset:0 maxLength:0 tag:tag]; |
||
4535 | } |
||
4536 | |||
4537 | - (void)readDataToData:(NSData *)data |
||
4538 | withTimeout:(NSTimeInterval)timeout |
||
4539 | buffer:(NSMutableData *)buffer |
||
4540 | bufferOffset:(NSUInteger)offset |
||
4541 | tag:(long)tag |
||
4542 | { |
||
4543 | [self readDataToData:data withTimeout:timeout buffer:buffer bufferOffset:offset maxLength:0 tag:tag]; |
||
4544 | } |
||
4545 | |||
4546 | - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(NSUInteger)length tag:(long)tag |
||
4547 | { |
||
4548 | [self readDataToData:data withTimeout:timeout buffer:nil bufferOffset:0 maxLength:length tag:tag]; |
||
4549 | } |
||
4550 | |||
4551 | - (void)readDataToData:(NSData *)data |
||
4552 | withTimeout:(NSTimeInterval)timeout |
||
4553 | buffer:(NSMutableData *)buffer |
||
4554 | bufferOffset:(NSUInteger)offset |
||
4555 | maxLength:(NSUInteger)maxLength |
||
4556 | tag:(long)tag |
||
4557 | { |
||
4558 | if ([data length] == 0) { |
||
4559 | LogWarn(@"Cannot read: [data length] == 0"); |
||
4560 | return; |
||
4561 | } |
||
4562 | if (offset > [buffer length]) { |
||
4563 | LogWarn(@"Cannot read: offset > [buffer length]"); |
||
4564 | return; |
||
4565 | } |
||
4566 | if (maxLength > 0 && maxLength < [data length]) { |
||
4567 | LogWarn(@"Cannot read: maxLength > 0 && maxLength < [data length]"); |
||
4568 | return; |
||
4569 | } |
||
4570 | |||
4571 | GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer |
||
4572 | startOffset:offset |
||
4573 | maxLength:maxLength |
||
4574 | timeout:timeout |
||
4575 | readLength:0 |
||
4576 | terminator:data |
||
4577 | tag:tag]; |
||
4578 | |||
4579 | dispatch_async(socketQueue, ^{ @autoreleasepool { |
||
4580 | |||
4581 | LogTrace(); |
||
4582 | |||
4583 | if ((self->flags & kSocketStarted) && !(self->flags & kForbidReadsWrites)) |
||
4584 | { |
||
4585 | [self->readQueue addObject:packet]; |
||
4586 | [self maybeDequeueRead]; |
||
4587 | } |
||
4588 | }}); |
||
4589 | |||
4590 | // Do not rely on the block being run in order to release the packet, |
||
4591 | // as the queue might get released without the block completing. |
||
4592 | } |
||
4593 | |||
4594 | - (float)progressOfReadReturningTag:(long *)tagPtr bytesDone:(NSUInteger *)donePtr total:(NSUInteger *)totalPtr |
||
4595 | { |
||
4596 | __block float result = 0.0F; |
||
4597 | |||
4598 | dispatch_block_t block = ^{ |
||
4599 | |||
4600 | if (!self->currentRead || ![self->currentRead isKindOfClass:[GCDAsyncReadPacket class]]) |
||
4601 | { |
||
4602 | // We're not reading anything right now. |
||
4603 | |||
4604 | if (tagPtr != NULL) *tagPtr = 0; |
||
4605 | if (donePtr != NULL) *donePtr = 0; |
||
4606 | if (totalPtr != NULL) *totalPtr = 0; |
||
4607 | |||
4608 | result = NAN; |
||
4609 | } |
||
4610 | else |
||
4611 | { |
||
4612 | // It's only possible to know the progress of our read if we're reading to a certain length. |
||
4613 | // If we're reading to data, we of course have no idea when the data will arrive. |
||
4614 | // If we're reading to timeout, then we have no idea when the next chunk of data will arrive. |
||
4615 | |||
4616 | NSUInteger done = self->currentRead->bytesDone; |
||
4617 | NSUInteger total = self->currentRead->readLength; |
||
4618 | |||
4619 | if (tagPtr != NULL) *tagPtr = self->currentRead->tag; |
||
4620 | if (donePtr != NULL) *donePtr = done; |
||
4621 | if (totalPtr != NULL) *totalPtr = total; |
||
4622 | |||
4623 | if (total > 0) |
||
4624 | result = (float)done / (float)total; |
||
4625 | else |
||
4626 | result = 1.0F; |
||
4627 | } |
||
4628 | }; |
||
4629 | |||
4630 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
4631 | block(); |
||
4632 | else |
||
4633 | dispatch_sync(socketQueue, block); |
||
4634 | |||
4635 | return result; |
||
4636 | } |
||
4637 | |||
4638 | /** |
||
4639 | * This method starts a new read, if needed. |
||
4640 | * |
||
4641 | * It is called when: |
||
4642 | * - a user requests a read |
||
4643 | * - after a read request has finished (to handle the next request) |
||
4644 | * - immediately after the socket opens to handle any pending requests |
||
4645 | * |
||
4646 | * This method also handles auto-disconnect post read/write completion. |
||
4647 | **/ |
||
4648 | - (void)maybeDequeueRead |
||
4649 | { |
||
4650 | LogTrace(); |
||
4651 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
4652 | |||
4653 | // If we're not currently processing a read AND we have an available read stream |
||
4654 | if ((currentRead == nil) && (flags & kConnected)) |
||
4655 | { |
||
4656 | if ([readQueue count] > 0) |
||
4657 | { |
||
4658 | // Dequeue the next object in the write queue |
||
4659 | currentRead = [readQueue objectAtIndex:0]; |
||
4660 | [readQueue removeObjectAtIndex:0]; |
||
4661 | |||
4662 | |||
4663 | if ([currentRead isKindOfClass:[GCDAsyncSpecialPacket class]]) |
||
4664 | { |
||
4665 | LogVerbose(@"Dequeued GCDAsyncSpecialPacket"); |
||
4666 | |||
4667 | // Attempt to start TLS |
||
4668 | flags |= kStartingReadTLS; |
||
4669 | |||
4670 | // This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are set |
||
4671 | [self maybeStartTLS]; |
||
4672 | } |
||
4673 | else |
||
4674 | { |
||
4675 | LogVerbose(@"Dequeued GCDAsyncReadPacket"); |
||
4676 | |||
4677 | // Setup read timer (if needed) |
||
4678 | [self setupReadTimerWithTimeout:currentRead->timeout]; |
||
4679 | |||
4680 | // Immediately read, if possible |
||
4681 | [self doReadData]; |
||
4682 | } |
||
4683 | } |
||
4684 | else if (flags & kDisconnectAfterReads) |
||
4685 | { |
||
4686 | if (flags & kDisconnectAfterWrites) |
||
4687 | { |
||
4688 | if (([writeQueue count] == 0) && (currentWrite == nil)) |
||
4689 | { |
||
4690 | [self closeWithError:nil]; |
||
4691 | } |
||
4692 | } |
||
4693 | else |
||
4694 | { |
||
4695 | [self closeWithError:nil]; |
||
4696 | } |
||
4697 | } |
||
4698 | else if (flags & kSocketSecure) |
||
4699 | { |
||
4700 | [self flushSSLBuffers]; |
||
4701 | |||
4702 | // Edge case: |
||
4703 | // |
||
4704 | // We just drained all data from the ssl buffers, |
||
4705 | // and all known data from the socket (socketFDBytesAvailable). |
||
4706 | // |
||
4707 | // If we didn't get any data from this process, |
||
4708 | // then we may have reached the end of the TCP stream. |
||
4709 | // |
||
4710 | // Be sure callbacks are enabled so we're notified about a disconnection. |
||
4711 | |||
4712 | if ([preBuffer availableBytes] == 0) |
||
4713 | { |
||
4714 | if ([self usingCFStreamForTLS]) { |
||
4715 | // Callbacks never disabled |
||
4716 | } |
||
4717 | else { |
||
4718 | [self resumeReadSource]; |
||
4719 | } |
||
4720 | } |
||
4721 | } |
||
4722 | } |
||
4723 | } |
||
4724 | |||
4725 | - (void)flushSSLBuffers |
||
4726 | { |
||
4727 | LogTrace(); |
||
4728 | |||
4729 | NSAssert((flags & kSocketSecure), @"Cannot flush ssl buffers on non-secure socket"); |
||
4730 | |||
4731 | if ([preBuffer availableBytes] > 0) |
||
4732 | { |
||
4733 | // Only flush the ssl buffers if the prebuffer is empty. |
||
4734 | // This is to avoid growing the prebuffer inifinitely large. |
||
4735 | |||
4736 | return; |
||
4737 | } |
||
4738 | |||
4739 | #if TARGET_OS_IPHONE |
||
4740 | |||
4741 | if ([self usingCFStreamForTLS]) |
||
4742 | { |
||
4743 | if ((flags & kSecureSocketHasBytesAvailable) && CFReadStreamHasBytesAvailable(readStream)) |
||
4744 | { |
||
4745 | LogVerbose(@"%@ - Flushing ssl buffers into prebuffer...", THIS_METHOD); |
||
4746 | |||
4747 | CFIndex defaultBytesToRead = (1024 * 4); |
||
4748 | |||
4749 | [preBuffer ensureCapacityForWrite:defaultBytesToRead]; |
||
4750 | |||
4751 | uint8_t *buffer = [preBuffer writeBuffer]; |
||
4752 | |||
4753 | CFIndex result = CFReadStreamRead(readStream, buffer, defaultBytesToRead); |
||
4754 | LogVerbose(@"%@ - CFReadStreamRead(): result = %i", THIS_METHOD, (int)result); |
||
4755 | |||
4756 | if (result > 0) |
||
4757 | { |
||
4758 | [preBuffer didWrite:result]; |
||
4759 | } |
||
4760 | |||
4761 | flags &= ~kSecureSocketHasBytesAvailable; |
||
4762 | } |
||
4763 | |||
4764 | return; |
||
4765 | } |
||
4766 | |||
4767 | #endif |
||
4768 | |||
4769 | __block NSUInteger estimatedBytesAvailable = 0; |
||
4770 | |||
4771 | dispatch_block_t updateEstimatedBytesAvailable = ^{ |
||
4772 | |||
4773 | // Figure out if there is any data available to be read |
||
4774 | // |
||
4775 | // socketFDBytesAvailable <- Number of encrypted bytes we haven't read from the bsd socket |
||
4776 | // [sslPreBuffer availableBytes] <- Number of encrypted bytes we've buffered from bsd socket |
||
4777 | // sslInternalBufSize <- Number of decrypted bytes SecureTransport has buffered |
||
4778 | // |
||
4779 | // We call the variable "estimated" because we don't know how many decrypted bytes we'll get |
||
4780 | // from the encrypted bytes in the sslPreBuffer. |
||
4781 | // However, we do know this is an upper bound on the estimation. |
||
4782 | |||
4783 | estimatedBytesAvailable = self->socketFDBytesAvailable + [self->sslPreBuffer availableBytes]; |
||
4784 | |||
4785 | size_t sslInternalBufSize = 0; |
||
4786 | SSLGetBufferedReadSize(self->sslContext, &sslInternalBufSize); |
||
4787 | |||
4788 | estimatedBytesAvailable += sslInternalBufSize; |
||
4789 | }; |
||
4790 | |||
4791 | updateEstimatedBytesAvailable(); |
||
4792 | |||
4793 | if (estimatedBytesAvailable > 0) |
||
4794 | { |
||
4795 | LogVerbose(@"%@ - Flushing ssl buffers into prebuffer...", THIS_METHOD); |
||
4796 | |||
4797 | BOOL done = NO; |
||
4798 | do |
||
4799 | { |
||
4800 | LogVerbose(@"%@ - estimatedBytesAvailable = %lu", THIS_METHOD, (unsigned long)estimatedBytesAvailable); |
||
4801 | |||
4802 | // Make sure there's enough room in the prebuffer |
||
4803 | |||
4804 | [preBuffer ensureCapacityForWrite:estimatedBytesAvailable]; |
||
4805 | |||
4806 | // Read data into prebuffer |
||
4807 | |||
4808 | uint8_t *buffer = [preBuffer writeBuffer]; |
||
4809 | size_t bytesRead = 0; |
||
4810 | |||
4811 | OSStatus result = SSLRead(sslContext, buffer, (size_t)estimatedBytesAvailable, &bytesRead); |
||
4812 | LogVerbose(@"%@ - read from secure socket = %u", THIS_METHOD, (unsigned)bytesRead); |
||
4813 | |||
4814 | if (bytesRead > 0) |
||
4815 | { |
||
4816 | [preBuffer didWrite:bytesRead]; |
||
4817 | } |
||
4818 | |||
4819 | LogVerbose(@"%@ - prebuffer.length = %zu", THIS_METHOD, [preBuffer availableBytes]); |
||
4820 | |||
4821 | if (result != noErr) |
||
4822 | { |
||
4823 | done = YES; |
||
4824 | } |
||
4825 | else |
||
4826 | { |
||
4827 | updateEstimatedBytesAvailable(); |
||
4828 | } |
||
4829 | |||
4830 | } while (!done && estimatedBytesAvailable > 0); |
||
4831 | } |
||
4832 | } |
||
4833 | |||
4834 | - (void)doReadData |
||
4835 | { |
||
4836 | LogTrace(); |
||
4837 | |||
4838 | // This method is called on the socketQueue. |
||
4839 | // It might be called directly, or via the readSource when data is available to be read. |
||
4840 | |||
4841 | if ((currentRead == nil) || (flags & kReadsPaused)) |
||
4842 | { |
||
4843 | LogVerbose(@"No currentRead or kReadsPaused"); |
||
4844 | |||
4845 | // Unable to read at this time |
||
4846 | |||
4847 | if (flags & kSocketSecure) |
||
4848 | { |
||
4849 | // Here's the situation: |
||
4850 | // |
||
4851 | // We have an established secure connection. |
||
4852 | // There may not be a currentRead, but there might be encrypted data sitting around for us. |
||
4853 | // When the user does get around to issuing a read, that encrypted data will need to be decrypted. |
||
4854 | // |
||
4855 | // So why make the user wait? |
||
4856 | // We might as well get a head start on decrypting some data now. |
||
4857 | // |
||
4858 | // The other reason we do this has to do with detecting a socket disconnection. |
||
4859 | // The SSL/TLS protocol has it's own disconnection handshake. |
||
4860 | // So when a secure socket is closed, a "goodbye" packet comes across the wire. |
||
4861 | // We want to make sure we read the "goodbye" packet so we can properly detect the TCP disconnection. |
||
4862 | |||
4863 | [self flushSSLBuffers]; |
||
4864 | } |
||
4865 | |||
4866 | if ([self usingCFStreamForTLS]) |
||
4867 | { |
||
4868 | // CFReadStream only fires once when there is available data. |
||
4869 | // It won't fire again until we've invoked CFReadStreamRead. |
||
4870 | } |
||
4871 | else |
||
4872 | { |
||
4873 | // If the readSource is firing, we need to pause it |
||
4874 | // or else it will continue to fire over and over again. |
||
4875 | // |
||
4876 | // If the readSource is not firing, |
||
4877 | // we want it to continue monitoring the socket. |
||
4878 | |||
4879 | if (socketFDBytesAvailable > 0) |
||
4880 | { |
||
4881 | [self suspendReadSource]; |
||
4882 | } |
||
4883 | } |
||
4884 | return; |
||
4885 | } |
||
4886 | |||
4887 | BOOL hasBytesAvailable = NO; |
||
4888 | unsigned long estimatedBytesAvailable = 0; |
||
4889 | |||
4890 | if ([self usingCFStreamForTLS]) |
||
4891 | { |
||
4892 | #if TARGET_OS_IPHONE |
||
4893 | |||
4894 | // Requested CFStream, rather than SecureTransport, for TLS (via GCDAsyncSocketUseCFStreamForTLS) |
||
4895 | |||
4896 | estimatedBytesAvailable = 0; |
||
4897 | if ((flags & kSecureSocketHasBytesAvailable) && CFReadStreamHasBytesAvailable(readStream)) |
||
4898 | hasBytesAvailable = YES; |
||
4899 | else |
||
4900 | hasBytesAvailable = NO; |
||
4901 | |||
4902 | #endif |
||
4903 | } |
||
4904 | else |
||
4905 | { |
||
4906 | estimatedBytesAvailable = socketFDBytesAvailable; |
||
4907 | |||
4908 | if (flags & kSocketSecure) |
||
4909 | { |
||
4910 | // There are 2 buffers to be aware of here. |
||
4911 | // |
||
4912 | // We are using SecureTransport, a TLS/SSL security layer which sits atop TCP. |
||
4913 | // We issue a read to the SecureTranport API, which in turn issues a read to our SSLReadFunction. |
||
4914 | // Our SSLReadFunction then reads from the BSD socket and returns the encrypted data to SecureTransport. |
||
4915 | // SecureTransport then decrypts the data, and finally returns the decrypted data back to us. |
||
4916 | // |
||
4917 | // The first buffer is one we create. |
||
4918 | // SecureTransport often requests small amounts of data. |
||
4919 | // This has to do with the encypted packets that are coming across the TCP stream. |
||
4920 | // But it's non-optimal to do a bunch of small reads from the BSD socket. |
||
4921 | // So our SSLReadFunction reads all available data from the socket (optimizing the sys call) |
||
4922 | // and may store excess in the sslPreBuffer. |
||
4923 | |||
4924 | estimatedBytesAvailable += [sslPreBuffer availableBytes]; |
||
4925 | |||
4926 | // The second buffer is within SecureTransport. |
||
4927 | // As mentioned earlier, there are encrypted packets coming across the TCP stream. |
||
4928 | // SecureTransport needs the entire packet to decrypt it. |
||
4929 | // But if the entire packet produces X bytes of decrypted data, |
||
4930 | // and we only asked SecureTransport for X/2 bytes of data, |
||
4931 | // it must store the extra X/2 bytes of decrypted data for the next read. |
||
4932 | // |
||
4933 | // The SSLGetBufferedReadSize function will tell us the size of this internal buffer. |
||
4934 | // From the documentation: |
||
4935 | // |
||
4936 | // "This function does not block or cause any low-level read operations to occur." |
||
4937 | |||
4938 | size_t sslInternalBufSize = 0; |
||
4939 | SSLGetBufferedReadSize(sslContext, &sslInternalBufSize); |
||
4940 | |||
4941 | estimatedBytesAvailable += sslInternalBufSize; |
||
4942 | } |
||
4943 | |||
4944 | hasBytesAvailable = (estimatedBytesAvailable > 0); |
||
4945 | } |
||
4946 | |||
4947 | if ((hasBytesAvailable == NO) && ([preBuffer availableBytes] == 0)) |
||
4948 | { |
||
4949 | LogVerbose(@"No data available to read..."); |
||
4950 | |||
4951 | // No data available to read. |
||
4952 | |||
4953 | if (![self usingCFStreamForTLS]) |
||
4954 | { |
||
4955 | // Need to wait for readSource to fire and notify us of |
||
4956 | // available data in the socket's internal read buffer. |
||
4957 | |||
4958 | [self resumeReadSource]; |
||
4959 | } |
||
4960 | return; |
||
4961 | } |
||
4962 | |||
4963 | if (flags & kStartingReadTLS) |
||
4964 | { |
||
4965 | LogVerbose(@"Waiting for SSL/TLS handshake to complete"); |
||
4966 | |||
4967 | // The readQueue is waiting for SSL/TLS handshake to complete. |
||
4968 | |||
4969 | if (flags & kStartingWriteTLS) |
||
4970 | { |
||
4971 | if ([self usingSecureTransportForTLS] && lastSSLHandshakeError == errSSLWouldBlock) |
||
4972 | { |
||
4973 | // We are in the process of a SSL Handshake. |
||
4974 | // We were waiting for incoming data which has just arrived. |
||
4975 | |||
4976 | [self ssl_continueSSLHandshake]; |
||
4977 | } |
||
4978 | } |
||
4979 | else |
||
4980 | { |
||
4981 | // We are still waiting for the writeQueue to drain and start the SSL/TLS process. |
||
4982 | // We now know data is available to read. |
||
4983 | |||
4984 | if (![self usingCFStreamForTLS]) |
||
4985 | { |
||
4986 | // Suspend the read source or else it will continue to fire nonstop. |
||
4987 | |||
4988 | [self suspendReadSource]; |
||
4989 | } |
||
4990 | } |
||
4991 | |||
4992 | return; |
||
4993 | } |
||
4994 | |||
4995 | BOOL done = NO; // Completed read operation |
||
4996 | NSError *error = nil; // Error occurred |
||
4997 | |||
4998 | NSUInteger totalBytesReadForCurrentRead = 0; |
||
4999 | |||
5000 | // |
||
5001 | // STEP 1 - READ FROM PREBUFFER |
||
5002 | // |
||
5003 | |||
5004 | if ([preBuffer availableBytes] > 0) |
||
5005 | { |
||
5006 | // There are 3 types of read packets: |
||
5007 | // |
||
5008 | // 1) Read all available data. |
||
5009 | // 2) Read a specific length of data. |
||
5010 | // 3) Read up to a particular terminator. |
||
5011 | |||
5012 | NSUInteger bytesToCopy; |
||
5013 | |||
5014 | if (currentRead->term != nil) |
||
5015 | { |
||
5016 | // Read type #3 - read up to a terminator |
||
5017 | |||
5018 | bytesToCopy = [currentRead readLengthForTermWithPreBuffer:preBuffer found:&done]; |
||
5019 | } |
||
5020 | else |
||
5021 | { |
||
5022 | // Read type #1 or #2 |
||
5023 | |||
5024 | bytesToCopy = [currentRead readLengthForNonTermWithHint:[preBuffer availableBytes]]; |
||
5025 | } |
||
5026 | |||
5027 | // Make sure we have enough room in the buffer for our read. |
||
5028 | |||
5029 | [currentRead ensureCapacityForAdditionalDataOfLength:bytesToCopy]; |
||
5030 | |||
5031 | // Copy bytes from prebuffer into packet buffer |
||
5032 | |||
5033 | uint8_t *buffer = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset + |
||
5034 | currentRead->bytesDone; |
||
5035 | |||
5036 | memcpy(buffer, [preBuffer readBuffer], bytesToCopy); |
||
5037 | |||
5038 | // Remove the copied bytes from the preBuffer |
||
5039 | [preBuffer didRead:bytesToCopy]; |
||
5040 | |||
5041 | LogVerbose(@"copied(%lu) preBufferLength(%zu)", (unsigned long)bytesToCopy, [preBuffer availableBytes]); |
||
5042 | |||
5043 | // Update totals |
||
5044 | |||
5045 | currentRead->bytesDone += bytesToCopy; |
||
5046 | totalBytesReadForCurrentRead += bytesToCopy; |
||
5047 | |||
5048 | // Check to see if the read operation is done |
||
5049 | |||
5050 | if (currentRead->readLength > 0) |
||
5051 | { |
||
5052 | // Read type #2 - read a specific length of data |
||
5053 | |||
5054 | done = (currentRead->bytesDone == currentRead->readLength); |
||
5055 | } |
||
5056 | else if (currentRead->term != nil) |
||
5057 | { |
||
5058 | // Read type #3 - read up to a terminator |
||
5059 | |||
5060 | // Our 'done' variable was updated via the readLengthForTermWithPreBuffer:found: method |
||
5061 | |||
5062 | if (!done && currentRead->maxLength > 0) |
||
5063 | { |
||
5064 | // We're not done and there's a set maxLength. |
||
5065 | // Have we reached that maxLength yet? |
||
5066 | |||
5067 | if (currentRead->bytesDone >= currentRead->maxLength) |
||
5068 | { |
||
5069 | error = [self readMaxedOutError]; |
||
5070 | } |
||
5071 | } |
||
5072 | } |
||
5073 | else |
||
5074 | { |
||
5075 | // Read type #1 - read all available data |
||
5076 | // |
||
5077 | // We're done as soon as |
||
5078 | // - we've read all available data (in prebuffer and socket) |
||
5079 | // - we've read the maxLength of read packet. |
||
5080 | |||
5081 | done = ((currentRead->maxLength > 0) && (currentRead->bytesDone == currentRead->maxLength)); |
||
5082 | } |
||
5083 | |||
5084 | } |
||
5085 | |||
5086 | // |
||
5087 | // STEP 2 - READ FROM SOCKET |
||
5088 | // |
||
5089 | |||
5090 | BOOL socketEOF = (flags & kSocketHasReadEOF) ? YES : NO; // Nothing more to read via socket (end of file) |
||
5091 | BOOL waiting = !done && !error && !socketEOF && !hasBytesAvailable; // Ran out of data, waiting for more |
||
5092 | |||
5093 | if (!done && !error && !socketEOF && hasBytesAvailable) |
||
5094 | { |
||
5095 | NSAssert(([preBuffer availableBytes] == 0), @"Invalid logic"); |
||
5096 | |||
5097 | BOOL readIntoPreBuffer = NO; |
||
5098 | uint8_t *buffer = NULL; |
||
5099 | size_t bytesRead = 0; |
||
5100 | |||
5101 | if (flags & kSocketSecure) |
||
5102 | { |
||
5103 | if ([self usingCFStreamForTLS]) |
||
5104 | { |
||
5105 | #if TARGET_OS_IPHONE |
||
5106 | |||
5107 | // Using CFStream, rather than SecureTransport, for TLS |
||
5108 | |||
5109 | NSUInteger defaultReadLength = (1024 * 32); |
||
5110 | |||
5111 | NSUInteger bytesToRead = [currentRead optimalReadLengthWithDefault:defaultReadLength |
||
5112 | shouldPreBuffer:&readIntoPreBuffer]; |
||
5113 | |||
5114 | // Make sure we have enough room in the buffer for our read. |
||
5115 | // |
||
5116 | // We are either reading directly into the currentRead->buffer, |
||
5117 | // or we're reading into the temporary preBuffer. |
||
5118 | |||
5119 | if (readIntoPreBuffer) |
||
5120 | { |
||
5121 | [preBuffer ensureCapacityForWrite:bytesToRead]; |
||
5122 | |||
5123 | buffer = [preBuffer writeBuffer]; |
||
5124 | } |
||
5125 | else |
||
5126 | { |
||
5127 | [currentRead ensureCapacityForAdditionalDataOfLength:bytesToRead]; |
||
5128 | |||
5129 | buffer = (uint8_t *)[currentRead->buffer mutableBytes] |
||
5130 | + currentRead->startOffset |
||
5131 | + currentRead->bytesDone; |
||
5132 | } |
||
5133 | |||
5134 | // Read data into buffer |
||
5135 | |||
5136 | CFIndex result = CFReadStreamRead(readStream, buffer, (CFIndex)bytesToRead); |
||
5137 | LogVerbose(@"CFReadStreamRead(): result = %i", (int)result); |
||
5138 | |||
5139 | if (result < 0) |
||
5140 | { |
||
5141 | error = (__bridge_transfer NSError *)CFReadStreamCopyError(readStream); |
||
5142 | } |
||
5143 | else if (result == 0) |
||
5144 | { |
||
5145 | socketEOF = YES; |
||
5146 | } |
||
5147 | else |
||
5148 | { |
||
5149 | waiting = YES; |
||
5150 | bytesRead = (size_t)result; |
||
5151 | } |
||
5152 | |||
5153 | // We only know how many decrypted bytes were read. |
||
5154 | // The actual number of bytes read was likely more due to the overhead of the encryption. |
||
5155 | // So we reset our flag, and rely on the next callback to alert us of more data. |
||
5156 | flags &= ~kSecureSocketHasBytesAvailable; |
||
5157 | |||
5158 | #endif |
||
5159 | } |
||
5160 | else |
||
5161 | { |
||
5162 | // Using SecureTransport for TLS |
||
5163 | // |
||
5164 | // We know: |
||
5165 | // - how many bytes are available on the socket |
||
5166 | // - how many encrypted bytes are sitting in the sslPreBuffer |
||
5167 | // - how many decypted bytes are sitting in the sslContext |
||
5168 | // |
||
5169 | // But we do NOT know: |
||
5170 | // - how many encypted bytes are sitting in the sslContext |
||
5171 | // |
||
5172 | // So we play the regular game of using an upper bound instead. |
||
5173 | |||
5174 | NSUInteger defaultReadLength = (1024 * 32); |
||
5175 | |||
5176 | if (defaultReadLength < estimatedBytesAvailable) { |
||
5177 | defaultReadLength = estimatedBytesAvailable + (1024 * 16); |
||
5178 | } |
||
5179 | |||
5180 | NSUInteger bytesToRead = [currentRead optimalReadLengthWithDefault:defaultReadLength |
||
5181 | shouldPreBuffer:&readIntoPreBuffer]; |
||
5182 | |||
5183 | if (bytesToRead > SIZE_MAX) { // NSUInteger may be bigger than size_t |
||
5184 | bytesToRead = SIZE_MAX; |
||
5185 | } |
||
5186 | |||
5187 | // Make sure we have enough room in the buffer for our read. |
||
5188 | // |
||
5189 | // We are either reading directly into the currentRead->buffer, |
||
5190 | // or we're reading into the temporary preBuffer. |
||
5191 | |||
5192 | if (readIntoPreBuffer) |
||
5193 | { |
||
5194 | [preBuffer ensureCapacityForWrite:bytesToRead]; |
||
5195 | |||
5196 | buffer = [preBuffer writeBuffer]; |
||
5197 | } |
||
5198 | else |
||
5199 | { |
||
5200 | [currentRead ensureCapacityForAdditionalDataOfLength:bytesToRead]; |
||
5201 | |||
5202 | buffer = (uint8_t *)[currentRead->buffer mutableBytes] |
||
5203 | + currentRead->startOffset |
||
5204 | + currentRead->bytesDone; |
||
5205 | } |
||
5206 | |||
5207 | // The documentation from Apple states: |
||
5208 | // |
||
5209 | // "a read operation might return errSSLWouldBlock, |
||
5210 | // indicating that less data than requested was actually transferred" |
||
5211 | // |
||
5212 | // However, starting around 10.7, the function will sometimes return noErr, |
||
5213 | // even if it didn't read as much data as requested. So we need to watch out for that. |
||
5214 | |||
5215 | OSStatus result; |
||
5216 | do |
||
5217 | { |
||
5218 | void *loop_buffer = buffer + bytesRead; |
||
5219 | size_t loop_bytesToRead = (size_t)bytesToRead - bytesRead; |
||
5220 | size_t loop_bytesRead = 0; |
||
5221 | |||
5222 | result = SSLRead(sslContext, loop_buffer, loop_bytesToRead, &loop_bytesRead); |
||
5223 | LogVerbose(@"read from secure socket = %u", (unsigned)loop_bytesRead); |
||
5224 | |||
5225 | bytesRead += loop_bytesRead; |
||
5226 | |||
5227 | } while ((result == noErr) && (bytesRead < bytesToRead)); |
||
5228 | |||
5229 | |||
5230 | if (result != noErr) |
||
5231 | { |
||
5232 | if (result == errSSLWouldBlock) |
||
5233 | waiting = YES; |
||
5234 | else |
||
5235 | { |
||
5236 | if (result == errSSLClosedGraceful || result == errSSLClosedAbort) |
||
5237 | { |
||
5238 | // We've reached the end of the stream. |
||
5239 | // Handle this the same way we would an EOF from the socket. |
||
5240 | socketEOF = YES; |
||
5241 | sslErrCode = result; |
||
5242 | } |
||
5243 | else |
||
5244 | { |
||
5245 | error = [self sslError:result]; |
||
5246 | } |
||
5247 | } |
||
5248 | // It's possible that bytesRead > 0, even if the result was errSSLWouldBlock. |
||
5249 | // This happens when the SSLRead function is able to read some data, |
||
5250 | // but not the entire amount we requested. |
||
5251 | |||
5252 | if (bytesRead <= 0) |
||
5253 | { |
||
5254 | bytesRead = 0; |
||
5255 | } |
||
5256 | } |
||
5257 | |||
5258 | // Do not modify socketFDBytesAvailable. |
||
5259 | // It will be updated via the SSLReadFunction(). |
||
5260 | } |
||
5261 | } |
||
5262 | else |
||
5263 | { |
||
5264 | // Normal socket operation |
||
5265 | |||
5266 | NSUInteger bytesToRead; |
||
5267 | |||
5268 | // There are 3 types of read packets: |
||
5269 | // |
||
5270 | // 1) Read all available data. |
||
5271 | // 2) Read a specific length of data. |
||
5272 | // 3) Read up to a particular terminator. |
||
5273 | |||
5274 | if (currentRead->term != nil) |
||
5275 | { |
||
5276 | // Read type #3 - read up to a terminator |
||
5277 | |||
5278 | bytesToRead = [currentRead readLengthForTermWithHint:estimatedBytesAvailable |
||
5279 | shouldPreBuffer:&readIntoPreBuffer]; |
||
5280 | } |
||
5281 | else |
||
5282 | { |
||
5283 | // Read type #1 or #2 |
||
5284 | |||
5285 | bytesToRead = [currentRead readLengthForNonTermWithHint:estimatedBytesAvailable]; |
||
5286 | } |
||
5287 | |||
5288 | if (bytesToRead > SIZE_MAX) { // NSUInteger may be bigger than size_t (read param 3) |
||
5289 | bytesToRead = SIZE_MAX; |
||
5290 | } |
||
5291 | |||
5292 | // Make sure we have enough room in the buffer for our read. |
||
5293 | // |
||
5294 | // We are either reading directly into the currentRead->buffer, |
||
5295 | // or we're reading into the temporary preBuffer. |
||
5296 | |||
5297 | if (readIntoPreBuffer) |
||
5298 | { |
||
5299 | [preBuffer ensureCapacityForWrite:bytesToRead]; |
||
5300 | |||
5301 | buffer = [preBuffer writeBuffer]; |
||
5302 | } |
||
5303 | else |
||
5304 | { |
||
5305 | [currentRead ensureCapacityForAdditionalDataOfLength:bytesToRead]; |
||
5306 | |||
5307 | buffer = (uint8_t *)[currentRead->buffer mutableBytes] |
||
5308 | + currentRead->startOffset |
||
5309 | + currentRead->bytesDone; |
||
5310 | } |
||
5311 | |||
5312 | // Read data into buffer |
||
5313 | |||
5314 | int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; |
||
5315 | |||
5316 | ssize_t result = read(socketFD, buffer, (size_t)bytesToRead); |
||
5317 | LogVerbose(@"read from socket = %i", (int)result); |
||
5318 | |||
5319 | if (result < 0) |
||
5320 | { |
||
5321 | if (errno == EWOULDBLOCK) |
||
5322 | waiting = YES; |
||
5323 | else |
||
5324 | error = [self errorWithErrno:errno reason:@"Error in read() function"]; |
||
5325 | |||
5326 | socketFDBytesAvailable = 0; |
||
5327 | } |
||
5328 | else if (result == 0) |
||
5329 | { |
||
5330 | socketEOF = YES; |
||
5331 | socketFDBytesAvailable = 0; |
||
5332 | } |
||
5333 | else |
||
5334 | { |
||
5335 | bytesRead = result; |
||
5336 | |||
5337 | if (bytesRead < bytesToRead) |
||
5338 | { |
||
5339 | // The read returned less data than requested. |
||
5340 | // This means socketFDBytesAvailable was a bit off due to timing, |
||
5341 | // because we read from the socket right when the readSource event was firing. |
||
5342 | socketFDBytesAvailable = 0; |
||
5343 | } |
||
5344 | else |
||
5345 | { |
||
5346 | if (socketFDBytesAvailable <= bytesRead) |
||
5347 | socketFDBytesAvailable = 0; |
||
5348 | else |
||
5349 | socketFDBytesAvailable -= bytesRead; |
||
5350 | } |
||
5351 | |||
5352 | if (socketFDBytesAvailable == 0) |
||
5353 | { |
||
5354 | waiting = YES; |
||
5355 | } |
||
5356 | } |
||
5357 | } |
||
5358 | |||
5359 | if (bytesRead > 0) |
||
5360 | { |
||
5361 | // Check to see if the read operation is done |
||
5362 | |||
5363 | if (currentRead->readLength > 0) |
||
5364 | { |
||
5365 | // Read type #2 - read a specific length of data |
||
5366 | // |
||
5367 | // Note: We should never be using a prebuffer when we're reading a specific length of data. |
||
5368 | |||
5369 | NSAssert(readIntoPreBuffer == NO, @"Invalid logic"); |
||
5370 | |||
5371 | currentRead->bytesDone += bytesRead; |
||
5372 | totalBytesReadForCurrentRead += bytesRead; |
||
5373 | |||
5374 | done = (currentRead->bytesDone == currentRead->readLength); |
||
5375 | } |
||
5376 | else if (currentRead->term != nil) |
||
5377 | { |
||
5378 | // Read type #3 - read up to a terminator |
||
5379 | |||
5380 | if (readIntoPreBuffer) |
||
5381 | { |
||
5382 | // We just read a big chunk of data into the preBuffer |
||
5383 | |||
5384 | [preBuffer didWrite:bytesRead]; |
||
5385 | LogVerbose(@"read data into preBuffer - preBuffer.length = %zu", [preBuffer availableBytes]); |
||
5386 | |||
5387 | // Search for the terminating sequence |
||
5388 | |||
5389 | NSUInteger bytesToCopy = [currentRead readLengthForTermWithPreBuffer:preBuffer found:&done]; |
||
5390 | LogVerbose(@"copying %lu bytes from preBuffer", (unsigned long)bytesToCopy); |
||
5391 | |||
5392 | // Ensure there's room on the read packet's buffer |
||
5393 | |||
5394 | [currentRead ensureCapacityForAdditionalDataOfLength:bytesToCopy]; |
||
5395 | |||
5396 | // Copy bytes from prebuffer into read buffer |
||
5397 | |||
5398 | uint8_t *readBuf = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset |
||
5399 | + currentRead->bytesDone; |
||
5400 | |||
5401 | memcpy(readBuf, [preBuffer readBuffer], bytesToCopy); |
||
5402 | |||
5403 | // Remove the copied bytes from the prebuffer |
||
5404 | [preBuffer didRead:bytesToCopy]; |
||
5405 | LogVerbose(@"preBuffer.length = %zu", [preBuffer availableBytes]); |
||
5406 | |||
5407 | // Update totals |
||
5408 | currentRead->bytesDone += bytesToCopy; |
||
5409 | totalBytesReadForCurrentRead += bytesToCopy; |
||
5410 | |||
5411 | // Our 'done' variable was updated via the readLengthForTermWithPreBuffer:found: method above |
||
5412 | } |
||
5413 | else |
||
5414 | { |
||
5415 | // We just read a big chunk of data directly into the packet's buffer. |
||
5416 | // We need to move any overflow into the prebuffer. |
||
5417 | |||
5418 | NSInteger overflow = [currentRead searchForTermAfterPreBuffering:bytesRead]; |
||
5419 | |||
5420 | if (overflow == 0) |
||
5421 | { |
||
5422 | // Perfect match! |
||
5423 | // Every byte we read stays in the read buffer, |
||
5424 | // and the last byte we read was the last byte of the term. |
||
5425 | |||
5426 | currentRead->bytesDone += bytesRead; |
||
5427 | totalBytesReadForCurrentRead += bytesRead; |
||
5428 | done = YES; |
||
5429 | } |
||
5430 | else if (overflow > 0) |
||
5431 | { |
||
5432 | // The term was found within the data that we read, |
||
5433 | // and there are extra bytes that extend past the end of the term. |
||
5434 | // We need to move these excess bytes out of the read packet and into the prebuffer. |
||
5435 | |||
5436 | NSInteger underflow = bytesRead - overflow; |
||
5437 | |||
5438 | // Copy excess data into preBuffer |
||
5439 | |||
5440 | LogVerbose(@"copying %ld overflow bytes into preBuffer", (long)overflow); |
||
5441 | [preBuffer ensureCapacityForWrite:overflow]; |
||
5442 | |||
5443 | uint8_t *overflowBuffer = buffer + underflow; |
||
5444 | memcpy([preBuffer writeBuffer], overflowBuffer, overflow); |
||
5445 | |||
5446 | [preBuffer didWrite:overflow]; |
||
5447 | LogVerbose(@"preBuffer.length = %zu", [preBuffer availableBytes]); |
||
5448 | |||
5449 | // Note: The completeCurrentRead method will trim the buffer for us. |
||
5450 | |||
5451 | currentRead->bytesDone += underflow; |
||
5452 | totalBytesReadForCurrentRead += underflow; |
||
5453 | done = YES; |
||
5454 | } |
||
5455 | else |
||
5456 | { |
||
5457 | // The term was not found within the data that we read. |
||
5458 | |||
5459 | currentRead->bytesDone += bytesRead; |
||
5460 | totalBytesReadForCurrentRead += bytesRead; |
||
5461 | done = NO; |
||
5462 | } |
||
5463 | } |
||
5464 | |||
5465 | if (!done && currentRead->maxLength > 0) |
||
5466 | { |
||
5467 | // We're not done and there's a set maxLength. |
||
5468 | // Have we reached that maxLength yet? |
||
5469 | |||
5470 | if (currentRead->bytesDone >= currentRead->maxLength) |
||
5471 | { |
||
5472 | error = [self readMaxedOutError]; |
||
5473 | } |
||
5474 | } |
||
5475 | } |
||
5476 | else |
||
5477 | { |
||
5478 | // Read type #1 - read all available data |
||
5479 | |||
5480 | if (readIntoPreBuffer) |
||
5481 | { |
||
5482 | // We just read a chunk of data into the preBuffer |
||
5483 | |||
5484 | [preBuffer didWrite:bytesRead]; |
||
5485 | |||
5486 | // Now copy the data into the read packet. |
||
5487 | // |
||
5488 | // Recall that we didn't read directly into the packet's buffer to avoid |
||
5489 | // over-allocating memory since we had no clue how much data was available to be read. |
||
5490 | // |
||
5491 | // Ensure there's room on the read packet's buffer |
||
5492 | |||
5493 | [currentRead ensureCapacityForAdditionalDataOfLength:bytesRead]; |
||
5494 | |||
5495 | // Copy bytes from prebuffer into read buffer |
||
5496 | |||
5497 | uint8_t *readBuf = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset |
||
5498 | + currentRead->bytesDone; |
||
5499 | |||
5500 | memcpy(readBuf, [preBuffer readBuffer], bytesRead); |
||
5501 | |||
5502 | // Remove the copied bytes from the prebuffer |
||
5503 | [preBuffer didRead:bytesRead]; |
||
5504 | |||
5505 | // Update totals |
||
5506 | currentRead->bytesDone += bytesRead; |
||
5507 | totalBytesReadForCurrentRead += bytesRead; |
||
5508 | } |
||
5509 | else |
||
5510 | { |
||
5511 | currentRead->bytesDone += bytesRead; |
||
5512 | totalBytesReadForCurrentRead += bytesRead; |
||
5513 | } |
||
5514 | |||
5515 | done = YES; |
||
5516 | } |
||
5517 | |||
5518 | } // if (bytesRead > 0) |
||
5519 | |||
5520 | } // if (!done && !error && !socketEOF && hasBytesAvailable) |
||
5521 | |||
5522 | |||
5523 | if (!done && currentRead->readLength == 0 && currentRead->term == nil) |
||
5524 | { |
||
5525 | // Read type #1 - read all available data |
||
5526 | // |
||
5527 | // We might arrive here if we read data from the prebuffer but not from the socket. |
||
5528 | |||
5529 | done = (totalBytesReadForCurrentRead > 0); |
||
5530 | } |
||
5531 | |||
5532 | // Check to see if we're done, or if we've made progress |
||
5533 | |||
5534 | if (done) |
||
5535 | { |
||
5536 | [self completeCurrentRead]; |
||
5537 | |||
5538 | if (!error && (!socketEOF || [preBuffer availableBytes] > 0)) |
||
5539 | { |
||
5540 | [self maybeDequeueRead]; |
||
5541 | } |
||
5542 | } |
||
5543 | else if (totalBytesReadForCurrentRead > 0) |
||
5544 | { |
||
5545 | // We're not done read type #2 or #3 yet, but we have read in some bytes |
||
5546 | // |
||
5547 | // We ensure that `waiting` is set in order to resume the readSource (if it is suspended). It is |
||
5548 | // possible to reach this point and `waiting` not be set, if the current read's length is |
||
5549 | // sufficiently large. In that case, we may have read to some upperbound successfully, but |
||
5550 | // that upperbound could be smaller than the desired length. |
||
5551 | waiting = YES; |
||
5552 | |||
5553 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
5554 | |||
5555 | if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didReadPartialDataOfLength:tag:)]) |
||
5556 | { |
||
5557 | long theReadTag = currentRead->tag; |
||
5558 | |||
5559 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
5560 | |||
5561 | [theDelegate socket:self didReadPartialDataOfLength:totalBytesReadForCurrentRead tag:theReadTag]; |
||
5562 | }}); |
||
5563 | } |
||
5564 | } |
||
5565 | |||
5566 | // Check for errors |
||
5567 | |||
5568 | if (error) |
||
5569 | { |
||
5570 | [self closeWithError:error]; |
||
5571 | } |
||
5572 | else if (socketEOF) |
||
5573 | { |
||
5574 | [self doReadEOF]; |
||
5575 | } |
||
5576 | else if (waiting) |
||
5577 | { |
||
5578 | if (![self usingCFStreamForTLS]) |
||
5579 | { |
||
5580 | // Monitor the socket for readability (if we're not already doing so) |
||
5581 | [self resumeReadSource]; |
||
5582 | } |
||
5583 | } |
||
5584 | |||
5585 | // Do not add any code here without first adding return statements in the error cases above. |
||
5586 | } |
||
5587 | |||
5588 | - (void)doReadEOF |
||
5589 | { |
||
5590 | LogTrace(); |
||
5591 | |||
5592 | // This method may be called more than once. |
||
5593 | // If the EOF is read while there is still data in the preBuffer, |
||
5594 | // then this method may be called continually after invocations of doReadData to see if it's time to disconnect. |
||
5595 | |||
5596 | flags |= kSocketHasReadEOF; |
||
5597 | |||
5598 | if (flags & kSocketSecure) |
||
5599 | { |
||
5600 | // If the SSL layer has any buffered data, flush it into the preBuffer now. |
||
5601 | |||
5602 | [self flushSSLBuffers]; |
||
5603 | } |
||
5604 | |||
5605 | BOOL shouldDisconnect = NO; |
||
5606 | NSError *error = nil; |
||
5607 | |||
5608 | if ((flags & kStartingReadTLS) || (flags & kStartingWriteTLS)) |
||
5609 | { |
||
5610 | // We received an EOF during or prior to startTLS. |
||
5611 | // The SSL/TLS handshake is now impossible, so this is an unrecoverable situation. |
||
5612 | |||
5613 | shouldDisconnect = YES; |
||
5614 | |||
5615 | if ([self usingSecureTransportForTLS]) |
||
5616 | { |
||
5617 | error = [self sslError:errSSLClosedAbort]; |
||
5618 | } |
||
5619 | } |
||
5620 | else if (flags & kReadStreamClosed) |
||
5621 | { |
||
5622 | // The preBuffer has already been drained. |
||
5623 | // The config allows half-duplex connections. |
||
5624 | // We've previously checked the socket, and it appeared writeable. |
||
5625 | // So we marked the read stream as closed and notified the delegate. |
||
5626 | // |
||
5627 | // As per the half-duplex contract, the socket will be closed when a write fails, |
||
5628 | // or when the socket is manually closed. |
||
5629 | |||
5630 | shouldDisconnect = NO; |
||
5631 | } |
||
5632 | else if ([preBuffer availableBytes] > 0) |
||
5633 | { |
||
5634 | LogVerbose(@"Socket reached EOF, but there is still data available in prebuffer"); |
||
5635 | |||
5636 | // Although we won't be able to read any more data from the socket, |
||
5637 | // there is existing data that has been prebuffered that we can read. |
||
5638 | |||
5639 | shouldDisconnect = NO; |
||
5640 | } |
||
5641 | else if (config & kAllowHalfDuplexConnection) |
||
5642 | { |
||
5643 | // We just received an EOF (end of file) from the socket's read stream. |
||
5644 | // This means the remote end of the socket (the peer we're connected to) |
||
5645 | // has explicitly stated that it will not be sending us any more data. |
||
5646 | // |
||
5647 | // Query the socket to see if it is still writeable. (Perhaps the peer will continue reading data from us) |
||
5648 | |||
5649 | int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; |
||
5650 | |||
5651 | struct pollfd pfd[1]; |
||
5652 | pfd[0].fd = socketFD; |
||
5653 | pfd[0].events = POLLOUT; |
||
5654 | pfd[0].revents = 0; |
||
5655 | |||
5656 | poll(pfd, 1, 0); |
||
5657 | |||
5658 | if (pfd[0].revents & POLLOUT) |
||
5659 | { |
||
5660 | // Socket appears to still be writeable |
||
5661 | |||
5662 | shouldDisconnect = NO; |
||
5663 | flags |= kReadStreamClosed; |
||
5664 | |||
5665 | // Notify the delegate that we're going half-duplex |
||
5666 | |||
5667 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
5668 | |||
5669 | if (delegateQueue && [theDelegate respondsToSelector:@selector(socketDidCloseReadStream:)]) |
||
5670 | { |
||
5671 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
5672 | |||
5673 | [theDelegate socketDidCloseReadStream:self]; |
||
5674 | }}); |
||
5675 | } |
||
5676 | } |
||
5677 | else |
||
5678 | { |
||
5679 | shouldDisconnect = YES; |
||
5680 | } |
||
5681 | } |
||
5682 | else |
||
5683 | { |
||
5684 | shouldDisconnect = YES; |
||
5685 | } |
||
5686 | |||
5687 | |||
5688 | if (shouldDisconnect) |
||
5689 | { |
||
5690 | if (error == nil) |
||
5691 | { |
||
5692 | if ([self usingSecureTransportForTLS]) |
||
5693 | { |
||
5694 | if (sslErrCode != noErr && sslErrCode != errSSLClosedGraceful) |
||
5695 | { |
||
5696 | error = [self sslError:sslErrCode]; |
||
5697 | } |
||
5698 | else |
||
5699 | { |
||
5700 | error = [self connectionClosedError]; |
||
5701 | } |
||
5702 | } |
||
5703 | else |
||
5704 | { |
||
5705 | error = [self connectionClosedError]; |
||
5706 | } |
||
5707 | } |
||
5708 | [self closeWithError:error]; |
||
5709 | } |
||
5710 | else |
||
5711 | { |
||
5712 | if (![self usingCFStreamForTLS]) |
||
5713 | { |
||
5714 | // Suspend the read source (if needed) |
||
5715 | |||
5716 | [self suspendReadSource]; |
||
5717 | } |
||
5718 | } |
||
5719 | } |
||
5720 | |||
5721 | - (void)completeCurrentRead |
||
5722 | { |
||
5723 | LogTrace(); |
||
5724 | |||
5725 | NSAssert(currentRead, @"Trying to complete current read when there is no current read."); |
||
5726 | |||
5727 | |||
5728 | NSData *result = nil; |
||
5729 | |||
5730 | if (currentRead->bufferOwner) |
||
5731 | { |
||
5732 | // We created the buffer on behalf of the user. |
||
5733 | // Trim our buffer to be the proper size. |
||
5734 | [currentRead->buffer setLength:currentRead->bytesDone]; |
||
5735 | |||
5736 | result = currentRead->buffer; |
||
5737 | } |
||
5738 | else |
||
5739 | { |
||
5740 | // We did NOT create the buffer. |
||
5741 | // The buffer is owned by the caller. |
||
5742 | // Only trim the buffer if we had to increase its size. |
||
5743 | |||
5744 | if ([currentRead->buffer length] > currentRead->originalBufferLength) |
||
5745 | { |
||
5746 | NSUInteger readSize = currentRead->startOffset + currentRead->bytesDone; |
||
5747 | NSUInteger origSize = currentRead->originalBufferLength; |
||
5748 | |||
5749 | NSUInteger buffSize = MAX(readSize, origSize); |
||
5750 | |||
5751 | [currentRead->buffer setLength:buffSize]; |
||
5752 | } |
||
5753 | |||
5754 | uint8_t *buffer = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset; |
||
5755 | |||
5756 | result = [NSData dataWithBytesNoCopy:buffer length:currentRead->bytesDone freeWhenDone:NO]; |
||
5757 | } |
||
5758 | |||
5759 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
5760 | |||
5761 | if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didReadData:withTag:)]) |
||
5762 | { |
||
5763 | GCDAsyncReadPacket *theRead = currentRead; // Ensure currentRead retained since result may not own buffer |
||
5764 | |||
5765 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
5766 | |||
5767 | [theDelegate socket:self didReadData:result withTag:theRead->tag]; |
||
5768 | }}); |
||
5769 | } |
||
5770 | |||
5771 | [self endCurrentRead]; |
||
5772 | } |
||
5773 | |||
5774 | - (void)endCurrentRead |
||
5775 | { |
||
5776 | if (readTimer) |
||
5777 | { |
||
5778 | dispatch_source_cancel(readTimer); |
||
5779 | readTimer = NULL; |
||
5780 | } |
||
5781 | |||
5782 | currentRead = nil; |
||
5783 | } |
||
5784 | |||
5785 | - (void)setupReadTimerWithTimeout:(NSTimeInterval)timeout |
||
5786 | { |
||
5787 | if (timeout >= 0.0) |
||
5788 | { |
||
5789 | readTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, socketQueue); |
||
5790 | |||
5791 | __weak GCDAsyncSocket *weakSelf = self; |
||
5792 | |||
5793 | dispatch_source_set_event_handler(readTimer, ^{ @autoreleasepool { |
||
5794 | #pragma clang diagnostic push |
||
5795 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
5796 | |||
5797 | __strong GCDAsyncSocket *strongSelf = weakSelf; |
||
5798 | if (strongSelf == nil) return_from_block; |
||
5799 | |||
5800 | [strongSelf doReadTimeout]; |
||
5801 | |||
5802 | #pragma clang diagnostic pop |
||
5803 | }}); |
||
5804 | |||
5805 | #if !OS_OBJECT_USE_OBJC |
||
5806 | dispatch_source_t theReadTimer = readTimer; |
||
5807 | dispatch_source_set_cancel_handler(readTimer, ^{ |
||
5808 | #pragma clang diagnostic push |
||
5809 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
5810 | |||
5811 | LogVerbose(@"dispatch_release(readTimer)"); |
||
5812 | dispatch_release(theReadTimer); |
||
5813 | |||
5814 | #pragma clang diagnostic pop |
||
5815 | }); |
||
5816 | #endif |
||
5817 | |||
5818 | dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)); |
||
5819 | |||
5820 | dispatch_source_set_timer(readTimer, tt, DISPATCH_TIME_FOREVER, 0); |
||
5821 | dispatch_resume(readTimer); |
||
5822 | } |
||
5823 | } |
||
5824 | |||
5825 | - (void)doReadTimeout |
||
5826 | { |
||
5827 | // This is a little bit tricky. |
||
5828 | // Ideally we'd like to synchronously query the delegate about a timeout extension. |
||
5829 | // But if we do so synchronously we risk a possible deadlock. |
||
5830 | // So instead we have to do so asynchronously, and callback to ourselves from within the delegate block. |
||
5831 | |||
5832 | flags |= kReadsPaused; |
||
5833 | |||
5834 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
5835 | |||
5836 | if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:shouldTimeoutReadWithTag:elapsed:bytesDone:)]) |
||
5837 | { |
||
5838 | GCDAsyncReadPacket *theRead = currentRead; |
||
5839 | |||
5840 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
5841 | |||
5842 | NSTimeInterval timeoutExtension = 0.0; |
||
5843 | |||
5844 | timeoutExtension = [theDelegate socket:self shouldTimeoutReadWithTag:theRead->tag |
||
5845 | elapsed:theRead->timeout |
||
5846 | bytesDone:theRead->bytesDone]; |
||
5847 | |||
5848 | dispatch_async(self->socketQueue, ^{ @autoreleasepool { |
||
5849 | |||
5850 | [self doReadTimeoutWithExtension:timeoutExtension]; |
||
5851 | }}); |
||
5852 | }}); |
||
5853 | } |
||
5854 | else |
||
5855 | { |
||
5856 | [self doReadTimeoutWithExtension:0.0]; |
||
5857 | } |
||
5858 | } |
||
5859 | |||
5860 | - (void)doReadTimeoutWithExtension:(NSTimeInterval)timeoutExtension |
||
5861 | { |
||
5862 | if (currentRead) |
||
5863 | { |
||
5864 | if (timeoutExtension > 0.0) |
||
5865 | { |
||
5866 | currentRead->timeout += timeoutExtension; |
||
5867 | |||
5868 | // Reschedule the timer |
||
5869 | dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeoutExtension * NSEC_PER_SEC)); |
||
5870 | dispatch_source_set_timer(readTimer, tt, DISPATCH_TIME_FOREVER, 0); |
||
5871 | |||
5872 | // Unpause reads, and continue |
||
5873 | flags &= ~kReadsPaused; |
||
5874 | [self doReadData]; |
||
5875 | } |
||
5876 | else |
||
5877 | { |
||
5878 | LogVerbose(@"ReadTimeout"); |
||
5879 | |||
5880 | [self closeWithError:[self readTimeoutError]]; |
||
5881 | } |
||
5882 | } |
||
5883 | } |
||
5884 | |||
5885 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
5886 | #pragma mark Writing |
||
5887 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
5888 | |||
5889 | - (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag |
||
5890 | { |
||
5891 | if ([data length] == 0) return; |
||
5892 | |||
5893 | GCDAsyncWritePacket *packet = [[GCDAsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag]; |
||
5894 | |||
5895 | dispatch_async(socketQueue, ^{ @autoreleasepool { |
||
5896 | |||
5897 | LogTrace(); |
||
5898 | |||
5899 | if ((self->flags & kSocketStarted) && !(self->flags & kForbidReadsWrites)) |
||
5900 | { |
||
5901 | [self->writeQueue addObject:packet]; |
||
5902 | [self maybeDequeueWrite]; |
||
5903 | } |
||
5904 | }}); |
||
5905 | |||
5906 | // Do not rely on the block being run in order to release the packet, |
||
5907 | // as the queue might get released without the block completing. |
||
5908 | } |
||
5909 | |||
5910 | - (float)progressOfWriteReturningTag:(long *)tagPtr bytesDone:(NSUInteger *)donePtr total:(NSUInteger *)totalPtr |
||
5911 | { |
||
5912 | __block float result = 0.0F; |
||
5913 | |||
5914 | dispatch_block_t block = ^{ |
||
5915 | |||
5916 | if (!self->currentWrite || ![self->currentWrite isKindOfClass:[GCDAsyncWritePacket class]]) |
||
5917 | { |
||
5918 | // We're not writing anything right now. |
||
5919 | |||
5920 | if (tagPtr != NULL) *tagPtr = 0; |
||
5921 | if (donePtr != NULL) *donePtr = 0; |
||
5922 | if (totalPtr != NULL) *totalPtr = 0; |
||
5923 | |||
5924 | result = NAN; |
||
5925 | } |
||
5926 | else |
||
5927 | { |
||
5928 | NSUInteger done = self->currentWrite->bytesDone; |
||
5929 | NSUInteger total = [self->currentWrite->buffer length]; |
||
5930 | |||
5931 | if (tagPtr != NULL) *tagPtr = self->currentWrite->tag; |
||
5932 | if (donePtr != NULL) *donePtr = done; |
||
5933 | if (totalPtr != NULL) *totalPtr = total; |
||
5934 | |||
5935 | result = (float)done / (float)total; |
||
5936 | } |
||
5937 | }; |
||
5938 | |||
5939 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
5940 | block(); |
||
5941 | else |
||
5942 | dispatch_sync(socketQueue, block); |
||
5943 | |||
5944 | return result; |
||
5945 | } |
||
5946 | |||
5947 | /** |
||
5948 | * Conditionally starts a new write. |
||
5949 | * |
||
5950 | * It is called when: |
||
5951 | * - a user requests a write |
||
5952 | * - after a write request has finished (to handle the next request) |
||
5953 | * - immediately after the socket opens to handle any pending requests |
||
5954 | * |
||
5955 | * This method also handles auto-disconnect post read/write completion. |
||
5956 | **/ |
||
5957 | - (void)maybeDequeueWrite |
||
5958 | { |
||
5959 | LogTrace(); |
||
5960 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
5961 | |||
5962 | |||
5963 | // If we're not currently processing a write AND we have an available write stream |
||
5964 | if ((currentWrite == nil) && (flags & kConnected)) |
||
5965 | { |
||
5966 | if ([writeQueue count] > 0) |
||
5967 | { |
||
5968 | // Dequeue the next object in the write queue |
||
5969 | currentWrite = [writeQueue objectAtIndex:0]; |
||
5970 | [writeQueue removeObjectAtIndex:0]; |
||
5971 | |||
5972 | |||
5973 | if ([currentWrite isKindOfClass:[GCDAsyncSpecialPacket class]]) |
||
5974 | { |
||
5975 | LogVerbose(@"Dequeued GCDAsyncSpecialPacket"); |
||
5976 | |||
5977 | // Attempt to start TLS |
||
5978 | flags |= kStartingWriteTLS; |
||
5979 | |||
5980 | // This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are set |
||
5981 | [self maybeStartTLS]; |
||
5982 | } |
||
5983 | else |
||
5984 | { |
||
5985 | LogVerbose(@"Dequeued GCDAsyncWritePacket"); |
||
5986 | |||
5987 | // Setup write timer (if needed) |
||
5988 | [self setupWriteTimerWithTimeout:currentWrite->timeout]; |
||
5989 | |||
5990 | // Immediately write, if possible |
||
5991 | [self doWriteData]; |
||
5992 | } |
||
5993 | } |
||
5994 | else if (flags & kDisconnectAfterWrites) |
||
5995 | { |
||
5996 | if (flags & kDisconnectAfterReads) |
||
5997 | { |
||
5998 | if (([readQueue count] == 0) && (currentRead == nil)) |
||
5999 | { |
||
6000 | [self closeWithError:nil]; |
||
6001 | } |
||
6002 | } |
||
6003 | else |
||
6004 | { |
||
6005 | [self closeWithError:nil]; |
||
6006 | } |
||
6007 | } |
||
6008 | } |
||
6009 | } |
||
6010 | |||
6011 | - (void)doWriteData |
||
6012 | { |
||
6013 | LogTrace(); |
||
6014 | |||
6015 | // This method is called by the writeSource via the socketQueue |
||
6016 | |||
6017 | if ((currentWrite == nil) || (flags & kWritesPaused)) |
||
6018 | { |
||
6019 | LogVerbose(@"No currentWrite or kWritesPaused"); |
||
6020 | |||
6021 | // Unable to write at this time |
||
6022 | |||
6023 | if ([self usingCFStreamForTLS]) |
||
6024 | { |
||
6025 | // CFWriteStream only fires once when there is available data. |
||
6026 | // It won't fire again until we've invoked CFWriteStreamWrite. |
||
6027 | } |
||
6028 | else |
||
6029 | { |
||
6030 | // If the writeSource is firing, we need to pause it |
||
6031 | // or else it will continue to fire over and over again. |
||
6032 | |||
6033 | if (flags & kSocketCanAcceptBytes) |
||
6034 | { |
||
6035 | [self suspendWriteSource]; |
||
6036 | } |
||
6037 | } |
||
6038 | return; |
||
6039 | } |
||
6040 | |||
6041 | if (!(flags & kSocketCanAcceptBytes)) |
||
6042 | { |
||
6043 | LogVerbose(@"No space available to write..."); |
||
6044 | |||
6045 | // No space available to write. |
||
6046 | |||
6047 | if (![self usingCFStreamForTLS]) |
||
6048 | { |
||
6049 | // Need to wait for writeSource to fire and notify us of |
||
6050 | // available space in the socket's internal write buffer. |
||
6051 | |||
6052 | [self resumeWriteSource]; |
||
6053 | } |
||
6054 | return; |
||
6055 | } |
||
6056 | |||
6057 | if (flags & kStartingWriteTLS) |
||
6058 | { |
||
6059 | LogVerbose(@"Waiting for SSL/TLS handshake to complete"); |
||
6060 | |||
6061 | // The writeQueue is waiting for SSL/TLS handshake to complete. |
||
6062 | |||
6063 | if (flags & kStartingReadTLS) |
||
6064 | { |
||
6065 | if ([self usingSecureTransportForTLS] && lastSSLHandshakeError == errSSLWouldBlock) |
||
6066 | { |
||
6067 | // We are in the process of a SSL Handshake. |
||
6068 | // We were waiting for available space in the socket's internal OS buffer to continue writing. |
||
6069 | |||
6070 | [self ssl_continueSSLHandshake]; |
||
6071 | } |
||
6072 | } |
||
6073 | else |
||
6074 | { |
||
6075 | // We are still waiting for the readQueue to drain and start the SSL/TLS process. |
||
6076 | // We now know we can write to the socket. |
||
6077 | |||
6078 | if (![self usingCFStreamForTLS]) |
||
6079 | { |
||
6080 | // Suspend the write source or else it will continue to fire nonstop. |
||
6081 | |||
6082 | [self suspendWriteSource]; |
||
6083 | } |
||
6084 | } |
||
6085 | |||
6086 | return; |
||
6087 | } |
||
6088 | |||
6089 | // Note: This method is not called if currentWrite is a GCDAsyncSpecialPacket (startTLS packet) |
||
6090 | |||
6091 | BOOL waiting = NO; |
||
6092 | NSError *error = nil; |
||
6093 | size_t bytesWritten = 0; |
||
6094 | |||
6095 | if (flags & kSocketSecure) |
||
6096 | { |
||
6097 | if ([self usingCFStreamForTLS]) |
||
6098 | { |
||
6099 | #if TARGET_OS_IPHONE |
||
6100 | |||
6101 | // |
||
6102 | // Writing data using CFStream (over internal TLS) |
||
6103 | // |
||
6104 | |||
6105 | const uint8_t *buffer = (const uint8_t *)[currentWrite->buffer bytes] + currentWrite->bytesDone; |
||
6106 | |||
6107 | NSUInteger bytesToWrite = [currentWrite->buffer length] - currentWrite->bytesDone; |
||
6108 | |||
6109 | if (bytesToWrite > SIZE_MAX) // NSUInteger may be bigger than size_t (write param 3) |
||
6110 | { |
||
6111 | bytesToWrite = SIZE_MAX; |
||
6112 | } |
||
6113 | |||
6114 | CFIndex result = CFWriteStreamWrite(writeStream, buffer, (CFIndex)bytesToWrite); |
||
6115 | LogVerbose(@"CFWriteStreamWrite(%lu) = %li", (unsigned long)bytesToWrite, result); |
||
6116 | |||
6117 | if (result < 0) |
||
6118 | { |
||
6119 | error = (__bridge_transfer NSError *)CFWriteStreamCopyError(writeStream); |
||
6120 | } |
||
6121 | else |
||
6122 | { |
||
6123 | bytesWritten = (size_t)result; |
||
6124 | |||
6125 | // We always set waiting to true in this scenario. |
||
6126 | // CFStream may have altered our underlying socket to non-blocking. |
||
6127 | // Thus if we attempt to write without a callback, we may end up blocking our queue. |
||
6128 | waiting = YES; |
||
6129 | } |
||
6130 | |||
6131 | #endif |
||
6132 | } |
||
6133 | else |
||
6134 | { |
||
6135 | // We're going to use the SSLWrite function. |
||
6136 | // |
||
6137 | // OSStatus SSLWrite(SSLContextRef context, const void *data, size_t dataLength, size_t *processed) |
||
6138 | // |
||
6139 | // Parameters: |
||
6140 | // context - An SSL session context reference. |
||
6141 | // data - A pointer to the buffer of data to write. |
||
6142 | // dataLength - The amount, in bytes, of data to write. |
||
6143 | // processed - On return, the length, in bytes, of the data actually written. |
||
6144 | // |
||
6145 | // It sounds pretty straight-forward, |
||
6146 | // but there are a few caveats you should be aware of. |
||
6147 | // |
||
6148 | // The SSLWrite method operates in a non-obvious (and rather annoying) manner. |
||
6149 | // According to the documentation: |
||
6150 | // |
||
6151 | // Because you may configure the underlying connection to operate in a non-blocking manner, |
||
6152 | // a write operation might return errSSLWouldBlock, indicating that less data than requested |
||
6153 | // was actually transferred. In this case, you should repeat the call to SSLWrite until some |
||
6154 | // other result is returned. |
||
6155 | // |
||
6156 | // This sounds perfect, but when our SSLWriteFunction returns errSSLWouldBlock, |
||
6157 | // then the SSLWrite method returns (with the proper errSSLWouldBlock return value), |
||
6158 | // but it sets processed to dataLength !! |
||
6159 | // |
||
6160 | // In other words, if the SSLWrite function doesn't completely write all the data we tell it to, |
||
6161 | // then it doesn't tell us how many bytes were actually written. So, for example, if we tell it to |
||
6162 | // write 256 bytes then it might actually write 128 bytes, but then report 0 bytes written. |
||
6163 | // |
||
6164 | // You might be wondering: |
||
6165 | // If the SSLWrite function doesn't tell us how many bytes were written, |
||
6166 | // then how in the world are we supposed to update our parameters (buffer & bytesToWrite) |
||
6167 | // for the next time we invoke SSLWrite? |
||
6168 | // |
||
6169 | // The answer is that SSLWrite cached all the data we told it to write, |
||
6170 | // and it will push out that data next time we call SSLWrite. |
||
6171 | // If we call SSLWrite with new data, it will push out the cached data first, and then the new data. |
||
6172 | // If we call SSLWrite with empty data, then it will simply push out the cached data. |
||
6173 | // |
||
6174 | // For this purpose we're going to break large writes into a series of smaller writes. |
||
6175 | // This allows us to report progress back to the delegate. |
||
6176 | |||
6177 | OSStatus result; |
||
6178 | |||
6179 | BOOL hasCachedDataToWrite = (sslWriteCachedLength > 0); |
||
6180 | BOOL hasNewDataToWrite = YES; |
||
6181 | |||
6182 | if (hasCachedDataToWrite) |
||
6183 | { |
||
6184 | size_t processed = 0; |
||
6185 | |||
6186 | result = SSLWrite(sslContext, NULL, 0, &processed); |
||
6187 | |||
6188 | if (result == noErr) |
||
6189 | { |
||
6190 | bytesWritten = sslWriteCachedLength; |
||
6191 | sslWriteCachedLength = 0; |
||
6192 | |||
6193 | if ([currentWrite->buffer length] == (currentWrite->bytesDone + bytesWritten)) |
||
6194 | { |
||
6195 | // We've written all data for the current write. |
||
6196 | hasNewDataToWrite = NO; |
||
6197 | } |
||
6198 | } |
||
6199 | else |
||
6200 | { |
||
6201 | if (result == errSSLWouldBlock) |
||
6202 | { |
||
6203 | waiting = YES; |
||
6204 | } |
||
6205 | else |
||
6206 | { |
||
6207 | error = [self sslError:result]; |
||
6208 | } |
||
6209 | |||
6210 | // Can't write any new data since we were unable to write the cached data. |
||
6211 | hasNewDataToWrite = NO; |
||
6212 | } |
||
6213 | } |
||
6214 | |||
6215 | if (hasNewDataToWrite) |
||
6216 | { |
||
6217 | const uint8_t *buffer = (const uint8_t *)[currentWrite->buffer bytes] |
||
6218 | + currentWrite->bytesDone |
||
6219 | + bytesWritten; |
||
6220 | |||
6221 | NSUInteger bytesToWrite = [currentWrite->buffer length] - currentWrite->bytesDone - bytesWritten; |
||
6222 | |||
6223 | if (bytesToWrite > SIZE_MAX) // NSUInteger may be bigger than size_t (write param 3) |
||
6224 | { |
||
6225 | bytesToWrite = SIZE_MAX; |
||
6226 | } |
||
6227 | |||
6228 | size_t bytesRemaining = bytesToWrite; |
||
6229 | |||
6230 | BOOL keepLooping = YES; |
||
6231 | while (keepLooping) |
||
6232 | { |
||
6233 | const size_t sslMaxBytesToWrite = 32768; |
||
6234 | size_t sslBytesToWrite = MIN(bytesRemaining, sslMaxBytesToWrite); |
||
6235 | size_t sslBytesWritten = 0; |
||
6236 | |||
6237 | result = SSLWrite(sslContext, buffer, sslBytesToWrite, &sslBytesWritten); |
||
6238 | |||
6239 | if (result == noErr) |
||
6240 | { |
||
6241 | buffer += sslBytesWritten; |
||
6242 | bytesWritten += sslBytesWritten; |
||
6243 | bytesRemaining -= sslBytesWritten; |
||
6244 | |||
6245 | keepLooping = (bytesRemaining > 0); |
||
6246 | } |
||
6247 | else |
||
6248 | { |
||
6249 | if (result == errSSLWouldBlock) |
||
6250 | { |
||
6251 | waiting = YES; |
||
6252 | sslWriteCachedLength = sslBytesToWrite; |
||
6253 | } |
||
6254 | else |
||
6255 | { |
||
6256 | error = [self sslError:result]; |
||
6257 | } |
||
6258 | |||
6259 | keepLooping = NO; |
||
6260 | } |
||
6261 | |||
6262 | } // while (keepLooping) |
||
6263 | |||
6264 | } // if (hasNewDataToWrite) |
||
6265 | } |
||
6266 | } |
||
6267 | else |
||
6268 | { |
||
6269 | // |
||
6270 | // Writing data directly over raw socket |
||
6271 | // |
||
6272 | |||
6273 | int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; |
||
6274 | |||
6275 | const uint8_t *buffer = (const uint8_t *)[currentWrite->buffer bytes] + currentWrite->bytesDone; |
||
6276 | |||
6277 | NSUInteger bytesToWrite = [currentWrite->buffer length] - currentWrite->bytesDone; |
||
6278 | |||
6279 | if (bytesToWrite > SIZE_MAX) // NSUInteger may be bigger than size_t (write param 3) |
||
6280 | { |
||
6281 | bytesToWrite = SIZE_MAX; |
||
6282 | } |
||
6283 | |||
6284 | ssize_t result = write(socketFD, buffer, (size_t)bytesToWrite); |
||
6285 | LogVerbose(@"wrote to socket = %zd", result); |
||
6286 | |||
6287 | // Check results |
||
6288 | if (result < 0) |
||
6289 | { |
||
6290 | if (errno == EWOULDBLOCK) |
||
6291 | { |
||
6292 | waiting = YES; |
||
6293 | } |
||
6294 | else |
||
6295 | { |
||
6296 | error = [self errorWithErrno:errno reason:@"Error in write() function"]; |
||
6297 | } |
||
6298 | } |
||
6299 | else |
||
6300 | { |
||
6301 | bytesWritten = result; |
||
6302 | } |
||
6303 | } |
||
6304 | |||
6305 | // We're done with our writing. |
||
6306 | // If we explictly ran into a situation where the socket told us there was no room in the buffer, |
||
6307 | // then we immediately resume listening for notifications. |
||
6308 | // |
||
6309 | // We must do this before we dequeue another write, |
||
6310 | // as that may in turn invoke this method again. |
||
6311 | // |
||
6312 | // Note that if CFStream is involved, it may have maliciously put our socket in blocking mode. |
||
6313 | |||
6314 | if (waiting) |
||
6315 | { |
||
6316 | flags &= ~kSocketCanAcceptBytes; |
||
6317 | |||
6318 | if (![self usingCFStreamForTLS]) |
||
6319 | { |
||
6320 | [self resumeWriteSource]; |
||
6321 | } |
||
6322 | } |
||
6323 | |||
6324 | // Check our results |
||
6325 | |||
6326 | BOOL done = NO; |
||
6327 | |||
6328 | if (bytesWritten > 0) |
||
6329 | { |
||
6330 | // Update total amount read for the current write |
||
6331 | currentWrite->bytesDone += bytesWritten; |
||
6332 | LogVerbose(@"currentWrite->bytesDone = %lu", (unsigned long)currentWrite->bytesDone); |
||
6333 | |||
6334 | // Is packet done? |
||
6335 | done = (currentWrite->bytesDone == [currentWrite->buffer length]); |
||
6336 | } |
||
6337 | |||
6338 | if (done) |
||
6339 | { |
||
6340 | [self completeCurrentWrite]; |
||
6341 | |||
6342 | if (!error) |
||
6343 | { |
||
6344 | dispatch_async(socketQueue, ^{ @autoreleasepool{ |
||
6345 | |||
6346 | [self maybeDequeueWrite]; |
||
6347 | }}); |
||
6348 | } |
||
6349 | } |
||
6350 | else |
||
6351 | { |
||
6352 | // We were unable to finish writing the data, |
||
6353 | // so we're waiting for another callback to notify us of available space in the lower-level output buffer. |
||
6354 | |||
6355 | if (!waiting && !error) |
||
6356 | { |
||
6357 | // This would be the case if our write was able to accept some data, but not all of it. |
||
6358 | |||
6359 | flags &= ~kSocketCanAcceptBytes; |
||
6360 | |||
6361 | if (![self usingCFStreamForTLS]) |
||
6362 | { |
||
6363 | [self resumeWriteSource]; |
||
6364 | } |
||
6365 | } |
||
6366 | |||
6367 | if (bytesWritten > 0) |
||
6368 | { |
||
6369 | // We're not done with the entire write, but we have written some bytes |
||
6370 | |||
6371 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
6372 | |||
6373 | if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didWritePartialDataOfLength:tag:)]) |
||
6374 | { |
||
6375 | long theWriteTag = currentWrite->tag; |
||
6376 | |||
6377 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
6378 | |||
6379 | [theDelegate socket:self didWritePartialDataOfLength:bytesWritten tag:theWriteTag]; |
||
6380 | }}); |
||
6381 | } |
||
6382 | } |
||
6383 | } |
||
6384 | |||
6385 | // Check for errors |
||
6386 | |||
6387 | if (error) |
||
6388 | { |
||
6389 | [self closeWithError:[self errorWithErrno:errno reason:@"Error in write() function"]]; |
||
6390 | } |
||
6391 | |||
6392 | // Do not add any code here without first adding a return statement in the error case above. |
||
6393 | } |
||
6394 | |||
6395 | - (void)completeCurrentWrite |
||
6396 | { |
||
6397 | LogTrace(); |
||
6398 | |||
6399 | NSAssert(currentWrite, @"Trying to complete current write when there is no current write."); |
||
6400 | |||
6401 | |||
6402 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
6403 | |||
6404 | if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didWriteDataWithTag:)]) |
||
6405 | { |
||
6406 | long theWriteTag = currentWrite->tag; |
||
6407 | |||
6408 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
6409 | |||
6410 | [theDelegate socket:self didWriteDataWithTag:theWriteTag]; |
||
6411 | }}); |
||
6412 | } |
||
6413 | |||
6414 | [self endCurrentWrite]; |
||
6415 | } |
||
6416 | |||
6417 | - (void)endCurrentWrite |
||
6418 | { |
||
6419 | if (writeTimer) |
||
6420 | { |
||
6421 | dispatch_source_cancel(writeTimer); |
||
6422 | writeTimer = NULL; |
||
6423 | } |
||
6424 | |||
6425 | currentWrite = nil; |
||
6426 | } |
||
6427 | |||
6428 | - (void)setupWriteTimerWithTimeout:(NSTimeInterval)timeout |
||
6429 | { |
||
6430 | if (timeout >= 0.0) |
||
6431 | { |
||
6432 | writeTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, socketQueue); |
||
6433 | |||
6434 | __weak GCDAsyncSocket *weakSelf = self; |
||
6435 | |||
6436 | dispatch_source_set_event_handler(writeTimer, ^{ @autoreleasepool { |
||
6437 | #pragma clang diagnostic push |
||
6438 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
6439 | |||
6440 | __strong GCDAsyncSocket *strongSelf = weakSelf; |
||
6441 | if (strongSelf == nil) return_from_block; |
||
6442 | |||
6443 | [strongSelf doWriteTimeout]; |
||
6444 | |||
6445 | #pragma clang diagnostic pop |
||
6446 | }}); |
||
6447 | |||
6448 | #if !OS_OBJECT_USE_OBJC |
||
6449 | dispatch_source_t theWriteTimer = writeTimer; |
||
6450 | dispatch_source_set_cancel_handler(writeTimer, ^{ |
||
6451 | #pragma clang diagnostic push |
||
6452 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
6453 | |||
6454 | LogVerbose(@"dispatch_release(writeTimer)"); |
||
6455 | dispatch_release(theWriteTimer); |
||
6456 | |||
6457 | #pragma clang diagnostic pop |
||
6458 | }); |
||
6459 | #endif |
||
6460 | |||
6461 | dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)); |
||
6462 | |||
6463 | dispatch_source_set_timer(writeTimer, tt, DISPATCH_TIME_FOREVER, 0); |
||
6464 | dispatch_resume(writeTimer); |
||
6465 | } |
||
6466 | } |
||
6467 | |||
6468 | - (void)doWriteTimeout |
||
6469 | { |
||
6470 | // This is a little bit tricky. |
||
6471 | // Ideally we'd like to synchronously query the delegate about a timeout extension. |
||
6472 | // But if we do so synchronously we risk a possible deadlock. |
||
6473 | // So instead we have to do so asynchronously, and callback to ourselves from within the delegate block. |
||
6474 | |||
6475 | flags |= kWritesPaused; |
||
6476 | |||
6477 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
6478 | |||
6479 | if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:shouldTimeoutWriteWithTag:elapsed:bytesDone:)]) |
||
6480 | { |
||
6481 | GCDAsyncWritePacket *theWrite = currentWrite; |
||
6482 | |||
6483 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
6484 | |||
6485 | NSTimeInterval timeoutExtension = 0.0; |
||
6486 | |||
6487 | timeoutExtension = [theDelegate socket:self shouldTimeoutWriteWithTag:theWrite->tag |
||
6488 | elapsed:theWrite->timeout |
||
6489 | bytesDone:theWrite->bytesDone]; |
||
6490 | |||
6491 | dispatch_async(self->socketQueue, ^{ @autoreleasepool { |
||
6492 | |||
6493 | [self doWriteTimeoutWithExtension:timeoutExtension]; |
||
6494 | }}); |
||
6495 | }}); |
||
6496 | } |
||
6497 | else |
||
6498 | { |
||
6499 | [self doWriteTimeoutWithExtension:0.0]; |
||
6500 | } |
||
6501 | } |
||
6502 | |||
6503 | - (void)doWriteTimeoutWithExtension:(NSTimeInterval)timeoutExtension |
||
6504 | { |
||
6505 | if (currentWrite) |
||
6506 | { |
||
6507 | if (timeoutExtension > 0.0) |
||
6508 | { |
||
6509 | currentWrite->timeout += timeoutExtension; |
||
6510 | |||
6511 | // Reschedule the timer |
||
6512 | dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeoutExtension * NSEC_PER_SEC)); |
||
6513 | dispatch_source_set_timer(writeTimer, tt, DISPATCH_TIME_FOREVER, 0); |
||
6514 | |||
6515 | // Unpause writes, and continue |
||
6516 | flags &= ~kWritesPaused; |
||
6517 | [self doWriteData]; |
||
6518 | } |
||
6519 | else |
||
6520 | { |
||
6521 | LogVerbose(@"WriteTimeout"); |
||
6522 | |||
6523 | [self closeWithError:[self writeTimeoutError]]; |
||
6524 | } |
||
6525 | } |
||
6526 | } |
||
6527 | |||
6528 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
6529 | #pragma mark Security |
||
6530 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
6531 | |||
6532 | - (void)startTLS:(NSDictionary *)tlsSettings |
||
6533 | { |
||
6534 | LogTrace(); |
||
6535 | |||
6536 | if (tlsSettings == nil) |
||
6537 | { |
||
6538 | // Passing nil/NULL to CFReadStreamSetProperty will appear to work the same as passing an empty dictionary, |
||
6539 | // but causes problems if we later try to fetch the remote host's certificate. |
||
6540 | // |
||
6541 | // To be exact, it causes the following to return NULL instead of the normal result: |
||
6542 | // CFReadStreamCopyProperty(readStream, kCFStreamPropertySSLPeerCertificates) |
||
6543 | // |
||
6544 | // So we use an empty dictionary instead, which works perfectly. |
||
6545 | |||
6546 | tlsSettings = [NSDictionary dictionary]; |
||
6547 | } |
||
6548 | |||
6549 | GCDAsyncSpecialPacket *packet = [[GCDAsyncSpecialPacket alloc] initWithTLSSettings:tlsSettings]; |
||
6550 | |||
6551 | dispatch_async(socketQueue, ^{ @autoreleasepool { |
||
6552 | |||
6553 | if ((self->flags & kSocketStarted) && !(self->flags & kQueuedTLS) && !(self->flags & kForbidReadsWrites)) |
||
6554 | { |
||
6555 | [self->readQueue addObject:packet]; |
||
6556 | [self->writeQueue addObject:packet]; |
||
6557 | |||
6558 | self->flags |= kQueuedTLS; |
||
6559 | |||
6560 | [self maybeDequeueRead]; |
||
6561 | [self maybeDequeueWrite]; |
||
6562 | } |
||
6563 | }}); |
||
6564 | |||
6565 | } |
||
6566 | |||
6567 | - (void)maybeStartTLS |
||
6568 | { |
||
6569 | // We can't start TLS until: |
||
6570 | // - All queued reads prior to the user calling startTLS are complete |
||
6571 | // - All queued writes prior to the user calling startTLS are complete |
||
6572 | // |
||
6573 | // We'll know these conditions are met when both kStartingReadTLS and kStartingWriteTLS are set |
||
6574 | |||
6575 | if ((flags & kStartingReadTLS) && (flags & kStartingWriteTLS)) |
||
6576 | { |
||
6577 | BOOL useSecureTransport = YES; |
||
6578 | |||
6579 | #if TARGET_OS_IPHONE |
||
6580 | { |
||
6581 | GCDAsyncSpecialPacket *tlsPacket = (GCDAsyncSpecialPacket *)currentRead; |
||
6582 | NSDictionary *tlsSettings = @{}; |
||
6583 | if (tlsPacket) { |
||
6584 | tlsSettings = tlsPacket->tlsSettings; |
||
6585 | } |
||
6586 | NSNumber *value = [tlsSettings objectForKey:GCDAsyncSocketUseCFStreamForTLS]; |
||
6587 | if (value && [value boolValue]) |
||
6588 | useSecureTransport = NO; |
||
6589 | } |
||
6590 | #endif |
||
6591 | |||
6592 | if (useSecureTransport) |
||
6593 | { |
||
6594 | [self ssl_startTLS]; |
||
6595 | } |
||
6596 | else |
||
6597 | { |
||
6598 | #if TARGET_OS_IPHONE |
||
6599 | [self cf_startTLS]; |
||
6600 | #endif |
||
6601 | } |
||
6602 | } |
||
6603 | } |
||
6604 | |||
6605 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
6606 | #pragma mark Security via SecureTransport |
||
6607 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
6608 | |||
6609 | - (OSStatus)sslReadWithBuffer:(void *)buffer length:(size_t *)bufferLength |
||
6610 | { |
||
6611 | LogVerbose(@"sslReadWithBuffer:%p length:%lu", buffer, (unsigned long)*bufferLength); |
||
6612 | |||
6613 | if ((socketFDBytesAvailable == 0) && ([sslPreBuffer availableBytes] == 0)) |
||
6614 | { |
||
6615 | LogVerbose(@"%@ - No data available to read...", THIS_METHOD); |
||
6616 | |||
6617 | // No data available to read. |
||
6618 | // |
||
6619 | // Need to wait for readSource to fire and notify us of |
||
6620 | // available data in the socket's internal read buffer. |
||
6621 | |||
6622 | [self resumeReadSource]; |
||
6623 | |||
6624 | *bufferLength = 0; |
||
6625 | return errSSLWouldBlock; |
||
6626 | } |
||
6627 | |||
6628 | size_t totalBytesRead = 0; |
||
6629 | size_t totalBytesLeftToBeRead = *bufferLength; |
||
6630 | |||
6631 | BOOL done = NO; |
||
6632 | BOOL socketError = NO; |
||
6633 | |||
6634 | // |
||
6635 | // STEP 1 : READ FROM SSL PRE BUFFER |
||
6636 | // |
||
6637 | |||
6638 | size_t sslPreBufferLength = [sslPreBuffer availableBytes]; |
||
6639 | |||
6640 | if (sslPreBufferLength > 0) |
||
6641 | { |
||
6642 | LogVerbose(@"%@: Reading from SSL pre buffer...", THIS_METHOD); |
||
6643 | |||
6644 | size_t bytesToCopy; |
||
6645 | if (sslPreBufferLength > totalBytesLeftToBeRead) |
||
6646 | bytesToCopy = totalBytesLeftToBeRead; |
||
6647 | else |
||
6648 | bytesToCopy = sslPreBufferLength; |
||
6649 | |||
6650 | LogVerbose(@"%@: Copying %zu bytes from sslPreBuffer", THIS_METHOD, bytesToCopy); |
||
6651 | |||
6652 | memcpy(buffer, [sslPreBuffer readBuffer], bytesToCopy); |
||
6653 | [sslPreBuffer didRead:bytesToCopy]; |
||
6654 | |||
6655 | LogVerbose(@"%@: sslPreBuffer.length = %zu", THIS_METHOD, [sslPreBuffer availableBytes]); |
||
6656 | |||
6657 | totalBytesRead += bytesToCopy; |
||
6658 | totalBytesLeftToBeRead -= bytesToCopy; |
||
6659 | |||
6660 | done = (totalBytesLeftToBeRead == 0); |
||
6661 | |||
6662 | if (done) LogVerbose(@"%@: Complete", THIS_METHOD); |
||
6663 | } |
||
6664 | |||
6665 | // |
||
6666 | // STEP 2 : READ FROM SOCKET |
||
6667 | // |
||
6668 | |||
6669 | if (!done && (socketFDBytesAvailable > 0)) |
||
6670 | { |
||
6671 | LogVerbose(@"%@: Reading from socket...", THIS_METHOD); |
||
6672 | |||
6673 | int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; |
||
6674 | |||
6675 | BOOL readIntoPreBuffer; |
||
6676 | size_t bytesToRead; |
||
6677 | uint8_t *buf; |
||
6678 | |||
6679 | if (socketFDBytesAvailable > totalBytesLeftToBeRead) |
||
6680 | { |
||
6681 | // Read all available data from socket into sslPreBuffer. |
||
6682 | // Then copy requested amount into dataBuffer. |
||
6683 | |||
6684 | LogVerbose(@"%@: Reading into sslPreBuffer...", THIS_METHOD); |
||
6685 | |||
6686 | [sslPreBuffer ensureCapacityForWrite:socketFDBytesAvailable]; |
||
6687 | |||
6688 | readIntoPreBuffer = YES; |
||
6689 | bytesToRead = (size_t)socketFDBytesAvailable; |
||
6690 | buf = [sslPreBuffer writeBuffer]; |
||
6691 | } |
||
6692 | else |
||
6693 | { |
||
6694 | // Read available data from socket directly into dataBuffer. |
||
6695 | |||
6696 | LogVerbose(@"%@: Reading directly into dataBuffer...", THIS_METHOD); |
||
6697 | |||
6698 | readIntoPreBuffer = NO; |
||
6699 | bytesToRead = totalBytesLeftToBeRead; |
||
6700 | buf = (uint8_t *)buffer + totalBytesRead; |
||
6701 | } |
||
6702 | |||
6703 | ssize_t result = read(socketFD, buf, bytesToRead); |
||
6704 | LogVerbose(@"%@: read from socket = %zd", THIS_METHOD, result); |
||
6705 | |||
6706 | if (result < 0) |
||
6707 | { |
||
6708 | LogVerbose(@"%@: read errno = %i", THIS_METHOD, errno); |
||
6709 | |||
6710 | if (errno != EWOULDBLOCK) |
||
6711 | { |
||
6712 | socketError = YES; |
||
6713 | } |
||
6714 | |||
6715 | socketFDBytesAvailable = 0; |
||
6716 | } |
||
6717 | else if (result == 0) |
||
6718 | { |
||
6719 | LogVerbose(@"%@: read EOF", THIS_METHOD); |
||
6720 | |||
6721 | socketError = YES; |
||
6722 | socketFDBytesAvailable = 0; |
||
6723 | } |
||
6724 | else |
||
6725 | { |
||
6726 | size_t bytesReadFromSocket = result; |
||
6727 | |||
6728 | if (socketFDBytesAvailable > bytesReadFromSocket) |
||
6729 | socketFDBytesAvailable -= bytesReadFromSocket; |
||
6730 | else |
||
6731 | socketFDBytesAvailable = 0; |
||
6732 | |||
6733 | if (readIntoPreBuffer) |
||
6734 | { |
||
6735 | [sslPreBuffer didWrite:bytesReadFromSocket]; |
||
6736 | |||
6737 | size_t bytesToCopy = MIN(totalBytesLeftToBeRead, bytesReadFromSocket); |
||
6738 | |||
6739 | LogVerbose(@"%@: Copying %zu bytes out of sslPreBuffer", THIS_METHOD, bytesToCopy); |
||
6740 | |||
6741 | memcpy((uint8_t *)buffer + totalBytesRead, [sslPreBuffer readBuffer], bytesToCopy); |
||
6742 | [sslPreBuffer didRead:bytesToCopy]; |
||
6743 | |||
6744 | totalBytesRead += bytesToCopy; |
||
6745 | totalBytesLeftToBeRead -= bytesToCopy; |
||
6746 | |||
6747 | LogVerbose(@"%@: sslPreBuffer.length = %zu", THIS_METHOD, [sslPreBuffer availableBytes]); |
||
6748 | } |
||
6749 | else |
||
6750 | { |
||
6751 | totalBytesRead += bytesReadFromSocket; |
||
6752 | totalBytesLeftToBeRead -= bytesReadFromSocket; |
||
6753 | } |
||
6754 | |||
6755 | done = (totalBytesLeftToBeRead == 0); |
||
6756 | |||
6757 | if (done) LogVerbose(@"%@: Complete", THIS_METHOD); |
||
6758 | } |
||
6759 | } |
||
6760 | |||
6761 | *bufferLength = totalBytesRead; |
||
6762 | |||
6763 | if (done) |
||
6764 | return noErr; |
||
6765 | |||
6766 | if (socketError) |
||
6767 | return errSSLClosedAbort; |
||
6768 | |||
6769 | return errSSLWouldBlock; |
||
6770 | } |
||
6771 | |||
6772 | - (OSStatus)sslWriteWithBuffer:(const void *)buffer length:(size_t *)bufferLength |
||
6773 | { |
||
6774 | if (!(flags & kSocketCanAcceptBytes)) |
||
6775 | { |
||
6776 | // Unable to write. |
||
6777 | // |
||
6778 | // Need to wait for writeSource to fire and notify us of |
||
6779 | // available space in the socket's internal write buffer. |
||
6780 | |||
6781 | [self resumeWriteSource]; |
||
6782 | |||
6783 | *bufferLength = 0; |
||
6784 | return errSSLWouldBlock; |
||
6785 | } |
||
6786 | |||
6787 | size_t bytesToWrite = *bufferLength; |
||
6788 | size_t bytesWritten = 0; |
||
6789 | |||
6790 | BOOL done = NO; |
||
6791 | BOOL socketError = NO; |
||
6792 | |||
6793 | int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; |
||
6794 | |||
6795 | ssize_t result = write(socketFD, buffer, bytesToWrite); |
||
6796 | |||
6797 | if (result < 0) |
||
6798 | { |
||
6799 | if (errno != EWOULDBLOCK) |
||
6800 | { |
||
6801 | socketError = YES; |
||
6802 | } |
||
6803 | |||
6804 | flags &= ~kSocketCanAcceptBytes; |
||
6805 | } |
||
6806 | else if (result == 0) |
||
6807 | { |
||
6808 | flags &= ~kSocketCanAcceptBytes; |
||
6809 | } |
||
6810 | else |
||
6811 | { |
||
6812 | bytesWritten = result; |
||
6813 | |||
6814 | done = (bytesWritten == bytesToWrite); |
||
6815 | } |
||
6816 | |||
6817 | *bufferLength = bytesWritten; |
||
6818 | |||
6819 | if (done) |
||
6820 | return noErr; |
||
6821 | |||
6822 | if (socketError) |
||
6823 | return errSSLClosedAbort; |
||
6824 | |||
6825 | return errSSLWouldBlock; |
||
6826 | } |
||
6827 | |||
6828 | static OSStatus SSLReadFunction(SSLConnectionRef connection, void *data, size_t *dataLength) |
||
6829 | { |
||
6830 | GCDAsyncSocket *asyncSocket = (__bridge GCDAsyncSocket *)connection; |
||
6831 | |||
6832 | NSCAssert(dispatch_get_specific(asyncSocket->IsOnSocketQueueOrTargetQueueKey), @"What the deuce?"); |
||
6833 | |||
6834 | return [asyncSocket sslReadWithBuffer:data length:dataLength]; |
||
6835 | } |
||
6836 | |||
6837 | static OSStatus SSLWriteFunction(SSLConnectionRef connection, const void *data, size_t *dataLength) |
||
6838 | { |
||
6839 | GCDAsyncSocket *asyncSocket = (__bridge GCDAsyncSocket *)connection; |
||
6840 | |||
6841 | NSCAssert(dispatch_get_specific(asyncSocket->IsOnSocketQueueOrTargetQueueKey), @"What the deuce?"); |
||
6842 | |||
6843 | return [asyncSocket sslWriteWithBuffer:data length:dataLength]; |
||
6844 | } |
||
6845 | |||
6846 | - (void)ssl_startTLS |
||
6847 | { |
||
6848 | LogTrace(); |
||
6849 | |||
6850 | LogVerbose(@"Starting TLS (via SecureTransport)..."); |
||
6851 | |||
6852 | OSStatus status; |
||
6853 | |||
6854 | GCDAsyncSpecialPacket *tlsPacket = (GCDAsyncSpecialPacket *)currentRead; |
||
6855 | if (tlsPacket == nil) // Code to quiet the analyzer |
||
6856 | { |
||
6857 | NSAssert(NO, @"Logic error"); |
||
6858 | |||
6859 | [self closeWithError:[self otherError:@"Logic error"]]; |
||
6860 | return; |
||
6861 | } |
||
6862 | NSDictionary *tlsSettings = tlsPacket->tlsSettings; |
||
6863 | |||
6864 | // Create SSLContext, and setup IO callbacks and connection ref |
||
6865 | |||
6866 | NSNumber *isServerNumber = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLIsServer]; |
||
6867 | BOOL isServer = [isServerNumber boolValue]; |
||
6868 | |||
6869 | #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) |
||
6870 | { |
||
6871 | if (isServer) |
||
6872 | sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType); |
||
6873 | else |
||
6874 | sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); |
||
6875 | |||
6876 | if (sslContext == NULL) |
||
6877 | { |
||
6878 | [self closeWithError:[self otherError:@"Error in SSLCreateContext"]]; |
||
6879 | return; |
||
6880 | } |
||
6881 | } |
||
6882 | #else // (__MAC_OS_X_VERSION_MIN_REQUIRED < 1080) |
||
6883 | { |
||
6884 | status = SSLNewContext(isServer, &sslContext); |
||
6885 | if (status != noErr) |
||
6886 | { |
||
6887 | [self closeWithError:[self otherError:@"Error in SSLNewContext"]]; |
||
6888 | return; |
||
6889 | } |
||
6890 | } |
||
6891 | #endif |
||
6892 | |||
6893 | status = SSLSetIOFuncs(sslContext, &SSLReadFunction, &SSLWriteFunction); |
||
6894 | if (status != noErr) |
||
6895 | { |
||
6896 | [self closeWithError:[self otherError:@"Error in SSLSetIOFuncs"]]; |
||
6897 | return; |
||
6898 | } |
||
6899 | |||
6900 | status = SSLSetConnection(sslContext, (__bridge SSLConnectionRef)self); |
||
6901 | if (status != noErr) |
||
6902 | { |
||
6903 | [self closeWithError:[self otherError:@"Error in SSLSetConnection"]]; |
||
6904 | return; |
||
6905 | } |
||
6906 | |||
6907 | |||
6908 | NSNumber *shouldManuallyEvaluateTrust = [tlsSettings objectForKey:GCDAsyncSocketManuallyEvaluateTrust]; |
||
6909 | if ([shouldManuallyEvaluateTrust boolValue]) |
||
6910 | { |
||
6911 | if (isServer) |
||
6912 | { |
||
6913 | [self closeWithError:[self otherError:@"Manual trust validation is not supported for server sockets"]]; |
||
6914 | return; |
||
6915 | } |
||
6916 | |||
6917 | status = SSLSetSessionOption(sslContext, kSSLSessionOptionBreakOnServerAuth, true); |
||
6918 | if (status != noErr) |
||
6919 | { |
||
6920 | [self closeWithError:[self otherError:@"Error in SSLSetSessionOption"]]; |
||
6921 | return; |
||
6922 | } |
||
6923 | |||
6924 | #if !TARGET_OS_IPHONE && (__MAC_OS_X_VERSION_MIN_REQUIRED < 1080) |
||
6925 | |||
6926 | // Note from Apple's documentation: |
||
6927 | // |
||
6928 | // It is only necessary to call SSLSetEnableCertVerify on the Mac prior to OS X 10.8. |
||
6929 | // On OS X 10.8 and later setting kSSLSessionOptionBreakOnServerAuth always disables the |
||
6930 | // built-in trust evaluation. All versions of iOS behave like OS X 10.8 and thus |
||
6931 | // SSLSetEnableCertVerify is not available on that platform at all. |
||
6932 | |||
6933 | status = SSLSetEnableCertVerify(sslContext, NO); |
||
6934 | if (status != noErr) |
||
6935 | { |
||
6936 | [self closeWithError:[self otherError:@"Error in SSLSetEnableCertVerify"]]; |
||
6937 | return; |
||
6938 | } |
||
6939 | |||
6940 | #endif |
||
6941 | } |
||
6942 | |||
6943 | // Configure SSLContext from given settings |
||
6944 | // |
||
6945 | // Checklist: |
||
6946 | // 1. kCFStreamSSLPeerName |
||
6947 | // 2. kCFStreamSSLCertificates |
||
6948 | // 3. GCDAsyncSocketSSLPeerID |
||
6949 | // 4. GCDAsyncSocketSSLProtocolVersionMin |
||
6950 | // 5. GCDAsyncSocketSSLProtocolVersionMax |
||
6951 | // 6. GCDAsyncSocketSSLSessionOptionFalseStart |
||
6952 | // 7. GCDAsyncSocketSSLSessionOptionSendOneByteRecord |
||
6953 | // 8. GCDAsyncSocketSSLCipherSuites |
||
6954 | // 9. GCDAsyncSocketSSLDiffieHellmanParameters (Mac) |
||
6955 | // 10. GCDAsyncSocketSSLALPN |
||
6956 | // |
||
6957 | // Deprecated (throw error): |
||
6958 | // 10. kCFStreamSSLAllowsAnyRoot |
||
6959 | // 11. kCFStreamSSLAllowsExpiredRoots |
||
6960 | // 12. kCFStreamSSLAllowsExpiredCertificates |
||
6961 | // 13. kCFStreamSSLValidatesCertificateChain |
||
6962 | // 14. kCFStreamSSLLevel |
||
6963 | |||
6964 | NSObject *value; |
||
6965 | |||
6966 | // 1. kCFStreamSSLPeerName |
||
6967 | |||
6968 | value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLPeerName]; |
||
6969 | if ([value isKindOfClass:[NSString class]]) |
||
6970 | { |
||
6971 | NSString *peerName = (NSString *)value; |
||
6972 | |||
6973 | const char *peer = [peerName UTF8String]; |
||
6974 | size_t peerLen = strlen(peer); |
||
6975 | |||
6976 | status = SSLSetPeerDomainName(sslContext, peer, peerLen); |
||
6977 | if (status != noErr) |
||
6978 | { |
||
6979 | [self closeWithError:[self otherError:@"Error in SSLSetPeerDomainName"]]; |
||
6980 | return; |
||
6981 | } |
||
6982 | } |
||
6983 | else if (value) |
||
6984 | { |
||
6985 | NSAssert(NO, @"Invalid value for kCFStreamSSLPeerName. Value must be of type NSString."); |
||
6986 | |||
6987 | [self closeWithError:[self otherError:@"Invalid value for kCFStreamSSLPeerName."]]; |
||
6988 | return; |
||
6989 | } |
||
6990 | |||
6991 | // 2. kCFStreamSSLCertificates |
||
6992 | |||
6993 | value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLCertificates]; |
||
6994 | if ([value isKindOfClass:[NSArray class]]) |
||
6995 | { |
||
6996 | NSArray *certs = (NSArray *)value; |
||
6997 | |||
6998 | status = SSLSetCertificate(sslContext, (__bridge CFArrayRef)certs); |
||
6999 | if (status != noErr) |
||
7000 | { |
||
7001 | [self closeWithError:[self otherError:@"Error in SSLSetCertificate"]]; |
||
7002 | return; |
||
7003 | } |
||
7004 | } |
||
7005 | else if (value) |
||
7006 | { |
||
7007 | NSAssert(NO, @"Invalid value for kCFStreamSSLCertificates. Value must be of type NSArray."); |
||
7008 | |||
7009 | [self closeWithError:[self otherError:@"Invalid value for kCFStreamSSLCertificates."]]; |
||
7010 | return; |
||
7011 | } |
||
7012 | |||
7013 | // 3. GCDAsyncSocketSSLPeerID |
||
7014 | |||
7015 | value = [tlsSettings objectForKey:GCDAsyncSocketSSLPeerID]; |
||
7016 | if ([value isKindOfClass:[NSData class]]) |
||
7017 | { |
||
7018 | NSData *peerIdData = (NSData *)value; |
||
7019 | |||
7020 | status = SSLSetPeerID(sslContext, [peerIdData bytes], [peerIdData length]); |
||
7021 | if (status != noErr) |
||
7022 | { |
||
7023 | [self closeWithError:[self otherError:@"Error in SSLSetPeerID"]]; |
||
7024 | return; |
||
7025 | } |
||
7026 | } |
||
7027 | else if (value) |
||
7028 | { |
||
7029 | NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLPeerID. Value must be of type NSData." |
||
7030 | @" (You can convert strings to data using a method like" |
||
7031 | @" [string dataUsingEncoding:NSUTF8StringEncoding])"); |
||
7032 | |||
7033 | [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLPeerID."]]; |
||
7034 | return; |
||
7035 | } |
||
7036 | |||
7037 | // 4. GCDAsyncSocketSSLProtocolVersionMin |
||
7038 | |||
7039 | value = [tlsSettings objectForKey:GCDAsyncSocketSSLProtocolVersionMin]; |
||
7040 | if ([value isKindOfClass:[NSNumber class]]) |
||
7041 | { |
||
7042 | SSLProtocol minProtocol = (SSLProtocol)[(NSNumber *)value intValue]; |
||
7043 | if (minProtocol != kSSLProtocolUnknown) |
||
7044 | { |
||
7045 | status = SSLSetProtocolVersionMin(sslContext, minProtocol); |
||
7046 | if (status != noErr) |
||
7047 | { |
||
7048 | [self closeWithError:[self otherError:@"Error in SSLSetProtocolVersionMin"]]; |
||
7049 | return; |
||
7050 | } |
||
7051 | } |
||
7052 | } |
||
7053 | else if (value) |
||
7054 | { |
||
7055 | NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLProtocolVersionMin. Value must be of type NSNumber."); |
||
7056 | |||
7057 | [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLProtocolVersionMin."]]; |
||
7058 | return; |
||
7059 | } |
||
7060 | |||
7061 | // 5. GCDAsyncSocketSSLProtocolVersionMax |
||
7062 | |||
7063 | value = [tlsSettings objectForKey:GCDAsyncSocketSSLProtocolVersionMax]; |
||
7064 | if ([value isKindOfClass:[NSNumber class]]) |
||
7065 | { |
||
7066 | SSLProtocol maxProtocol = (SSLProtocol)[(NSNumber *)value intValue]; |
||
7067 | if (maxProtocol != kSSLProtocolUnknown) |
||
7068 | { |
||
7069 | status = SSLSetProtocolVersionMax(sslContext, maxProtocol); |
||
7070 | if (status != noErr) |
||
7071 | { |
||
7072 | [self closeWithError:[self otherError:@"Error in SSLSetProtocolVersionMax"]]; |
||
7073 | return; |
||
7074 | } |
||
7075 | } |
||
7076 | } |
||
7077 | else if (value) |
||
7078 | { |
||
7079 | NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLProtocolVersionMax. Value must be of type NSNumber."); |
||
7080 | |||
7081 | [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLProtocolVersionMax."]]; |
||
7082 | return; |
||
7083 | } |
||
7084 | |||
7085 | // 6. GCDAsyncSocketSSLSessionOptionFalseStart |
||
7086 | |||
7087 | value = [tlsSettings objectForKey:GCDAsyncSocketSSLSessionOptionFalseStart]; |
||
7088 | if ([value isKindOfClass:[NSNumber class]]) |
||
7089 | { |
||
7090 | NSNumber *falseStart = (NSNumber *)value; |
||
7091 | status = SSLSetSessionOption(sslContext, kSSLSessionOptionFalseStart, [falseStart boolValue]); |
||
7092 | if (status != noErr) |
||
7093 | { |
||
7094 | [self closeWithError:[self otherError:@"Error in SSLSetSessionOption (kSSLSessionOptionFalseStart)"]]; |
||
7095 | return; |
||
7096 | } |
||
7097 | } |
||
7098 | else if (value) |
||
7099 | { |
||
7100 | NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLSessionOptionFalseStart. Value must be of type NSNumber."); |
||
7101 | |||
7102 | [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLSessionOptionFalseStart."]]; |
||
7103 | return; |
||
7104 | } |
||
7105 | |||
7106 | // 7. GCDAsyncSocketSSLSessionOptionSendOneByteRecord |
||
7107 | |||
7108 | value = [tlsSettings objectForKey:GCDAsyncSocketSSLSessionOptionSendOneByteRecord]; |
||
7109 | if ([value isKindOfClass:[NSNumber class]]) |
||
7110 | { |
||
7111 | NSNumber *oneByteRecord = (NSNumber *)value; |
||
7112 | status = SSLSetSessionOption(sslContext, kSSLSessionOptionSendOneByteRecord, [oneByteRecord boolValue]); |
||
7113 | if (status != noErr) |
||
7114 | { |
||
7115 | [self closeWithError: |
||
7116 | [self otherError:@"Error in SSLSetSessionOption (kSSLSessionOptionSendOneByteRecord)"]]; |
||
7117 | return; |
||
7118 | } |
||
7119 | } |
||
7120 | else if (value) |
||
7121 | { |
||
7122 | NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLSessionOptionSendOneByteRecord." |
||
7123 | @" Value must be of type NSNumber."); |
||
7124 | |||
7125 | [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLSessionOptionSendOneByteRecord."]]; |
||
7126 | return; |
||
7127 | } |
||
7128 | |||
7129 | // 8. GCDAsyncSocketSSLCipherSuites |
||
7130 | |||
7131 | value = [tlsSettings objectForKey:GCDAsyncSocketSSLCipherSuites]; |
||
7132 | if ([value isKindOfClass:[NSArray class]]) |
||
7133 | { |
||
7134 | NSArray *cipherSuites = (NSArray *)value; |
||
7135 | NSUInteger numberCiphers = [cipherSuites count]; |
||
7136 | SSLCipherSuite ciphers[numberCiphers]; |
||
7137 | |||
7138 | NSUInteger cipherIndex; |
||
7139 | for (cipherIndex = 0; cipherIndex < numberCiphers; cipherIndex++) |
||
7140 | { |
||
7141 | NSNumber *cipherObject = [cipherSuites objectAtIndex:cipherIndex]; |
||
7142 | ciphers[cipherIndex] = (SSLCipherSuite)[cipherObject unsignedIntValue]; |
||
7143 | } |
||
7144 | |||
7145 | status = SSLSetEnabledCiphers(sslContext, ciphers, numberCiphers); |
||
7146 | if (status != noErr) |
||
7147 | { |
||
7148 | [self closeWithError:[self otherError:@"Error in SSLSetEnabledCiphers"]]; |
||
7149 | return; |
||
7150 | } |
||
7151 | } |
||
7152 | else if (value) |
||
7153 | { |
||
7154 | NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLCipherSuites. Value must be of type NSArray."); |
||
7155 | |||
7156 | [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLCipherSuites."]]; |
||
7157 | return; |
||
7158 | } |
||
7159 | |||
7160 | // 9. GCDAsyncSocketSSLDiffieHellmanParameters |
||
7161 | |||
7162 | #if !TARGET_OS_IPHONE |
||
7163 | value = [tlsSettings objectForKey:GCDAsyncSocketSSLDiffieHellmanParameters]; |
||
7164 | if ([value isKindOfClass:[NSData class]]) |
||
7165 | { |
||
7166 | NSData *diffieHellmanData = (NSData *)value; |
||
7167 | |||
7168 | status = SSLSetDiffieHellmanParams(sslContext, [diffieHellmanData bytes], [diffieHellmanData length]); |
||
7169 | if (status != noErr) |
||
7170 | { |
||
7171 | [self closeWithError:[self otherError:@"Error in SSLSetDiffieHellmanParams"]]; |
||
7172 | return; |
||
7173 | } |
||
7174 | } |
||
7175 | else if (value) |
||
7176 | { |
||
7177 | NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLDiffieHellmanParameters. Value must be of type NSData."); |
||
7178 | |||
7179 | [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLDiffieHellmanParameters."]]; |
||
7180 | return; |
||
7181 | } |
||
7182 | #endif |
||
7183 | |||
7184 | // 10. kCFStreamSSLCertificates |
||
7185 | value = [tlsSettings objectForKey:GCDAsyncSocketSSLALPN]; |
||
7186 | if ([value isKindOfClass:[NSArray class]]) |
||
7187 | { |
||
7188 | if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, *)) |
||
7189 | { |
||
7190 | CFArrayRef protocols = (__bridge CFArrayRef)((NSArray *) value); |
||
7191 | status = SSLSetALPNProtocols(sslContext, protocols); |
||
7192 | if (status != noErr) |
||
7193 | { |
||
7194 | [self closeWithError:[self otherError:@"Error in SSLSetALPNProtocols"]]; |
||
7195 | return; |
||
7196 | } |
||
7197 | } |
||
7198 | else |
||
7199 | { |
||
7200 | NSAssert(NO, @"Security option unavailable - GCDAsyncSocketSSLALPN" |
||
7201 | @" - iOS 11.0, macOS 10.13 required"); |
||
7202 | [self closeWithError:[self otherError:@"Security option unavailable - GCDAsyncSocketSSLALPN"]]; |
||
7203 | } |
||
7204 | } |
||
7205 | else if (value) |
||
7206 | { |
||
7207 | NSAssert(NO, @"Invalid value for GCDAsyncSocketSSLALPN. Value must be of type NSArray."); |
||
7208 | |||
7209 | [self closeWithError:[self otherError:@"Invalid value for GCDAsyncSocketSSLALPN."]]; |
||
7210 | return; |
||
7211 | } |
||
7212 | |||
7213 | // DEPRECATED checks |
||
7214 | |||
7215 | // 10. kCFStreamSSLAllowsAnyRoot |
||
7216 | |||
7217 | #pragma clang diagnostic push |
||
7218 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
||
7219 | value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLAllowsAnyRoot]; |
||
7220 | #pragma clang diagnostic pop |
||
7221 | if (value) |
||
7222 | { |
||
7223 | NSAssert(NO, @"Security option unavailable - kCFStreamSSLAllowsAnyRoot" |
||
7224 | @" - You must use manual trust evaluation"); |
||
7225 | |||
7226 | [self closeWithError:[self otherError:@"Security option unavailable - kCFStreamSSLAllowsAnyRoot"]]; |
||
7227 | return; |
||
7228 | } |
||
7229 | |||
7230 | // 11. kCFStreamSSLAllowsExpiredRoots |
||
7231 | |||
7232 | #pragma clang diagnostic push |
||
7233 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
||
7234 | value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLAllowsExpiredRoots]; |
||
7235 | #pragma clang diagnostic pop |
||
7236 | if (value) |
||
7237 | { |
||
7238 | NSAssert(NO, @"Security option unavailable - kCFStreamSSLAllowsExpiredRoots" |
||
7239 | @" - You must use manual trust evaluation"); |
||
7240 | |||
7241 | [self closeWithError:[self otherError:@"Security option unavailable - kCFStreamSSLAllowsExpiredRoots"]]; |
||
7242 | return; |
||
7243 | } |
||
7244 | |||
7245 | // 12. kCFStreamSSLValidatesCertificateChain |
||
7246 | |||
7247 | #pragma clang diagnostic push |
||
7248 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
||
7249 | value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLValidatesCertificateChain]; |
||
7250 | #pragma clang diagnostic pop |
||
7251 | if (value) |
||
7252 | { |
||
7253 | NSAssert(NO, @"Security option unavailable - kCFStreamSSLValidatesCertificateChain" |
||
7254 | @" - You must use manual trust evaluation"); |
||
7255 | |||
7256 | [self closeWithError:[self otherError:@"Security option unavailable - kCFStreamSSLValidatesCertificateChain"]]; |
||
7257 | return; |
||
7258 | } |
||
7259 | |||
7260 | // 13. kCFStreamSSLAllowsExpiredCertificates |
||
7261 | |||
7262 | #pragma clang diagnostic push |
||
7263 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
||
7264 | value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLAllowsExpiredCertificates]; |
||
7265 | #pragma clang diagnostic pop |
||
7266 | if (value) |
||
7267 | { |
||
7268 | NSAssert(NO, @"Security option unavailable - kCFStreamSSLAllowsExpiredCertificates" |
||
7269 | @" - You must use manual trust evaluation"); |
||
7270 | |||
7271 | [self closeWithError:[self otherError:@"Security option unavailable - kCFStreamSSLAllowsExpiredCertificates"]]; |
||
7272 | return; |
||
7273 | } |
||
7274 | |||
7275 | // 14. kCFStreamSSLLevel |
||
7276 | |||
7277 | #pragma clang diagnostic push |
||
7278 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
||
7279 | value = [tlsSettings objectForKey:(__bridge NSString *)kCFStreamSSLLevel]; |
||
7280 | #pragma clang diagnostic pop |
||
7281 | if (value) |
||
7282 | { |
||
7283 | NSAssert(NO, @"Security option unavailable - kCFStreamSSLLevel" |
||
7284 | @" - You must use GCDAsyncSocketSSLProtocolVersionMin & GCDAsyncSocketSSLProtocolVersionMax"); |
||
7285 | |||
7286 | [self closeWithError:[self otherError:@"Security option unavailable - kCFStreamSSLLevel"]]; |
||
7287 | return; |
||
7288 | } |
||
7289 | |||
7290 | // Setup the sslPreBuffer |
||
7291 | // |
||
7292 | // Any data in the preBuffer needs to be moved into the sslPreBuffer, |
||
7293 | // as this data is now part of the secure read stream. |
||
7294 | |||
7295 | sslPreBuffer = [[GCDAsyncSocketPreBuffer alloc] initWithCapacity:(1024 * 4)]; |
||
7296 | |||
7297 | size_t preBufferLength = [preBuffer availableBytes]; |
||
7298 | |||
7299 | if (preBufferLength > 0) |
||
7300 | { |
||
7301 | [sslPreBuffer ensureCapacityForWrite:preBufferLength]; |
||
7302 | |||
7303 | memcpy([sslPreBuffer writeBuffer], [preBuffer readBuffer], preBufferLength); |
||
7304 | [preBuffer didRead:preBufferLength]; |
||
7305 | [sslPreBuffer didWrite:preBufferLength]; |
||
7306 | } |
||
7307 | |||
7308 | sslErrCode = lastSSLHandshakeError = noErr; |
||
7309 | |||
7310 | // Start the SSL Handshake process |
||
7311 | |||
7312 | [self ssl_continueSSLHandshake]; |
||
7313 | } |
||
7314 | |||
7315 | - (void)ssl_continueSSLHandshake |
||
7316 | { |
||
7317 | LogTrace(); |
||
7318 | |||
7319 | // If the return value is noErr, the session is ready for normal secure communication. |
||
7320 | // If the return value is errSSLWouldBlock, the SSLHandshake function must be called again. |
||
7321 | // If the return value is errSSLServerAuthCompleted, we ask delegate if we should trust the |
||
7322 | // server and then call SSLHandshake again to resume the handshake or close the connection |
||
7323 | // errSSLPeerBadCert SSL error. |
||
7324 | // Otherwise, the return value indicates an error code. |
||
7325 | |||
7326 | OSStatus status = SSLHandshake(sslContext); |
||
7327 | lastSSLHandshakeError = status; |
||
7328 | |||
7329 | if (status == noErr) |
||
7330 | { |
||
7331 | LogVerbose(@"SSLHandshake complete"); |
||
7332 | |||
7333 | flags &= ~kStartingReadTLS; |
||
7334 | flags &= ~kStartingWriteTLS; |
||
7335 | |||
7336 | flags |= kSocketSecure; |
||
7337 | |||
7338 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
7339 | |||
7340 | if (delegateQueue && [theDelegate respondsToSelector:@selector(socketDidSecure:)]) |
||
7341 | { |
||
7342 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
7343 | |||
7344 | [theDelegate socketDidSecure:self]; |
||
7345 | }}); |
||
7346 | } |
||
7347 | |||
7348 | [self endCurrentRead]; |
||
7349 | [self endCurrentWrite]; |
||
7350 | |||
7351 | [self maybeDequeueRead]; |
||
7352 | [self maybeDequeueWrite]; |
||
7353 | } |
||
7354 | else if (status == errSSLPeerAuthCompleted) |
||
7355 | { |
||
7356 | LogVerbose(@"SSLHandshake peerAuthCompleted - awaiting delegate approval"); |
||
7357 | |||
7358 | __block SecTrustRef trust = NULL; |
||
7359 | status = SSLCopyPeerTrust(sslContext, &trust); |
||
7360 | if (status != noErr) |
||
7361 | { |
||
7362 | [self closeWithError:[self sslError:status]]; |
||
7363 | return; |
||
7364 | } |
||
7365 | |||
7366 | int aStateIndex = stateIndex; |
||
7367 | dispatch_queue_t theSocketQueue = socketQueue; |
||
7368 | |||
7369 | __weak GCDAsyncSocket *weakSelf = self; |
||
7370 | |||
7371 | void (^comletionHandler)(BOOL) = ^(BOOL shouldTrust){ @autoreleasepool { |
||
7372 | #pragma clang diagnostic push |
||
7373 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
7374 | |||
7375 | dispatch_async(theSocketQueue, ^{ @autoreleasepool { |
||
7376 | |||
7377 | if (trust) { |
||
7378 | CFRelease(trust); |
||
7379 | trust = NULL; |
||
7380 | } |
||
7381 | |||
7382 | __strong GCDAsyncSocket *strongSelf = weakSelf; |
||
7383 | if (strongSelf) |
||
7384 | { |
||
7385 | [strongSelf ssl_shouldTrustPeer:shouldTrust stateIndex:aStateIndex]; |
||
7386 | } |
||
7387 | }}); |
||
7388 | |||
7389 | #pragma clang diagnostic pop |
||
7390 | }}; |
||
7391 | |||
7392 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
7393 | |||
7394 | if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didReceiveTrust:completionHandler:)]) |
||
7395 | { |
||
7396 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
7397 | |||
7398 | [theDelegate socket:self didReceiveTrust:trust completionHandler:comletionHandler]; |
||
7399 | }}); |
||
7400 | } |
||
7401 | else |
||
7402 | { |
||
7403 | if (trust) { |
||
7404 | CFRelease(trust); |
||
7405 | trust = NULL; |
||
7406 | } |
||
7407 | |||
7408 | NSString *msg = @"GCDAsyncSocketManuallyEvaluateTrust specified in tlsSettings," |
||
7409 | @" but delegate doesn't implement socket:shouldTrustPeer:"; |
||
7410 | |||
7411 | [self closeWithError:[self otherError:msg]]; |
||
7412 | return; |
||
7413 | } |
||
7414 | } |
||
7415 | else if (status == errSSLWouldBlock) |
||
7416 | { |
||
7417 | LogVerbose(@"SSLHandshake continues..."); |
||
7418 | |||
7419 | // Handshake continues... |
||
7420 | // |
||
7421 | // This method will be called again from doReadData or doWriteData. |
||
7422 | } |
||
7423 | else |
||
7424 | { |
||
7425 | [self closeWithError:[self sslError:status]]; |
||
7426 | } |
||
7427 | } |
||
7428 | |||
7429 | - (void)ssl_shouldTrustPeer:(BOOL)shouldTrust stateIndex:(int)aStateIndex |
||
7430 | { |
||
7431 | LogTrace(); |
||
7432 | |||
7433 | if (aStateIndex != stateIndex) |
||
7434 | { |
||
7435 | LogInfo(@"Ignoring ssl_shouldTrustPeer - invalid state (maybe disconnected)"); |
||
7436 | |||
7437 | // One of the following is true |
||
7438 | // - the socket was disconnected |
||
7439 | // - the startTLS operation timed out |
||
7440 | // - the completionHandler was already invoked once |
||
7441 | |||
7442 | return; |
||
7443 | } |
||
7444 | |||
7445 | // Increment stateIndex to ensure completionHandler can only be called once. |
||
7446 | stateIndex++; |
||
7447 | |||
7448 | if (shouldTrust) |
||
7449 | { |
||
7450 | NSAssert(lastSSLHandshakeError == errSSLPeerAuthCompleted, @"ssl_shouldTrustPeer called when last error is %d and not errSSLPeerAuthCompleted", (int)lastSSLHandshakeError); |
||
7451 | [self ssl_continueSSLHandshake]; |
||
7452 | } |
||
7453 | else |
||
7454 | { |
||
7455 | [self closeWithError:[self sslError:errSSLPeerBadCert]]; |
||
7456 | } |
||
7457 | } |
||
7458 | |||
7459 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
7460 | #pragma mark Security via CFStream |
||
7461 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
7462 | |||
7463 | #if TARGET_OS_IPHONE |
||
7464 | |||
7465 | - (void)cf_finishSSLHandshake |
||
7466 | { |
||
7467 | LogTrace(); |
||
7468 | |||
7469 | if ((flags & kStartingReadTLS) && (flags & kStartingWriteTLS)) |
||
7470 | { |
||
7471 | flags &= ~kStartingReadTLS; |
||
7472 | flags &= ~kStartingWriteTLS; |
||
7473 | |||
7474 | flags |= kSocketSecure; |
||
7475 | |||
7476 | __strong id<GCDAsyncSocketDelegate> theDelegate = delegate; |
||
7477 | |||
7478 | if (delegateQueue && [theDelegate respondsToSelector:@selector(socketDidSecure:)]) |
||
7479 | { |
||
7480 | dispatch_async(delegateQueue, ^{ @autoreleasepool { |
||
7481 | |||
7482 | [theDelegate socketDidSecure:self]; |
||
7483 | }}); |
||
7484 | } |
||
7485 | |||
7486 | [self endCurrentRead]; |
||
7487 | [self endCurrentWrite]; |
||
7488 | |||
7489 | [self maybeDequeueRead]; |
||
7490 | [self maybeDequeueWrite]; |
||
7491 | } |
||
7492 | } |
||
7493 | |||
7494 | - (void)cf_abortSSLHandshake:(NSError *)error |
||
7495 | { |
||
7496 | LogTrace(); |
||
7497 | |||
7498 | if ((flags & kStartingReadTLS) && (flags & kStartingWriteTLS)) |
||
7499 | { |
||
7500 | flags &= ~kStartingReadTLS; |
||
7501 | flags &= ~kStartingWriteTLS; |
||
7502 | |||
7503 | [self closeWithError:error]; |
||
7504 | } |
||
7505 | } |
||
7506 | |||
7507 | - (void)cf_startTLS |
||
7508 | { |
||
7509 | LogTrace(); |
||
7510 | |||
7511 | LogVerbose(@"Starting TLS (via CFStream)..."); |
||
7512 | |||
7513 | if ([preBuffer availableBytes] > 0) |
||
7514 | { |
||
7515 | NSString *msg = @"Invalid TLS transition. Handshake has already been read from socket."; |
||
7516 | |||
7517 | [self closeWithError:[self otherError:msg]]; |
||
7518 | return; |
||
7519 | } |
||
7520 | |||
7521 | [self suspendReadSource]; |
||
7522 | [self suspendWriteSource]; |
||
7523 | |||
7524 | socketFDBytesAvailable = 0; |
||
7525 | flags &= ~kSocketCanAcceptBytes; |
||
7526 | flags &= ~kSecureSocketHasBytesAvailable; |
||
7527 | |||
7528 | flags |= kUsingCFStreamForTLS; |
||
7529 | |||
7530 | if (![self createReadAndWriteStream]) |
||
7531 | { |
||
7532 | [self closeWithError:[self otherError:@"Error in CFStreamCreatePairWithSocket"]]; |
||
7533 | return; |
||
7534 | } |
||
7535 | |||
7536 | if (![self registerForStreamCallbacksIncludingReadWrite:YES]) |
||
7537 | { |
||
7538 | [self closeWithError:[self otherError:@"Error in CFStreamSetClient"]]; |
||
7539 | return; |
||
7540 | } |
||
7541 | |||
7542 | if (![self addStreamsToRunLoop]) |
||
7543 | { |
||
7544 | [self closeWithError:[self otherError:@"Error in CFStreamScheduleWithRunLoop"]]; |
||
7545 | return; |
||
7546 | } |
||
7547 | |||
7548 | NSAssert([currentRead isKindOfClass:[GCDAsyncSpecialPacket class]], @"Invalid read packet for startTLS"); |
||
7549 | NSAssert([currentWrite isKindOfClass:[GCDAsyncSpecialPacket class]], @"Invalid write packet for startTLS"); |
||
7550 | |||
7551 | GCDAsyncSpecialPacket *tlsPacket = (GCDAsyncSpecialPacket *)currentRead; |
||
7552 | CFDictionaryRef tlsSettings = (__bridge CFDictionaryRef)tlsPacket->tlsSettings; |
||
7553 | |||
7554 | // Getting an error concerning kCFStreamPropertySSLSettings ? |
||
7555 | // You need to add the CFNetwork framework to your iOS application. |
||
7556 | |||
7557 | BOOL r1 = CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, tlsSettings); |
||
7558 | BOOL r2 = CFWriteStreamSetProperty(writeStream, kCFStreamPropertySSLSettings, tlsSettings); |
||
7559 | |||
7560 | // For some reason, starting around the time of iOS 4.3, |
||
7561 | // the first call to set the kCFStreamPropertySSLSettings will return true, |
||
7562 | // but the second will return false. |
||
7563 | // |
||
7564 | // Order doesn't seem to matter. |
||
7565 | // So you could call CFReadStreamSetProperty and then CFWriteStreamSetProperty, or you could reverse the order. |
||
7566 | // Either way, the first call will return true, and the second returns false. |
||
7567 | // |
||
7568 | // Interestingly, this doesn't seem to affect anything. |
||
7569 | // Which is not altogether unusual, as the documentation seems to suggest that (for many settings) |
||
7570 | // setting it on one side of the stream automatically sets it for the other side of the stream. |
||
7571 | // |
||
7572 | // Although there isn't anything in the documentation to suggest that the second attempt would fail. |
||
7573 | // |
||
7574 | // Furthermore, this only seems to affect streams that are negotiating a security upgrade. |
||
7575 | // In other words, the socket gets connected, there is some back-and-forth communication over the unsecure |
||
7576 | // connection, and then a startTLS is issued. |
||
7577 | // So this mostly affects newer protocols (XMPP, IMAP) as opposed to older protocols (HTTPS). |
||
7578 | |||
7579 | if (!r1 && !r2) // Yes, the && is correct - workaround for apple bug. |
||
7580 | { |
||
7581 | [self closeWithError:[self otherError:@"Error in CFStreamSetProperty"]]; |
||
7582 | return; |
||
7583 | } |
||
7584 | |||
7585 | if (![self openStreams]) |
||
7586 | { |
||
7587 | [self closeWithError:[self otherError:@"Error in CFStreamOpen"]]; |
||
7588 | return; |
||
7589 | } |
||
7590 | |||
7591 | LogVerbose(@"Waiting for SSL Handshake to complete..."); |
||
7592 | } |
||
7593 | |||
7594 | #endif |
||
7595 | |||
7596 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
7597 | #pragma mark CFStream |
||
7598 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
7599 | |||
7600 | #if TARGET_OS_IPHONE |
||
7601 | |||
7602 | + (void)ignore:(id)_ |
||
7603 | {} |
||
7604 | |||
7605 | + (void)startCFStreamThreadIfNeeded |
||
7606 | { |
||
7607 | LogTrace(); |
||
7608 | |||
7609 | static dispatch_once_t predicate; |
||
7610 | dispatch_once(&predicate, ^{ |
||
7611 | |||
7612 | cfstreamThreadRetainCount = 0; |
||
7613 | cfstreamThreadSetupQueue = dispatch_queue_create("GCDAsyncSocket-CFStreamThreadSetup", DISPATCH_QUEUE_SERIAL); |
||
7614 | }); |
||
7615 | |||
7616 | dispatch_sync(cfstreamThreadSetupQueue, ^{ @autoreleasepool { |
||
7617 | |||
7618 | if (++cfstreamThreadRetainCount == 1) |
||
7619 | { |
||
7620 | cfstreamThread = [[NSThread alloc] initWithTarget:self |
||
7621 | selector:@selector(cfstreamThread:) |
||
7622 | object:nil]; |
||
7623 | [cfstreamThread start]; |
||
7624 | } |
||
7625 | }}); |
||
7626 | } |
||
7627 | |||
7628 | + (void)stopCFStreamThreadIfNeeded |
||
7629 | { |
||
7630 | LogTrace(); |
||
7631 | |||
7632 | // The creation of the cfstreamThread is relatively expensive. |
||
7633 | // So we'd like to keep it available for recycling. |
||
7634 | // However, there's a tradeoff here, because it shouldn't remain alive forever. |
||
7635 | // So what we're going to do is use a little delay before taking it down. |
||
7636 | // This way it can be reused properly in situations where multiple sockets are continually in flux. |
||
7637 | |||
7638 | int delayInSeconds = 30; |
||
7639 | dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); |
||
7640 | dispatch_after(when, cfstreamThreadSetupQueue, ^{ @autoreleasepool { |
||
7641 | #pragma clang diagnostic push |
||
7642 | #pragma clang diagnostic warning "-Wimplicit-retain-self" |
||
7643 | |||
7644 | if (cfstreamThreadRetainCount == 0) |
||
7645 | { |
||
7646 | LogWarn(@"Logic error concerning cfstreamThread start / stop"); |
||
7647 | return_from_block; |
||
7648 | } |
||
7649 | |||
7650 | if (--cfstreamThreadRetainCount == 0) |
||
7651 | { |
||
7652 | [cfstreamThread cancel]; // set isCancelled flag |
||
7653 | |||
7654 | // wake up the thread |
||
7655 | [[self class] performSelector:@selector(ignore:) |
||
7656 | onThread:cfstreamThread |
||
7657 | withObject:[NSNull null] |
||
7658 | waitUntilDone:NO]; |
||
7659 | |||
7660 | cfstreamThread = nil; |
||
7661 | } |
||
7662 | |||
7663 | #pragma clang diagnostic pop |
||
7664 | }}); |
||
7665 | } |
||
7666 | |||
7667 | + (void)cfstreamThread:(id)unused { @autoreleasepool |
||
7668 | { |
||
7669 | [[NSThread currentThread] setName:GCDAsyncSocketThreadName]; |
||
7670 | |||
7671 | LogInfo(@"CFStreamThread: Started"); |
||
7672 | |||
7673 | // We can't run the run loop unless it has an associated input source or a timer. |
||
7674 | // So we'll just create a timer that will never fire - unless the server runs for decades. |
||
7675 | [NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow] |
||
7676 | target:self |
||
7677 | selector:@selector(ignore:) |
||
7678 | userInfo:nil |
||
7679 | repeats:YES]; |
||
7680 | |||
7681 | NSThread *currentThread = [NSThread currentThread]; |
||
7682 | NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; |
||
7683 | |||
7684 | BOOL isCancelled = [currentThread isCancelled]; |
||
7685 | |||
7686 | while (!isCancelled && [currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) |
||
7687 | { |
||
7688 | isCancelled = [currentThread isCancelled]; |
||
7689 | } |
||
7690 | |||
7691 | LogInfo(@"CFStreamThread: Stopped"); |
||
7692 | }} |
||
7693 | |||
7694 | + (void)scheduleCFStreams:(GCDAsyncSocket *)asyncSocket |
||
7695 | { |
||
7696 | LogTrace(); |
||
7697 | NSAssert([NSThread currentThread] == cfstreamThread, @"Invoked on wrong thread"); |
||
7698 | |||
7699 | CFRunLoopRef runLoop = CFRunLoopGetCurrent(); |
||
7700 | |||
7701 | if (asyncSocket->readStream) |
||
7702 | CFReadStreamScheduleWithRunLoop(asyncSocket->readStream, runLoop, kCFRunLoopDefaultMode); |
||
7703 | |||
7704 | if (asyncSocket->writeStream) |
||
7705 | CFWriteStreamScheduleWithRunLoop(asyncSocket->writeStream, runLoop, kCFRunLoopDefaultMode); |
||
7706 | } |
||
7707 | |||
7708 | + (void)unscheduleCFStreams:(GCDAsyncSocket *)asyncSocket |
||
7709 | { |
||
7710 | LogTrace(); |
||
7711 | NSAssert([NSThread currentThread] == cfstreamThread, @"Invoked on wrong thread"); |
||
7712 | |||
7713 | CFRunLoopRef runLoop = CFRunLoopGetCurrent(); |
||
7714 | |||
7715 | if (asyncSocket->readStream) |
||
7716 | CFReadStreamUnscheduleFromRunLoop(asyncSocket->readStream, runLoop, kCFRunLoopDefaultMode); |
||
7717 | |||
7718 | if (asyncSocket->writeStream) |
||
7719 | CFWriteStreamUnscheduleFromRunLoop(asyncSocket->writeStream, runLoop, kCFRunLoopDefaultMode); |
||
7720 | } |
||
7721 | |||
7722 | static void CFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo) |
||
7723 | { |
||
7724 | GCDAsyncSocket *asyncSocket = (__bridge GCDAsyncSocket *)pInfo; |
||
7725 | |||
7726 | switch(type) |
||
7727 | { |
||
7728 | case kCFStreamEventHasBytesAvailable: |
||
7729 | { |
||
7730 | dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool { |
||
7731 | |||
7732 | LogCVerbose(@"CFReadStreamCallback - HasBytesAvailable"); |
||
7733 | |||
7734 | if (asyncSocket->readStream != stream) |
||
7735 | return_from_block; |
||
7736 | |||
7737 | if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS)) |
||
7738 | { |
||
7739 | // If we set kCFStreamPropertySSLSettings before we opened the streams, this might be a lie. |
||
7740 | // (A callback related to the tcp stream, but not to the SSL layer). |
||
7741 | |||
7742 | if (CFReadStreamHasBytesAvailable(asyncSocket->readStream)) |
||
7743 | { |
||
7744 | asyncSocket->flags |= kSecureSocketHasBytesAvailable; |
||
7745 | [asyncSocket cf_finishSSLHandshake]; |
||
7746 | } |
||
7747 | } |
||
7748 | else |
||
7749 | { |
||
7750 | asyncSocket->flags |= kSecureSocketHasBytesAvailable; |
||
7751 | [asyncSocket doReadData]; |
||
7752 | } |
||
7753 | }}); |
||
7754 | |||
7755 | break; |
||
7756 | } |
||
7757 | default: |
||
7758 | { |
||
7759 | NSError *error = (__bridge_transfer NSError *)CFReadStreamCopyError(stream); |
||
7760 | |||
7761 | if (error == nil && type == kCFStreamEventEndEncountered) |
||
7762 | { |
||
7763 | error = [asyncSocket connectionClosedError]; |
||
7764 | } |
||
7765 | |||
7766 | dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool { |
||
7767 | |||
7768 | LogCVerbose(@"CFReadStreamCallback - Other"); |
||
7769 | |||
7770 | if (asyncSocket->readStream != stream) |
||
7771 | return_from_block; |
||
7772 | |||
7773 | if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS)) |
||
7774 | { |
||
7775 | [asyncSocket cf_abortSSLHandshake:error]; |
||
7776 | } |
||
7777 | else |
||
7778 | { |
||
7779 | [asyncSocket closeWithError:error]; |
||
7780 | } |
||
7781 | }}); |
||
7782 | |||
7783 | break; |
||
7784 | } |
||
7785 | } |
||
7786 | |||
7787 | } |
||
7788 | |||
7789 | static void CFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo) |
||
7790 | { |
||
7791 | GCDAsyncSocket *asyncSocket = (__bridge GCDAsyncSocket *)pInfo; |
||
7792 | |||
7793 | switch(type) |
||
7794 | { |
||
7795 | case kCFStreamEventCanAcceptBytes: |
||
7796 | { |
||
7797 | dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool { |
||
7798 | |||
7799 | LogCVerbose(@"CFWriteStreamCallback - CanAcceptBytes"); |
||
7800 | |||
7801 | if (asyncSocket->writeStream != stream) |
||
7802 | return_from_block; |
||
7803 | |||
7804 | if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS)) |
||
7805 | { |
||
7806 | // If we set kCFStreamPropertySSLSettings before we opened the streams, this might be a lie. |
||
7807 | // (A callback related to the tcp stream, but not to the SSL layer). |
||
7808 | |||
7809 | if (CFWriteStreamCanAcceptBytes(asyncSocket->writeStream)) |
||
7810 | { |
||
7811 | asyncSocket->flags |= kSocketCanAcceptBytes; |
||
7812 | [asyncSocket cf_finishSSLHandshake]; |
||
7813 | } |
||
7814 | } |
||
7815 | else |
||
7816 | { |
||
7817 | asyncSocket->flags |= kSocketCanAcceptBytes; |
||
7818 | [asyncSocket doWriteData]; |
||
7819 | } |
||
7820 | }}); |
||
7821 | |||
7822 | break; |
||
7823 | } |
||
7824 | default: |
||
7825 | { |
||
7826 | NSError *error = (__bridge_transfer NSError *)CFWriteStreamCopyError(stream); |
||
7827 | |||
7828 | if (error == nil && type == kCFStreamEventEndEncountered) |
||
7829 | { |
||
7830 | error = [asyncSocket connectionClosedError]; |
||
7831 | } |
||
7832 | |||
7833 | dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool { |
||
7834 | |||
7835 | LogCVerbose(@"CFWriteStreamCallback - Other"); |
||
7836 | |||
7837 | if (asyncSocket->writeStream != stream) |
||
7838 | return_from_block; |
||
7839 | |||
7840 | if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS)) |
||
7841 | { |
||
7842 | [asyncSocket cf_abortSSLHandshake:error]; |
||
7843 | } |
||
7844 | else |
||
7845 | { |
||
7846 | [asyncSocket closeWithError:error]; |
||
7847 | } |
||
7848 | }}); |
||
7849 | |||
7850 | break; |
||
7851 | } |
||
7852 | } |
||
7853 | |||
7854 | } |
||
7855 | |||
7856 | - (BOOL)createReadAndWriteStream |
||
7857 | { |
||
7858 | LogTrace(); |
||
7859 | |||
7860 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
7861 | |||
7862 | |||
7863 | if (readStream || writeStream) |
||
7864 | { |
||
7865 | // Streams already created |
||
7866 | return YES; |
||
7867 | } |
||
7868 | |||
7869 | int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; |
||
7870 | |||
7871 | if (socketFD == SOCKET_NULL) |
||
7872 | { |
||
7873 | // Cannot create streams without a file descriptor |
||
7874 | return NO; |
||
7875 | } |
||
7876 | |||
7877 | if (![self isConnected]) |
||
7878 | { |
||
7879 | // Cannot create streams until file descriptor is connected |
||
7880 | return NO; |
||
7881 | } |
||
7882 | |||
7883 | LogVerbose(@"Creating read and write stream..."); |
||
7884 | |||
7885 | CFStreamCreatePairWithSocket(NULL, (CFSocketNativeHandle)socketFD, &readStream, &writeStream); |
||
7886 | |||
7887 | // The kCFStreamPropertyShouldCloseNativeSocket property should be false by default (for our case). |
||
7888 | // But let's not take any chances. |
||
7889 | |||
7890 | if (readStream) |
||
7891 | CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); |
||
7892 | if (writeStream) |
||
7893 | CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); |
||
7894 | |||
7895 | if ((readStream == NULL) || (writeStream == NULL)) |
||
7896 | { |
||
7897 | LogWarn(@"Unable to create read and write stream..."); |
||
7898 | |||
7899 | if (readStream) |
||
7900 | { |
||
7901 | CFReadStreamClose(readStream); |
||
7902 | CFRelease(readStream); |
||
7903 | readStream = NULL; |
||
7904 | } |
||
7905 | if (writeStream) |
||
7906 | { |
||
7907 | CFWriteStreamClose(writeStream); |
||
7908 | CFRelease(writeStream); |
||
7909 | writeStream = NULL; |
||
7910 | } |
||
7911 | |||
7912 | return NO; |
||
7913 | } |
||
7914 | |||
7915 | return YES; |
||
7916 | } |
||
7917 | |||
7918 | - (BOOL)registerForStreamCallbacksIncludingReadWrite:(BOOL)includeReadWrite |
||
7919 | { |
||
7920 | LogVerbose(@"%@ %@", THIS_METHOD, (includeReadWrite ? @"YES" : @"NO")); |
||
7921 | |||
7922 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
7923 | NSAssert((readStream != NULL && writeStream != NULL), @"Read/Write stream is null"); |
||
7924 | |||
7925 | streamContext.version = 0; |
||
7926 | streamContext.info = (__bridge void *)(self); |
||
7927 | streamContext.retain = nil; |
||
7928 | streamContext.release = nil; |
||
7929 | streamContext.copyDescription = nil; |
||
7930 | |||
7931 | CFOptionFlags readStreamEvents = kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; |
||
7932 | if (includeReadWrite) |
||
7933 | readStreamEvents |= kCFStreamEventHasBytesAvailable; |
||
7934 | |||
7935 | if (!CFReadStreamSetClient(readStream, readStreamEvents, &CFReadStreamCallback, &streamContext)) |
||
7936 | { |
||
7937 | return NO; |
||
7938 | } |
||
7939 | |||
7940 | CFOptionFlags writeStreamEvents = kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; |
||
7941 | if (includeReadWrite) |
||
7942 | writeStreamEvents |= kCFStreamEventCanAcceptBytes; |
||
7943 | |||
7944 | if (!CFWriteStreamSetClient(writeStream, writeStreamEvents, &CFWriteStreamCallback, &streamContext)) |
||
7945 | { |
||
7946 | return NO; |
||
7947 | } |
||
7948 | |||
7949 | return YES; |
||
7950 | } |
||
7951 | |||
7952 | - (BOOL)addStreamsToRunLoop |
||
7953 | { |
||
7954 | LogTrace(); |
||
7955 | |||
7956 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
7957 | NSAssert((readStream != NULL && writeStream != NULL), @"Read/Write stream is null"); |
||
7958 | |||
7959 | if (!(flags & kAddedStreamsToRunLoop)) |
||
7960 | { |
||
7961 | LogVerbose(@"Adding streams to runloop..."); |
||
7962 | |||
7963 | [[self class] startCFStreamThreadIfNeeded]; |
||
7964 | dispatch_sync(cfstreamThreadSetupQueue, ^{ |
||
7965 | [[self class] performSelector:@selector(scheduleCFStreams:) |
||
7966 | onThread:cfstreamThread |
||
7967 | withObject:self |
||
7968 | waitUntilDone:YES]; |
||
7969 | }); |
||
7970 | flags |= kAddedStreamsToRunLoop; |
||
7971 | } |
||
7972 | |||
7973 | return YES; |
||
7974 | } |
||
7975 | |||
7976 | - (void)removeStreamsFromRunLoop |
||
7977 | { |
||
7978 | LogTrace(); |
||
7979 | |||
7980 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
7981 | NSAssert((readStream != NULL && writeStream != NULL), @"Read/Write stream is null"); |
||
7982 | |||
7983 | if (flags & kAddedStreamsToRunLoop) |
||
7984 | { |
||
7985 | LogVerbose(@"Removing streams from runloop..."); |
||
7986 | |||
7987 | dispatch_sync(cfstreamThreadSetupQueue, ^{ |
||
7988 | [[self class] performSelector:@selector(unscheduleCFStreams:) |
||
7989 | onThread:cfstreamThread |
||
7990 | withObject:self |
||
7991 | waitUntilDone:YES]; |
||
7992 | }); |
||
7993 | [[self class] stopCFStreamThreadIfNeeded]; |
||
7994 | |||
7995 | flags &= ~kAddedStreamsToRunLoop; |
||
7996 | } |
||
7997 | } |
||
7998 | |||
7999 | - (BOOL)openStreams |
||
8000 | { |
||
8001 | LogTrace(); |
||
8002 | |||
8003 | NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); |
||
8004 | NSAssert((readStream != NULL && writeStream != NULL), @"Read/Write stream is null"); |
||
8005 | |||
8006 | CFStreamStatus readStatus = CFReadStreamGetStatus(readStream); |
||
8007 | CFStreamStatus writeStatus = CFWriteStreamGetStatus(writeStream); |
||
8008 | |||
8009 | if ((readStatus == kCFStreamStatusNotOpen) || (writeStatus == kCFStreamStatusNotOpen)) |
||
8010 | { |
||
8011 | LogVerbose(@"Opening read and write stream..."); |
||
8012 | |||
8013 | BOOL r1 = CFReadStreamOpen(readStream); |
||
8014 | BOOL r2 = CFWriteStreamOpen(writeStream); |
||
8015 | |||
8016 | if (!r1 || !r2) |
||
8017 | { |
||
8018 | LogError(@"Error in CFStreamOpen"); |
||
8019 | return NO; |
||
8020 | } |
||
8021 | } |
||
8022 | |||
8023 | return YES; |
||
8024 | } |
||
8025 | |||
8026 | #endif |
||
8027 | |||
8028 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
8029 | #pragma mark Advanced |
||
8030 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
8031 | |||
8032 | /** |
||
8033 | * See header file for big discussion of this method. |
||
8034 | **/ |
||
8035 | - (BOOL)autoDisconnectOnClosedReadStream |
||
8036 | { |
||
8037 | // Note: YES means kAllowHalfDuplexConnection is OFF |
||
8038 | |||
8039 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
8040 | { |
||
8041 | return ((config & kAllowHalfDuplexConnection) == 0); |
||
8042 | } |
||
8043 | else |
||
8044 | { |
||
8045 | __block BOOL result; |
||
8046 | |||
8047 | dispatch_sync(socketQueue, ^{ |
||
8048 | result = ((self->config & kAllowHalfDuplexConnection) == 0); |
||
8049 | }); |
||
8050 | |||
8051 | return result; |
||
8052 | } |
||
8053 | } |
||
8054 | |||
8055 | /** |
||
8056 | * See header file for big discussion of this method. |
||
8057 | **/ |
||
8058 | - (void)setAutoDisconnectOnClosedReadStream:(BOOL)flag |
||
8059 | { |
||
8060 | // Note: YES means kAllowHalfDuplexConnection is OFF |
||
8061 | |||
8062 | dispatch_block_t block = ^{ |
||
8063 | |||
8064 | if (flag) |
||
8065 | self->config &= ~kAllowHalfDuplexConnection; |
||
8066 | else |
||
8067 | self->config |= kAllowHalfDuplexConnection; |
||
8068 | }; |
||
8069 | |||
8070 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
8071 | block(); |
||
8072 | else |
||
8073 | dispatch_async(socketQueue, block); |
||
8074 | } |
||
8075 | |||
8076 | |||
8077 | /** |
||
8078 | * See header file for big discussion of this method. |
||
8079 | **/ |
||
8080 | - (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketNewTargetQueue |
||
8081 | { |
||
8082 | void *nonNullUnusedPointer = (__bridge void *)self; |
||
8083 | dispatch_queue_set_specific(socketNewTargetQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL); |
||
8084 | } |
||
8085 | |||
8086 | /** |
||
8087 | * See header file for big discussion of this method. |
||
8088 | **/ |
||
8089 | - (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketOldTargetQueue |
||
8090 | { |
||
8091 | dispatch_queue_set_specific(socketOldTargetQueue, IsOnSocketQueueOrTargetQueueKey, NULL, NULL); |
||
8092 | } |
||
8093 | |||
8094 | /** |
||
8095 | * See header file for big discussion of this method. |
||
8096 | **/ |
||
8097 | - (void)performBlock:(dispatch_block_t)block |
||
8098 | { |
||
8099 | if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
8100 | block(); |
||
8101 | else |
||
8102 | dispatch_sync(socketQueue, block); |
||
8103 | } |
||
8104 | |||
8105 | /** |
||
8106 | * Questions? Have you read the header file? |
||
8107 | **/ |
||
8108 | - (int)socketFD |
||
8109 | { |
||
8110 | if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
8111 | { |
||
8112 | LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); |
||
8113 | return SOCKET_NULL; |
||
8114 | } |
||
8115 | |||
8116 | if (socket4FD != SOCKET_NULL) |
||
8117 | return socket4FD; |
||
8118 | else |
||
8119 | return socket6FD; |
||
8120 | } |
||
8121 | |||
8122 | /** |
||
8123 | * Questions? Have you read the header file? |
||
8124 | **/ |
||
8125 | - (int)socket4FD |
||
8126 | { |
||
8127 | if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
8128 | { |
||
8129 | LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); |
||
8130 | return SOCKET_NULL; |
||
8131 | } |
||
8132 | |||
8133 | return socket4FD; |
||
8134 | } |
||
8135 | |||
8136 | /** |
||
8137 | * Questions? Have you read the header file? |
||
8138 | **/ |
||
8139 | - (int)socket6FD |
||
8140 | { |
||
8141 | if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
8142 | { |
||
8143 | LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); |
||
8144 | return SOCKET_NULL; |
||
8145 | } |
||
8146 | |||
8147 | return socket6FD; |
||
8148 | } |
||
8149 | |||
8150 | #if TARGET_OS_IPHONE |
||
8151 | |||
8152 | /** |
||
8153 | * Questions? Have you read the header file? |
||
8154 | **/ |
||
8155 | - (CFReadStreamRef)readStream |
||
8156 | { |
||
8157 | if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
8158 | { |
||
8159 | LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); |
||
8160 | return NULL; |
||
8161 | } |
||
8162 | |||
8163 | if (readStream == NULL) |
||
8164 | [self createReadAndWriteStream]; |
||
8165 | |||
8166 | return readStream; |
||
8167 | } |
||
8168 | |||
8169 | /** |
||
8170 | * Questions? Have you read the header file? |
||
8171 | **/ |
||
8172 | - (CFWriteStreamRef)writeStream |
||
8173 | { |
||
8174 | if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
8175 | { |
||
8176 | LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); |
||
8177 | return NULL; |
||
8178 | } |
||
8179 | |||
8180 | if (writeStream == NULL) |
||
8181 | [self createReadAndWriteStream]; |
||
8182 | |||
8183 | return writeStream; |
||
8184 | } |
||
8185 | |||
8186 | - (BOOL)enableBackgroundingOnSocketWithCaveat:(BOOL)caveat |
||
8187 | { |
||
8188 | if (![self createReadAndWriteStream]) |
||
8189 | { |
||
8190 | // Error occurred creating streams (perhaps socket isn't open) |
||
8191 | return NO; |
||
8192 | } |
||
8193 | |||
8194 | BOOL r1, r2; |
||
8195 | |||
8196 | LogVerbose(@"Enabling backgrouding on socket"); |
||
8197 | |||
8198 | #pragma clang diagnostic push |
||
8199 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
||
8200 | r1 = CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); |
||
8201 | r2 = CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); |
||
8202 | #pragma clang diagnostic pop |
||
8203 | |||
8204 | if (!r1 || !r2) |
||
8205 | { |
||
8206 | return NO; |
||
8207 | } |
||
8208 | |||
8209 | if (!caveat) |
||
8210 | { |
||
8211 | if (![self openStreams]) |
||
8212 | { |
||
8213 | return NO; |
||
8214 | } |
||
8215 | } |
||
8216 | |||
8217 | return YES; |
||
8218 | } |
||
8219 | |||
8220 | /** |
||
8221 | * Questions? Have you read the header file? |
||
8222 | **/ |
||
8223 | - (BOOL)enableBackgroundingOnSocket |
||
8224 | { |
||
8225 | LogTrace(); |
||
8226 | |||
8227 | if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
8228 | { |
||
8229 | LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); |
||
8230 | return NO; |
||
8231 | } |
||
8232 | |||
8233 | return [self enableBackgroundingOnSocketWithCaveat:NO]; |
||
8234 | } |
||
8235 | |||
8236 | - (BOOL)enableBackgroundingOnSocketWithCaveat // Deprecated in iOS 4.??? |
||
8237 | { |
||
8238 | // This method was created as a workaround for a bug in iOS. |
||
8239 | // Apple has since fixed this bug. |
||
8240 | // I'm not entirely sure which version of iOS they fixed it in... |
||
8241 | |||
8242 | LogTrace(); |
||
8243 | |||
8244 | if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
8245 | { |
||
8246 | LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); |
||
8247 | return NO; |
||
8248 | } |
||
8249 | |||
8250 | return [self enableBackgroundingOnSocketWithCaveat:YES]; |
||
8251 | } |
||
8252 | |||
8253 | #endif |
||
8254 | |||
8255 | - (SSLContextRef)sslContext |
||
8256 | { |
||
8257 | if (!dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) |
||
8258 | { |
||
8259 | LogWarn(@"%@ - Method only available from within the context of a performBlock: invocation", THIS_METHOD); |
||
8260 | return NULL; |
||
8261 | } |
||
8262 | |||
8263 | return sslContext; |
||
8264 | } |
||
8265 | |||
8266 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
8267 | #pragma mark Class Utilities |
||
8268 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
||
8269 | |||
8270 | + (NSMutableArray *)lookupHost:(NSString *)host port:(uint16_t)port error:(NSError **)errPtr |
||
8271 | { |
||
8272 | LogTrace(); |
||
8273 | |||
8274 | NSMutableArray *addresses = nil; |
||
8275 | NSError *error = nil; |
||
8276 | |||
8277 | if ([host isEqualToString:@"localhost"] || [host isEqualToString:@"loopback"]) |
||
8278 | { |
||
8279 | // Use LOOPBACK address |
||
8280 | struct sockaddr_in nativeAddr4; |
||
8281 | nativeAddr4.sin_len = sizeof(struct sockaddr_in); |
||
8282 | nativeAddr4.sin_family = AF_INET; |
||
8283 | nativeAddr4.sin_port = htons(port); |
||
8284 | nativeAddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
||
8285 | memset(&(nativeAddr4.sin_zero), 0, sizeof(nativeAddr4.sin_zero)); |
||
8286 | |||
8287 | struct sockaddr_in6 nativeAddr6; |
||
8288 | nativeAddr6.sin6_len = sizeof(struct sockaddr_in6); |
||
8289 | nativeAddr6.sin6_family = AF_INET6; |
||
8290 | nativeAddr6.sin6_port = htons(port); |
||
8291 | nativeAddr6.sin6_flowinfo = 0; |
||
8292 | nativeAddr6.sin6_addr = in6addr_loopback; |
||
8293 | nativeAddr6.sin6_scope_id = 0; |
||
8294 | |||
8295 | // Wrap the native address structures |
||
8296 | |||
8297 | NSData *address4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; |
||
8298 | NSData *address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; |
||
8299 | |||
8300 | addresses = [NSMutableArray arrayWithCapacity:2]; |
||
8301 | [addresses addObject:address4]; |
||
8302 | [addresses addObject:address6]; |
||
8303 | } |
||
8304 | else |
||
8305 | { |
||
8306 | NSString *portStr = [NSString stringWithFormat:@"%hu", port]; |
||
8307 | |||
8308 | struct addrinfo hints, *res, *res0; |
||
8309 | |||
8310 | memset(&hints, 0, sizeof(hints)); |
||
8311 | hints.ai_family = PF_UNSPEC; |
||
8312 | hints.ai_socktype = SOCK_STREAM; |
||
8313 | hints.ai_protocol = IPPROTO_TCP; |
||
8314 | |||
8315 | int gai_error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0); |
||
8316 | |||
8317 | if (gai_error) |
||
8318 | { |
||
8319 | error = [self gaiError:gai_error]; |
||
8320 | } |
||
8321 | else |
||
8322 | { |
||
8323 | NSUInteger capacity = 0; |
||
8324 | for (res = res0; res; res = res->ai_next) |
||
8325 | { |
||
8326 | if (res->ai_family == AF_INET || res->ai_family == AF_INET6) { |
||
8327 | capacity++; |
||
8328 | } |
||
8329 | } |
||
8330 | |||
8331 | addresses = [NSMutableArray arrayWithCapacity:capacity]; |
||
8332 | |||
8333 | for (res = res0; res; res = res->ai_next) |
||
8334 | { |
||
8335 | if (res->ai_family == AF_INET) |
||
8336 | { |
||
8337 | // Found IPv4 address. |
||
8338 | // Wrap the native address structure, and add to results. |
||
8339 | |||
8340 | NSData *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; |
||
8341 | [addresses addObject:address4]; |
||
8342 | } |
||
8343 | else if (res->ai_family == AF_INET6) |
||
8344 | { |
||
8345 | // Fixes connection issues with IPv6 |
||
8346 | // https://github.com/robbiehanson/CocoaAsyncSocket/issues/429#issuecomment-222477158 |
||
8347 | |||
8348 | // Found IPv6 address. |
||
8349 | // Wrap the native address structure, and add to results. |
||
8350 | |||
8351 | struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)(void *)res->ai_addr; |
||
8352 | in_port_t *portPtr = &sockaddr->sin6_port; |
||
8353 | if ((portPtr != NULL) && (*portPtr == 0)) { |
||
8354 | *portPtr = htons(port); |
||
8355 | } |
||
8356 | |||
8357 | NSData *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]; |
||
8358 | [addresses addObject:address6]; |
||
8359 | } |
||
8360 | } |
||
8361 | freeaddrinfo(res0); |
||
8362 | |||
8363 | if ([addresses count] == 0) |
||
8364 | { |
||
8365 | error = [self gaiError:EAI_FAIL]; |
||
8366 | } |
||
8367 | } |
||
8368 | } |
||
8369 | |||
8370 | if (errPtr) *errPtr = error; |
||
8371 | return addresses; |
||
8372 | } |
||
8373 | |||
8374 | + (NSString *)hostFromSockaddr4:(const struct sockaddr_in *)pSockaddr4 |
||
8375 | { |
||
8376 | char addrBuf[INET_ADDRSTRLEN]; |
||
8377 | |||
8378 | if (inet_ntop(AF_INET, &pSockaddr4->sin_addr, addrBuf, (socklen_t)sizeof(addrBuf)) == NULL) |
||
8379 | { |
||
8380 | addrBuf[0] = '\0'; |
||
8381 | } |
||
8382 | |||
8383 | return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding]; |
||
8384 | } |
||
8385 | |||
8386 | + (NSString *)hostFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6 |
||
8387 | { |
||
8388 | char addrBuf[INET6_ADDRSTRLEN]; |
||
8389 | |||
8390 | if (inet_ntop(AF_INET6, &pSockaddr6->sin6_addr, addrBuf, (socklen_t)sizeof(addrBuf)) == NULL) |
||
8391 | { |
||
8392 | addrBuf[0] = '\0'; |
||
8393 | } |
||
8394 | |||
8395 | return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding]; |
||
8396 | } |
||
8397 | |||
8398 | + (uint16_t)portFromSockaddr4:(const struct sockaddr_in *)pSockaddr4 |
||
8399 | { |
||
8400 | return ntohs(pSockaddr4->sin_port); |
||
8401 | } |
||
8402 | |||
8403 | + (uint16_t)portFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6 |
||
8404 | { |
||
8405 | return ntohs(pSockaddr6->sin6_port); |
||
8406 | } |
||
8407 | |||
8408 | + (NSURL *)urlFromSockaddrUN:(const struct sockaddr_un *)pSockaddr |
||
8409 | { |
||
8410 | NSString *path = [NSString stringWithUTF8String:pSockaddr->sun_path]; |
||
8411 | return [NSURL fileURLWithPath:path]; |
||
8412 | } |
||
8413 | |||
8414 | + (NSString *)hostFromAddress:(NSData *)address |
||
8415 | { |
||
8416 | NSString *host; |
||
8417 | |||
8418 | if ([self getHost:&host port:NULL fromAddress:address]) |
||
8419 | return host; |
||
8420 | else |
||
8421 | return nil; |
||
8422 | } |
||
8423 | |||
8424 | + (uint16_t)portFromAddress:(NSData *)address |
||
8425 | { |
||
8426 | uint16_t port; |
||
8427 | |||
8428 | if ([self getHost:NULL port:&port fromAddress:address]) |
||
8429 | return port; |
||
8430 | else |
||
8431 | return 0; |
||
8432 | } |
||
8433 | |||
8434 | + (BOOL)isIPv4Address:(NSData *)address |
||
8435 | { |
||
8436 | if ([address length] >= sizeof(struct sockaddr)) |
||
8437 | { |
||
8438 | const struct sockaddr *sockaddrX = [address bytes]; |
||
8439 | |||
8440 | if (sockaddrX->sa_family == AF_INET) { |
||
8441 | return YES; |
||
8442 | } |
||
8443 | } |
||
8444 | |||
8445 | return NO; |
||
8446 | } |
||
8447 | |||
8448 | + (BOOL)isIPv6Address:(NSData *)address |
||
8449 | { |
||
8450 | if ([address length] >= sizeof(struct sockaddr)) |
||
8451 | { |
||
8452 | const struct sockaddr *sockaddrX = [address bytes]; |
||
8453 | |||
8454 | if (sockaddrX->sa_family == AF_INET6) { |
||
8455 | return YES; |
||
8456 | } |
||
8457 | } |
||
8458 | |||
8459 | return NO; |
||
8460 | } |
||
8461 | |||
8462 | + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr fromAddress:(NSData *)address |
||
8463 | { |
||
8464 | return [self getHost:hostPtr port:portPtr family:NULL fromAddress:address]; |
||
8465 | } |
||
8466 | |||
8467 | + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr family:(sa_family_t *)afPtr fromAddress:(NSData *)address |
||
8468 | { |
||
8469 | if ([address length] >= sizeof(struct sockaddr)) |
||
8470 | { |
||
8471 | const struct sockaddr *sockaddrX = [address bytes]; |
||
8472 | |||
8473 | if (sockaddrX->sa_family == AF_INET) |
||
8474 | { |
||
8475 | if ([address length] >= sizeof(struct sockaddr_in)) |
||
8476 | { |
||
8477 | struct sockaddr_in sockaddr4; |
||
8478 | memcpy(&sockaddr4, sockaddrX, sizeof(sockaddr4)); |
||
8479 | |||
8480 | if (hostPtr) *hostPtr = [self hostFromSockaddr4:&sockaddr4]; |
||
8481 | if (portPtr) *portPtr = [self portFromSockaddr4:&sockaddr4]; |
||
8482 | if (afPtr) *afPtr = AF_INET; |
||
8483 | |||
8484 | return YES; |
||
8485 | } |
||
8486 | } |
||
8487 | else if (sockaddrX->sa_family == AF_INET6) |
||
8488 | { |
||
8489 | if ([address length] >= sizeof(struct sockaddr_in6)) |
||
8490 | { |
||
8491 | struct sockaddr_in6 sockaddr6; |
||
8492 | memcpy(&sockaddr6, sockaddrX, sizeof(sockaddr6)); |
||
8493 | |||
8494 | if (hostPtr) *hostPtr = [self hostFromSockaddr6:&sockaddr6]; |
||
8495 | if (portPtr) *portPtr = [self portFromSockaddr6:&sockaddr6]; |
||
8496 | if (afPtr) *afPtr = AF_INET6; |
||
8497 | |||
8498 | return YES; |
||
8499 | } |
||
8500 | } |
||
8501 | } |
||
8502 | |||
8503 | return NO; |
||
8504 | } |
||
8505 | |||
8506 | + (NSData *)CRLFData |
||
8507 | { |
||
8508 | return [NSData dataWithBytes:"\x0D\x0A" length:2]; |
||
8509 | } |
||
8510 | |||
8511 | + (NSData *)CRData |
||
8512 | { |
||
8513 | return [NSData dataWithBytes:"\x0D" length:1]; |
||
8514 | } |
||
8515 | |||
8516 | + (NSData *)LFData |
||
8517 | { |
||
8518 | return [NSData dataWithBytes:"\x0A" length:1]; |
||
8519 | } |
||
8520 | |||
8521 | + (NSData *)ZeroData |
||
8522 | { |
||
8523 | return [NSData dataWithBytes:"" length:1]; |
||
8524 | } |
||
8525 | |||
8526 | @end |