PageRenderTime 46ms CodeModel.GetById 8ms RepoModel.GetById 1ms app.codeStats 0ms

/io/libraries/CocoaAsyncSocket/AsyncUdpSocket.m

https://gitlab.com/base.io/io
Objective C | 1728 lines | 1234 code | 293 blank | 201 comment | 289 complexity | ccc9f97c800b4413504173c441d8f6ea MD5 | raw file
  1. //
  2. // AsyncUdpSocket.m
  3. //
  4. // This class is in the public domain.
  5. // Originally created by Robbie Hanson on Wed Oct 01 2008.
  6. // Updated and maintained by Deusty Designs and the Mac development community.
  7. //
  8. // http://code.google.com/p/cocoaasyncsocket/
  9. //
  10. #if ! __has_feature(objc_arc)
  11. #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  12. #endif
  13. #import "AsyncUdpSocket.h"
  14. #import <sys/socket.h>
  15. #import <netinet/in.h>
  16. #import <arpa/inet.h>
  17. #import <sys/ioctl.h>
  18. #import <net/if.h>
  19. #import <netdb.h>
  20. #if TARGET_OS_IPHONE
  21. // Note: You may need to add the CFNetwork Framework to your project
  22. #import <CFNetwork/CFNetwork.h>
  23. #endif
  24. #define SENDQUEUE_CAPACITY 5 // Initial capacity
  25. #define RECEIVEQUEUE_CAPACITY 5 // Initial capacity
  26. #define DEFAULT_MAX_RECEIVE_BUFFER_SIZE 9216
  27. NSString *const AsyncUdpSocketException = @"AsyncUdpSocketException";
  28. NSString *const AsyncUdpSocketErrorDomain = @"AsyncUdpSocketErrorDomain";
  29. #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
  30. // Mutex lock used by all instances of AsyncUdpSocket, to protect getaddrinfo.
  31. // Prior to Mac OS X 10.5 this method was not thread-safe.
  32. static NSString *getaddrinfoLock = @"lock";
  33. #endif
  34. enum AsyncUdpSocketFlags
  35. {
  36. kDidBind = 1 << 0, // If set, bind has been called.
  37. kDidConnect = 1 << 1, // If set, connect has been called.
  38. kSock4CanAcceptBytes = 1 << 2, // If set, we know socket4 can accept bytes. If unset, it's unknown.
  39. kSock6CanAcceptBytes = 1 << 3, // If set, we know socket6 can accept bytes. If unset, it's unknown.
  40. kSock4HasBytesAvailable = 1 << 4, // If set, we know socket4 has bytes available. If unset, it's unknown.
  41. kSock6HasBytesAvailable = 1 << 5, // If set, we know socket6 has bytes available. If unset, it's unknown.
  42. kForbidSendReceive = 1 << 6, // If set, no new send or receive operations are allowed to be queued.
  43. kCloseAfterSends = 1 << 7, // If set, close as soon as no more sends are queued.
  44. kCloseAfterReceives = 1 << 8, // If set, close as soon as no more receives are queued.
  45. kDidClose = 1 << 9, // If set, the socket has been closed, and should not be used anymore.
  46. kDequeueSendScheduled = 1 << 10, // If set, a maybeDequeueSend operation is already scheduled.
  47. kDequeueReceiveScheduled = 1 << 11, // If set, a maybeDequeueReceive operation is already scheduled.
  48. kFlipFlop = 1 << 12, // Used to alternate between IPv4 and IPv6 sockets.
  49. };
  50. @interface AsyncUdpSocket (Private)
  51. // Run Loop
  52. - (void)runLoopAddSource:(CFRunLoopSourceRef)source;
  53. - (void)runLoopRemoveSource:(CFRunLoopSourceRef)source;
  54. - (void)runLoopAddTimer:(NSTimer *)timer;
  55. - (void)runLoopRemoveTimer:(NSTimer *)timer;
  56. // Utilities
  57. - (NSString *)addressHost4:(struct sockaddr_in *)pSockaddr4;
  58. - (NSString *)addressHost6:(struct sockaddr_in6 *)pSockaddr6;
  59. - (NSString *)addressHost:(struct sockaddr *)pSockaddr;
  60. // Disconnect Implementation
  61. - (void)emptyQueues;
  62. - (void)closeSocket4;
  63. - (void)closeSocket6;
  64. - (void)maybeScheduleClose;
  65. // Errors
  66. - (NSError *)getErrnoError;
  67. - (NSError *)getSocketError;
  68. - (NSError *)getIPv4UnavailableError;
  69. - (NSError *)getIPv6UnavailableError;
  70. - (NSError *)getSendTimeoutError;
  71. - (NSError *)getReceiveTimeoutError;
  72. // Diagnostics
  73. - (NSString *)connectedHost:(CFSocketRef)socket;
  74. - (UInt16)connectedPort:(CFSocketRef)socket;
  75. - (NSString *)localHost:(CFSocketRef)socket;
  76. - (UInt16)localPort:(CFSocketRef)socket;
  77. // Sending
  78. - (BOOL)canAcceptBytes:(CFSocketRef)sockRef;
  79. - (void)scheduleDequeueSend;
  80. - (void)maybeDequeueSend;
  81. - (void)doSend:(CFSocketRef)sockRef;
  82. - (void)completeCurrentSend;
  83. - (void)failCurrentSend:(NSError *)error;
  84. - (void)endCurrentSend;
  85. - (void)doSendTimeout:(NSTimer *)timer;
  86. // Receiving
  87. - (BOOL)hasBytesAvailable:(CFSocketRef)sockRef;
  88. - (void)scheduleDequeueReceive;
  89. - (void)maybeDequeueReceive;
  90. - (void)doReceive4;
  91. - (void)doReceive6;
  92. - (void)doReceive:(CFSocketRef)sockRef;
  93. - (BOOL)maybeCompleteCurrentReceive;
  94. - (void)failCurrentReceive:(NSError *)error;
  95. - (void)endCurrentReceive;
  96. - (void)doReceiveTimeout:(NSTimer *)timer;
  97. @end
  98. static void MyCFSocketCallback(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);
  99. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  100. #pragma mark -
  101. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  102. /**
  103. * The AsyncSendPacket encompasses the instructions for a single send/write.
  104. **/
  105. @interface AsyncSendPacket : NSObject
  106. {
  107. @public
  108. NSData *buffer;
  109. NSData *address;
  110. NSTimeInterval timeout;
  111. long tag;
  112. }
  113. - (id)initWithData:(NSData *)d address:(NSData *)a timeout:(NSTimeInterval)t tag:(long)i;
  114. @end
  115. @implementation AsyncSendPacket
  116. - (id)initWithData:(NSData *)d address:(NSData *)a timeout:(NSTimeInterval)t tag:(long)i
  117. {
  118. if((self = [super init]))
  119. {
  120. buffer = d;
  121. address = a;
  122. timeout = t;
  123. tag = i;
  124. }
  125. return self;
  126. }
  127. @end
  128. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  129. #pragma mark -
  130. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  131. /**
  132. * The AsyncReceivePacket encompasses the instructions for a single receive/read.
  133. **/
  134. @interface AsyncReceivePacket : NSObject
  135. {
  136. @public
  137. NSTimeInterval timeout;
  138. long tag;
  139. NSData *buffer;
  140. NSString *host;
  141. UInt16 port;
  142. }
  143. - (id)initWithTimeout:(NSTimeInterval)t tag:(long)i;
  144. @end
  145. @implementation AsyncReceivePacket
  146. - (id)initWithTimeout:(NSTimeInterval)t tag:(long)i
  147. {
  148. if((self = [super init]))
  149. {
  150. timeout = t;
  151. tag = i;
  152. buffer = nil;
  153. host = nil;
  154. port = 0;
  155. }
  156. return self;
  157. }
  158. @end
  159. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  160. #pragma mark -
  161. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  162. @implementation AsyncUdpSocket
  163. - (id)initWithDelegate:(id)delegate userData:(long)userData enableIPv4:(BOOL)enableIPv4 enableIPv6:(BOOL)enableIPv6
  164. {
  165. if((self = [super init]))
  166. {
  167. theFlags = 0;
  168. theDelegate = delegate;
  169. theUserData = userData;
  170. maxReceiveBufferSize = DEFAULT_MAX_RECEIVE_BUFFER_SIZE;
  171. theSendQueue = [[NSMutableArray alloc] initWithCapacity:SENDQUEUE_CAPACITY];
  172. theCurrentSend = nil;
  173. theSendTimer = nil;
  174. theReceiveQueue = [[NSMutableArray alloc] initWithCapacity:RECEIVEQUEUE_CAPACITY];
  175. theCurrentReceive = nil;
  176. theReceiveTimer = nil;
  177. // Socket context
  178. theContext.version = 0;
  179. theContext.info = (__bridge void *)self;
  180. theContext.retain = nil;
  181. theContext.release = nil;
  182. theContext.copyDescription = nil;
  183. // Create the sockets
  184. theSocket4 = NULL;
  185. theSocket6 = NULL;
  186. if(enableIPv4)
  187. {
  188. theSocket4 = CFSocketCreate(kCFAllocatorDefault,
  189. PF_INET,
  190. SOCK_DGRAM,
  191. IPPROTO_UDP,
  192. kCFSocketReadCallBack | kCFSocketWriteCallBack,
  193. (CFSocketCallBack)&MyCFSocketCallback,
  194. &theContext);
  195. }
  196. if(enableIPv6)
  197. {
  198. theSocket6 = CFSocketCreate(kCFAllocatorDefault,
  199. PF_INET6,
  200. SOCK_DGRAM,
  201. IPPROTO_UDP,
  202. kCFSocketReadCallBack | kCFSocketWriteCallBack,
  203. (CFSocketCallBack)&MyCFSocketCallback,
  204. &theContext);
  205. }
  206. // Disable continuous callbacks for read and write.
  207. // If we don't do this, the socket(s) will just sit there firing read callbacks
  208. // at us hundreds of times a second if we don't immediately read the available data.
  209. if(theSocket4)
  210. {
  211. CFSocketSetSocketFlags(theSocket4, kCFSocketCloseOnInvalidate);
  212. }
  213. if(theSocket6)
  214. {
  215. CFSocketSetSocketFlags(theSocket6, kCFSocketCloseOnInvalidate);
  216. }
  217. // Get the CFRunLoop to which the socket should be attached.
  218. theRunLoop = CFRunLoopGetCurrent();
  219. // Set default run loop modes
  220. theRunLoopModes = [NSArray arrayWithObject:NSDefaultRunLoopMode];
  221. // Attach the sockets to the run loop
  222. if(theSocket4)
  223. {
  224. theSource4 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, theSocket4, 0);
  225. [self runLoopAddSource:theSource4];
  226. }
  227. if(theSocket6)
  228. {
  229. theSource6 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, theSocket6, 0);
  230. [self runLoopAddSource:theSource6];
  231. }
  232. cachedLocalPort = 0;
  233. cachedConnectedPort = 0;
  234. }
  235. return self;
  236. }
  237. - (id)init
  238. {
  239. return [self initWithDelegate:nil userData:0 enableIPv4:YES enableIPv6:YES];
  240. }
  241. - (id)initWithDelegate:(id)delegate
  242. {
  243. return [self initWithDelegate:delegate userData:0 enableIPv4:YES enableIPv6:YES];
  244. }
  245. - (id)initWithDelegate:(id)delegate userData:(long)userData
  246. {
  247. return [self initWithDelegate:delegate userData:userData enableIPv4:YES enableIPv6:YES];
  248. }
  249. - (id)initIPv4
  250. {
  251. return [self initWithDelegate:nil userData:0 enableIPv4:YES enableIPv6:NO];
  252. }
  253. - (id)initIPv6
  254. {
  255. return [self initWithDelegate:nil userData:0 enableIPv4:NO enableIPv6:YES];
  256. }
  257. - (void) dealloc
  258. {
  259. [self close];
  260. [NSObject cancelPreviousPerformRequestsWithTarget:theDelegate selector:@selector(onUdpSocketDidClose:) object:self];
  261. [NSObject cancelPreviousPerformRequestsWithTarget:self];
  262. }
  263. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  264. #pragma mark Accessors
  265. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  266. - (id)delegate
  267. {
  268. return theDelegate;
  269. }
  270. - (void)setDelegate:(id)delegate
  271. {
  272. theDelegate = delegate;
  273. }
  274. - (long)userData
  275. {
  276. return theUserData;
  277. }
  278. - (void)setUserData:(long)userData
  279. {
  280. theUserData = userData;
  281. }
  282. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  283. #pragma mark Run Loop
  284. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  285. - (void)runLoopAddSource:(CFRunLoopSourceRef)source
  286. {
  287. for (NSString *runLoopMode in theRunLoopModes)
  288. {
  289. CFRunLoopAddSource(theRunLoop, source, (__bridge CFStringRef)runLoopMode);
  290. }
  291. }
  292. - (void)runLoopRemoveSource:(CFRunLoopSourceRef)source
  293. {
  294. for (NSString *runLoopMode in theRunLoopModes)
  295. {
  296. CFRunLoopRemoveSource(theRunLoop, source, (__bridge CFStringRef)runLoopMode);
  297. }
  298. }
  299. - (void)runLoopAddTimer:(NSTimer *)timer
  300. {
  301. for (NSString *runLoopMode in theRunLoopModes)
  302. {
  303. CFRunLoopAddTimer(theRunLoop, (__bridge CFRunLoopTimerRef)timer, (__bridge CFStringRef)runLoopMode);
  304. }
  305. }
  306. - (void)runLoopRemoveTimer:(NSTimer *)timer
  307. {
  308. for (NSString *runLoopMode in theRunLoopModes)
  309. {
  310. CFRunLoopRemoveTimer(theRunLoop, (__bridge CFRunLoopTimerRef)timer, (__bridge CFStringRef)runLoopMode);
  311. }
  312. }
  313. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  314. #pragma mark Configuration
  315. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  316. - (UInt32)maxReceiveBufferSize
  317. {
  318. return maxReceiveBufferSize;
  319. }
  320. - (void)setMaxReceiveBufferSize:(UInt32)max
  321. {
  322. maxReceiveBufferSize = max;
  323. }
  324. /**
  325. * See the header file for a full explanation of this method.
  326. **/
  327. - (BOOL)moveToRunLoop:(NSRunLoop *)runLoop
  328. {
  329. NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
  330. @"moveToRunLoop must be called from within the current RunLoop!");
  331. if(runLoop == nil)
  332. {
  333. return NO;
  334. }
  335. if(theRunLoop == [runLoop getCFRunLoop])
  336. {
  337. return YES;
  338. }
  339. [NSObject cancelPreviousPerformRequestsWithTarget:self];
  340. theFlags &= ~kDequeueSendScheduled;
  341. theFlags &= ~kDequeueReceiveScheduled;
  342. if(theSource4) [self runLoopRemoveSource:theSource4];
  343. if(theSource6) [self runLoopRemoveSource:theSource6];
  344. if(theSendTimer) [self runLoopRemoveTimer:theSendTimer];
  345. if(theReceiveTimer) [self runLoopRemoveTimer:theReceiveTimer];
  346. theRunLoop = [runLoop getCFRunLoop];
  347. if(theSendTimer) [self runLoopAddTimer:theSendTimer];
  348. if(theReceiveTimer) [self runLoopAddTimer:theReceiveTimer];
  349. if(theSource4) [self runLoopAddSource:theSource4];
  350. if(theSource6) [self runLoopAddSource:theSource6];
  351. [runLoop performSelector:@selector(maybeDequeueSend) target:self argument:nil order:0 modes:theRunLoopModes];
  352. [runLoop performSelector:@selector(maybeDequeueReceive) target:self argument:nil order:0 modes:theRunLoopModes];
  353. [runLoop performSelector:@selector(maybeScheduleClose) target:self argument:nil order:0 modes:theRunLoopModes];
  354. return YES;
  355. }
  356. /**
  357. * See the header file for a full explanation of this method.
  358. **/
  359. - (BOOL)setRunLoopModes:(NSArray *)runLoopModes
  360. {
  361. NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
  362. @"setRunLoopModes must be called from within the current RunLoop!");
  363. if([runLoopModes count] == 0)
  364. {
  365. return NO;
  366. }
  367. if([theRunLoopModes isEqualToArray:runLoopModes])
  368. {
  369. return YES;
  370. }
  371. [NSObject cancelPreviousPerformRequestsWithTarget:self];
  372. theFlags &= ~kDequeueSendScheduled;
  373. theFlags &= ~kDequeueReceiveScheduled;
  374. if(theSource4) [self runLoopRemoveSource:theSource4];
  375. if(theSource6) [self runLoopRemoveSource:theSource6];
  376. if(theSendTimer) [self runLoopRemoveTimer:theSendTimer];
  377. if(theReceiveTimer) [self runLoopRemoveTimer:theReceiveTimer];
  378. theRunLoopModes = [runLoopModes copy];
  379. if(theSendTimer) [self runLoopAddTimer:theSendTimer];
  380. if(theReceiveTimer) [self runLoopAddTimer:theReceiveTimer];
  381. if(theSource4) [self runLoopAddSource:theSource4];
  382. if(theSource6) [self runLoopAddSource:theSource6];
  383. [self performSelector:@selector(maybeDequeueSend) withObject:nil afterDelay:0 inModes:theRunLoopModes];
  384. [self performSelector:@selector(maybeDequeueReceive) withObject:nil afterDelay:0 inModes:theRunLoopModes];
  385. [self performSelector:@selector(maybeScheduleClose) withObject:nil afterDelay:0 inModes:theRunLoopModes];
  386. return YES;
  387. }
  388. - (NSArray *)runLoopModes
  389. {
  390. return [theRunLoopModes copy];
  391. }
  392. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  393. #pragma mark Utilities:
  394. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  395. /**
  396. * Attempts to convert the given host/port into and IPv4 and/or IPv6 data structure.
  397. * The data structure is of type sockaddr_in for IPv4 and sockaddr_in6 for IPv6.
  398. *
  399. * Returns zero on success, or one of the error codes listed in gai_strerror if an error occurs (as per getaddrinfo).
  400. **/
  401. - (int)convertForBindHost:(NSString *)host
  402. port:(UInt16)port
  403. intoAddress4:(NSData **)address4
  404. address6:(NSData **)address6
  405. {
  406. if(host == nil || ([host length] == 0))
  407. {
  408. // Use ANY address
  409. struct sockaddr_in nativeAddr;
  410. nativeAddr.sin_len = sizeof(struct sockaddr_in);
  411. nativeAddr.sin_family = AF_INET;
  412. nativeAddr.sin_port = htons(port);
  413. nativeAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  414. memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero));
  415. struct sockaddr_in6 nativeAddr6;
  416. nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
  417. nativeAddr6.sin6_family = AF_INET6;
  418. nativeAddr6.sin6_port = htons(port);
  419. nativeAddr6.sin6_flowinfo = 0;
  420. nativeAddr6.sin6_addr = in6addr_any;
  421. nativeAddr6.sin6_scope_id = 0;
  422. // Wrap the native address structures for CFSocketSetAddress.
  423. if(address4) *address4 = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)];
  424. if(address6) *address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
  425. return 0;
  426. }
  427. else if([host isEqualToString:@"localhost"] || [host isEqualToString:@"loopback"])
  428. {
  429. // Note: getaddrinfo("localhost",...) fails on 10.5.3
  430. // Use LOOPBACK address
  431. struct sockaddr_in nativeAddr;
  432. nativeAddr.sin_len = sizeof(struct sockaddr_in);
  433. nativeAddr.sin_family = AF_INET;
  434. nativeAddr.sin_port = htons(port);
  435. nativeAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  436. memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero));
  437. struct sockaddr_in6 nativeAddr6;
  438. nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
  439. nativeAddr6.sin6_family = AF_INET6;
  440. nativeAddr6.sin6_port = htons(port);
  441. nativeAddr6.sin6_flowinfo = 0;
  442. nativeAddr6.sin6_addr = in6addr_loopback;
  443. nativeAddr6.sin6_scope_id = 0;
  444. // Wrap the native address structures for CFSocketSetAddress.
  445. if(address4) *address4 = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)];
  446. if(address6) *address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
  447. return 0;
  448. }
  449. else
  450. {
  451. NSString *portStr = [NSString stringWithFormat:@"%hu", port];
  452. #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
  453. @synchronized (getaddrinfoLock)
  454. #endif
  455. {
  456. struct addrinfo hints, *res, *res0;
  457. memset(&hints, 0, sizeof(hints));
  458. hints.ai_family = PF_UNSPEC;
  459. hints.ai_socktype = SOCK_DGRAM;
  460. hints.ai_protocol = IPPROTO_UDP;
  461. hints.ai_flags = AI_PASSIVE;
  462. int error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0);
  463. if(error) return error;
  464. for(res = res0; res; res = res->ai_next)
  465. {
  466. if(address4 && !*address4 && (res->ai_family == AF_INET))
  467. {
  468. // Found IPv4 address
  469. // Wrap the native address structures for CFSocketSetAddress.
  470. if(address4) *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
  471. }
  472. else if(address6 && !*address6 && (res->ai_family == AF_INET6))
  473. {
  474. // Found IPv6 address
  475. // Wrap the native address structures for CFSocketSetAddress.
  476. if(address6) *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
  477. }
  478. }
  479. freeaddrinfo(res0);
  480. }
  481. return 0;
  482. }
  483. }
  484. /**
  485. * Attempts to convert the given host/port into and IPv4 and/or IPv6 data structure.
  486. * The data structure is of type sockaddr_in for IPv4 and sockaddr_in6 for IPv6.
  487. *
  488. * Returns zero on success, or one of the error codes listed in gai_strerror if an error occurs (as per getaddrinfo).
  489. **/
  490. - (int)convertForSendHost:(NSString *)host
  491. port:(UInt16)port
  492. intoAddress4:(NSData **)address4
  493. address6:(NSData **)address6
  494. {
  495. if(host == nil || ([host length] == 0))
  496. {
  497. // We're not binding, so what are we supposed to do with this?
  498. return EAI_NONAME;
  499. }
  500. else if([host isEqualToString:@"localhost"] || [host isEqualToString:@"loopback"])
  501. {
  502. // Note: getaddrinfo("localhost",...) fails on 10.5.3
  503. // Use LOOPBACK address
  504. struct sockaddr_in nativeAddr;
  505. nativeAddr.sin_len = sizeof(struct sockaddr_in);
  506. nativeAddr.sin_family = AF_INET;
  507. nativeAddr.sin_port = htons(port);
  508. nativeAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  509. memset(&(nativeAddr.sin_zero), 0, sizeof(nativeAddr.sin_zero));
  510. struct sockaddr_in6 nativeAddr6;
  511. nativeAddr6.sin6_len = sizeof(struct sockaddr_in6);
  512. nativeAddr6.sin6_family = AF_INET6;
  513. nativeAddr6.sin6_port = htons(port);
  514. nativeAddr6.sin6_flowinfo = 0;
  515. nativeAddr6.sin6_addr = in6addr_loopback;
  516. nativeAddr6.sin6_scope_id = 0;
  517. // Wrap the native address structures for CFSocketSetAddress.
  518. if(address4) *address4 = [NSData dataWithBytes:&nativeAddr length:sizeof(nativeAddr)];
  519. if(address6) *address6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)];
  520. return 0;
  521. }
  522. else
  523. {
  524. NSString *portStr = [NSString stringWithFormat:@"%hu", port];
  525. #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
  526. @synchronized (getaddrinfoLock)
  527. #endif
  528. {
  529. struct addrinfo hints, *res, *res0;
  530. memset(&hints, 0, sizeof(hints));
  531. hints.ai_family = PF_UNSPEC;
  532. hints.ai_socktype = SOCK_DGRAM;
  533. hints.ai_protocol = IPPROTO_UDP;
  534. // No passive flag on a send or connect
  535. int error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0);
  536. if(error) return error;
  537. for(res = res0; res; res = res->ai_next)
  538. {
  539. if(address4 && !*address4 && (res->ai_family == AF_INET))
  540. {
  541. // Found IPv4 address
  542. // Wrap the native address structures for CFSocketSetAddress.
  543. if(address4) *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
  544. }
  545. else if(address6 && !*address6 && (res->ai_family == AF_INET6))
  546. {
  547. // Found IPv6 address
  548. // Wrap the native address structures for CFSocketSetAddress.
  549. if(address6) *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
  550. }
  551. }
  552. freeaddrinfo(res0);
  553. }
  554. return 0;
  555. }
  556. }
  557. - (NSString *)addressHost4:(struct sockaddr_in *)pSockaddr4
  558. {
  559. char addrBuf[INET_ADDRSTRLEN];
  560. if(inet_ntop(AF_INET, &pSockaddr4->sin_addr, addrBuf, sizeof(addrBuf)) == NULL)
  561. {
  562. [NSException raise:NSInternalInconsistencyException format:@"Cannot convert address to string."];
  563. }
  564. return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding];
  565. }
  566. - (NSString *)addressHost6:(struct sockaddr_in6 *)pSockaddr6
  567. {
  568. char addrBuf[INET6_ADDRSTRLEN];
  569. if(inet_ntop(AF_INET6, &pSockaddr6->sin6_addr, addrBuf, sizeof(addrBuf)) == NULL)
  570. {
  571. [NSException raise:NSInternalInconsistencyException format:@"Cannot convert address to string."];
  572. }
  573. return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding];
  574. }
  575. - (NSString *)addressHost:(struct sockaddr *)pSockaddr
  576. {
  577. if(pSockaddr->sa_family == AF_INET)
  578. {
  579. return [self addressHost4:(struct sockaddr_in *)pSockaddr];
  580. }
  581. else
  582. {
  583. return [self addressHost6:(struct sockaddr_in6 *)pSockaddr];
  584. }
  585. }
  586. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  587. #pragma mark Socket Implementation:
  588. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  589. /**
  590. * Binds the underlying socket(s) to the given port.
  591. * The socket(s) will be able to receive data on any interface.
  592. *
  593. * On success, returns YES.
  594. * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
  595. **/
  596. - (BOOL)bindToPort:(UInt16)port error:(NSError **)errPtr
  597. {
  598. return [self bindToAddress:nil port:port error:errPtr];
  599. }
  600. /**
  601. * Binds the underlying socket(s) to the given address and port.
  602. * The sockets(s) will be able to receive data only on the given interface.
  603. *
  604. * To receive data on any interface, pass nil or "".
  605. * To receive data only on the loopback interface, pass "localhost" or "loopback".
  606. *
  607. * On success, returns YES.
  608. * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
  609. **/
  610. - (BOOL)bindToAddress:(NSString *)host port:(UInt16)port error:(NSError **)errPtr
  611. {
  612. if(theFlags & kDidClose)
  613. {
  614. [NSException raise:AsyncUdpSocketException
  615. format:@"The socket is closed."];
  616. }
  617. if(theFlags & kDidBind)
  618. {
  619. [NSException raise:AsyncUdpSocketException
  620. format:@"Cannot bind a socket more than once."];
  621. }
  622. if(theFlags & kDidConnect)
  623. {
  624. [NSException raise:AsyncUdpSocketException
  625. format:@"Cannot bind after connecting. If needed, bind first, then connect."];
  626. }
  627. // Convert the given host/port into native address structures for CFSocketSetAddress
  628. NSData *address4 = nil, *address6 = nil;
  629. int gai_error = [self convertForBindHost:host port:port intoAddress4:&address4 address6:&address6];
  630. if(gai_error)
  631. {
  632. if(errPtr)
  633. {
  634. NSString *errMsg = [NSString stringWithCString:gai_strerror(gai_error) encoding:NSASCIIStringEncoding];
  635. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  636. *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:gai_error userInfo:info];
  637. }
  638. return NO;
  639. }
  640. NSAssert((address4 || address6), @"address4 and address6 are nil");
  641. // Set the SO_REUSEADDR flags
  642. int reuseOn = 1;
  643. if (theSocket4) setsockopt(CFSocketGetNative(theSocket4), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
  644. if (theSocket6) setsockopt(CFSocketGetNative(theSocket6), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
  645. // Bind the sockets
  646. if(address4)
  647. {
  648. if(theSocket4)
  649. {
  650. CFSocketError error = CFSocketSetAddress(theSocket4, (__bridge CFDataRef)address4);
  651. if(error != kCFSocketSuccess)
  652. {
  653. if(errPtr) *errPtr = [self getSocketError];
  654. return NO;
  655. }
  656. if(!address6)
  657. {
  658. // Using IPv4 only
  659. [self closeSocket6];
  660. }
  661. }
  662. else if(!address6)
  663. {
  664. if(errPtr) *errPtr = [self getIPv4UnavailableError];
  665. return NO;
  666. }
  667. }
  668. if(address6)
  669. {
  670. // Note: The iPhone doesn't currently support IPv6
  671. if(theSocket6)
  672. {
  673. CFSocketError error = CFSocketSetAddress(theSocket6, (__bridge CFDataRef)address6);
  674. if(error != kCFSocketSuccess)
  675. {
  676. if(errPtr) *errPtr = [self getSocketError];
  677. return NO;
  678. }
  679. if(!address4)
  680. {
  681. // Using IPv6 only
  682. [self closeSocket4];
  683. }
  684. }
  685. else if(!address4)
  686. {
  687. if(errPtr) *errPtr = [self getIPv6UnavailableError];
  688. return NO;
  689. }
  690. }
  691. theFlags |= kDidBind;
  692. return YES;
  693. }
  694. /**
  695. * Connects the underlying UDP socket to the given host and port.
  696. * If an IPv4 address is resolved, the IPv4 socket is connected, and the IPv6 socket is invalidated and released.
  697. * If an IPv6 address is resolved, the IPv6 socket is connected, and the IPv4 socket is invalidated and released.
  698. *
  699. * On success, returns YES.
  700. * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
  701. **/
  702. - (BOOL)connectToHost:(NSString *)host onPort:(UInt16)port error:(NSError **)errPtr
  703. {
  704. if(theFlags & kDidClose)
  705. {
  706. [NSException raise:AsyncUdpSocketException
  707. format:@"The socket is closed."];
  708. }
  709. if(theFlags & kDidConnect)
  710. {
  711. [NSException raise:AsyncUdpSocketException
  712. format:@"Cannot connect a socket more than once."];
  713. }
  714. // Convert the given host/port into native address structures for CFSocketSetAddress
  715. NSData *address4 = nil, *address6 = nil;
  716. int error = [self convertForSendHost:host port:port intoAddress4:&address4 address6:&address6];
  717. if(error)
  718. {
  719. if(errPtr)
  720. {
  721. NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding];
  722. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  723. *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info];
  724. }
  725. return NO;
  726. }
  727. NSAssert((address4 || address6), @"address4 and address6 are nil");
  728. // We only want to connect via a single interface.
  729. // IPv4 is currently preferred, but this may change in the future.
  730. CFSocketError sockErr;
  731. if (address4)
  732. {
  733. if (theSocket4)
  734. {
  735. sockErr = CFSocketConnectToAddress(theSocket4, (__bridge CFDataRef)address4, (CFTimeInterval)0.0);
  736. if (sockErr != kCFSocketSuccess)
  737. {
  738. if(errPtr) *errPtr = [self getSocketError];
  739. return NO;
  740. }
  741. theFlags |= kDidConnect;
  742. // We're connected to an IPv4 address, so no need for the IPv6 socket
  743. [self closeSocket6];
  744. return YES;
  745. }
  746. else if(!address6)
  747. {
  748. if(errPtr) *errPtr = [self getIPv4UnavailableError];
  749. return NO;
  750. }
  751. }
  752. if (address6)
  753. {
  754. // Note: The iPhone doesn't currently support IPv6
  755. if (theSocket6)
  756. {
  757. sockErr = CFSocketConnectToAddress(theSocket6, (__bridge CFDataRef)address6, (CFTimeInterval)0.0);
  758. if (sockErr != kCFSocketSuccess)
  759. {
  760. if(errPtr) *errPtr = [self getSocketError];
  761. return NO;
  762. }
  763. theFlags |= kDidConnect;
  764. // We're connected to an IPv6 address, so no need for the IPv4 socket
  765. [self closeSocket4];
  766. return YES;
  767. }
  768. else
  769. {
  770. if(errPtr) *errPtr = [self getIPv6UnavailableError];
  771. return NO;
  772. }
  773. }
  774. // It shouldn't be possible to get to this point because either address4 or address6 was non-nil.
  775. if(errPtr) *errPtr = nil;
  776. return NO;
  777. }
  778. /**
  779. * Connects the underlying UDP socket to the remote address.
  780. * If the address is an IPv4 address, the IPv4 socket is connected, and the IPv6 socket is invalidated and released.
  781. * If the address is an IPv6 address, the IPv6 socket is connected, and the IPv4 socket is invalidated and released.
  782. *
  783. * The address is a native address structure, as may be returned from API's such as Bonjour.
  784. * An address may be created manually by simply wrapping a sockaddr_in or sockaddr_in6 in an NSData object.
  785. *
  786. * On success, returns YES.
  787. * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr.
  788. **/
  789. - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr
  790. {
  791. if (theFlags & kDidClose)
  792. {
  793. [NSException raise:AsyncUdpSocketException
  794. format:@"The socket is closed."];
  795. }
  796. if (theFlags & kDidConnect)
  797. {
  798. [NSException raise:AsyncUdpSocketException
  799. format:@"Cannot connect a socket more than once."];
  800. }
  801. CFSocketError sockErr;
  802. // Is remoteAddr an IPv4 address?
  803. if ([remoteAddr length] == sizeof(struct sockaddr_in))
  804. {
  805. if (theSocket4)
  806. {
  807. sockErr = CFSocketConnectToAddress(theSocket4, (__bridge CFDataRef)remoteAddr, (CFTimeInterval)0.0);
  808. if (sockErr != kCFSocketSuccess)
  809. {
  810. if(errPtr) *errPtr = [self getSocketError];
  811. return NO;
  812. }
  813. theFlags |= kDidConnect;
  814. // We're connected to an IPv4 address, so no need for the IPv6 socket
  815. [self closeSocket6];
  816. return YES;
  817. }
  818. else
  819. {
  820. if(errPtr) *errPtr = [self getIPv4UnavailableError];
  821. return NO;
  822. }
  823. }
  824. // Is remoteAddr an IPv6 address?
  825. if ([remoteAddr length] == sizeof(struct sockaddr_in6))
  826. {
  827. if (theSocket6)
  828. {
  829. sockErr = CFSocketConnectToAddress(theSocket6, (__bridge CFDataRef)remoteAddr, (CFTimeInterval)0.0);
  830. if (sockErr != kCFSocketSuccess)
  831. {
  832. if(errPtr) *errPtr = [self getSocketError];
  833. return NO;
  834. }
  835. theFlags |= kDidConnect;
  836. // We're connected to an IPv6 address, so no need for the IPv4 socket
  837. [self closeSocket4];
  838. return YES;
  839. }
  840. else
  841. {
  842. if(errPtr) *errPtr = [self getIPv6UnavailableError];
  843. return NO;
  844. }
  845. }
  846. // The remoteAddr was invalid
  847. if(errPtr)
  848. {
  849. NSString *errMsg = @"remoteAddr parameter is not a valid address";
  850. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  851. *errPtr = [NSError errorWithDomain:AsyncUdpSocketErrorDomain
  852. code:AsyncUdpSocketBadParameter
  853. userInfo:info];
  854. }
  855. return NO;
  856. }
  857. /**
  858. * Join multicast group
  859. *
  860. * Group should be a multicast IP address (eg. @"239.255.250.250" for IPv4).
  861. * Address is local interface for IPv4, but currently defaults under IPv6.
  862. **/
  863. - (BOOL)joinMulticastGroup:(NSString *)group error:(NSError **)errPtr
  864. {
  865. return [self joinMulticastGroup:group withAddress:nil error:errPtr];
  866. }
  867. - (BOOL)joinMulticastGroup:(NSString *)group withAddress:(NSString *)address error:(NSError **)errPtr
  868. {
  869. if(theFlags & kDidClose)
  870. {
  871. [NSException raise:AsyncUdpSocketException
  872. format:@"The socket is closed."];
  873. }
  874. if(!(theFlags & kDidBind))
  875. {
  876. [NSException raise:AsyncUdpSocketException
  877. format:@"Must bind a socket before joining a multicast group."];
  878. }
  879. if(theFlags & kDidConnect)
  880. {
  881. [NSException raise:AsyncUdpSocketException
  882. format:@"Cannot join a multicast group if connected."];
  883. }
  884. // Get local interface address
  885. // Convert the given host/port into native address structures for CFSocketSetAddress
  886. NSData *address4 = nil, *address6 = nil;
  887. int error = [self convertForBindHost:address port:0 intoAddress4:&address4 address6:&address6];
  888. if(error)
  889. {
  890. if(errPtr)
  891. {
  892. NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding];
  893. NSString *errDsc = [NSString stringWithFormat:@"Invalid parameter 'address': %@", errMsg];
  894. NSDictionary *info = [NSDictionary dictionaryWithObject:errDsc forKey:NSLocalizedDescriptionKey];
  895. *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info];
  896. }
  897. return NO;
  898. }
  899. NSAssert((address4 || address6), @"address4 and address6 are nil");
  900. // Get multicast address (group)
  901. NSData *group4 = nil, *group6 = nil;
  902. error = [self convertForBindHost:group port:0 intoAddress4:&group4 address6:&group6];
  903. if(error)
  904. {
  905. if(errPtr)
  906. {
  907. NSString *errMsg = [NSString stringWithCString:gai_strerror(error) encoding:NSASCIIStringEncoding];
  908. NSString *errDsc = [NSString stringWithFormat:@"Invalid parameter 'group': %@", errMsg];
  909. NSDictionary *info = [NSDictionary dictionaryWithObject:errDsc forKey:NSLocalizedDescriptionKey];
  910. *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:error userInfo:info];
  911. }
  912. return NO;
  913. }
  914. NSAssert((group4 || group6), @"group4 and group6 are nil");
  915. if(theSocket4 && group4 && address4)
  916. {
  917. const struct sockaddr_in* nativeAddress = [address4 bytes];
  918. const struct sockaddr_in* nativeGroup = [group4 bytes];
  919. struct ip_mreq imreq;
  920. imreq.imr_multiaddr = nativeGroup->sin_addr;
  921. imreq.imr_interface = nativeAddress->sin_addr;
  922. // JOIN multicast group on default interface
  923. error = setsockopt(CFSocketGetNative(theSocket4), IPPROTO_IP, IP_ADD_MEMBERSHIP,
  924. (const void *)&imreq, sizeof(struct ip_mreq));
  925. if(error)
  926. {
  927. if(errPtr)
  928. {
  929. NSString *errMsg = @"Unable to join IPv4 multicast group";
  930. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  931. *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainPOSIX" code:error userInfo:info];
  932. }
  933. return NO;
  934. }
  935. // Using IPv4 only
  936. [self closeSocket6];
  937. return YES;
  938. }
  939. if(theSocket6 && group6 && address6)
  940. {
  941. const struct sockaddr_in6* nativeGroup = [group6 bytes];
  942. struct ipv6_mreq imreq;
  943. imreq.ipv6mr_multiaddr = nativeGroup->sin6_addr;
  944. imreq.ipv6mr_interface = 0;
  945. // JOIN multicast group on default interface
  946. error = setsockopt(CFSocketGetNative(theSocket6), IPPROTO_IP, IPV6_JOIN_GROUP,
  947. (const void *)&imreq, sizeof(struct ipv6_mreq));
  948. if(error)
  949. {
  950. if(errPtr)
  951. {
  952. NSString *errMsg = @"Unable to join IPv6 multicast group";
  953. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  954. *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainPOSIX" code:error userInfo:info];
  955. }
  956. return NO;
  957. }
  958. // Using IPv6 only
  959. [self closeSocket4];
  960. return YES;
  961. }
  962. // The given address and group didn't match the existing socket(s).
  963. // This means there were no compatible combination of all IPv4 or IPv6 socket, group and address.
  964. if(errPtr)
  965. {
  966. NSString *errMsg = @"Invalid group and/or address, not matching existing socket(s)";
  967. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  968. *errPtr = [NSError errorWithDomain:AsyncUdpSocketErrorDomain
  969. code:AsyncUdpSocketBadParameter
  970. userInfo:info];
  971. }
  972. return NO;
  973. }
  974. /**
  975. * By default, the underlying socket in the OS will not allow you to send broadcast messages.
  976. * In order to send broadcast messages, you need to enable this functionality in the socket.
  977. *
  978. * A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is
  979. * delivered to every host on the network.
  980. * The reason this is generally disabled by default is to prevent
  981. * accidental broadcast messages from flooding the network.
  982. **/
  983. - (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr
  984. {
  985. if (theSocket4)
  986. {
  987. int value = flag ? 1 : 0;
  988. int error = setsockopt(CFSocketGetNative(theSocket4), SOL_SOCKET, SO_BROADCAST,
  989. (const void *)&value, sizeof(value));
  990. if(error)
  991. {
  992. if(errPtr)
  993. {
  994. NSString *errMsg = @"Unable to enable broadcast message sending";
  995. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  996. *errPtr = [NSError errorWithDomain:@"kCFStreamErrorDomainPOSIX" code:error userInfo:info];
  997. }
  998. return NO;
  999. }
  1000. }
  1001. // IPv6 does not implement broadcast, the ability to send a packet to all hosts on the attached link.
  1002. // The same effect can be achieved by sending a packet to the link-local all hosts multicast group.
  1003. return YES;
  1004. }
  1005. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1006. #pragma mark Disconnect Implementation:
  1007. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1008. - (void)emptyQueues
  1009. {
  1010. if (theCurrentSend) [self endCurrentSend];
  1011. if (theCurrentReceive) [self endCurrentReceive];
  1012. [theSendQueue removeAllObjects];
  1013. [theReceiveQueue removeAllObjects];
  1014. [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueSend) object:nil];
  1015. [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(maybeDequeueReceive) object:nil];
  1016. theFlags &= ~kDequeueSendScheduled;
  1017. theFlags &= ~kDequeueReceiveScheduled;
  1018. }
  1019. - (void)closeSocket4
  1020. {
  1021. if (theSocket4 != NULL)
  1022. {
  1023. CFSocketInvalidate(theSocket4);
  1024. CFRelease(theSocket4);
  1025. theSocket4 = NULL;
  1026. }
  1027. if (theSource4 != NULL)
  1028. {
  1029. [self runLoopRemoveSource:theSource4];
  1030. CFRelease(theSource4);
  1031. theSource4 = NULL;
  1032. }
  1033. }
  1034. - (void)closeSocket6
  1035. {
  1036. if (theSocket6 != NULL)
  1037. {
  1038. CFSocketInvalidate(theSocket6);
  1039. CFRelease(theSocket6);
  1040. theSocket6 = NULL;
  1041. }
  1042. if (theSource6 != NULL)
  1043. {
  1044. [self runLoopRemoveSource:theSource6];
  1045. CFRelease(theSource6);
  1046. theSource6 = NULL;
  1047. }
  1048. }
  1049. - (void)close
  1050. {
  1051. [self emptyQueues];
  1052. [self closeSocket4];
  1053. [self closeSocket6];
  1054. theRunLoop = NULL;
  1055. // Delay notification to give user freedom to release without returning here and core-dumping.
  1056. if ([theDelegate respondsToSelector:@selector(onUdpSocketDidClose:)])
  1057. {
  1058. [theDelegate performSelector:@selector(onUdpSocketDidClose:)
  1059. withObject:self
  1060. afterDelay:0
  1061. inModes:theRunLoopModes];
  1062. }
  1063. theFlags |= kDidClose;
  1064. }
  1065. - (void)closeAfterSending
  1066. {
  1067. if(theFlags & kDidClose) return;
  1068. theFlags |= (kForbidSendReceive | kCloseAfterSends);
  1069. [self maybeScheduleClose];
  1070. }
  1071. - (void)closeAfterReceiving
  1072. {
  1073. if(theFlags & kDidClose) return;
  1074. theFlags |= (kForbidSendReceive | kCloseAfterReceives);
  1075. [self maybeScheduleClose];
  1076. }
  1077. - (void)closeAfterSendingAndReceiving
  1078. {
  1079. if(theFlags & kDidClose) return;
  1080. theFlags |= (kForbidSendReceive | kCloseAfterSends | kCloseAfterReceives);
  1081. [self maybeScheduleClose];
  1082. }
  1083. - (void)maybeScheduleClose
  1084. {
  1085. BOOL shouldDisconnect = NO;
  1086. if(theFlags & kCloseAfterSends)
  1087. {
  1088. if(([theSendQueue count] == 0) && (theCurrentSend == nil))
  1089. {
  1090. if(theFlags & kCloseAfterReceives)
  1091. {
  1092. if(([theReceiveQueue count] == 0) && (theCurrentReceive == nil))
  1093. {
  1094. shouldDisconnect = YES;
  1095. }
  1096. }
  1097. else
  1098. {
  1099. shouldDisconnect = YES;
  1100. }
  1101. }
  1102. }
  1103. else if(theFlags & kCloseAfterReceives)
  1104. {
  1105. if(([theReceiveQueue count] == 0) && (theCurrentReceive == nil))
  1106. {
  1107. shouldDisconnect = YES;
  1108. }
  1109. }
  1110. if(shouldDisconnect)
  1111. {
  1112. [self performSelector:@selector(close) withObject:nil afterDelay:0 inModes:theRunLoopModes];
  1113. }
  1114. }
  1115. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1116. #pragma mark Errors
  1117. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1118. /**
  1119. * Returns a standard error object for the current errno value.
  1120. * Errno is used for low-level BSD socket errors.
  1121. **/
  1122. - (NSError *)getErrnoError
  1123. {
  1124. NSString *errorMsg = [NSString stringWithUTF8String:strerror(errno)];
  1125. NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMsg forKey:NSLocalizedDescriptionKey];
  1126. return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo];
  1127. }
  1128. /**
  1129. * Returns a standard error message for a CFSocket error.
  1130. * Unfortunately, CFSocket offers no feedback on its errors.
  1131. **/
  1132. - (NSError *)getSocketError
  1133. {
  1134. NSString *errMsg = @"General CFSocket error";
  1135. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1136. return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketCFSocketError userInfo:info];
  1137. }
  1138. - (NSError *)getIPv4UnavailableError
  1139. {
  1140. NSString *errMsg = @"IPv4 is unavailable due to binding/connecting using IPv6 only";
  1141. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1142. return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketIPv4Unavailable userInfo:info];
  1143. }
  1144. - (NSError *)getIPv6UnavailableError
  1145. {
  1146. NSString *errMsg = @"IPv6 is unavailable due to binding/connecting using IPv4 only or is not supported on this platform";
  1147. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1148. return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketIPv6Unavailable userInfo:info];
  1149. }
  1150. - (NSError *)getSendTimeoutError
  1151. {
  1152. NSString *errMsg = @"Send operation timed out";
  1153. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1154. return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketSendTimeoutError userInfo:info];
  1155. }
  1156. - (NSError *)getReceiveTimeoutError
  1157. {
  1158. NSString *errMsg = @"Receive operation timed out";
  1159. NSDictionary *info = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey];
  1160. return [NSError errorWithDomain:AsyncUdpSocketErrorDomain code:AsyncUdpSocketReceiveTimeoutError userInfo:info];
  1161. }
  1162. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1163. #pragma mark Diagnostics
  1164. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1165. - (NSString *)localHost
  1166. {
  1167. if(cachedLocalHost) return cachedLocalHost;
  1168. if(theSocket4)
  1169. return [self localHost:theSocket4];
  1170. else
  1171. return [self localHost:theSocket6];
  1172. }
  1173. - (UInt16)localPort
  1174. {
  1175. if(cachedLocalPort > 0) return cachedLocalPort;
  1176. if(theSocket4)
  1177. return [self localPort:theSocket4];
  1178. else
  1179. return [self localPort:theSocket6];
  1180. }
  1181. - (NSString *)connectedHost
  1182. {
  1183. if(cachedConnectedHost) return cachedConnectedHost;
  1184. if(theSocket4)
  1185. return [self connectedHost:theSocket4];
  1186. else
  1187. return [self connectedHost:theSocket6];
  1188. }
  1189. - (UInt16)connectedPort
  1190. {
  1191. if(cachedConnectedPort > 0) return cachedConnectedPort;
  1192. if(theSocket4)
  1193. return [self connectedPort:theSocket4];
  1194. else
  1195. return [self connectedPort:theSocket6];
  1196. }
  1197. - (NSString *)localHost:(CFSocketRef)theSocket
  1198. {
  1199. if (theSocket == NULL) return nil;
  1200. // Unfortunately we can't use CFSocketCopyAddress.
  1201. // The CFSocket library caches the address the first time you call CFSocketCopyAddress.
  1202. // So if this is called prior to binding/connecting/sending, it won't be updated again when necessary,
  1203. // and will continue to return the old value of the socket address.
  1204. NSString *result = nil;
  1205. if (theSocket == theSocket4)
  1206. {
  1207. struct sockaddr_in sockaddr4;
  1208. socklen_t sockaddr4len = sizeof(sockaddr4);
  1209. if (getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0)
  1210. {
  1211. return nil;
  1212. }
  1213. result = [self addressHost4:&sockaddr4];
  1214. }
  1215. else
  1216. {
  1217. struct sockaddr_in6 sockaddr6;
  1218. socklen_t sockaddr6len = sizeof(sockaddr6);
  1219. if (getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0)
  1220. {
  1221. return nil;
  1222. }
  1223. result = [self addressHost6:&sockaddr6];
  1224. }
  1225. if (theFlags & kDidBind)
  1226. {
  1227. cachedLocalHost = [result copy];
  1228. }
  1229. return result;
  1230. }
  1231. - (UInt16)localPort:(CFSocketRef)theSocket
  1232. {
  1233. if (theSocket == NULL) return 0;
  1234. // Unfortunately we can't use CFSocketCopyAddress.
  1235. // The CFSocket library caches the address the first time you call CFSocketCopyAddress.
  1236. // So if this is called prior to binding/connecting/sending, it won't be updated again when necessary,
  1237. // and will continue to return the old value of the socket address.
  1238. UInt16 result = 0;
  1239. if (theSocket == theSocket4)
  1240. {
  1241. struct sockaddr_in sockaddr4;
  1242. socklen_t sockaddr4len = sizeof(sockaddr4);
  1243. if (getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0)
  1244. {
  1245. return 0;
  1246. }
  1247. result = ntohs(sockaddr4.sin_port);
  1248. }
  1249. else
  1250. {
  1251. struct sockaddr_in6 sockaddr6;
  1252. socklen_t sockaddr6len = sizeof(sockaddr6);
  1253. if (getsockname(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0)
  1254. {
  1255. return 0;
  1256. }
  1257. result = ntohs(sockaddr6.sin6_port);
  1258. }
  1259. if (theFlags & kDidBind)
  1260. {
  1261. cachedLocalPort = result;
  1262. }
  1263. return result;
  1264. }
  1265. - (NSString *)connectedHost:(CFSocketRef)theSocket
  1266. {
  1267. if (theSocket == NULL) return nil;
  1268. // Unfortunately we can't use CFSocketCopyPeerAddress.
  1269. // The CFSocket library caches the address the first time you call CFSocketCopyPeerAddress.
  1270. // So if this is called prior to binding/connecting/sending, it may not be updated again when necessary,
  1271. // and will continue to return the old value of the socket peer address.
  1272. NSString *result = nil;
  1273. if (theSocket == theSocket4)
  1274. {
  1275. struct sockaddr_in sockaddr4;
  1276. socklen_t sockaddr4len = sizeof(sockaddr4);
  1277. if (getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0)
  1278. {
  1279. return nil;
  1280. }
  1281. result = [self addressHost4:&sockaddr4];
  1282. }
  1283. else
  1284. {
  1285. struct sockaddr_in6 sockaddr6;
  1286. socklen_t sockaddr6len = sizeof(sockaddr6);
  1287. if (getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0)
  1288. {
  1289. return nil;
  1290. }
  1291. result = [self addressHost6:&sockaddr6];
  1292. }
  1293. if (theFlags & kDidConnect)
  1294. {
  1295. cachedConnectedHost = [result copy];
  1296. }
  1297. return result;
  1298. }
  1299. - (UInt16)connectedPort:(CFSocketRef)theSocket
  1300. {
  1301. if(theSocket == NULL) return 0;
  1302. // Unfortunately we can't use CFSocketCopyPeerAddress.
  1303. // The CFSocket library caches the address the first time you call CFSocketCopyPeerAddress.
  1304. // So if this is called prior to binding/connecting/sending, it may not be updated again when necessary,
  1305. // and will continue to return the old value of the socket peer address.
  1306. UInt16 result = 0;
  1307. if(theSocket == theSocket4)
  1308. {
  1309. struct sockaddr_in sockaddr4;
  1310. socklen_t sockaddr4len = sizeof(sockaddr4);
  1311. if(getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr4, &sockaddr4len) < 0)
  1312. {
  1313. return 0;
  1314. }
  1315. result = ntohs(sockaddr4.sin_port);
  1316. }
  1317. else
  1318. {
  1319. struct sockaddr_in6 sockaddr6;
  1320. socklen_t sockaddr6len = sizeof(sockaddr6);
  1321. if(getpeername(CFSocketGetNative(theSocket), (struct sockaddr *)&sockaddr6, &sockaddr6len) < 0)
  1322. {
  1323. return 0;
  1324. }
  1325. result = ntohs(sockaddr6.sin6_port);
  1326. }
  1327. if(theFlags & kDidConnect)
  1328. {
  1329. cachedConnectedPort = result;
  1330. }
  1331. return result;
  1332. }
  1333. - (BOOL)isConnected
  1334. {
  1335. return (((theFlags & kDidConnect) != 0) && ((theFlags & kDidClose) == 0));
  1336. }
  1337. - (BOOL)isConnectedToHost:(NSString *)host port:(UInt16)port
  1338. {
  1339. return [[self connectedHost] isEqualToString:host] && ([self connectedPort] == port);
  1340. }
  1341. - (BOOL)isClosed
  1342. {
  1343. return (theFlags & kDidClose) ? YES : NO;
  1344. }
  1345. - (BOOL)isIPv4
  1346. {
  1347. return (theSocket4 != NULL);
  1348. }
  1349. - (BOOL)isIPv6
  1350. {
  1351. return (theSocket6 != NULL);
  1352. }
  1353. - (unsigned int)maximumTransmissionUnit
  1354. {
  1355. CFSocketNativeHandle theNativeSocket;
  1356. if(theSocket4)
  1357. theNativeSocket = CFSocketGetNative(theSocket4);
  1358. else if(theSocket6)
  1359. theNativeSocket = CFSocketGetNative(theSocket6);
  1360. else
  1361. return 0;
  1362. if(theNativeSocket == 0)
  1363. {
  1364. return 0;
  1365. }
  1366. struct ifreq ifr;
  1367. bzero(&ifr, sizeof(ifr));
  1368. if(if_indextoname(theNativeSocket, ifr.ifr_name) == NULL)
  1369. {
  1370. return 0;
  1371. }
  1372. if(ioctl(theNativeSocket, SIOCGIFMTU, &ifr) >= 0)
  1373. {
  1374. return ifr.ifr_mtu;
  1375. }
  1376. return 0;
  1377. }
  1378. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1379. #pragma mark Sending
  1380. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1381. - (BOOL)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
  1382. {
  1383. if([data length] == 0) return NO;
  1384. if(theFlags & kForbidSendReceive) return NO;
  1385. if(theFlags & kDidClose) return NO;
  1386. // This method is only for connected sockets
  1387. if(![self isConnected]) return NO;
  1388. AsyncSendPacket *packet = [[AsyncSendPacket alloc] initWithData:data address:nil timeout:timeout tag:tag];
  1389. [theSendQueue addObject:packet];
  1390. [self scheduleDequeueSend];
  1391. return YES;
  1392. }
  1393. - (BOOL)sendData:(NSData *)data
  1394. toHost:(NSString *)host
  1395. port:(UInt16)port
  1396. withTimeout:(NSTimeInterval)timeout
  1397. tag:(long)tag
  1398. {
  1399. if([data length] == 0) return NO;
  1400. if(theFlags & kForbidSendReceive) return NO;
  1401. if(theFlags & kDidClose) return NO;
  1402. // This method is only for non-connected sockets
  1403. if([self isConnected]) return NO;
  1404. NSData *address4 = nil, *address6 = nil;
  1405. [self convertForSendHost:host port:port intoAddress4:&address4 address6:&address6];
  1406. AsyncSendPacket *packet = nil;
  1407. if(address4 && theSocket4)
  1408. packet = [[AsyncSendPacket alloc] initWithData:data address:address4 timeout:timeout tag:tag];
  1409. else if(address6 && theSocket6)
  1410. packet = [[AsyncSendPacket alloc] initWithData:data address:address6 timeout:timeout tag:tag];
  1411. else
  1412. return NO;
  1413. [theSendQueue addObject:packet];
  1414. [self scheduleDequeueSend];
  1415. return YES;
  1416. }
  1417. - (BOOL)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag
  1418. {
  1419. if([data length] == 0) return NO;
  1420. if(theFlags & kForbidSendReceive) return NO;
  1421. if(theFlags & kDidClose) return NO;
  1422. // This method is only for non-connected sockets
  1423. if([self isConnected]) return NO;
  1424. if([remoteAddr length] == sizeof(struct sockaddr_in) && !theSocket4)
  1425. return NO;
  1426. if([remoteAddr length] == sizeof(struct sockaddr_in6) && !theSocket6)
  1427. return NO;
  1428. AsyncSendPacket *packet = [[AsyncSendPacket alloc] initWithData:data address:remoteAddr timeout:timeout tag:tag];
  1429. [theSendQueue addObject:packet];
  1430. [self scheduleDequeueSend];
  1431. return YES;
  1432. }
  1433. - (BOOL)canAcceptBytes:(CFSocketRef)sockRef
  1434. {
  1435. if(so