PageRenderTime 40ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/ep-020/test_longpress_pinch/build/iphone/Classes/AsyncUdpSocket.m

https://gitlab.com/intelij/Forging-Titanium
Objective C | 2343 lines | 1699 code | 384 blank | 260 comment | 400 complexity | 5b4a27cd1c881c25c92bf6f595fff53b MD5 | raw file
Possible License(s): Apache-2.0

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

Large files files are truncated, but you can click here to view the full file