/mio/GCDAsyncUdpSocket.m
Objective C | 1936 lines | 1357 code | 450 blank | 129 comment | 261 complexity | 30039fa0fafa5f9bc4ecafda7d81e233 MD5 | raw file
- //
- // GCDAsyncUdpSocket
- //
- // This class is in the public domain.
- // Originally created by Robbie Hanson of Deusty LLC.
- // Updated and maintained by Deusty LLC and the Apple development community.
- //
- // https://github.com/robbiehanson/CocoaAsyncSocket
- //
- #if ! __has_feature(objc_arc)
- #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
- // For more information see: https://github.com/robbiehanson/CocoaAsyncSocket/wiki/ARC
- #endif
- /**
- * Does ARC support support GCD objects?
- * It does if the minimum deployment target is iOS 6+ or Mac OS X 8+
- **/
- #if TARGET_OS_IPHONE
- // Compiling for iOS
- #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000 // iOS 6.0 or later
- #define NEEDS_DISPATCH_RETAIN_RELEASE 0
- #else // iOS 5.X or earlier
- #define NEEDS_DISPATCH_RETAIN_RELEASE 1
- #endif
- #else
- // Compiling for Mac OS X
- #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 // Mac OS X 10.8 or later
- #define NEEDS_DISPATCH_RETAIN_RELEASE 0
- #else
- #define NEEDS_DISPATCH_RETAIN_RELEASE 1 // Mac OS X 10.7 or earlier
- #endif
- #endif
- #import "GCDAsyncUdpSocket.h"
- #if TARGET_OS_IPHONE
- #import <CFNetwork/CFNetwork.h>
- #import <UIKit/UIKit.h>
- #endif
- #import <arpa/inet.h>
- #import <fcntl.h>
- #import <ifaddrs.h>
- #import <netdb.h>
- #import <net/if.h>
- #import <sys/socket.h>
- #import <sys/types.h>
- #if 0
- // Logging Enabled - See log level below
- // Logging uses the CocoaLumberjack framework (which is also GCD based).
- // http://code.google.com/p/cocoalumberjack/
- //
- // It allows us to do a lot of logging without significantly slowing down the code.
- #import "DDLog.h"
- #define LogAsync NO
- #define LogContext 65535
- #define LogObjc(flg, frmt, ...) LOG_OBJC_MAYBE(LogAsync, logLevel, flg, LogContext, frmt, ##__VA_ARGS__)
- #define LogC(flg, frmt, ...) LOG_C_MAYBE(LogAsync, logLevel, flg, LogContext, frmt, ##__VA_ARGS__)
- #define LogError(frmt, ...) LogObjc(LOG_FLAG_ERROR, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__)
- #define LogWarn(frmt, ...) LogObjc(LOG_FLAG_WARN, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__)
- #define LogInfo(frmt, ...) LogObjc(LOG_FLAG_INFO, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__)
- #define LogVerbose(frmt, ...) LogObjc(LOG_FLAG_VERBOSE, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__)
- #define LogCError(frmt, ...) LogC(LOG_FLAG_ERROR, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__)
- #define LogCWarn(frmt, ...) LogC(LOG_FLAG_WARN, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__)
- #define LogCInfo(frmt, ...) LogC(LOG_FLAG_INFO, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__)
- #define LogCVerbose(frmt, ...) LogC(LOG_FLAG_VERBOSE, (@"%@: " frmt), THIS_FILE, ##__VA_ARGS__)
- #define LogTrace() LogObjc(LOG_FLAG_VERBOSE, @"%@: %@", THIS_FILE, THIS_METHOD)
- #define LogCTrace() LogC(LOG_FLAG_VERBOSE, @"%@: %s", THIS_FILE, __FUNCTION__)
- // Log levels : off, error, warn, info, verbose
- static const int logLevel = LOG_LEVEL_VERBOSE;
- #else
- // Logging Disabled
- #define LogError(frmt, ...) {}
- #define LogWarn(frmt, ...) {}
- #define LogInfo(frmt, ...) {}
- #define LogVerbose(frmt, ...) {}
- #define LogCError(frmt, ...) {}
- #define LogCWarn(frmt, ...) {}
- #define LogCInfo(frmt, ...) {}
- #define LogCVerbose(frmt, ...) {}
- #define LogTrace() {}
- #define LogCTrace(frmt, ...) {}
- #endif
- /**
- * Seeing a return statements within an inner block
- * can sometimes be mistaken for a return point of the enclosing method.
- * This makes inline blocks a bit easier to read.
- **/
- #define return_from_block return
- /**
- * A socket file descriptor is really just an integer.
- * It represents the index of the socket within the kernel.
- * This makes invalid file descriptor comparisons easier to read.
- **/
- #define SOCKET_NULL -1
- /**
- * Just to type less code.
- **/
- #define AutoreleasedBlock(block) ^{ @autoreleasepool { block(); }}
- @class GCDAsyncUdpSendPacket;
- NSString *const GCDAsyncUdpSocketException = @"GCDAsyncUdpSocketException";
- NSString *const GCDAsyncUdpSocketErrorDomain = @"GCDAsyncUdpSocketErrorDomain";
- NSString *const GCDAsyncUdpSocketQueueName = @"GCDAsyncUdpSocket";
- NSString *const GCDAsyncUdpSocketThreadName = @"GCDAsyncUdpSocket-CFStream";
- enum GCDAsyncUdpSocketFlags
- {
- kDidCreateSockets = 1 << 0, // If set, the sockets have been created.
- kDidBind = 1 << 1, // If set, bind has been called.
- kConnecting = 1 << 2, // If set, a connection attempt is in progress.
- kDidConnect = 1 << 3, // If set, socket is connected.
- kReceiveOnce = 1 << 4, // If set, one-at-a-time receive is enabled
- kReceiveContinuous = 1 << 5, // If set, continuous receive is enabled
- kIPv4Deactivated = 1 << 6, // If set, socket4 was closed due to bind or connect on IPv6.
- kIPv6Deactivated = 1 << 7, // If set, socket6 was closed due to bind or connect on IPv4.
- kSend4SourceSuspended = 1 << 8, // If set, send4Source is suspended.
- kSend6SourceSuspended = 1 << 9, // If set, send6Source is suspended.
- kReceive4SourceSuspended = 1 << 10, // If set, receive4Source is suspended.
- kReceive6SourceSuspended = 1 << 11, // If set, receive6Source is suspended.
- kSock4CanAcceptBytes = 1 << 12, // If set, we know socket4 can accept bytes. If unset, it's unknown.
- kSock6CanAcceptBytes = 1 << 13, // If set, we know socket6 can accept bytes. If unset, it's unknown.
- kForbidSendReceive = 1 << 14, // If set, no new send or receive operations are allowed to be queued.
- kCloseAfterSends = 1 << 15, // If set, close as soon as no more sends are queued.
- kFlipFlop = 1 << 16, // Used to alternate between IPv4 and IPv6 sockets.
- #if TARGET_OS_IPHONE
- kAddedStreamListener = 1 << 17, // If set, CFStreams have been added to listener thread
- #endif
- };
- enum GCDAsyncUdpSocketConfig
- {
- kIPv4Disabled = 1 << 0, // If set, IPv4 is disabled
- kIPv6Disabled = 1 << 1, // If set, IPv6 is disabled
- kPreferIPv4 = 1 << 2, // If set, IPv4 is preferred over IPv6
- kPreferIPv6 = 1 << 3, // If set, IPv6 is preferred over IPv4
- };
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- @interface GCDAsyncUdpSocket ()
- {
- id delegate;
- dispatch_queue_t delegateQueue;
-
- GCDAsyncUdpSocketReceiveFilterBlock receiveFilterBlock;
- dispatch_queue_t receiveFilterQueue;
- BOOL receiveFilterAsync;
-
- GCDAsyncUdpSocketSendFilterBlock sendFilterBlock;
- dispatch_queue_t sendFilterQueue;
- BOOL sendFilterAsync;
-
- uint32_t flags;
- uint16_t config;
-
- uint16_t max4ReceiveSize;
- uint32_t max6ReceiveSize;
-
- int socket4FD;
- int socket6FD;
-
- dispatch_queue_t socketQueue;
-
- dispatch_source_t send4Source;
- dispatch_source_t send6Source;
- dispatch_source_t receive4Source;
- dispatch_source_t receive6Source;
- dispatch_source_t sendTimer;
-
- GCDAsyncUdpSendPacket *currentSend;
- NSMutableArray *sendQueue;
-
- unsigned long socket4FDBytesAvailable;
- unsigned long socket6FDBytesAvailable;
-
- uint32_t pendingFilterOperations;
-
- NSData *cachedLocalAddress4;
- NSString *cachedLocalHost4;
- uint16_t cachedLocalPort4;
-
- NSData *cachedLocalAddress6;
- NSString *cachedLocalHost6;
- uint16_t cachedLocalPort6;
-
- NSData *cachedConnectedAddress;
- NSString *cachedConnectedHost;
- uint16_t cachedConnectedPort;
- int cachedConnectedFamily;
-
- #if TARGET_OS_IPHONE
- CFStreamClientContext streamContext;
- CFReadStreamRef readStream4;
- CFReadStreamRef readStream6;
- CFWriteStreamRef writeStream4;
- CFWriteStreamRef writeStream6;
- #endif
-
- id userData;
- }
- - (void)resumeSend4Source;
- - (void)resumeSend6Source;
- - (void)resumeReceive4Source;
- - (void)resumeReceive6Source;
- - (void)closeSockets;
- - (void)maybeConnect;
- - (BOOL)connectWithAddress4:(NSData *)address4 error:(NSError **)errPtr;
- - (BOOL)connectWithAddress6:(NSData *)address6 error:(NSError **)errPtr;
- - (void)maybeDequeueSend;
- - (void)doPreSend;
- - (void)doSend;
- - (void)endCurrentSend;
- - (void)setupSendTimerWithTimeout:(NSTimeInterval)timeout;
- - (void)doReceive;
- - (void)doReceiveEOF;
- - (void)closeWithError:(NSError *)error;
- - (BOOL)performMulticastRequest:(int)requestType forGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr;
- #if TARGET_OS_IPHONE
- - (BOOL)createReadAndWriteStreams:(NSError **)errPtr;
- - (BOOL)registerForStreamCallbacks:(NSError **)errPtr;
- - (BOOL)addStreamsToRunLoop:(NSError **)errPtr;
- - (BOOL)openStreams:(NSError **)errPtr;
- - (void)removeStreamsFromRunLoop;
- - (void)closeReadAndWriteStreams;
- #endif
- + (NSString *)hostFromSockaddr4:(const struct sockaddr_in *)pSockaddr4;
- + (NSString *)hostFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6;
- + (uint16_t)portFromSockaddr4:(const struct sockaddr_in *)pSockaddr4;
- + (uint16_t)portFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6;
- #if TARGET_OS_IPHONE
- // Forward declaration
- + (void)listenerThread;
- #endif
- @end
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- /**
- * The GCDAsyncUdpSendPacket encompasses the instructions for a single send/write.
- **/
- @interface GCDAsyncUdpSendPacket : NSObject {
- @public
- NSData *buffer;
- NSTimeInterval timeout;
- long tag;
-
- BOOL resolveInProgress;
- BOOL filterInProgress;
-
- NSArray *resolvedAddresses;
- NSError *resolveError;
-
- NSData *address;
- int addressFamily;
- }
- - (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i;
- @end
- @implementation GCDAsyncUdpSendPacket
- - (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i
- {
- if ((self = [super init]))
- {
- buffer = d;
- timeout = t;
- tag = i;
-
- resolveInProgress = NO;
- }
- return self;
- }
- @end
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- @interface GCDAsyncUdpSpecialPacket : NSObject {
- @public
- // uint8_t type;
-
- BOOL resolveInProgress;
-
- NSArray *addresses;
- NSError *error;
- }
- - (id)init;
- @end
- @implementation GCDAsyncUdpSpecialPacket
- - (id)init
- {
- self = [super init];
- return self;
- }
- @end
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- @implementation GCDAsyncUdpSocket
- - (id)init
- {
- LogTrace();
-
- return [self initWithDelegate:nil delegateQueue:NULL socketQueue:NULL];
- }
- - (id)initWithSocketQueue:(dispatch_queue_t)sq
- {
- LogTrace();
-
- return [self initWithDelegate:nil delegateQueue:NULL socketQueue:sq];
- }
- - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq
- {
- LogTrace();
-
- return [self initWithDelegate:aDelegate delegateQueue:dq socketQueue:NULL];
- }
- - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq
- {
- LogTrace();
-
- if ((self = [super init]))
- {
- delegate = aDelegate;
-
- if (dq)
- {
- delegateQueue = dq;
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- dispatch_retain(delegateQueue);
- #endif
- }
-
- max4ReceiveSize = 9216;
- max6ReceiveSize = 9216;
-
- socket4FD = SOCKET_NULL;
- socket6FD = SOCKET_NULL;
-
- if (sq)
- {
- NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
- @"The given socketQueue parameter must not be a concurrent queue.");
- NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
- @"The given socketQueue parameter must not be a concurrent queue.");
- NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
- @"The given socketQueue parameter must not be a concurrent queue.");
-
- socketQueue = sq;
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- dispatch_retain(socketQueue);
- #endif
- }
- else
- {
- socketQueue = dispatch_queue_create([GCDAsyncUdpSocketQueueName UTF8String], NULL);
- }
-
- currentSend = nil;
- sendQueue = [[NSMutableArray alloc] initWithCapacity:5];
-
- #if TARGET_OS_IPHONE
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(applicationWillEnterForeground:)
- name:UIApplicationWillEnterForegroundNotification
- object:nil];
- #endif
- }
- return self;
- }
- - (void)dealloc
- {
- LogInfo(@"%@ - %@ (start)", THIS_METHOD, self);
-
- #if TARGET_OS_IPHONE
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- #endif
-
- if (dispatch_get_current_queue() == socketQueue)
- {
- [self closeWithError:nil];
- }
- else
- {
- dispatch_sync(socketQueue, ^{
- [self closeWithError:nil];
- });
- }
-
- delegate = nil;
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- if (delegateQueue) dispatch_release(delegateQueue);
- #endif
- delegateQueue = NULL;
-
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- if (socketQueue) dispatch_release(socketQueue);
- #endif
- socketQueue = NULL;
-
- LogInfo(@"%@ - %@ (finish)", THIS_METHOD, self);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark Configuration
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- - (id)delegate
- {
- if (dispatch_get_current_queue() == socketQueue)
- {
- return delegate;
- }
- else
- {
- __block id result = nil;
-
- dispatch_sync(socketQueue, ^{
- result = delegate;
- });
-
- return result;
- }
- }
- - (void)setDelegate:(id)newDelegate synchronously:(BOOL)synchronously
- {
- dispatch_block_t block = ^{
- delegate = newDelegate;
- };
-
- if (dispatch_get_current_queue() == socketQueue) {
- block();
- }
- else {
- if (synchronously)
- dispatch_sync(socketQueue, block);
- else
- dispatch_async(socketQueue, block);
- }
- }
- - (void)setDelegate:(id)newDelegate
- {
- [self setDelegate:newDelegate synchronously:NO];
- }
- - (void)synchronouslySetDelegate:(id)newDelegate
- {
- [self setDelegate:newDelegate synchronously:YES];
- }
- - (dispatch_queue_t)delegateQueue
- {
- if (dispatch_get_current_queue() == socketQueue)
- {
- return delegateQueue;
- }
- else
- {
- __block dispatch_queue_t result = NULL;
-
- dispatch_sync(socketQueue, ^{
- result = delegateQueue;
- });
-
- return result;
- }
- }
- - (void)setDelegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously
- {
- dispatch_block_t block = ^{
-
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- if (delegateQueue) dispatch_release(delegateQueue);
- if (newDelegateQueue) dispatch_retain(newDelegateQueue);
- #endif
-
- delegateQueue = newDelegateQueue;
- };
-
- if (dispatch_get_current_queue() == socketQueue) {
- block();
- }
- else {
- if (synchronously)
- dispatch_sync(socketQueue, block);
- else
- dispatch_async(socketQueue, block);
- }
- }
- - (void)setDelegateQueue:(dispatch_queue_t)newDelegateQueue
- {
- [self setDelegateQueue:newDelegateQueue synchronously:NO];
- }
- - (void)synchronouslySetDelegateQueue:(dispatch_queue_t)newDelegateQueue
- {
- [self setDelegateQueue:newDelegateQueue synchronously:YES];
- }
- - (void)getDelegate:(id *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr
- {
- if (dispatch_get_current_queue() == socketQueue)
- {
- if (delegatePtr) *delegatePtr = delegate;
- if (delegateQueuePtr) *delegateQueuePtr = delegateQueue;
- }
- else
- {
- __block id dPtr = NULL;
- __block dispatch_queue_t dqPtr = NULL;
-
- dispatch_sync(socketQueue, ^{
- dPtr = delegate;
- dqPtr = delegateQueue;
- });
-
- if (delegatePtr) *delegatePtr = dPtr;
- if (delegateQueuePtr) *delegateQueuePtr = dqPtr;
- }
- }
- - (void)setDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously
- {
- dispatch_block_t block = ^{
-
- delegate = newDelegate;
-
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- if (delegateQueue) dispatch_release(delegateQueue);
- if (newDelegateQueue) dispatch_retain(newDelegateQueue);
- #endif
-
- delegateQueue = newDelegateQueue;
- };
-
- if (dispatch_get_current_queue() == socketQueue) {
- block();
- }
- else {
- if (synchronously)
- dispatch_sync(socketQueue, block);
- else
- dispatch_async(socketQueue, block);
- }
- }
- - (void)setDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue
- {
- [self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:NO];
- }
- - (void)synchronouslySetDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue
- {
- [self setDelegate:newDelegate delegateQueue:newDelegateQueue synchronously:YES];
- }
- - (BOOL)isIPv4Enabled
- {
- // Note: YES means kIPv4Disabled is OFF
-
- __block BOOL result = NO;
-
- dispatch_block_t block = ^{
-
- result = ((config & kIPv4Disabled) == 0);
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_sync(socketQueue, block);
-
- return result;
- }
- - (void)setIPv4Enabled:(BOOL)flag
- {
- // Note: YES means kIPv4Disabled is OFF
-
- dispatch_block_t block = ^{
-
- LogVerbose(@"%@ %@", THIS_METHOD, (flag ? @"YES" : @"NO"));
-
- if (flag)
- config &= ~kIPv4Disabled;
- else
- config |= kIPv4Disabled;
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_async(socketQueue, block);
- }
- - (BOOL)isIPv6Enabled
- {
- // Note: YES means kIPv6Disabled is OFF
-
- __block BOOL result = NO;
-
- dispatch_block_t block = ^{
-
- result = ((config & kIPv6Disabled) == 0);
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_sync(socketQueue, block);
-
- return result;
- }
- - (void)setIPv6Enabled:(BOOL)flag
- {
- // Note: YES means kIPv6Disabled is OFF
-
- dispatch_block_t block = ^{
-
- LogVerbose(@"%@ %@", THIS_METHOD, (flag ? @"YES" : @"NO"));
-
- if (flag)
- config &= ~kIPv6Disabled;
- else
- config |= kIPv6Disabled;
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_async(socketQueue, block);
- }
- - (BOOL)isIPv4Preferred
- {
- __block BOOL result = NO;
-
- dispatch_block_t block = ^{
- result = (config & kPreferIPv4) ? YES : NO;
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_sync(socketQueue, block);
-
- return result;
- }
- - (BOOL)isIPv6Preferred
- {
- __block BOOL result = NO;
-
- dispatch_block_t block = ^{
- result = (config & kPreferIPv6) ? YES : NO;
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_sync(socketQueue, block);
-
- return result;
- }
- - (BOOL)isIPVersionNeutral
- {
- __block BOOL result = NO;
-
- dispatch_block_t block = ^{
- result = (config & (kPreferIPv4 | kPreferIPv6)) == 0;
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_sync(socketQueue, block);
-
- return result;
- }
- - (void)setPreferIPv4
- {
- dispatch_block_t block = ^{
-
- LogTrace();
-
- config |= kPreferIPv4;
- config &= ~kPreferIPv6;
-
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_async(socketQueue, block);
- }
- - (void)setPreferIPv6
- {
- dispatch_block_t block = ^{
-
- LogTrace();
-
- config &= ~kPreferIPv4;
- config |= kPreferIPv6;
-
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_async(socketQueue, block);
- }
- - (void)setIPVersionNeutral
- {
- dispatch_block_t block = ^{
-
- LogTrace();
-
- config &= ~kPreferIPv4;
- config &= ~kPreferIPv6;
-
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_async(socketQueue, block);
- }
- - (uint16_t)maxReceiveIPv4BufferSize
- {
- __block uint16_t result = 0;
-
- dispatch_block_t block = ^{
-
- result = max4ReceiveSize;
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_sync(socketQueue, block);
-
- return result;
- }
- - (void)setMaxReceiveIPv4BufferSize:(uint16_t)max
- {
- dispatch_block_t block = ^{
-
- LogVerbose(@"%@ %u", THIS_METHOD, (unsigned)max);
-
- max4ReceiveSize = max;
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_async(socketQueue, block);
- }
- - (uint32_t)maxReceiveIPv6BufferSize
- {
- __block uint32_t result = 0;
-
- dispatch_block_t block = ^{
-
- result = max6ReceiveSize;
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_sync(socketQueue, block);
-
- return result;
- }
- - (void)setMaxReceiveIPv6BufferSize:(uint32_t)max
- {
- dispatch_block_t block = ^{
-
- LogVerbose(@"%@ %u", THIS_METHOD, (unsigned)max);
-
- max6ReceiveSize = max;
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_async(socketQueue, block);
- }
- - (id)userData
- {
- __block id result = nil;
-
- dispatch_block_t block = ^{
-
- result = userData;
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_sync(socketQueue, block);
-
- return result;
- }
- - (void)setUserData:(id)arbitraryUserData
- {
- dispatch_block_t block = ^{
-
- if (userData != arbitraryUserData)
- {
- userData = arbitraryUserData;
- }
- };
-
- if (dispatch_get_current_queue() == socketQueue)
- block();
- else
- dispatch_async(socketQueue, block);
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark Delegate Helpers
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)notifyDidConnectToAddress:(NSData *)anAddress
- {
- LogTrace();
-
- if (delegateQueue && [delegate respondsToSelector:@selector(udpSocket:didConnectToAddress:)])
- {
- id theDelegate = delegate;
- NSData *address = [anAddress copy]; // In case param is NSMutableData
-
- dispatch_async(delegateQueue, ^{ @autoreleasepool {
-
- [theDelegate udpSocket:self didConnectToAddress:address];
- }});
- }
- }
- - (void)notifyDidNotConnect:(NSError *)error
- {
- LogTrace();
-
- if (delegateQueue && [delegate respondsToSelector:@selector(udpSocket:didNotConnect:)])
- {
- id theDelegate = delegate;
-
- dispatch_async(delegateQueue, ^{ @autoreleasepool {
-
- [theDelegate udpSocket:self didNotConnect:error];
- }});
- }
- }
- - (void)notifyDidSendDataWithTag:(long)tag
- {
- LogTrace();
-
- if (delegateQueue && [delegate respondsToSelector:@selector(udpSocket:didSendDataWithTag:)])
- {
- id theDelegate = delegate;
-
- dispatch_async(delegateQueue, ^{ @autoreleasepool {
-
- [theDelegate udpSocket:self didSendDataWithTag:tag];
- }});
- }
- }
- - (void)notifyDidNotSendDataWithTag:(long)tag dueToError:(NSError *)error
- {
- LogTrace();
-
- if (delegateQueue && [delegate respondsToSelector:@selector(udpSocket:didNotSendDataWithTag:dueToError:)])
- {
- id theDelegate = delegate;
-
- dispatch_async(delegateQueue, ^{ @autoreleasepool {
-
- [theDelegate udpSocket:self didNotSendDataWithTag:tag dueToError:error];
- }});
- }
- }
- - (void)notifyDidReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)context
- {
- LogTrace();
-
- SEL selector = @selector(udpSocket:didReceiveData:fromAddress:withFilterContext:);
-
- if (delegateQueue && [delegate respondsToSelector:selector])
- {
- id theDelegate = delegate;
-
- dispatch_async(delegateQueue, ^{ @autoreleasepool {
-
- [theDelegate udpSocket:self didReceiveData:data fromAddress:address withFilterContext:context];
- }});
- }
- }
- - (void)notifyDidCloseWithError:(NSError *)error
- {
- LogTrace();
-
- if (delegateQueue && [delegate respondsToSelector:@selector(udpSocketDidClose:withError:)])
- {
- id theDelegate = delegate;
-
- dispatch_async(delegateQueue, ^{ @autoreleasepool {
-
- [theDelegate udpSocketDidClose:self withError:error];
- }});
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark Errors
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- - (NSError *)badConfigError:(NSString *)errMsg
- {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-
- return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain
- code:GCDAsyncUdpSocketBadConfigError
- userInfo:userInfo];
- }
- - (NSError *)badParamError:(NSString *)errMsg
- {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-
- return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain
- code:GCDAsyncUdpSocketBadParamError
- userInfo:userInfo];
- }
- - (NSError *)gaiError:(int)gai_error
- {
- NSString *errMsg = [NSString stringWithCString:gai_strerror(gai_error) encoding:NSASCIIStringEncoding];
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-
- return [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:gai_error userInfo:userInfo];
- }
- - (NSError *)errnoErrorWithReason:(NSString *)reason
- {
- NSString *errMsg = [NSString stringWithUTF8String:strerror(errno)];
- NSDictionary *userInfo;
-
- if (reason)
- userInfo = [NSDictionary dictionaryWithObjectsAndKeys:errMsg, NSLocalizedDescriptionKey,
- reason, NSLocalizedFailureReasonErrorKey, nil];
- else
- userInfo = [NSDictionary dictionaryWithObjectsAndKeys:errMsg, NSLocalizedDescriptionKey, nil];
-
- return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo];
- }
- - (NSError *)errnoError
- {
- return [self errnoErrorWithReason:nil];
- }
- /**
- * Returns a standard send timeout error.
- **/
- - (NSError *)sendTimeoutError
- {
- NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncUdpSocketSendTimeoutError",
- @"GCDAsyncUdpSocket", [NSBundle mainBundle],
- @"Send operation timed out", nil);
-
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-
- return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain
- code:GCDAsyncUdpSocketSendTimeoutError
- userInfo:userInfo];
- }
- - (NSError *)socketClosedError
- {
- NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncUdpSocketClosedError",
- @"GCDAsyncUdpSocket", [NSBundle mainBundle],
- @"Socket closed", nil);
-
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-
- return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain code:GCDAsyncUdpSocketClosedError userInfo:userInfo];
- }
- - (NSError *)otherError:(NSString *)errMsg
- {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
-
- return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain
- code:GCDAsyncUdpSocketOtherError
- userInfo:userInfo];
- }
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark Utilities
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- - (BOOL)preOp:(NSError **)errPtr
- {
- NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue");
-
- if (delegate == nil) // Must have delegate set
- {
- if (errPtr)
- {
- NSString *msg = @"Attempting to use socket without a delegate. Set a delegate first.";
- *errPtr = [self badConfigError:msg];
- }
- return NO;
- }
-
- if (delegateQueue == NULL) // Must have delegate queue set
- {
- if (errPtr)
- {
- NSString *msg = @"Attempting to use socket without a delegate queue. Set a delegate queue first.";
- *errPtr = [self badConfigError:msg];
- }
- return NO;
- }
-
- return YES;
- }
- /**
- * This method executes on a global concurrent queue.
- * When complete, it executes the given completion block on the socketQueue.
- **/
- - (void)asyncResolveHost:(NSString *)aHost
- port:(uint16_t)port
- withCompletionBlock:(void (^)(NSArray *addresses, NSError *error))completionBlock
- {
- LogTrace();
-
- // Check parameter(s)
-
- if (aHost == nil)
- {
- NSString *msg = @"The host param is nil. Should be domain name or IP address string.";
- NSError *error = [self badParamError:msg];
-
- // We should still use dispatch_async since this method is expected to be asynchronous
-
- dispatch_async(socketQueue, ^{ @autoreleasepool {
-
- completionBlock(nil, error);
- }});
-
- return;
- }
-
- // It's possible that the given aHost parameter is actually a NSMutableString.
- // So we want to copy it now, within this block that will be executed synchronously.
- // This way the asynchronous lookup block below doesn't have to worry about it changing.
-
- NSString *host = [aHost copy];
-
-
- dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_async(globalConcurrentQueue, ^{ @autoreleasepool {
-
- NSMutableArray *addresses = [NSMutableArray arrayWithCapacity:2];
- NSError *error = nil;
-
- if ([host isEqualToString:@"localhost"] || [host isEqualToString:@"loopback"])
- {
- // Use LOOPBACK address
- struct sockaddr_in sockaddr4;
- memset(&sockaddr4, 0, sizeof(sockaddr4));
-
- sockaddr4.sin_len = sizeof(struct sockaddr_in);
- sockaddr4.sin_family = AF_INET;
- sockaddr4.sin_port = htons(port);
- sockaddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- struct sockaddr_in6 sockaddr6;
- memset(&sockaddr6, 0, sizeof(sockaddr6));
-
- sockaddr6.sin6_len = sizeof(struct sockaddr_in6);
- sockaddr6.sin6_family = AF_INET6;
- sockaddr6.sin6_port = htons(port);
- sockaddr6.sin6_addr = in6addr_loopback;
-
- // Wrap the native address structures and add to list
- [addresses addObject:[NSData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)]];
- [addresses addObject:[NSData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)]];
- }
- else
- {
- NSString *portStr = [NSString stringWithFormat:@"%hu", port];
-
- struct addrinfo hints, *res, *res0;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = PF_UNSPEC;
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_protocol = IPPROTO_UDP;
-
- int gai_error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0);
-
- if (gai_error)
- {
- error = [self gaiError:gai_error];
- }
- else
- {
- for(res = res0; res; res = res->ai_next)
- {
- if (res->ai_family == AF_INET)
- {
- // Found IPv4 address
- // Wrap the native address structure and add to list
-
- [addresses addObject:[NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]];
- }
- else if (res->ai_family == AF_INET6)
- {
- // Found IPv6 address
- // Wrap the native address structure and add to list
-
- [addresses addObject:[NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]];
- }
- }
- freeaddrinfo(res0);
-
- if ([addresses count] == 0)
- {
- error = [self gaiError:EAI_FAIL];
- }
- }
- }
-
- dispatch_async(socketQueue, ^{ @autoreleasepool {
-
- completionBlock(addresses, error);
- }});
-
- }});
- }
- /**
- * This method picks an address from the given list of addresses.
- * The address picked depends upon which protocols are disabled, deactived, & preferred.
- *
- * Returns the address family (AF_INET or AF_INET6) of the picked address,
- * or AF_UNSPEC and the corresponding error is there's a problem.
- **/
- - (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses:(NSArray *)addresses
- {
- NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue");
- NSAssert([addresses count] > 0, @"Expected at least one address");
-
- int resultAF = AF_UNSPEC;
- NSData *resultAddress = nil;
- NSError *resultError = nil;
-
- // Check for problems
-
- BOOL resolvedIPv4Address = NO;
- BOOL resolvedIPv6Address = NO;
-
- for (NSData *address in addresses)
- {
- switch ([[self class] familyFromAddress:address])
- {
- case AF_INET : resolvedIPv4Address = YES; break;
- case AF_INET6 : resolvedIPv6Address = YES; break;
-
- default : NSAssert(NO, @"Addresses array contains invalid address");
- }
- }
-
- BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO;
- BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO;
-
- if (isIPv4Disabled && !resolvedIPv6Address)
- {
- NSString *msg = @"IPv4 has been disabled and DNS lookup found no IPv6 address(es).";
- resultError = [self otherError:msg];
-
- if (addressPtr) *addressPtr = resultAddress;
- if (errorPtr) *errorPtr = resultError;
-
- return resultAF;
- }
-
- if (isIPv6Disabled && !resolvedIPv4Address)
- {
- NSString *msg = @"IPv6 has been disabled and DNS lookup found no IPv4 address(es).";
- resultError = [self otherError:msg];
-
- if (addressPtr) *addressPtr = resultAddress;
- if (errorPtr) *errorPtr = resultError;
-
- return resultAF;
- }
-
- BOOL isIPv4Deactivated = (flags & kIPv4Deactivated) ? YES : NO;
- BOOL isIPv6Deactivated = (flags & kIPv6Deactivated) ? YES : NO;
-
- if (isIPv4Deactivated && !resolvedIPv6Address)
- {
- NSString *msg = @"IPv4 has been deactivated due to bind/connect, and DNS lookup found no IPv6 address(es).";
- resultError = [self otherError:msg];
-
- if (addressPtr) *addressPtr = resultAddress;
- if (errorPtr) *errorPtr = resultError;
-
- return resultAF;
- }
-
- if (isIPv6Deactivated && !resolvedIPv4Address)
- {
- NSString *msg = @"IPv6 has been deactivated due to bind/connect, and DNS lookup found no IPv4 address(es).";
- resultError = [self otherError:msg];
-
- if (addressPtr) *addressPtr = resultAddress;
- if (errorPtr) *errorPtr = resultError;
-
- return resultAF;
- }
-
- // Extract first IPv4 and IPv6 address in list
-
- BOOL ipv4WasFirstInList = YES;
- NSData *address4 = nil;
- NSData *address6 = nil;
-
- for (NSData *address in addresses)
- {
- int af = [[self class] familyFromAddress:address];
-
- if (af == AF_INET)
- {
- if (address4 == nil)
- {
- address4 = address;
-
- if (address6)
- break;
- else
- ipv4WasFirstInList = YES;
- }
- }
- else // af == AF_INET6
- {
- if (address6 == nil)
- {
- address6 = address;
-
- if (address4)
- break;
- else
- ipv4WasFirstInList = NO;
- }
- }
- }
-
- // Determine socket type
-
- BOOL preferIPv4 = (config & kPreferIPv4) ? YES : NO;
- BOOL preferIPv6 = (config & kPreferIPv6) ? YES : NO;
-
- BOOL useIPv4 = ((preferIPv4 && address4) || (address6 == nil));
- BOOL useIPv6 = ((preferIPv6 && address6) || (address4 == nil));
-
- NSAssert(!(preferIPv4 && preferIPv6), @"Invalid config state");
- NSAssert(!(useIPv4 && useIPv6), @"Invalid logic");
-
- if (useIPv4 || (!useIPv6 && ipv4WasFirstInList))
- {
- resultAF = AF_INET;
- resultAddress = address4;
- }
- else
- {
- resultAF = AF_INET6;
- resultAddress = address6;
- }
-
- if (addressPtr) *addressPtr = resultAddress;
- if (errorPtr) *errorPtr = resultError;
-
- return resultAF;
- }
- /**
- * Finds the address(es) of an interface description.
- * An inteface description may be an interface name (en0, en1, lo0) or corresponding IP (192.168.4.34).
- **/
- - (void)convertIntefaceDescription:(NSString *)interfaceDescription
- port:(uint16_t)port
- intoAddress4:(NSData **)interfaceAddr4Ptr
- address6:(NSData **)interfaceAddr6Ptr
- {
- NSData *addr4 = nil;
- NSData *addr6 = nil;
-
- if (interfaceDescription == nil)
- {
- // ANY address
-
- struct sockaddr_in sockaddr4;
- memset(&sockaddr4, 0, sizeof(sockaddr4));
-
- sockaddr4.sin_len = sizeof(sockaddr4);
- sockaddr4.sin_family = AF_INET;
- sockaddr4.sin_port = htons(port);
- sockaddr4.sin_addr.s_addr = htonl(INADDR_ANY);
-
- struct sockaddr_in6 sockaddr6;
- memset(&sockaddr6, 0, sizeof(sockaddr6));
-
- sockaddr6.sin6_len = sizeof(sockaddr6);
- sockaddr6.sin6_family = AF_INET6;
- sockaddr6.sin6_port = htons(port);
- sockaddr6.sin6_addr = in6addr_any;
-
- addr4 = [NSData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)];
- addr6 = [NSData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)];
- }
- else if ([interfaceDescription isEqualToString:@"localhost"] ||
- [interfaceDescription isEqualToString:@"loopback"])
- {
- // LOOPBACK address
-
- struct sockaddr_in sockaddr4;
- memset(&sockaddr4, 0, sizeof(sockaddr4));
-
- sockaddr4.sin_len = sizeof(struct sockaddr_in);
- sockaddr4.sin_family = AF_INET;
- sockaddr4.sin_port = htons(port);
- sockaddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- struct sockaddr_in6 sockaddr6;
- memset(&sockaddr6, 0, sizeof(sockaddr6));
-
- sockaddr6.sin6_len = sizeof(struct sockaddr_in6);
- sockaddr6.sin6_family = AF_INET6;
- sockaddr6.sin6_port = htons(port);
- sockaddr6.sin6_addr = in6addr_loopback;
-
- addr4 = [NSData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)];
- addr6 = [NSData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)];
- }
- else
- {
- const char *iface = [interfaceDescription UTF8String];
-
- struct ifaddrs *addrs;
- const struct ifaddrs *cursor;
-
- if ((getifaddrs(&addrs) == 0))
- {
- cursor = addrs;
- while (cursor != NULL)
- {
- if ((addr4 == nil) && (cursor->ifa_addr->sa_family == AF_INET))
- {
- // IPv4
-
- struct sockaddr_in *addr = (struct sockaddr_in *)cursor->ifa_addr;
-
- if (strcmp(cursor->ifa_name, iface) == 0)
- {
- // Name match
-
- struct sockaddr_in nativeAddr4 = *addr;
- nativeAddr4.sin_port = htons(port);
-
- addr4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)];
- }
- else
- {
- char ip[INET_ADDRSTRLEN];
-
- const char *conversion;
- conversion = inet_ntop(AF_INET, &addr->sin_addr, ip, sizeof(ip));
-
- if ((conversion != NULL) && (strcmp(ip, iface) == 0))
- {
- // IP match
-
- struct sockaddr_in nativeAddr4 = *addr;
- nativeAddr4.sin_port = htons(port);
-
- addr4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)];
- }
- }
- }
- else if ((addr6 == nil) && (cursor->ifa_addr->sa_family == AF_INET6))
- {
- // IPv6
-
- struct sockaddr_in6 *addr = (struct sockaddr_in6 *)cursor->ifa_addr;
-
- if (strcmp(cursor->ifa_name, iface) == 0)
- {
- // Name match
-
- struct sockaddr_in6 nativeAddr6 = *addr;
- nativeAddr6.sin6_port = htons(port);
-
- addr6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
- }
- else
- {
- char ip[INET6_ADDRSTRLEN];
-
- const char *conversion;
- conversion = inet_ntop(AF_INET6, &addr->sin6_addr, ip, sizeof(ip));
-
- if ((conversion != NULL) && (strcmp(ip, iface) == 0))
- {
- // IP match
-
- struct sockaddr_in6 nativeAddr6 = *addr;
- nativeAddr6.sin6_port = htons(port);
-
- addr6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
- }
- }
- }
-
- cursor = cursor->ifa_next;
- }
-
- freeifaddrs(addrs);
- }
- }
-
- if (interfaceAddr4Ptr) *interfaceAddr4Ptr = addr4;
- if (interfaceAddr6Ptr) *interfaceAddr6Ptr = addr6;
- }
- /**
- * Converts a numeric hostname into its corresponding address.
- * The hostname is expected to be an IPv4 or IPv6 address represented as a human-readable string. (e.g. 192.168.4.34)
- **/
- - (void)convertNumericHost:(NSString *)numericHost
- port:(uint16_t)port
- intoAddress4:(NSData **)addr4Ptr
- address6:(NSData **)addr6Ptr
- {
- NSData *addr4 = nil;
- NSData *addr6 = nil;
-
- if (numericHost)
- {
- NSString *portStr = [NSString stringWithFormat:@"%hu", port];
-
- struct addrinfo hints, *res, *res0;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = PF_UNSPEC;
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_protocol = IPPROTO_UDP;
- hints.ai_flags = AI_NUMERICHOST; // No name resolution should be attempted
-
- if (getaddrinfo([numericHost UTF8String], [portStr UTF8String], &hints, &res0) == 0)
- {
- for (res = res0; res; res = res->ai_next)
- {
- if ((addr4 == nil) && (res->ai_family == AF_INET))
- {
- // Found IPv4 address
- // Wrap the native address structure
- addr4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
- }
- else if ((addr6 == nil) && (res->ai_family == AF_INET6))
- {
- // Found IPv6 address
- // Wrap the native address structure
- addr6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
- }
- }
- freeaddrinfo(res0);
- }
- }
-
- if (addr4Ptr) *addr4Ptr = addr4;
- if (addr6Ptr) *addr6Ptr = addr6;
- }
- - (BOOL)isConnectedToAddress4:(NSData *)someAddr4
- {
- NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue");
- NSAssert(flags & kDidConnect, @"Not connected");
- NSAssert(cachedConnectedAddress, @"Expected cached connected address");
-
- if (cachedConnectedFamily != AF_INET)
- {
- return NO;
- }
-
- const struct sockaddr_in *sSockaddr4 = (struct sockaddr_in *)[someAddr4 bytes];
- const struct sockaddr_in *cSockaddr4 = (struct sockaddr_in *)[cachedConnectedAddress bytes];
-
- if (memcmp(&sSockaddr4->sin_addr, &cSockaddr4->sin_addr, sizeof(struct in_addr)) != 0)
- {
- return NO;
- }
- if (memcmp(&sSockaddr4->sin_port, &cSockaddr4->sin_port, sizeof(in_port_t)) != 0)
- {
- return NO;
- }
-
- return YES;
- }
- - (BOOL)isConnectedToAddress6:(NSData *)someAddr6
- {
- NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue");
- NSAssert(flags & kDidConnect, @"Not connected");
- NSAssert(cachedConnectedAddress, @"Expected cached connected address");
-
- if (cachedConnectedFamily != AF_INET6)
- {
- return NO;
- }
-
- const struct sockaddr_in6 *sSockaddr6 = (struct sockaddr_in6 *)[someAddr6 bytes];
- const struct sockaddr_in6 *cSockaddr6 = (struct sockaddr_in6 *)[cachedConnectedAddress bytes];
-
- if (memcmp(&sSockaddr6->sin6_addr, &cSockaddr6->sin6_addr, sizeof(struct in6_addr)) != 0)
- {
- return NO;
- }
- if (memcmp(&sSockaddr6->sin6_port, &cSockaddr6->sin6_port, sizeof(in_port_t)) != 0)
- {
- return NO;
- }
-
- return YES;
- }
- - (unsigned int)indexOfInterfaceAddr4:(NSData *)interfaceAddr4
- {
- if (interfaceAddr4 == nil)
- return 0;
- if ([interfaceAddr4 length] != sizeof(struct sockaddr_in))
- return 0;
-
- int result = 0;
- struct sockaddr_in *ifaceAddr = (struct sockaddr_in *)[interfaceAddr4 bytes];
-
- struct ifaddrs *addrs;
- const struct ifaddrs *cursor;
-
- if ((getifaddrs(&addrs) == 0))
- {
- cursor = addrs;
- while (cursor != NULL)
- {
- if (cursor->ifa_addr->sa_family == AF_INET)
- {
- // IPv4
-
- struct sockaddr_in *addr = (struct sockaddr_in *)cursor->ifa_addr;
-
- if (memcmp(&addr->sin_addr, &ifaceAddr->sin_addr, sizeof(struct in_addr)) == 0)
- {
- result = if_nametoindex(cursor->ifa_name);
- break;
- }
- }
-
- cursor = cursor->ifa_next;
- }
-
- freeifaddrs(addrs);
- }
-
- return result;
- }
- - (unsigned int)indexOfInterfaceAddr6:(NSData *)interfaceAddr6
- {
- if (interfaceAddr6 == nil)
- return 0;
- if ([interfaceAddr6 length] != sizeof(struct sockaddr_in6))
- return 0;
-
- int result = 0;
- struct sockaddr_in6 *ifaceAddr = (struct sockaddr_in6 *)[interfaceAddr6 bytes];
-
- struct ifaddrs *addrs;
- const struct ifaddrs *cursor;
-
- if ((getifaddrs(&addrs) == 0))
- {
- cursor = addrs;
- while (cursor != NULL)
- {
- if (cursor->ifa_addr->sa_family == AF_INET6)
- {
- // IPv6
-
- struct sockaddr_in6 *addr = (struct sockaddr_in6 *)cursor->ifa_addr;
-
- if (memcmp(&addr->sin6_addr, &ifaceAddr->sin6_addr, sizeof(struct in6_addr)) == 0)
- {
- result = if_nametoindex(cursor->ifa_name);
- break;
- }
- }
-
- cursor = cursor->ifa_next;
- }
-
- freeifaddrs(addrs);
- }
-
- return result;
- }
- - (void)setupSendAndReceiveSourcesForSocket4
- {
- LogTrace();
- NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue");
-
- send4Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socket4FD, 0, socketQueue);
- receive4Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket4FD, 0, socketQueue);
-
- // Setup event handlers
-
- dispatch_source_set_event_handler(send4Source, ^{ @autoreleasepool {
-
- LogVerbose(@"send4EventBlock");
- LogVerbose(@"dispatch_source_get_data(send4Source) = %lu", dispatch_source_get_data(send4Source));
-
- flags |= kSock4CanAcceptBytes;
-
- // If we're ready to send data, do so immediately.
- // Otherwise pause the send source or it will continue to fire over and over again.
-
- if (currentSend == nil)
- {
- LogVerbose(@"Nothing to send");
- [self suspendSend4Source];
- }
- else if (currentSend->resolveInProgress)
- {
- LogVerbose(@"currentSend - waiting for address resolve");
- [self suspendSend4Source];
- }
- else if (currentSend->filterInProgress)
- {
- LogVerbose(@"currentSend - waiting on sendFilter");
- [self suspendSend4Source];
- }
- else
- {
- [self doSend];
- }
-
- }});
-
- dispatch_source_set_event_handler(receive4Source, ^{ @autoreleasepool {
-
- LogVerbose(@"receive4EventBlock");
-
- socket4FDBytesAvailable = dispatch_source_get_data(receive4Source);
- LogVerbose(@"socket4FDBytesAvailable: %lu", socket4FDBytesAvailable);
-
- if (socket4FDBytesAvailable > 0)
- [self doReceive];
- else
- [self doReceiveEOF];
-
- }});
-
- // Setup cancel handlers
-
- __block int socketFDRefCount = 2;
-
- int theSocketFD = socket4FD;
-
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- dispatch_source_t theSendSource = send4Source;
- dispatch_source_t theReceiveSource = receive4Source;
- #endif
-
- dispatch_source_set_cancel_handler(send4Source, ^{
-
- LogVerbose(@"send4CancelBlock");
-
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- LogVerbose(@"dispatch_release(send4Source)");
- dispatch_release(theSendSource);
- #endif
-
- if (--socketFDRefCount == 0)
- {
- LogVerbose(@"close(socket4FD)");
- close(theSocketFD);
- }
- });
-
- dispatch_source_set_cancel_handler(receive4Source, ^{
-
- LogVerbose(@"receive4CancelBlock");
-
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- LogVerbose(@"dispatch_release(receive4Source)");
- dispatch_release(theReceiveSource);
- #endif
-
- if (--socketFDRefCount == 0)
- {
- LogVerbose(@"close(socket4FD)");
- close(theSocketFD);
- }
- });
-
- // We will not be able to receive until the socket is bound to a port,
- // either explicitly via bind, or implicitly by connect or by sending data.
- //
- // But we should be able to send immediately.
-
- socket4FDBytesAvailable = 0;
- flags |= kSock4CanAcceptBytes;
-
- flags |= kSend4SourceSuspended;
- flags |= kReceive4SourceSuspended;
- }
- - (void)setupSendAndReceiveSourcesForSocket6
- {
- LogTrace();
- NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue");
-
- send6Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socket6FD, 0, socketQueue);
- receive6Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket6FD, 0, socketQueue);
-
- // Setup event handlers
-
- dispatch_source_set_event_handler(send6Source, ^{ @autoreleasepool {
-
- LogVerbose(@"send6EventBlock");
- LogVerbose(@"dispatch_source_get_data(send6Source) = %lu", dispatch_source_get_data(send6Source));
-
- flags |= kSock6CanAcceptBytes;
-
- // If we're ready to send data, do so immediately.
- // Otherwise pause the send source or it will continue to fire over and over again.
-
- if (currentSend == nil)
- {
- LogVerbose(@"Nothing to send");
- [self suspendSend6Source];
- }
- else if (currentSend->resolveInProgress)
- {
- LogVerbose(@"currentSend - waiting for address resolve");
- [self suspendSend6Source];
- }
- else if (currentSend->filterInProgress)
- {
- LogVerbose(@"currentSend - waiting on sendFilter");
- [self suspendSend6Source];
- }
- else
- {
- [self doSend];
- }
-
- }});
-
- dispatch_source_set_event_handler(receive6Source, ^{ @autoreleasepool {
-
- LogVerbose(@"receive6EventBlock");
-
- socket6FDBytesAvailable = dispatch_source_get_data(receive6Source);
- LogVerbose(@"socket6FDBytesAvailable: %lu", socket6FDBytesAvailable);
-
- if (socket6FDBytesAvailable > 0)
- [self doReceive];
- else
- [self doReceiveEOF];
-
- }});
-
- // Setup cancel handlers
-
- __block int socketFDRefCount = 2;
-
- int theSocketFD = socket6FD;
-
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- dispatch_source_t theSendSource = send6Source;
- dispatch_source_t theReceiveSource = receive6Source;
- #endif
-
- dispatch_source_set_cancel_handler(send6Source, ^{
-
- LogVerbose(@"send6CancelBlock");
-
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- LogVerbose(@"dispatch_release(send6Source)");
- dispatch_release(theSendSource);
- #endif
-
- if (--socketFDRefCount == 0)
- {
- LogVerbose(@"close(socket6FD)");
- close(theSocketFD);
- }
- });
-
- dispatch_source_set_cancel_handler(receive6Source, ^{
-
- LogVerbose(@"receive6CancelBlock");
-
- #if NEEDS_DISPATCH_RETAIN_RELEASE
- LogVerbose(@"dispatch_release(receive6Source)");
- dispatch_release(theReceiveSource);
- #endif
-
- if (--socketFDRefCount == 0)
- {
- LogVerbose(@"close(socket6FD)");
- close(theSocketFD);
- }
- });
-
- // We will not be able to receive until the socket is bound to a port,
- // either explicitly via bind, or implicitly by connect or by sending data.
- //
- // But we should be able to send immediately.
-
- socket6FDBytesAvailable = 0;
- flags |= kSock6CanAcceptBytes;
-
- flags |= kSend6SourceSuspended;
- flags |= kReceive6SourceSuspended;
- }
- - (BOOL)createSocket4:(BOOL)useIPv4 socket6:(BOOL)useIPv6 error:(NSError **)errPtr
- {
- LogTrace();
-
- NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue");
- NSAssert(((flags & kDidCreateSockets) == 0), @"Sockets have already been created");
-
- // CreateSocket Block
- // This block will be invoked below.
-
- int(^createSocket)(int) = ^