From ed1c8a77a1197d660065d60fca39b8f2700c512a Mon Sep 17 00:00:00 2001 From: zi6xuan Date: Mon, 1 Apr 2019 11:51:08 +0900 Subject: [PATCH 01/11] Update react-native-udp.podspec (#83) * Update react-native-udp.podspec I found the bug on the iOS build 'React/RCTAssert.h' file not found --- react-native-udp.podspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/react-native-udp.podspec b/react-native-udp.podspec index 85004bdf..e396fe06 100644 --- a/react-native-udp.podspec +++ b/react-native-udp.podspec @@ -12,5 +12,6 @@ Pod::Spec.new do |s| s.platform = :ios, "7.0" s.source = { :git => package_json["repository"]["url"].gsub(/(http.*)/).first, :tag => "v#{s.version}" } s.source_files = 'ios/**/*.{h,m}' - + s.dependency 'React' + end From 8c0b945a6f09a126ae82019b3579d5ce615b8ad5 Mon Sep 17 00:00:00 2001 From: Mark Vayngrib Date: Sun, 31 Mar 2019 22:52:14 -0400 Subject: [PATCH 02/11] 2.5.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 57999eab..8b49b8e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-udp", - "version": "2.5.1", + "version": "2.5.2", "description": "node's dgram API for react-native", "main": "UdpSockets.js", "scripts": { From 2f4aaddfdc557e47c525c5afe5672ded5df29f82 Mon Sep 17 00:00:00 2001 From: Evgeny Gazdovsky Date: Wed, 10 Apr 2019 22:31:54 +0300 Subject: [PATCH 03/11] fix max MTU (#84) --- android/src/main/java/com/tradle/react/UdpReceiverTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/tradle/react/UdpReceiverTask.java b/android/src/main/java/com/tradle/react/UdpReceiverTask.java index 1eb20917..ff45ce68 100644 --- a/android/src/main/java/com/tradle/react/UdpReceiverTask.java +++ b/android/src/main/java/com/tradle/react/UdpReceiverTask.java @@ -23,7 +23,7 @@ */ public class UdpReceiverTask extends AsyncTask, Void, Void> { private static final String TAG = "UdpReceiverTask"; - private static final int MAX_UDP_DATAGRAM_LEN = 1024; + private static final int MAX_UDP_DATAGRAM_LEN = 0xffff; /** * An infinite loop to block and read data from the socket. From 9d55560d28a3a4603bca79329185aa4ce86a7e99 Mon Sep 17 00:00:00 2001 From: Mark Vayngrib Date: Wed, 10 Apr 2019 15:32:16 -0400 Subject: [PATCH 04/11] 2.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8b49b8e5..af9c2db3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-udp", - "version": "2.5.2", + "version": "2.6.0", "description": "node's dgram API for react-native", "main": "UdpSockets.js", "scripts": { From af1508f887a66bbfc1060f175491f9d454c36d44 Mon Sep 17 00:00:00 2001 From: Mark Vayngrib Date: Tue, 28 May 2019 22:43:01 -0400 Subject: [PATCH 05/11] add .git to .npmignore --- .npmignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.npmignore b/.npmignore index 66f90dd6..b85787b3 100644 --- a/.npmignore +++ b/.npmignore @@ -39,4 +39,5 @@ node_modules .DS_Store # examples is not required in npm package -examples/ \ No newline at end of file +examples/ +.git/ From e333144294db00239db8fe3d6f5434882d7f0126 Mon Sep 17 00:00:00 2001 From: Mark Vayngrib Date: Tue, 28 May 2019 22:43:08 -0400 Subject: [PATCH 06/11] 2.6.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index af9c2db3..60e58608 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-udp", - "version": "2.6.0", + "version": "2.6.1", "description": "node's dgram API for react-native", "main": "UdpSockets.js", "scripts": { From b34d76482962da83994616cd8ce562e03c750482 Mon Sep 17 00:00:00 2001 From: Chris Mark Date: Thu, 1 Aug 2019 19:08:25 -0400 Subject: [PATCH 07/11] Fix breaking change w/ Gradle dependencies (#88) This fixes a breaking API change with the Android Gradle plugin, which was deprecated as of version 3.0.0 and later made obsolete. When adding Gradle dependencies, the `compile` command has been removed and replaced with `implementation`/`api`. More information: https://docs.gradle.org/5.4.1/userguide/java_library_plugin.html#sec:java_library_separation https://developer.android.com/studio/build/dependencies?utm_source=android-studio#dependency_configurations --- android/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 1a8d75f9..8a2effa9 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -32,7 +32,7 @@ android { dependencies { def supportLibVersion = project.hasProperty('supportLibVersion') ? project.supportLibVersion : DEFAULT_SUPPORT_LIB_VERSION - compile fileTree(dir: 'libs', include: ['*.jar']) - compile "com.android.support:appcompat-v7:$supportLibVersion" - compile 'com.facebook.react:react-native:+' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "com.android.support:appcompat-v7:$supportLibVersion" + implementation 'com.facebook.react:react-native:+' } From e0ba725588d934236254e11da2e796e19337e67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joe=20Noe=CC=88l?= Date: Thu, 4 Aug 2016 16:33:11 +0100 Subject: [PATCH 08/11] Added handling of GCDAsyncUdpSocket's socketDidClose callback in order to bubble notification that the socket has been closed --- UdpSocket.js | 11 +++++++++++ ios/UdpSocketClient.h | 1 + ios/UdpSocketClient.m | 6 ++++++ ios/UdpSockets.m | 10 ++++++++++ 4 files changed, 28 insertions(+) diff --git a/UdpSocket.js b/UdpSocket.js index f21116a8..ecfe7e9f 100644 --- a/UdpSocket.js +++ b/UdpSocket.js @@ -53,6 +53,9 @@ function UdpSocket(options, onmessage) { this._subscription = DeviceEventEmitter.addListener( 'udp-' + this._id + '-data', this._onReceive.bind(this) ); + this._closeSubscription = DeviceEventEmitter.addListener( + 'udp-' + this._id + '-close', this._onClose.bind(this) + ); // ensure compatibility with node's EventEmitter if (!this.on) this.on = this.addListener.bind(this) @@ -124,6 +127,7 @@ UdpSocket.prototype.close = function (callback=noop) { this._destroying = true this._debug('closing') this._subscription.remove(); + this._closeSubscription.remove(); Sockets.close(this._id, err => { if (err) return this.emit('error', err) @@ -150,6 +154,13 @@ UdpSocket.prototype._onReceive = function(info) { this.emit('message', buf, rinfo) } +UdpSocket.prototype._onClose = function(info) { + if (info.error) { + this.emit('error', normalizeError(info.error)); + } + this.close(); +} + /** * socket.send(buf, offset, length, port, address, [callback]) * diff --git a/ios/UdpSocketClient.h b/ios/UdpSocketClient.h index 7c43af67..da5ef69d 100644 --- a/ios/UdpSocketClient.h +++ b/ios/UdpSocketClient.h @@ -30,6 +30,7 @@ typedef enum RCTUDPError RCTUDPError; @protocol SocketClientDelegate - (void)onData:(UdpSocketClient*) client data:(NSData *)data host:(NSString*) host port:(uint16_t) port; +- (void)onClose:(UdpSocketClient*) client error:(NSString*) error; @end diff --git a/ios/UdpSocketClient.m b/ios/UdpSocketClient.m index 82e3288e..df86dd50 100644 --- a/ios/UdpSocketClient.m +++ b/ios/UdpSocketClient.m @@ -201,6 +201,12 @@ - (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data [_clientDelegate onData:self data:data host:host port:port]; } +- (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError *)error +{ + if (!_clientDelegate) return; + [_clientDelegate onClose:self error:error.localizedDescription]; +} + - (NSError *)badParamError:(NSString *)errMsg { NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; diff --git a/ios/UdpSockets.m b/ios/UdpSockets.m index a007f212..ab2f7c97 100644 --- a/ios/UdpSockets.m +++ b/ios/UdpSockets.m @@ -138,6 +138,16 @@ - (void) onData:(UdpSocketClient*) client data:(NSData *)data host:(NSString *)h ]; } +-(void) onClose:(UdpSocketClient *)client error:(NSString *)error +{ + NSNumber *clientID = [[_clients allKeysForObject:client] objectAtIndex:0]; + [self.bridge.eventDispatcher sendDeviceEventWithName:[NSString stringWithFormat:@"udp-%@-close", clientID] + body:@{ + @"error": error + } + ]; +} + -(UdpSocketClient*)findClient:(nonnull NSNumber*)cId callback:(RCTResponseSenderBlock)callback { UdpSocketClient *client = _clients[cId]; From dd8b47b43d636548552443e5446ff24d7f1e3dda Mon Sep 17 00:00:00 2001 From: Dominique Date: Fri, 9 Jun 2017 13:18:56 +0200 Subject: [PATCH 09/11] feat(socket-lib): udpate GCDAsyncUdpSocket --- ios/CocoaAsyncSocket/GCDAsyncUdpSocket.h | 278 +-- ios/CocoaAsyncSocket/GCDAsyncUdpSocket.m | 1966 +++++++++++----------- 2 files changed, 1118 insertions(+), 1126 deletions(-) mode change 100644 => 100755 ios/CocoaAsyncSocket/GCDAsyncUdpSocket.h mode change 100644 => 100755 ios/CocoaAsyncSocket/GCDAsyncUdpSocket.m diff --git a/ios/CocoaAsyncSocket/GCDAsyncUdpSocket.h b/ios/CocoaAsyncSocket/GCDAsyncUdpSocket.h old mode 100644 new mode 100755 index b0b244d1..66759346 --- a/ios/CocoaAsyncSocket/GCDAsyncUdpSocket.h +++ b/ios/CocoaAsyncSocket/GCDAsyncUdpSocket.h @@ -1,10 +1,10 @@ -// +// // GCDAsyncUdpSocket -// +// // This class is in the public domain. // Originally created by Robbie Hanson of Deusty LLC. // Updated and maintained by Deusty LLC and the Apple development community. -// +// // https://github.com/robbiehanson/CocoaAsyncSocket // @@ -42,7 +42,7 @@ typedef NS_ENUM(NSInteger, GCDAsyncUdpSocketError) { * By design, UDP is a connectionless protocol, and connecting is not needed. * However, you may optionally choose to connect to a particular host for reasons * outlined in the documentation for the various connect methods listed above. - * + * * This method is called if one of the connect methods are invoked, and the connection is successful. **/ - (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address; @@ -51,7 +51,7 @@ typedef NS_ENUM(NSInteger, GCDAsyncUdpSocketError) { * By design, UDP is a connectionless protocol, and connecting is not needed. * However, you may optionally choose to connect to a particular host for reasons * outlined in the documentation for the various connect methods listed above. - * + * * This method is called if one of the connect methods are invoked, and the connection fails. * This may happen, for example, if a domain name is given for the host and the domain name is unable to be resolved. **/ @@ -85,76 +85,76 @@ typedef NS_ENUM(NSInteger, GCDAsyncUdpSocketError) { /** * You may optionally set a receive filter for the socket. * A filter can provide several useful features: - * + * * 1. Many times udp packets need to be parsed. * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. * The end result is a parallel socket io, datagram parsing, and packet processing. - * + * * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. * The filter can prevent such packets from arriving at the delegate. * And because the filter can run in its own independent queue, this doesn't slow down the delegate. - * + * * - Since the udp protocol does not guarantee delivery, udp packets may be lost. * Many protocols built atop udp thus provide various resend/re-request algorithms. * This sometimes results in duplicate packets arriving. * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. - * + * * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. * Such packets need to be ignored. - * + * * 3. Sometimes traffic shapers are needed to simulate real world environments. * A filter allows you to write custom code to simulate such environments. * The ability to code this yourself is especially helpful when your simulated environment * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), * or the system tools to handle this aren't available (e.g. on a mobile device). - * + * * @param data - The packet that was received. * @param address - The address the data was received from. * See utilities section for methods to extract info from address. * @param context - Out parameter you may optionally set, which will then be passed to the delegate method. * For example, filter block can parse the data and then, * pass the parsed data to the delegate. - * + * * @returns - YES if the received packet should be passed onto the delegate. * NO if the received packet should be discarded, and not reported to the delegete. - * + * * Example: - * + * * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { - * + * * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; - * + * * *context = response; * return (response != nil); * }; * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; - * + * **/ typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *address, id __nullable * __nonnull context); /** * You may optionally set a send filter for the socket. * A filter can provide several interesting possibilities: - * + * * 1. Optional caching of resolved addresses for domain names. * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. - * + * * 2. Reusable modules of code for bandwidth monitoring. - * + * * 3. Sometimes traffic shapers are needed to simulate real world environments. * A filter allows you to write custom code to simulate such environments. * The ability to code this yourself is especially helpful when your simulated environment * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), * or the system tools to handle this aren't available (e.g. on a mobile device). - * + * * @param data - The packet that was received. * @param address - The address the data was received from. * See utilities section for methods to extract info from address. * @param tag - The tag that was passed in the send method. - * + * * @returns - YES if the packet should actually be sent over the socket. * NO if the packet should be silently dropped (not sent over the socket). - * + * * Regardless of the return value, the delegate will be informed that the packet was successfully sent. * **/ @@ -167,10 +167,10 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, * GCDAsyncUdpSocket uses the standard delegate paradigm, * but executes all delegate callbacks on a given delegate dispatch queue. * This allows for maximum concurrency, while at the same time providing easy thread safety. - * + * * You MUST set a delegate AND delegate dispatch queue before attempting to * use the socket, or you will get an error. - * + * * The socket queue is optional. * If you pass NULL, GCDAsyncSocket will automatically create its own socket queue. * If you choose to provide a socket queue, the socket queue must not be a concurrent queue, @@ -199,11 +199,11 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * By default, both IPv4 and IPv6 are enabled. - * + * * This means GCDAsyncUdpSocket automatically supports both protocols, * and can send to IPv4 or IPv6 addresses, * as well as receive over IPv4 and IPv6. - * + * * For operations that require DNS resolution, GCDAsyncUdpSocket supports both IPv4 and IPv6. * If a DNS lookup returns only IPv4 results, GCDAsyncUdpSocket will automatically use IPv4. * If a DNS lookup returns only IPv6 results, GCDAsyncUdpSocket will automatically use IPv6. @@ -211,7 +211,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, * If IPv4 is preferred, then IPv4 is used. * If IPv6 is preferred, then IPv6 is used. * If neutral, then the first IP version in the resolved array will be used. - * + * * Starting with Mac OS X 10.7 Lion and iOS 5, the default IP preference is neutral. * On prior systems the default IP preference is IPv4. **/ @@ -232,16 +232,16 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Gets/Sets the maximum size of the buffer that will be allocated for receive operations. * The default maximum size is 65535 bytes. - * + * * The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535. * The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295. - * + * * Since the OS/GCD notifies us of the size of each received UDP packet, * the actual allocated buffer size for each packet is exact. * And in practice the size of UDP packets is generally much smaller than the max. * Indeed most protocols will send and receive packets of only a few bytes, * or will set a limit on the size of packets to prevent fragmentation in the IP layer. - * + * * If you set the buffer size too small, the sockets API in the OS will silently discard * any extra data, and you will not be notified of the error. **/ @@ -254,8 +254,8 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Gets/Sets the maximum size of the buffer that will be allocated for send operations. * The default maximum size is 65535 bytes. - * - * Given that a typical link MTU is 1500 bytes, a large UDP datagram will have to be + * + * Given that a typical link MTU is 1500 bytes, a large UDP datagram will have to be * fragmented, and that’s both expensive and risky (if one fragment goes missing, the * entire datagram is lost). You are much better off sending a large number of smaller * UDP datagrams, preferably using a path MTU algorithm to avoid fragmentation. @@ -277,10 +277,10 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Returns the local address info for the socket. - * + * * The localAddress method returns a sockaddr structure wrapped in a NSData object. * The localHost method returns the human readable IP address as a string. - * + * * Note: Address info may not be available until after the socket has been binded, connected * or until after data has been sent. **/ @@ -298,10 +298,10 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Returns the remote address info for the socket. - * + * * The connectedAddress method returns a sockaddr structure wrapped in a NSData object. * The connectedHost method returns the human readable IP address as a string. - * + * * Note: Since UDP is connectionless by design, connected address info * will not be available unless the socket is explicitly connected to a remote host/port. * If the socket is not connected, these methods will return nil / 0. @@ -325,7 +325,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Returns whether or not this socket is IPv4. - * + * * By default this will be true, unless: * - IPv4 is disabled (via setIPv4Enabled:) * - The socket is explicitly bound to an IPv6 address @@ -335,12 +335,12 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Returns whether or not this socket is IPv6. - * + * * By default this will be true, unless: * - IPv6 is disabled (via setIPv6Enabled:) * - The socket is explicitly bound to an IPv4 address * _ The socket is connected to an IPv4 address - * + * * This method will also return false on platforms that do not support IPv6. * Note: The iPhone does not currently support IPv6. **/ @@ -353,14 +353,14 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, * Binding should be done for server sockets that receive data prior to sending it. * Client sockets can skip binding, * as the OS will automatically assign the socket an available port when it starts sending data. - * + * * You may optionally pass a port number of zero to immediately bind the socket, * yet still allow the OS to automatically assign an available port. - * + * * You cannot bind a socket after its been connected. * You can only bind a socket once. * You can still connect a socket (if desired) after binding. - * + * * On success, returns YES. * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. **/ @@ -371,18 +371,18 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, * Binding should be done for server sockets that receive data prior to sending it. * Client sockets can skip binding, * as the OS will automatically assign the socket an available port when it starts sending data. - * + * * You may optionally pass a port number of zero to immediately bind the socket, * yet still allow the OS to automatically assign an available port. - * + * * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). * You may also use the special strings "localhost" or "loopback" to specify that * the socket only accept packets from the local machine. - * + * * You cannot bind a socket after its been connected. * You can only bind a socket once. * You can still connect a socket (if desired) after binding. - * + * * On success, returns YES. * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. **/ @@ -390,19 +390,19 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Binds the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object. - * + * * If you have an existing struct sockaddr you can convert it to a NSData object like so: * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; - * + * * Binding should be done for server sockets that receive data prior to sending it. * Client sockets can skip binding, * as the OS will automatically assign the socket an available port when it starts sending data. - * + * * You cannot bind a socket after its been connected. * You can only bind a socket once. * You can still connect a socket (if desired) after binding. - * + * * On success, returns YES. * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass NULL for errPtr. **/ @@ -413,20 +413,20 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Connects the UDP socket to the given host and port. * By design, UDP is a connectionless protocol, and connecting is not needed. - * + * * Choosing to connect to a specific host/port has the following effect: * - You will only be able to send data to the connected host/port. * - You will only be able to receive data from the connected host/port. * - You will receive ICMP messages that come from the connected host/port, such as "connection refused". - * + * * The actual process of connecting a UDP socket does not result in any communication on the socket. * It simply changes the internal state of the socket. - * + * * You cannot bind a socket after it has been connected. * You can only connect a socket once. - * + * * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). - * + * * This method is asynchronous as it requires a DNS lookup to resolve the given host name. * If an obvious error is detected, this method immediately returns NO and sets errPtr. * If you don't care about the error, you can pass nil for errPtr. @@ -437,27 +437,27 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Connects the UDP socket to the given address, specified as a sockaddr structure wrapped in a NSData object. - * + * * If you have an existing struct sockaddr you can convert it to a NSData object like so: * struct sockaddr sa -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len]; * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len]; - * + * * By design, UDP is a connectionless protocol, and connecting is not needed. - * + * * Choosing to connect to a specific address has the following effect: * - You will only be able to send data to the connected address. * - You will only be able to receive data from the connected address. * - You will receive ICMP messages that come from the connected address, such as "connection refused". - * + * * Connecting a UDP socket does not result in any communication on the socket. * It simply changes the internal state of the socket. - * + * * You cannot bind a socket after its been connected. * You can only connect a socket once. - * + * * On success, returns YES. * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. - * + * * Note: Unlike the connectToHost:onPort:error: method, this method does not require a DNS lookup. * Thus when this method returns, the connection has either failed or fully completed. * In other words, this method is synchronous, unlike the asynchronous connectToHost::: method. @@ -471,7 +471,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Join multicast group. * Group should be an IP address (eg @"225.228.0.1"). - * + * * On success, returns YES. * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. **/ @@ -481,7 +481,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, * Join multicast group. * Group should be an IP address (eg @"225.228.0.1"). * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35"). - * + * * On success, returns YES. * Otherwise returns NO, and sets errPtr. If you don't care about the error, you can pass nil for errPtr. **/ @@ -494,7 +494,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * By default, only one socket can be bound to a given IP address + port at a time. - * To enable multiple processes to simultaneously bind to the same address+port, + * To enable multiple processes to simultaneously bind to the same address+port, * you need to enable this functionality in the socket. All processes that wish to * use the address+port simultaneously must all enable reuse port on the socket * bound to that port. @@ -506,7 +506,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * By default, the underlying socket in the OS will not allow you to send broadcast messages. * In order to send broadcast messages, you need to enable this functionality in the socket. - * + * * A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is * delivered to every host on the network. * The reason this is generally disabled by default (by the OS) is to prevent @@ -518,30 +518,30 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Asynchronously sends the given data, with the given timeout and tag. - * + * * This method may only be used with a connected socket. * Recall that connecting is optional for a UDP socket. * For connected sockets, data can only be sent to the connected address. * For non-connected sockets, the remote destination is specified for each packet. * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. - * + * * @param data * The data to send. * If data is nil or zero-length, this method does nothing. * If passing NSMutableData, please read the thread-safety notice below. - * + * * @param timeout * The timeout for the send opeartion. * If the timeout value is negative, the send operation will not use a timeout. - * + * * @param tag * The tag is for your convenience. * It is not sent or received over the socket in any manner what-so-ever. * It is reported back as a parameter in the udpSocket:didSendDataWithTag: * or udpSocket:didNotSendDataWithTag:dueToError: methods. * You can use it as an array index, state id, type constant, etc. - * - * + * + * * Thread-Safety Note: * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method @@ -559,38 +559,38 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Asynchronously sends the given data, with the given timeout and tag, to the given host and port. - * + * * This method cannot be used with a connected socket. * Recall that connecting is optional for a UDP socket. * For connected sockets, data can only be sent to the connected address. * For non-connected sockets, the remote destination is specified for each packet. * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. - * + * * @param data * The data to send. * If data is nil or zero-length, this method does nothing. * If passing NSMutableData, please read the thread-safety notice below. - * + * * @param host * The destination to send the udp packet to. * May be specified as a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). * You may also use the convenience strings of "loopback" or "localhost". - * + * * @param port * The port of the host to send to. - * + * * @param timeout * The timeout for the send opeartion. * If the timeout value is negative, the send operation will not use a timeout. - * + * * @param tag * The tag is for your convenience. * It is not sent or received over the socket in any manner what-so-ever. * It is reported back as a parameter in the udpSocket:didSendDataWithTag: * or udpSocket:didNotSendDataWithTag:dueToError: methods. * You can use it as an array index, state id, type constant, etc. - * - * + * + * * Thread-Safety Note: * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method @@ -612,33 +612,33 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Asynchronously sends the given data, with the given timeout and tag, to the given address. - * + * * This method cannot be used with a connected socket. * Recall that connecting is optional for a UDP socket. * For connected sockets, data can only be sent to the connected address. * For non-connected sockets, the remote destination is specified for each packet. * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. - * + * * @param data * The data to send. * If data is nil or zero-length, this method does nothing. * If passing NSMutableData, please read the thread-safety notice below. - * + * * @param remoteAddr * The address to send the data to (specified as a sockaddr structure wrapped in a NSData object). - * + * * @param timeout * The timeout for the send opeartion. * If the timeout value is negative, the send operation will not use a timeout. - * + * * @param tag * The tag is for your convenience. * It is not sent or received over the socket in any manner what-so-ever. * It is reported back as a parameter in the udpSocket:didSendDataWithTag: * or udpSocket:didNotSendDataWithTag:dueToError: methods. * You can use it as an array index, state id, type constant, etc. - * - * + * + * * Thread-Safety Note: * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method @@ -657,21 +657,21 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * You may optionally set a send filter for the socket. * A filter can provide several interesting possibilities: - * + * * 1. Optional caching of resolved addresses for domain names. * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. - * + * * 2. Reusable modules of code for bandwidth monitoring. - * + * * 3. Sometimes traffic shapers are needed to simulate real world environments. * A filter allows you to write custom code to simulate such environments. * The ability to code this yourself is especially helpful when your simulated environment * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), * or the system tools to handle this aren't available (e.g. on a mobile device). - * + * * For more information about GCDAsyncUdpSocketSendFilterBlock, see the documentation for its typedef. * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. - * + * * Note: This method invokes setSendFilter:withQueue:isAsynchronous: (documented below), * passing YES for the isAsynchronous parameter. **/ @@ -680,11 +680,11 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * The receive filter can be run via dispatch_async or dispatch_sync. * Most typical situations call for asynchronous operation. - * + * * However, there are a few situations in which synchronous operation is preferred. * Such is the case when the filter is extremely minimal and fast. * This is because dispatch_sync is faster than dispatch_async. - * + * * If you choose synchronous operation, be aware of possible deadlock conditions. * Since the socket queue is executing your block via dispatch_sync, * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. @@ -698,23 +698,23 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * There are two modes of operation for receiving packets: one-at-a-time & continuous. - * + * * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, * where your state machine may not always be ready to process incoming packets. - * + * * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. * Receiving packets continuously is better suited to real-time streaming applications. - * + * * You may switch back and forth between one-at-a-time mode and continuous mode. * If the socket is currently in continuous mode, calling this method will switch it to one-at-a-time mode. - * + * * When a packet is received (and not filtered by the optional receive filter), * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. - * + * * If the socket is able to begin receiving packets, this method returns YES. * Otherwise it returns NO, and sets the errPtr with appropriate error information. - * + * * An example error: * You created a udp socket to act as a server, and immediately called receive. * You forgot to first bind the socket to a port number, and received a error with a message like: @@ -724,23 +724,23 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * There are two modes of operation for receiving packets: one-at-a-time & continuous. - * + * * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, * where your state machine may not always be ready to process incoming packets. - * + * * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. * Receiving packets continuously is better suited to real-time streaming applications. - * + * * You may switch back and forth between one-at-a-time mode and continuous mode. * If the socket is currently in one-at-a-time mode, calling this method will switch it to continuous mode. - * + * * For every received packet (not filtered by the optional receive filter), * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. - * + * * If the socket is able to begin receiving packets, this method returns YES. * Otherwise it returns NO, and sets the errPtr with appropriate error information. - * + * * An example error: * You created a udp socket to act as a server, and immediately called receive. * You forgot to first bind the socket to a port number, and received a error with a message like: @@ -751,7 +751,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * If the socket is currently receiving (beginReceiving has been called), this method pauses the receiving. * That is, it won't read any more packets from the underlying OS socket until beginReceiving is called again. - * + * * Important Note: * GCDAsyncUdpSocket may be running in parallel with your code. * That is, your delegate is likely running on a separate thread/dispatch_queue. @@ -765,45 +765,45 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * You may optionally set a receive filter for the socket. * This receive filter may be set to run in its own queue (independent of delegate queue). - * + * * A filter can provide several useful features. - * + * * 1. Many times udp packets need to be parsed. * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. * The end result is a parallel socket io, datagram parsing, and packet processing. - * + * * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. * The filter can prevent such packets from arriving at the delegate. * And because the filter can run in its own independent queue, this doesn't slow down the delegate. - * + * * - Since the udp protocol does not guarantee delivery, udp packets may be lost. * Many protocols built atop udp thus provide various resend/re-request algorithms. * This sometimes results in duplicate packets arriving. * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. - * + * * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. * Such packets need to be ignored. - * + * * 3. Sometimes traffic shapers are needed to simulate real world environments. * A filter allows you to write custom code to simulate such environments. * The ability to code this yourself is especially helpful when your simulated environment * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), * or the system tools to handle this aren't available (e.g. on a mobile device). - * + * * Example: - * + * * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { - * + * * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; - * + * * *context = response; * return (response != nil); * }; * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; - * + * * For more information about GCDAsyncUdpSocketReceiveFilterBlock, see the documentation for its typedef. * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. - * + * * Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below), * passing YES for the isAsynchronous parameter. **/ @@ -812,11 +812,11 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * The receive filter can be run via dispatch_async or dispatch_sync. * Most typical situations call for asynchronous operation. - * + * * However, there are a few situations in which synchronous operation is preferred. * Such is the case when the filter is extremely minimal and fast. * This is because dispatch_sync is faster than dispatch_async. - * + * * If you choose synchronous operation, be aware of possible deadlock conditions. * Since the socket queue is executing your block via dispatch_sync, * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. @@ -831,7 +831,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Immediately closes the underlying socket. * Any pending send operations are discarded. - * + * * The GCDAsyncUdpSocket instance may optionally be used again. * (it will setup/configure/use another unnderlying BSD socket). **/ @@ -839,7 +839,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * Closes the underlying socket after all pending send operations have been sent. - * + * * The GCDAsyncUdpSocket instance may optionally be used again. * (it will setup/configure/use another unnderlying BSD socket). **/ @@ -923,22 +923,22 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * It's not thread-safe to access certain variables from outside the socket's internal queue. - * + * * For example, the socket file descriptor. * File descriptors are simply integers which reference an index in the per-process file table. * However, when one requests a new file descriptor (by opening a file or socket), * the file descriptor returned is guaranteed to be the lowest numbered unused descriptor. * So if we're not careful, the following could be possible: - * + * * - Thread A invokes a method which returns the socket's file descriptor. * - The socket is closed via the socket's internal queue on thread B. * - Thread C opens a file, and subsequently receives the file descriptor that was previously the socket's FD. * - Thread A is now accessing/altering the file instead of the socket. - * + * * In addition to this, other variables are not actually objects, * and thus cannot be retained/released or even autoreleased. * An example is the sslContext, of type SSLContextRef, which is actually a malloc'd struct. - * + * * Although there are internal variables that make it difficult to maintain thread-safety, * it is important to provide access to these variables * to ensure this class can be used in a wide array of environments. @@ -946,7 +946,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, * The methods below can be invoked from within the block to access * those generally thread-unsafe internal variables in a thread-safe manner. * The given block will be invoked synchronously on the socket's internal queue. - * + * * If you save references to any protected variables and use them outside the block, you do so at your own peril. **/ - (void)performBlock:(dispatch_block_t)block; @@ -954,7 +954,7 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * These methods are only available from within the context of a performBlock: invocation. * See the documentation for the performBlock: method above. - * + * * Provides access to the socket's file descriptor(s). * If the socket isn't connected, or explicity bound to a particular interface, * it might actually have multiple internal socket file descriptors - one for IPv4 and one for IPv6. @@ -968,9 +968,9 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * These methods are only available from within the context of a performBlock: invocation. * See the documentation for the performBlock: method above. - * + * * Returns (creating if necessary) a CFReadStream/CFWriteStream for the internal socket. - * + * * Generally GCDAsyncUdpSocket doesn't use CFStream. (It uses the faster GCD API's.) * However, if you need one for any reason, * these methods are a convenient way to get access to a safe instance of one. @@ -981,22 +981,22 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, /** * This method is only available from within the context of a performBlock: invocation. * See the documentation for the performBlock: method above. - * + * * Configures the socket to allow it to operate when the iOS application has been backgrounded. * In other words, this method creates a read & write stream, and invokes: - * + * * CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); * CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); - * + * * Returns YES if successful, NO otherwise. - * + * * Example usage: - * + * * [asyncUdpSocket performBlock:^{ * [asyncUdpSocket enableBackgroundingOnSocket]; * }]; - * - * + * + * * NOTE : Apple doesn't currently support backgrounding UDP sockets. (Only TCP for now). **/ //- (BOOL)enableBackgroundingOnSockets; diff --git a/ios/CocoaAsyncSocket/GCDAsyncUdpSocket.m b/ios/CocoaAsyncSocket/GCDAsyncUdpSocket.m old mode 100644 new mode 100755 index 58a5fdd8..cc8452be --- a/ios/CocoaAsyncSocket/GCDAsyncUdpSocket.m +++ b/ios/CocoaAsyncSocket/GCDAsyncUdpSocket.m @@ -1,10 +1,10 @@ -// +// // GCDAsyncUdpSocket -// +// // This class is in the public domain. // Originally created by Robbie Hanson of Deusty LLC. // Updated and maintained by Deusty LLC and the Apple development community. -// +// // https://github.com/robbiehanson/CocoaAsyncSocket // @@ -35,7 +35,7 @@ // Logging uses the CocoaLumberjack framework (which is also GCD based). // http://code.google.com/p/cocoalumberjack/ -// +// // It allows us to do a lot of logging without significantly slowing down the code. #import "DDLog.h" @@ -97,7 +97,7 @@ /** * Just to type less code. **/ -#define AutoreleasedBlock(block) ^{ @autoreleasepool { block(); }} +#define AutoreleasedBlock(block) ^{ @autoreleasepool { block(); }} @class GCDAsyncUdpSendPacket; @@ -152,57 +152,56 @@ @interface GCDAsyncUdpSocket () __unsafe_unretained id delegate; #endif dispatch_queue_t delegateQueue; - + GCDAsyncUdpSocketReceiveFilterBlock receiveFilterBlock; dispatch_queue_t receiveFilterQueue; BOOL receiveFilterAsync; - + GCDAsyncUdpSocketSendFilterBlock sendFilterBlock; dispatch_queue_t sendFilterQueue; BOOL sendFilterAsync; - + uint32_t flags; uint16_t config; - + uint16_t max4ReceiveSize; uint32_t max6ReceiveSize; - uint16_t maxSendSize; - + int socket4FD; int socket6FD; - + dispatch_queue_t socketQueue; - + dispatch_source_t send4Source; dispatch_source_t send6Source; dispatch_source_t receive4Source; dispatch_source_t receive6Source; dispatch_source_t sendTimer; - + GCDAsyncUdpSendPacket *currentSend; NSMutableArray *sendQueue; - + unsigned long socket4FDBytesAvailable; unsigned long socket6FDBytesAvailable; - + uint32_t pendingFilterOperations; - + NSData *cachedLocalAddress4; NSString *cachedLocalHost4; uint16_t cachedLocalPort4; - + NSData *cachedLocalAddress6; NSString *cachedLocalHost6; uint16_t cachedLocalPort6; - + NSData *cachedConnectedAddress; NSString *cachedConnectedHost; uint16_t cachedConnectedPort; int cachedConnectedFamily; - void *IsOnSocketQueueOrTargetQueueKey; - + void *IsOnSocketQueueOrTargetQueueKey; + #if TARGET_OS_IPHONE CFStreamClientContext streamContext; CFReadStreamRef readStream4; @@ -210,7 +209,7 @@ @interface GCDAsyncUdpSocket () CFWriteStreamRef writeStream4; CFWriteStreamRef writeStream6; #endif - + id userData; } @@ -270,13 +269,13 @@ @interface GCDAsyncUdpSendPacket : NSObject { NSData *buffer; NSTimeInterval timeout; long tag; - + BOOL resolveInProgress; BOOL filterInProgress; - + NSArray *resolvedAddresses; NSError *resolveError; - + NSData *address; int addressFamily; } @@ -294,7 +293,7 @@ - (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i buffer = d; timeout = t; tag = i; - + resolveInProgress = NO; } return self; @@ -310,9 +309,9 @@ - (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i @interface GCDAsyncUdpSpecialPacket : NSObject { @public // uint8_t type; - + BOOL resolveInProgress; - + NSArray *addresses; NSError *error; } @@ -341,32 +340,32 @@ @implementation GCDAsyncUdpSocket - (id)init { LogTrace(); - + return [self initWithDelegate:nil delegateQueue:NULL socketQueue:NULL]; } - (id)initWithSocketQueue:(dispatch_queue_t)sq { LogTrace(); - + return [self initWithDelegate:nil delegateQueue:NULL socketQueue:sq]; } - (id)initWithDelegate:(id )aDelegate delegateQueue:(dispatch_queue_t)dq { LogTrace(); - + return [self initWithDelegate:aDelegate delegateQueue:dq socketQueue:NULL]; } - (id)initWithDelegate:(id )aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq { LogTrace(); - + if ((self = [super init])) { delegate = aDelegate; - + if (dq) { delegateQueue = dq; @@ -374,15 +373,14 @@ - (id)initWithDelegate:(id )aDelegate delegateQueue:( dispatch_retain(delegateQueue); #endif } - + max4ReceiveSize = 65535; max6ReceiveSize = 65535; - + maxSendSize = 65535; - socket4FD = SOCKET_NULL; socket6FD = SOCKET_NULL; - + if (sq) { NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), @@ -391,7 +389,7 @@ - (id)initWithDelegate:(id )aDelegate delegateQueue:( @"The given socketQueue parameter must not be a concurrent queue."); NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), @"The given socketQueue parameter must not be a concurrent queue."); - + socketQueue = sq; #if !OS_OBJECT_USE_OBJC dispatch_retain(socketQueue); @@ -423,10 +421,10 @@ - (id)initWithDelegate:(id )aDelegate delegateQueue:( void *nonNullUnusedPointer = (__bridge void *)self; dispatch_queue_set_specific(socketQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL); - + currentSend = nil; sendQueue = [[NSMutableArray alloc] initWithCapacity:5]; - + #if TARGET_OS_IPHONE [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) @@ -440,11 +438,11 @@ - (id)initWithDelegate:(id )aDelegate delegateQueue:( - (void)dealloc { LogInfo(@"%@ - %@ (start)", THIS_METHOD, self); - + #if TARGET_OS_IPHONE [[NSNotificationCenter defaultCenter] removeObserver:self]; #endif - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { [self closeWithError:nil]; @@ -455,18 +453,18 @@ - (void)dealloc [self closeWithError:nil]; }); } - + delegate = nil; #if !OS_OBJECT_USE_OBJC if (delegateQueue) dispatch_release(delegateQueue); #endif delegateQueue = NULL; - + #if !OS_OBJECT_USE_OBJC if (socketQueue) dispatch_release(socketQueue); #endif socketQueue = NULL; - + LogInfo(@"%@ - %@ (finish)", THIS_METHOD, self); } @@ -483,11 +481,11 @@ - (id)delegate else { __block id result = nil; - + dispatch_sync(socketQueue, ^{ result = delegate; }); - + return result; } } @@ -497,7 +495,7 @@ - (void)setDelegate:(id )newDelegate synchronously:(B dispatch_block_t block = ^{ delegate = newDelegate; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { block(); } @@ -528,11 +526,11 @@ - (dispatch_queue_t)delegateQueue else { __block dispatch_queue_t result = NULL; - + dispatch_sync(socketQueue, ^{ result = delegateQueue; }); - + return result; } } @@ -540,15 +538,15 @@ - (dispatch_queue_t)delegateQueue - (void)setDelegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously { dispatch_block_t block = ^{ - + #if !OS_OBJECT_USE_OBJC if (delegateQueue) dispatch_release(delegateQueue); if (newDelegateQueue) dispatch_retain(newDelegateQueue); #endif - + delegateQueue = newDelegateQueue; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { block(); } @@ -581,12 +579,12 @@ - (void)getDelegate:(id *)delegatePtr delegateQueue: { __block id dPtr = NULL; __block dispatch_queue_t dqPtr = NULL; - + dispatch_sync(socketQueue, ^{ dPtr = delegate; dqPtr = delegateQueue; }); - + if (delegatePtr) *delegatePtr = dPtr; if (delegateQueuePtr) *delegateQueuePtr = dqPtr; } @@ -595,17 +593,17 @@ - (void)getDelegate:(id *)delegatePtr delegateQueue: - (void)setDelegate:(id )newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL)synchronously { dispatch_block_t block = ^{ - + delegate = newDelegate; - + #if !OS_OBJECT_USE_OBJC if (delegateQueue) dispatch_release(delegateQueue); if (newDelegateQueue) dispatch_retain(newDelegateQueue); #endif - + delegateQueue = newDelegateQueue; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { block(); } @@ -630,36 +628,36 @@ - (void)synchronouslySetDelegate:(id )newDelegate del - (BOOL)isIPv4Enabled { // Note: YES means kIPv4Disabled is OFF - + __block BOOL result = NO; - + dispatch_block_t block = ^{ - + result = ((config & kIPv4Disabled) == 0); }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } - (void)setIPv4Enabled:(BOOL)flag { // Note: YES means kIPv4Disabled is OFF - + dispatch_block_t block = ^{ - + LogVerbose(@"%@ %@", THIS_METHOD, (flag ? @"YES" : @"NO")); - + if (flag) config &= ~kIPv4Disabled; else config |= kIPv4Disabled; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -669,36 +667,36 @@ - (void)setIPv4Enabled:(BOOL)flag - (BOOL)isIPv6Enabled { // Note: YES means kIPv6Disabled is OFF - + __block BOOL result = NO; - + dispatch_block_t block = ^{ - + result = ((config & kIPv6Disabled) == 0); }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } - (void)setIPv6Enabled:(BOOL)flag { // Note: YES means kIPv6Disabled is OFF - + dispatch_block_t block = ^{ - + LogVerbose(@"%@ %@", THIS_METHOD, (flag ? @"YES" : @"NO")); - + if (flag) config &= ~kIPv6Disabled; else config |= kIPv6Disabled; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -708,62 +706,62 @@ - (void)setIPv6Enabled:(BOOL)flag - (BOOL)isIPv4Preferred { __block BOOL result = NO; - + dispatch_block_t block = ^{ result = (config & kPreferIPv4) ? YES : NO; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } - (BOOL)isIPv6Preferred { __block BOOL result = NO; - + dispatch_block_t block = ^{ result = (config & kPreferIPv6) ? YES : NO; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } - (BOOL)isIPVersionNeutral { __block BOOL result = NO; - + dispatch_block_t block = ^{ result = (config & (kPreferIPv4 | kPreferIPv6)) == 0; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } - (void)setPreferIPv4 { dispatch_block_t block = ^{ - + LogTrace(); - + config |= kPreferIPv4; config &= ~kPreferIPv6; - + }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -773,14 +771,14 @@ - (void)setPreferIPv4 - (void)setPreferIPv6 { dispatch_block_t block = ^{ - + LogTrace(); - + config &= ~kPreferIPv4; config |= kPreferIPv6; - + }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -790,14 +788,14 @@ - (void)setPreferIPv6 - (void)setIPVersionNeutral { dispatch_block_t block = ^{ - + LogTrace(); - + config &= ~kPreferIPv4; config &= ~kPreferIPv6; - + }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -807,29 +805,29 @@ - (void)setIPVersionNeutral - (uint16_t)maxReceiveIPv4BufferSize { __block uint16_t result = 0; - + dispatch_block_t block = ^{ - + result = max4ReceiveSize; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } - (void)setMaxReceiveIPv4BufferSize:(uint16_t)max { dispatch_block_t block = ^{ - + LogVerbose(@"%@ %u", THIS_METHOD, (unsigned)max); - + max4ReceiveSize = max; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -839,29 +837,29 @@ - (void)setMaxReceiveIPv4BufferSize:(uint16_t)max - (uint32_t)maxReceiveIPv6BufferSize { __block uint32_t result = 0; - + dispatch_block_t block = ^{ - + result = max6ReceiveSize; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } - (void)setMaxReceiveIPv6BufferSize:(uint32_t)max { dispatch_block_t block = ^{ - + LogVerbose(@"%@ %u", THIS_METHOD, (unsigned)max); - + max6ReceiveSize = max; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -871,12 +869,12 @@ - (void)setMaxReceiveIPv6BufferSize:(uint32_t)max - (void)setMaxSendBufferSize:(uint16_t)max { dispatch_block_t block = ^{ - + LogVerbose(@"%@ %u", THIS_METHOD, (unsigned)max); - + maxSendSize = max; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -886,47 +884,46 @@ - (void)setMaxSendBufferSize:(uint16_t)max - (uint16_t)maxSendBufferSize { __block uint16_t result = 0; - + dispatch_block_t block = ^{ - + result = maxSendSize; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - return result; } - (id)userData { __block id result = nil; - + dispatch_block_t block = ^{ - + result = userData; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } - (void)setUserData:(id)arbitraryUserData { dispatch_block_t block = ^{ - + if (userData != arbitraryUserData) { userData = arbitraryUserData; } }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -940,14 +937,14 @@ - (void)setUserData:(id)arbitraryUserData - (void)notifyDidConnectToAddress:(NSData *)anAddress { LogTrace(); - + __strong id theDelegate = delegate; if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didConnectToAddress:)]) { NSData *address = [anAddress copy]; // In case param is NSMutableData - + dispatch_async(delegateQueue, ^{ @autoreleasepool { - + [theDelegate udpSocket:self didConnectToAddress:address]; }}); } @@ -956,12 +953,12 @@ - (void)notifyDidConnectToAddress:(NSData *)anAddress - (void)notifyDidNotConnect:(NSError *)error { LogTrace(); - + __strong id theDelegate = delegate; if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didNotConnect:)]) { dispatch_async(delegateQueue, ^{ @autoreleasepool { - + [theDelegate udpSocket:self didNotConnect:error]; }}); } @@ -970,12 +967,12 @@ - (void)notifyDidNotConnect:(NSError *)error - (void)notifyDidSendDataWithTag:(long)tag { LogTrace(); - + __strong id theDelegate = delegate; if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didSendDataWithTag:)]) { dispatch_async(delegateQueue, ^{ @autoreleasepool { - + [theDelegate udpSocket:self didSendDataWithTag:tag]; }}); } @@ -984,12 +981,12 @@ - (void)notifyDidSendDataWithTag:(long)tag - (void)notifyDidNotSendDataWithTag:(long)tag dueToError:(NSError *)error { LogTrace(); - + __strong id theDelegate = delegate; if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocket:didNotSendDataWithTag:dueToError:)]) { dispatch_async(delegateQueue, ^{ @autoreleasepool { - + [theDelegate udpSocket:self didNotSendDataWithTag:tag dueToError:error]; }}); } @@ -998,14 +995,14 @@ - (void)notifyDidNotSendDataWithTag:(long)tag dueToError:(NSError *)error - (void)notifyDidReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)context { LogTrace(); - + SEL selector = @selector(udpSocket:didReceiveData:fromAddress:withFilterContext:); - + __strong id theDelegate = delegate; if (delegateQueue && [theDelegate respondsToSelector:selector]) { dispatch_async(delegateQueue, ^{ @autoreleasepool { - + [theDelegate udpSocket:self didReceiveData:data fromAddress:address withFilterContext:context]; }}); } @@ -1014,12 +1011,12 @@ - (void)notifyDidReceiveData:(NSData *)data fromAddress:(NSData *)address withFi - (void)notifyDidCloseWithError:(NSError *)error { LogTrace(); - + __strong id theDelegate = delegate; if (delegateQueue && [theDelegate respondsToSelector:@selector(udpSocketDidClose:withError:)]) { dispatch_async(delegateQueue, ^{ @autoreleasepool { - + [theDelegate udpSocketDidClose:self withError:error]; }}); } @@ -1032,7 +1029,7 @@ - (void)notifyDidCloseWithError:(NSError *)error - (NSError *)badConfigError:(NSString *)errMsg { NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - + return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain code:GCDAsyncUdpSocketBadConfigError userInfo:userInfo]; @@ -1041,7 +1038,7 @@ - (NSError *)badConfigError:(NSString *)errMsg - (NSError *)badParamError:(NSString *)errMsg { NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - + return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain code:GCDAsyncUdpSocketBadParamError userInfo:userInfo]; @@ -1051,7 +1048,7 @@ - (NSError *)gaiError:(int)gai_error { NSString *errMsg = [NSString stringWithCString:gai_strerror(gai_error) encoding:NSASCIIStringEncoding]; NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - + return [NSError errorWithDomain:@"kCFStreamErrorDomainNetDB" code:gai_error userInfo:userInfo]; } @@ -1059,13 +1056,13 @@ - (NSError *)errnoErrorWithReason:(NSString *)reason { NSString *errMsg = [NSString stringWithUTF8String:strerror(errno)]; NSDictionary *userInfo; - + if (reason) userInfo = [NSDictionary dictionaryWithObjectsAndKeys:errMsg, NSLocalizedDescriptionKey, reason, NSLocalizedFailureReasonErrorKey, nil]; else userInfo = [NSDictionary dictionaryWithObjectsAndKeys:errMsg, NSLocalizedDescriptionKey, nil]; - + return [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:userInfo]; } @@ -1082,9 +1079,9 @@ - (NSError *)sendTimeoutError NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncUdpSocketSendTimeoutError", @"GCDAsyncUdpSocket", [NSBundle mainBundle], @"Send operation timed out", nil); - + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - + return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain code:GCDAsyncUdpSocketSendTimeoutError userInfo:userInfo]; @@ -1095,16 +1092,16 @@ - (NSError *)socketClosedError NSString *errMsg = NSLocalizedStringWithDefaultValue(@"GCDAsyncUdpSocketClosedError", @"GCDAsyncUdpSocket", [NSBundle mainBundle], @"Socket closed", nil); - + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - + return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain code:GCDAsyncUdpSocketClosedError userInfo:userInfo]; } - (NSError *)otherError:(NSString *)errMsg { NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errMsg forKey:NSLocalizedDescriptionKey]; - + return [NSError errorWithDomain:GCDAsyncUdpSocketErrorDomain code:GCDAsyncUdpSocketOtherError userInfo:userInfo]; @@ -1117,7 +1114,7 @@ - (NSError *)otherError:(NSString *)errMsg - (BOOL)preOp:(NSError **)errPtr { NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + if (delegate == nil) // Must have delegate set { if (errPtr) @@ -1127,7 +1124,7 @@ - (BOOL)preOp:(NSError **)errPtr } return NO; } - + if (delegateQueue == NULL) // Must have delegate queue set { if (errPtr) @@ -1137,7 +1134,7 @@ - (BOOL)preOp:(NSError **)errPtr } return NO; } - + return YES; } @@ -1150,56 +1147,56 @@ - (void)asyncResolveHost:(NSString *)aHost withCompletionBlock:(void (^)(NSArray *addresses, NSError *error))completionBlock { LogTrace(); - + // Check parameter(s) - + if (aHost == nil) { NSString *msg = @"The host param is nil. Should be domain name or IP address string."; NSError *error = [self badParamError:msg]; - + // We should still use dispatch_async since this method is expected to be asynchronous - + dispatch_async(socketQueue, ^{ @autoreleasepool { - + completionBlock(nil, error); }}); - + return; } - + // It's possible that the given aHost parameter is actually a NSMutableString. // So we want to copy it now, within this block that will be executed synchronously. // This way the asynchronous lookup block below doesn't have to worry about it changing. - + NSString *host = [aHost copy]; - - + + dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(globalConcurrentQueue, ^{ @autoreleasepool { - + NSMutableArray *addresses = [NSMutableArray arrayWithCapacity:2]; NSError *error = nil; - + if ([host isEqualToString:@"localhost"] || [host isEqualToString:@"loopback"]) { // Use LOOPBACK address struct sockaddr_in sockaddr4; memset(&sockaddr4, 0, sizeof(sockaddr4)); - + sockaddr4.sin_len = sizeof(struct sockaddr_in); sockaddr4.sin_family = AF_INET; sockaddr4.sin_port = htons(port); sockaddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - + struct sockaddr_in6 sockaddr6; memset(&sockaddr6, 0, sizeof(sockaddr6)); - + sockaddr6.sin6_len = sizeof(struct sockaddr_in6); sockaddr6.sin6_family = AF_INET6; sockaddr6.sin6_port = htons(port); sockaddr6.sin6_addr = in6addr_loopback; - + // Wrap the native address structures and add to list [addresses addObject:[NSData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)]]; [addresses addObject:[NSData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)]]; @@ -1207,16 +1204,16 @@ - (void)asyncResolveHost:(NSString *)aHost else { NSString *portStr = [NSString stringWithFormat:@"%hu", port]; - + struct addrinfo hints, *res, *res0; - + memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; - + int gai_error = getaddrinfo([host UTF8String], [portStr UTF8String], &hints, &res0); - + if (gai_error) { error = [self gaiError:gai_error]; @@ -1229,7 +1226,7 @@ - (void)asyncResolveHost:(NSString *)aHost { // Found IPv4 address // Wrap the native address structure and add to list - + [addresses addObject:[NSData dataWithBytes:res->ai_addr length:res->ai_addrlen]]; } else if (res->ai_family == AF_INET6) @@ -1249,26 +1246,26 @@ - (void)asyncResolveHost:(NSString *)aHost } } freeaddrinfo(res0); - + if ([addresses count] == 0) { error = [self gaiError:EAI_FAIL]; } } } - + dispatch_async(socketQueue, ^{ @autoreleasepool { - + completionBlock(addresses, error); }}); - + }}); } /** * This method picks an address from the given list of addresses. * The address picked depends upon which protocols are disabled, deactived, & preferred. - * + * * Returns the address family (AF_INET or AF_INET6) of the picked address, * or AF_UNSPEC and the corresponding error is there's a problem. **/ @@ -1276,93 +1273,93 @@ - (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses { NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert([addresses count] > 0, @"Expected at least one address"); - + int resultAF = AF_UNSPEC; NSData *resultAddress = nil; NSError *resultError = nil; - + // Check for problems - + BOOL resolvedIPv4Address = NO; BOOL resolvedIPv6Address = NO; - + for (NSData *address in addresses) { switch ([[self class] familyFromAddress:address]) { case AF_INET : resolvedIPv4Address = YES; break; case AF_INET6 : resolvedIPv6Address = YES; break; - + default : NSAssert(NO, @"Addresses array contains invalid address"); } } - + BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; - + if (isIPv4Disabled && !resolvedIPv6Address) { NSString *msg = @"IPv4 has been disabled and DNS lookup found no IPv6 address(es)."; resultError = [self otherError:msg]; - + if (addressPtr) *addressPtr = resultAddress; if (errorPtr) *errorPtr = resultError; - + return resultAF; } - + if (isIPv6Disabled && !resolvedIPv4Address) { NSString *msg = @"IPv6 has been disabled and DNS lookup found no IPv4 address(es)."; resultError = [self otherError:msg]; - + if (addressPtr) *addressPtr = resultAddress; if (errorPtr) *errorPtr = resultError; - + return resultAF; } - + BOOL isIPv4Deactivated = (flags & kIPv4Deactivated) ? YES : NO; BOOL isIPv6Deactivated = (flags & kIPv6Deactivated) ? YES : NO; - + if (isIPv4Deactivated && !resolvedIPv6Address) { NSString *msg = @"IPv4 has been deactivated due to bind/connect, and DNS lookup found no IPv6 address(es)."; resultError = [self otherError:msg]; - + if (addressPtr) *addressPtr = resultAddress; if (errorPtr) *errorPtr = resultError; - + return resultAF; } - + if (isIPv6Deactivated && !resolvedIPv4Address) { NSString *msg = @"IPv6 has been deactivated due to bind/connect, and DNS lookup found no IPv4 address(es)."; resultError = [self otherError:msg]; - + if (addressPtr) *addressPtr = resultAddress; if (errorPtr) *errorPtr = resultError; - + return resultAF; } - + // Extract first IPv4 and IPv6 address in list - + BOOL ipv4WasFirstInList = YES; NSData *address4 = nil; NSData *address6 = nil; - + for (NSData *address in addresses) { int af = [[self class] familyFromAddress:address]; - + if (af == AF_INET) { if (address4 == nil) { address4 = address; - + if (address6) break; else @@ -1374,7 +1371,7 @@ - (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses if (address6 == nil) { address6 = address; - + if (address4) break; else @@ -1382,18 +1379,18 @@ - (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses } } } - + // Determine socket type - + BOOL preferIPv4 = (config & kPreferIPv4) ? YES : NO; BOOL preferIPv6 = (config & kPreferIPv6) ? YES : NO; - + BOOL useIPv4 = ((preferIPv4 && address4) || (address6 == nil)); BOOL useIPv6 = ((preferIPv6 && address6) || (address4 == nil)); - + NSAssert(!(preferIPv4 && preferIPv6), @"Invalid config state"); NSAssert(!(useIPv4 && useIPv6), @"Invalid logic"); - + if (useIPv4 || (!useIPv6 && ipv4WasFirstInList)) { resultAF = AF_INET; @@ -1404,10 +1401,10 @@ - (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses resultAF = AF_INET6; resultAddress = address6; } - + if (addressPtr) *addressPtr = resultAddress; if (errorPtr) *errorPtr = resultError; - + return resultAF; } @@ -1422,27 +1419,27 @@ - (void)convertIntefaceDescription:(NSString *)interfaceDescription { NSData *addr4 = nil; NSData *addr6 = nil; - + if (interfaceDescription == nil) { // ANY address - + struct sockaddr_in sockaddr4; memset(&sockaddr4, 0, sizeof(sockaddr4)); - + sockaddr4.sin_len = sizeof(sockaddr4); sockaddr4.sin_family = AF_INET; sockaddr4.sin_port = htons(port); sockaddr4.sin_addr.s_addr = htonl(INADDR_ANY); - + struct sockaddr_in6 sockaddr6; memset(&sockaddr6, 0, sizeof(sockaddr6)); - + sockaddr6.sin6_len = sizeof(sockaddr6); sockaddr6.sin6_family = AF_INET6; sockaddr6.sin6_port = htons(port); sockaddr6.sin6_addr = in6addr_any; - + addr4 = [NSData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)]; addr6 = [NSData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)]; } @@ -1450,33 +1447,33 @@ - (void)convertIntefaceDescription:(NSString *)interfaceDescription [interfaceDescription isEqualToString:@"loopback"]) { // LOOPBACK address - + struct sockaddr_in sockaddr4; memset(&sockaddr4, 0, sizeof(sockaddr4)); - + sockaddr4.sin_len = sizeof(struct sockaddr_in); sockaddr4.sin_family = AF_INET; sockaddr4.sin_port = htons(port); sockaddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - + struct sockaddr_in6 sockaddr6; memset(&sockaddr6, 0, sizeof(sockaddr6)); - + sockaddr6.sin6_len = sizeof(struct sockaddr_in6); sockaddr6.sin6_family = AF_INET6; sockaddr6.sin6_port = htons(port); sockaddr6.sin6_addr = in6addr_loopback; - + addr4 = [NSData dataWithBytes:&sockaddr4 length:sizeof(sockaddr4)]; addr6 = [NSData dataWithBytes:&sockaddr6 length:sizeof(sockaddr6)]; } else { const char *iface = [interfaceDescription UTF8String]; - + struct ifaddrs *addrs; const struct ifaddrs *cursor; - + if ((getifaddrs(&addrs) == 0)) { cursor = addrs; @@ -1485,32 +1482,32 @@ - (void)convertIntefaceDescription:(NSString *)interfaceDescription if ((addr4 == nil) && (cursor->ifa_addr->sa_family == AF_INET)) { // IPv4 - + struct sockaddr_in *addr = (struct sockaddr_in *)cursor->ifa_addr; - + if (strcmp(cursor->ifa_name, iface) == 0) { // Name match - + struct sockaddr_in nativeAddr4 = *addr; nativeAddr4.sin_port = htons(port); - + addr4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; } else { char ip[INET_ADDRSTRLEN]; - + const char *conversion; conversion = inet_ntop(AF_INET, &addr->sin_addr, ip, sizeof(ip)); - + if ((conversion != NULL) && (strcmp(ip, iface) == 0)) { // IP match - + struct sockaddr_in nativeAddr4 = *addr; nativeAddr4.sin_port = htons(port); - + addr4 = [NSData dataWithBytes:&nativeAddr4 length:sizeof(nativeAddr4)]; } } @@ -1518,44 +1515,44 @@ - (void)convertIntefaceDescription:(NSString *)interfaceDescription else if ((addr6 == nil) && (cursor->ifa_addr->sa_family == AF_INET6)) { // IPv6 - + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)cursor->ifa_addr; - + if (strcmp(cursor->ifa_name, iface) == 0) { // Name match - + struct sockaddr_in6 nativeAddr6 = *addr; nativeAddr6.sin6_port = htons(port); - + addr6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; } else { char ip[INET6_ADDRSTRLEN]; - + const char *conversion; conversion = inet_ntop(AF_INET6, &addr->sin6_addr, ip, sizeof(ip)); - + if ((conversion != NULL) && (strcmp(ip, iface) == 0)) { // IP match - + struct sockaddr_in6 nativeAddr6 = *addr; nativeAddr6.sin6_port = htons(port); - + addr6 = [NSData dataWithBytes:&nativeAddr6 length:sizeof(nativeAddr6)]; } } } - + cursor = cursor->ifa_next; } - + freeifaddrs(addrs); } } - + if (interfaceAddr4Ptr) *interfaceAddr4Ptr = addr4; if (interfaceAddr6Ptr) *interfaceAddr6Ptr = addr6; } @@ -1571,19 +1568,19 @@ - (void)convertNumericHost:(NSString *)numericHost { NSData *addr4 = nil; NSData *addr6 = nil; - + if (numericHost) { NSString *portStr = [NSString stringWithFormat:@"%hu", port]; - + struct addrinfo hints, *res, *res0; - + memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_NUMERICHOST; // No name resolution should be attempted - + if (getaddrinfo([numericHost UTF8String], [portStr UTF8String], &hints, &res0) == 0) { for (res = res0; res; res = res->ai_next) @@ -1604,7 +1601,7 @@ - (void)convertNumericHost:(NSString *)numericHost freeaddrinfo(res0); } } - + if (addr4Ptr) *addr4Ptr = addr4; if (addr6Ptr) *addr6Ptr = addr6; } @@ -1614,15 +1611,15 @@ - (BOOL)isConnectedToAddress4:(NSData *)someAddr4 NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(flags & kDidConnect, @"Not connected"); NSAssert(cachedConnectedAddress, @"Expected cached connected address"); - + if (cachedConnectedFamily != AF_INET) { return NO; } - + const struct sockaddr_in *sSockaddr4 = (struct sockaddr_in *)[someAddr4 bytes]; const struct sockaddr_in *cSockaddr4 = (struct sockaddr_in *)[cachedConnectedAddress bytes]; - + if (memcmp(&sSockaddr4->sin_addr, &cSockaddr4->sin_addr, sizeof(struct in_addr)) != 0) { return NO; @@ -1631,7 +1628,7 @@ - (BOOL)isConnectedToAddress4:(NSData *)someAddr4 { return NO; } - + return YES; } @@ -1640,15 +1637,15 @@ - (BOOL)isConnectedToAddress6:(NSData *)someAddr6 NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(flags & kDidConnect, @"Not connected"); NSAssert(cachedConnectedAddress, @"Expected cached connected address"); - + if (cachedConnectedFamily != AF_INET6) { return NO; } - + const struct sockaddr_in6 *sSockaddr6 = (struct sockaddr_in6 *)[someAddr6 bytes]; const struct sockaddr_in6 *cSockaddr6 = (struct sockaddr_in6 *)[cachedConnectedAddress bytes]; - + if (memcmp(&sSockaddr6->sin6_addr, &cSockaddr6->sin6_addr, sizeof(struct in6_addr)) != 0) { return NO; @@ -1657,7 +1654,7 @@ - (BOOL)isConnectedToAddress6:(NSData *)someAddr6 { return NO; } - + return YES; } @@ -1667,13 +1664,13 @@ - (unsigned int)indexOfInterfaceAddr4:(NSData *)interfaceAddr4 return 0; if ([interfaceAddr4 length] != sizeof(struct sockaddr_in)) return 0; - + int result = 0; struct sockaddr_in *ifaceAddr = (struct sockaddr_in *)[interfaceAddr4 bytes]; - + struct ifaddrs *addrs; const struct ifaddrs *cursor; - + if ((getifaddrs(&addrs) == 0)) { cursor = addrs; @@ -1682,22 +1679,22 @@ - (unsigned int)indexOfInterfaceAddr4:(NSData *)interfaceAddr4 if (cursor->ifa_addr->sa_family == AF_INET) { // IPv4 - + struct sockaddr_in *addr = (struct sockaddr_in *)cursor->ifa_addr; - + if (memcmp(&addr->sin_addr, &ifaceAddr->sin_addr, sizeof(struct in_addr)) == 0) { result = if_nametoindex(cursor->ifa_name); break; } } - + cursor = cursor->ifa_next; } - + freeifaddrs(addrs); } - + return result; } @@ -1707,13 +1704,13 @@ - (unsigned int)indexOfInterfaceAddr6:(NSData *)interfaceAddr6 return 0; if ([interfaceAddr6 length] != sizeof(struct sockaddr_in6)) return 0; - + int result = 0; struct sockaddr_in6 *ifaceAddr = (struct sockaddr_in6 *)[interfaceAddr6 bytes]; - + struct ifaddrs *addrs; const struct ifaddrs *cursor; - + if ((getifaddrs(&addrs) == 0)) { cursor = addrs; @@ -1722,22 +1719,22 @@ - (unsigned int)indexOfInterfaceAddr6:(NSData *)interfaceAddr6 if (cursor->ifa_addr->sa_family == AF_INET6) { // IPv6 - + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)cursor->ifa_addr; - + if (memcmp(&addr->sin6_addr, &ifaceAddr->sin6_addr, sizeof(struct in6_addr)) == 0) { result = if_nametoindex(cursor->ifa_name); break; } } - + cursor = cursor->ifa_next; } - + freeifaddrs(addrs); } - + return result; } @@ -1745,22 +1742,22 @@ - (void)setupSendAndReceiveSourcesForSocket4 { LogTrace(); NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + send4Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socket4FD, 0, socketQueue); receive4Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket4FD, 0, socketQueue); - + // Setup event handlers - + dispatch_source_set_event_handler(send4Source, ^{ @autoreleasepool { - + LogVerbose(@"send4EventBlock"); LogVerbose(@"dispatch_source_get_data(send4Source) = %lu", dispatch_source_get_data(send4Source)); - + flags |= kSock4CanAcceptBytes; - + // If we're ready to send data, do so immediately. // Otherwise pause the send source or it will continue to fire over and over again. - + if (currentSend == nil) { LogVerbose(@"Nothing to send"); @@ -1780,74 +1777,74 @@ - (void)setupSendAndReceiveSourcesForSocket4 { [self doSend]; } - + }}); - + dispatch_source_set_event_handler(receive4Source, ^{ @autoreleasepool { - + LogVerbose(@"receive4EventBlock"); - + socket4FDBytesAvailable = dispatch_source_get_data(receive4Source); LogVerbose(@"socket4FDBytesAvailable: %lu", socket4FDBytesAvailable); - + if (socket4FDBytesAvailable > 0) [self doReceive]; else [self doReceiveEOF]; - + }}); - + // Setup cancel handlers - + __block int socketFDRefCount = 2; - + int theSocketFD = socket4FD; - + #if !OS_OBJECT_USE_OBJC dispatch_source_t theSendSource = send4Source; dispatch_source_t theReceiveSource = receive4Source; #endif - + dispatch_source_set_cancel_handler(send4Source, ^{ - + LogVerbose(@"send4CancelBlock"); - + #if !OS_OBJECT_USE_OBJC LogVerbose(@"dispatch_release(send4Source)"); dispatch_release(theSendSource); #endif - + if (--socketFDRefCount == 0) { LogVerbose(@"close(socket4FD)"); close(theSocketFD); } }); - + dispatch_source_set_cancel_handler(receive4Source, ^{ - + LogVerbose(@"receive4CancelBlock"); - + #if !OS_OBJECT_USE_OBJC LogVerbose(@"dispatch_release(receive4Source)"); dispatch_release(theReceiveSource); #endif - + if (--socketFDRefCount == 0) { LogVerbose(@"close(socket4FD)"); close(theSocketFD); } }); - + // We will not be able to receive until the socket is bound to a port, // either explicitly via bind, or implicitly by connect or by sending data. - // + // // But we should be able to send immediately. - + socket4FDBytesAvailable = 0; flags |= kSock4CanAcceptBytes; - + flags |= kSend4SourceSuspended; flags |= kReceive4SourceSuspended; } @@ -1856,22 +1853,22 @@ - (void)setupSendAndReceiveSourcesForSocket6 { LogTrace(); NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + send6Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socket6FD, 0, socketQueue); receive6Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket6FD, 0, socketQueue); - + // Setup event handlers - + dispatch_source_set_event_handler(send6Source, ^{ @autoreleasepool { - + LogVerbose(@"send6EventBlock"); LogVerbose(@"dispatch_source_get_data(send6Source) = %lu", dispatch_source_get_data(send6Source)); - + flags |= kSock6CanAcceptBytes; - + // If we're ready to send data, do so immediately. // Otherwise pause the send source or it will continue to fire over and over again. - + if (currentSend == nil) { LogVerbose(@"Nothing to send"); @@ -1891,74 +1888,74 @@ - (void)setupSendAndReceiveSourcesForSocket6 { [self doSend]; } - + }}); - + dispatch_source_set_event_handler(receive6Source, ^{ @autoreleasepool { - + LogVerbose(@"receive6EventBlock"); - + socket6FDBytesAvailable = dispatch_source_get_data(receive6Source); LogVerbose(@"socket6FDBytesAvailable: %lu", socket6FDBytesAvailable); - + if (socket6FDBytesAvailable > 0) [self doReceive]; else [self doReceiveEOF]; - + }}); - + // Setup cancel handlers - + __block int socketFDRefCount = 2; - + int theSocketFD = socket6FD; - + #if !OS_OBJECT_USE_OBJC dispatch_source_t theSendSource = send6Source; dispatch_source_t theReceiveSource = receive6Source; #endif - + dispatch_source_set_cancel_handler(send6Source, ^{ - + LogVerbose(@"send6CancelBlock"); - + #if !OS_OBJECT_USE_OBJC LogVerbose(@"dispatch_release(send6Source)"); dispatch_release(theSendSource); #endif - + if (--socketFDRefCount == 0) { LogVerbose(@"close(socket6FD)"); close(theSocketFD); } }); - + dispatch_source_set_cancel_handler(receive6Source, ^{ - + LogVerbose(@"receive6CancelBlock"); - + #if !OS_OBJECT_USE_OBJC LogVerbose(@"dispatch_release(receive6Source)"); dispatch_release(theReceiveSource); #endif - + if (--socketFDRefCount == 0) { LogVerbose(@"close(socket6FD)"); close(theSocketFD); } }); - + // We will not be able to receive until the socket is bound to a port, // either explicitly via bind, or implicitly by connect or by sending data. - // + // // But we should be able to send immediately. - + socket6FDBytesAvailable = 0; flags |= kSock6CanAcceptBytes; - + flags |= kSend6SourceSuspended; flags |= kReceive6SourceSuspended; } @@ -1966,61 +1963,60 @@ - (void)setupSendAndReceiveSourcesForSocket6 - (BOOL)createSocket4:(BOOL)useIPv4 socket6:(BOOL)useIPv6 error:(NSError **)errPtr { LogTrace(); - + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(((flags & kDidCreateSockets) == 0), @"Sockets have already been created"); - + // CreateSocket Block // This block will be invoked below. - + int(^createSocket)(int) = ^int (int domain) { - + int socketFD = socket(domain, SOCK_DGRAM, 0); - + if (socketFD == SOCKET_NULL) { if (errPtr) *errPtr = [self errnoErrorWithReason:@"Error in socket() function"]; - + return SOCKET_NULL; } - + int status; - + // Set socket options - + status = fcntl(socketFD, F_SETFL, O_NONBLOCK); if (status == -1) { if (errPtr) *errPtr = [self errnoErrorWithReason:@"Error enabling non-blocking IO on socket (fcntl)"]; - + close(socketFD); return SOCKET_NULL; } - + int reuseaddr = 1; status = setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)); if (status == -1) { if (errPtr) *errPtr = [self errnoErrorWithReason:@"Error enabling address reuse (setsockopt)"]; - + close(socketFD); return SOCKET_NULL; } - + int nosigpipe = 1; status = setsockopt(socketFD, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); if (status == -1) { if (errPtr) *errPtr = [self errnoErrorWithReason:@"Error disabling sigpipe (setsockopt)"]; - + close(socketFD); return SOCKET_NULL; } - /** * The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535. * The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295. @@ -2034,7 +2030,6 @@ - (BOOL)createSocket4:(BOOL)useIPv4 socket6:(BOOL)useIPv6 error:(NSError **)errP * Enlarge the maximum size of UDP packet. * I can not ensure the protocol type now so that the max size is set to 65535 :) **/ - status = setsockopt(socketFD, SOL_SOCKET, SO_SNDBUF, (const char*)&maxSendSize, sizeof(int)); if (status == -1) { @@ -2043,7 +2038,6 @@ - (BOOL)createSocket4:(BOOL)useIPv4 socket6:(BOOL)useIPv6 error:(NSError **)errP close(socketFD); return SOCKET_NULL; } - status = setsockopt(socketFD, SOL_SOCKET, SO_RCVBUF, (const char*)&maxSendSize, sizeof(int)); if (status == -1) { @@ -2053,16 +2047,16 @@ - (BOOL)createSocket4:(BOOL)useIPv4 socket6:(BOOL)useIPv6 error:(NSError **)errP return SOCKET_NULL; } - + return socketFD; }; - + // Create sockets depending upon given configuration. - + if (useIPv4) { LogVerbose(@"Creating IPv4 socket"); - + socket4FD = createSocket(AF_INET); if (socket4FD == SOCKET_NULL) { @@ -2070,33 +2064,33 @@ - (BOOL)createSocket4:(BOOL)useIPv4 socket6:(BOOL)useIPv6 error:(NSError **)errP return NO; } } - + if (useIPv6) { LogVerbose(@"Creating IPv6 socket"); - + socket6FD = createSocket(AF_INET6); if (socket6FD == SOCKET_NULL) { // errPtr set in local createSocket() block - + if (socket4FD != SOCKET_NULL) { close(socket4FD); socket4FD = SOCKET_NULL; } - + return NO; } } - + // Setup send and receive sources - + if (useIPv4) [self setupSendAndReceiveSourcesForSocket4]; if (useIPv6) [self setupSendAndReceiveSourcesForSocket6]; - + flags |= kDidCreateSockets; return YES; } @@ -2104,10 +2098,10 @@ - (BOOL)createSocket4:(BOOL)useIPv4 socket6:(BOOL)useIPv6 error:(NSError **)errP - (BOOL)createSockets:(NSError **)errPtr { LogTrace(); - + BOOL useIPv4 = [self isIPv4Enabled]; BOOL useIPv6 = [self isIPv6Enabled]; - + return [self createSocket4:useIPv4 socket6:useIPv6 error:errPtr]; } @@ -2116,7 +2110,7 @@ - (void)suspendSend4Source if (send4Source && !(flags & kSend4SourceSuspended)) { LogVerbose(@"dispatch_suspend(send4Source)"); - + dispatch_suspend(send4Source); flags |= kSend4SourceSuspended; } @@ -2127,7 +2121,7 @@ - (void)suspendSend6Source if (send6Source && !(flags & kSend6SourceSuspended)) { LogVerbose(@"dispatch_suspend(send6Source)"); - + dispatch_suspend(send6Source); flags |= kSend6SourceSuspended; } @@ -2138,7 +2132,7 @@ - (void)resumeSend4Source if (send4Source && (flags & kSend4SourceSuspended)) { LogVerbose(@"dispatch_resume(send4Source)"); - + dispatch_resume(send4Source); flags &= ~kSend4SourceSuspended; } @@ -2149,7 +2143,7 @@ - (void)resumeSend6Source if (send6Source && (flags & kSend6SourceSuspended)) { LogVerbose(@"dispatch_resume(send6Source)"); - + dispatch_resume(send6Source); flags &= ~kSend6SourceSuspended; } @@ -2160,7 +2154,7 @@ - (void)suspendReceive4Source if (receive4Source && !(flags & kReceive4SourceSuspended)) { LogVerbose(@"dispatch_suspend(receive4Source)"); - + dispatch_suspend(receive4Source); flags |= kReceive4SourceSuspended; } @@ -2171,7 +2165,7 @@ - (void)suspendReceive6Source if (receive6Source && !(flags & kReceive6SourceSuspended)) { LogVerbose(@"dispatch_suspend(receive6Source)"); - + dispatch_suspend(receive6Source); flags |= kReceive6SourceSuspended; } @@ -2182,7 +2176,7 @@ - (void)resumeReceive4Source if (receive4Source && (flags & kReceive4SourceSuspended)) { LogVerbose(@"dispatch_resume(receive4Source)"); - + dispatch_resume(receive4Source); flags &= ~kReceive4SourceSuspended; } @@ -2193,7 +2187,7 @@ - (void)resumeReceive6Source if (receive6Source && (flags & kReceive6SourceSuspended)) { LogVerbose(@"dispatch_resume(receive6Source)"); - + dispatch_resume(receive6Source); flags &= ~kReceive6SourceSuspended; } @@ -2205,32 +2199,32 @@ - (void)closeSocket4 { LogVerbose(@"dispatch_source_cancel(send4Source)"); dispatch_source_cancel(send4Source); - + LogVerbose(@"dispatch_source_cancel(receive4Source)"); dispatch_source_cancel(receive4Source); - + // For some crazy reason (in my opinion), cancelling a dispatch source doesn't // invoke the cancel handler if the dispatch source is paused. // So we have to unpause the source if needed. // This allows the cancel handler to be run, which in turn releases the source and closes the socket. - + [self resumeSend4Source]; [self resumeReceive4Source]; - + // The sockets will be closed by the cancel handlers of the corresponding source - + send4Source = NULL; receive4Source = NULL; - + socket4FD = SOCKET_NULL; - + // Clear socket states - + socket4FDBytesAvailable = 0; flags &= ~kSock4CanAcceptBytes; - + // Clear cached info - + cachedLocalAddress4 = nil; cachedLocalHost4 = nil; cachedLocalPort4 = 0; @@ -2243,32 +2237,32 @@ - (void)closeSocket6 { LogVerbose(@"dispatch_source_cancel(send6Source)"); dispatch_source_cancel(send6Source); - + LogVerbose(@"dispatch_source_cancel(receive6Source)"); dispatch_source_cancel(receive6Source); - + // For some crazy reason (in my opinion), cancelling a dispatch source doesn't // invoke the cancel handler if the dispatch source is paused. // So we have to unpause the source if needed. // This allows the cancel handler to be run, which in turn releases the source and closes the socket. - + [self resumeSend6Source]; [self resumeReceive6Source]; - + send6Source = NULL; receive6Source = NULL; - + // The sockets will be closed by the cancel handlers of the corresponding source - + socket6FD = SOCKET_NULL; - + // Clear socket states - + socket6FDBytesAvailable = 0; flags &= ~kSock6CanAcceptBytes; - + // Clear cached info - + cachedLocalAddress6 = nil; cachedLocalHost6 = nil; cachedLocalPort6 = 0; @@ -2279,7 +2273,7 @@ - (void)closeSockets { [self closeSocket4]; [self closeSocket6]; - + flags &= ~kDidCreateSockets; } @@ -2293,16 +2287,16 @@ - (BOOL)getLocalAddress:(NSData **)dataPtr forSocket:(int)socketFD withFamily:(int)socketFamily { - + NSData *data = nil; NSString *host = nil; uint16_t port = 0; - + if (socketFamily == AF_INET) { struct sockaddr_in sockaddr4; socklen_t sockaddr4len = sizeof(sockaddr4); - + if (getsockname(socketFD, (struct sockaddr *)&sockaddr4, &sockaddr4len) == 0) { data = [NSData dataWithBytes:&sockaddr4 length:sockaddr4len]; @@ -2318,7 +2312,7 @@ - (BOOL)getLocalAddress:(NSData **)dataPtr { struct sockaddr_in6 sockaddr6; socklen_t sockaddr6len = sizeof(sockaddr6); - + if (getsockname(socketFD, (struct sockaddr *)&sockaddr6, &sockaddr6len) == 0) { data = [NSData dataWithBytes:&sockaddr6 length:sockaddr6len]; @@ -2330,30 +2324,30 @@ - (BOOL)getLocalAddress:(NSData **)dataPtr LogWarn(@"Error in getsockname: %@", [self errnoError]); } } - + if (dataPtr) *dataPtr = data; if (hostPtr) *hostPtr = host; if (portPtr) *portPtr = port; - + return (data != nil); } - (void)maybeUpdateCachedLocalAddress4Info { NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + if ( cachedLocalAddress4 || ((flags & kDidBind) == 0) || (socket4FD == SOCKET_NULL) ) { return; } - + NSData *address = nil; NSString *host = nil; uint16_t port = 0; - + if ([self getLocalAddress:&address host:&host port:&port forSocket:socket4FD withFamily:AF_INET]) { - + cachedLocalAddress4 = address; cachedLocalHost4 = host; cachedLocalPort4 = port; @@ -2363,19 +2357,19 @@ - (void)maybeUpdateCachedLocalAddress4Info - (void)maybeUpdateCachedLocalAddress6Info { NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + if ( cachedLocalAddress6 || ((flags & kDidBind) == 0) || (socket6FD == SOCKET_NULL) ) { return; } - + NSData *address = nil; NSString *host = nil; uint16_t port = 0; - + if ([self getLocalAddress:&address host:&host port:&port forSocket:socket6FD withFamily:AF_INET6]) { - + cachedLocalAddress6 = address; cachedLocalHost6 = host; cachedLocalPort6 = port; @@ -2385,9 +2379,9 @@ - (void)maybeUpdateCachedLocalAddress6Info - (NSData *)localAddress { __block NSData *result = nil; - + dispatch_block_t block = ^{ - + if (socket4FD != SOCKET_NULL) { [self maybeUpdateCachedLocalAddress4Info]; @@ -2398,23 +2392,23 @@ - (NSData *)localAddress [self maybeUpdateCachedLocalAddress6Info]; result = cachedLocalAddress6; } - + }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (NSString *)localHost { __block NSString *result = nil; - + dispatch_block_t block = ^{ - + if (socket4FD != SOCKET_NULL) { [self maybeUpdateCachedLocalAddress4Info]; @@ -2426,21 +2420,21 @@ - (NSString *)localHost result = cachedLocalHost6; } }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (uint16_t)localPort { __block uint16_t result = 0; - + dispatch_block_t block = ^{ - + if (socket4FD != SOCKET_NULL) { [self maybeUpdateCachedLocalAddress4Info]; @@ -2452,142 +2446,142 @@ - (uint16_t)localPort result = cachedLocalPort6; } }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (NSData *)localAddress_IPv4 { __block NSData *result = nil; - + dispatch_block_t block = ^{ - + [self maybeUpdateCachedLocalAddress4Info]; result = cachedLocalAddress4; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (NSString *)localHost_IPv4 { __block NSString *result = nil; - + dispatch_block_t block = ^{ - + [self maybeUpdateCachedLocalAddress4Info]; result = cachedLocalHost4; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (uint16_t)localPort_IPv4 { __block uint16_t result = 0; - + dispatch_block_t block = ^{ - + [self maybeUpdateCachedLocalAddress4Info]; result = cachedLocalPort4; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (NSData *)localAddress_IPv6 { __block NSData *result = nil; - + dispatch_block_t block = ^{ - + [self maybeUpdateCachedLocalAddress6Info]; result = cachedLocalAddress6; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (NSString *)localHost_IPv6 { __block NSString *result = nil; - + dispatch_block_t block = ^{ - + [self maybeUpdateCachedLocalAddress6Info]; result = cachedLocalHost6; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (uint16_t)localPort_IPv6 { __block uint16_t result = 0; - + dispatch_block_t block = ^{ - + [self maybeUpdateCachedLocalAddress6Info]; result = cachedLocalPort6; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (void)maybeUpdateCachedConnectedAddressInfo { NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + if (cachedConnectedAddress || (flags & kDidConnect) == 0) { return; } - + NSData *data = nil; NSString *host = nil; uint16_t port = 0; int family = AF_UNSPEC; - + if (socket4FD != SOCKET_NULL) { struct sockaddr_in sockaddr4; socklen_t sockaddr4len = sizeof(sockaddr4); - + if (getpeername(socket4FD, (struct sockaddr *)&sockaddr4, &sockaddr4len) == 0) { data = [NSData dataWithBytes:&sockaddr4 length:sockaddr4len]; @@ -2604,7 +2598,7 @@ - (void)maybeUpdateCachedConnectedAddressInfo { struct sockaddr_in6 sockaddr6; socklen_t sockaddr6len = sizeof(sockaddr6); - + if (getpeername(socket6FD, (struct sockaddr *)&sockaddr6, &sockaddr6len) == 0) { data = [NSData dataWithBytes:&sockaddr6 length:sockaddr6len]; @@ -2617,8 +2611,8 @@ - (void)maybeUpdateCachedConnectedAddressInfo LogWarn(@"Error in getpeername: %@", [self errnoError]); } } - - + + cachedConnectedAddress = data; cachedConnectedHost = host; cachedConnectedPort = port; @@ -2628,96 +2622,96 @@ - (void)maybeUpdateCachedConnectedAddressInfo - (NSData *)connectedAddress { __block NSData *result = nil; - + dispatch_block_t block = ^{ - + [self maybeUpdateCachedConnectedAddressInfo]; result = cachedConnectedAddress; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (NSString *)connectedHost { __block NSString *result = nil; - + dispatch_block_t block = ^{ - + [self maybeUpdateCachedConnectedAddressInfo]; result = cachedConnectedHost; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (uint16_t)connectedPort { __block uint16_t result = 0; - + dispatch_block_t block = ^{ - + [self maybeUpdateCachedConnectedAddressInfo]; result = cachedConnectedPort; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, AutoreleasedBlock(block)); - + return result; } - (BOOL)isConnected { __block BOOL result = NO; - + dispatch_block_t block = ^{ result = (flags & kDidConnect) ? YES : NO; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } - (BOOL)isClosed { __block BOOL result = YES; - + dispatch_block_t block = ^{ - + result = (flags & kDidCreateSockets) ? NO : YES; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } - (BOOL)isIPv4 { __block BOOL result = NO; - + dispatch_block_t block = ^{ - + if (flags & kDidCreateSockets) { result = (socket4FD != SOCKET_NULL); @@ -2727,21 +2721,21 @@ - (BOOL)isIPv4 result = [self isIPv4Enabled]; } }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } - (BOOL)isIPv6 { __block BOOL result = NO; - + dispatch_block_t block = ^{ - + if (flags & kDidCreateSockets) { result = (socket6FD != SOCKET_NULL); @@ -2751,12 +2745,12 @@ - (BOOL)isIPv6 result = [self isIPv6Enabled]; } }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + return result; } @@ -2774,7 +2768,7 @@ - (BOOL)preBind:(NSError **)errPtr { return NO; } - + if (flags & kDidBind) { if (errPtr) @@ -2784,7 +2778,7 @@ - (BOOL)preBind:(NSError **)errPtr } return NO; } - + if ((flags & kConnecting) || (flags & kDidConnect)) { if (errPtr) @@ -2794,10 +2788,10 @@ - (BOOL)preBind:(NSError **)errPtr } return NO; } - + BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; - + if (isIPv4Disabled && isIPv6Disabled) // Must have IPv4 or IPv6 enabled { if (errPtr) @@ -2807,7 +2801,7 @@ - (BOOL)preBind:(NSError **)errPtr } return NO; } - + return YES; } @@ -2820,57 +2814,57 @@ - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError { __block BOOL result = NO; __block NSError *err = nil; - + dispatch_block_t block = ^{ @autoreleasepool { - + // Run through sanity checks - + if (![self preBind:&err]) { return_from_block; } - + // Check the given interface - + NSData *interface4 = nil; NSData *interface6 = nil; - + [self convertIntefaceDescription:interface port:port intoAddress4:&interface4 address6:&interface6]; - + if ((interface4 == nil) && (interface6 == nil)) { NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; err = [self badParamError:msg]; - + return_from_block; } - + BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; - + if (isIPv4Disabled && (interface6 == nil)) { NSString *msg = @"IPv4 has been disabled and specified interface doesn't support IPv6."; err = [self badParamError:msg]; - + return_from_block; } - + if (isIPv6Disabled && (interface4 == nil)) { NSString *msg = @"IPv6 has been disabled and specified interface doesn't support IPv4."; err = [self badParamError:msg]; - + return_from_block; } - + // Determine protocol(s) - + BOOL useIPv4 = !isIPv4Disabled && (interface4 != nil); BOOL useIPv6 = !isIPv6Disabled && (interface6 != nil); - + // Create the socket(s) if needed - + if ((flags & kDidCreateSockets) == 0) { if (![self createSocket4:useIPv4 socket6:useIPv6 error:&err]) @@ -2878,61 +2872,61 @@ - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError return_from_block; } } - + // Bind the socket(s) - + LogVerbose(@"Binding socket to port(%hu) interface(%@)", port, interface); - + if (useIPv4) { int status = bind(socket4FD, (struct sockaddr *)[interface4 bytes], (socklen_t)[interface4 length]); if (status == -1) { [self closeSockets]; - + NSString *reason = @"Error in bind() function"; err = [self errnoErrorWithReason:reason]; - + return_from_block; } } - + if (useIPv6) { int status = bind(socket6FD, (struct sockaddr *)[interface6 bytes], (socklen_t)[interface6 length]); if (status == -1) { [self closeSockets]; - + NSString *reason = @"Error in bind() function"; err = [self errnoErrorWithReason:reason]; - + return_from_block; } } - + // Update flags - + flags |= kDidBind; - + if (!useIPv4) flags |= kIPv4Deactivated; if (!useIPv6) flags |= kIPv6Deactivated; - + result = YES; - + }}; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + if (err) LogError(@"Error binding to port/interface: %@", err); - + if (errPtr) *errPtr = err; - + return result; } @@ -2940,57 +2934,57 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr { __block BOOL result = NO; __block NSError *err = nil; - + dispatch_block_t block = ^{ @autoreleasepool { - + // Run through sanity checks - + if (![self preBind:&err]) { return_from_block; } - + // Check the given address - + int addressFamily = [[self class] familyFromAddress:localAddr]; - + if (addressFamily == AF_UNSPEC) { NSString *msg = @"A valid IPv4 or IPv6 address was not given"; err = [self badParamError:msg]; - + return_from_block; } - + NSData *localAddr4 = (addressFamily == AF_INET) ? localAddr : nil; NSData *localAddr6 = (addressFamily == AF_INET6) ? localAddr : nil; - + BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; - + if (isIPv4Disabled && localAddr4) { NSString *msg = @"IPv4 has been disabled and an IPv4 address was passed."; err = [self badParamError:msg]; - + return_from_block; } - + if (isIPv6Disabled && localAddr6) { NSString *msg = @"IPv6 has been disabled and an IPv6 address was passed."; err = [self badParamError:msg]; - + return_from_block; } - + // Determine protocol(s) - + BOOL useIPv4 = !isIPv4Disabled && (localAddr4 != nil); BOOL useIPv6 = !isIPv6Disabled && (localAddr6 != nil); - + // Create the socket(s) if needed - + if ((flags & kDidCreateSockets) == 0) { if (![self createSocket4:useIPv4 socket6:useIPv6 error:&err]) @@ -2998,23 +2992,23 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr return_from_block; } } - + // Bind the socket(s) - + if (useIPv4) { LogVerbose(@"Binding socket to address(%@:%hu)", [[self class] hostFromAddress:localAddr4], [[self class] portFromAddress:localAddr4]); - + int status = bind(socket4FD, (struct sockaddr *)[localAddr4 bytes], (socklen_t)[localAddr4 length]); if (status == -1) { [self closeSockets]; - + NSString *reason = @"Error in bind() function"; err = [self errnoErrorWithReason:reason]; - + return_from_block; } } @@ -3023,41 +3017,41 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr LogVerbose(@"Binding socket to address(%@:%hu)", [[self class] hostFromAddress:localAddr6], [[self class] portFromAddress:localAddr6]); - + int status = bind(socket6FD, (struct sockaddr *)[localAddr6 bytes], (socklen_t)[localAddr6 length]); if (status == -1) { [self closeSockets]; - + NSString *reason = @"Error in bind() function"; err = [self errnoErrorWithReason:reason]; - + return_from_block; } } - + // Update flags - + flags |= kDidBind; - + if (!useIPv4) flags |= kIPv4Deactivated; if (!useIPv6) flags |= kIPv6Deactivated; - + result = YES; - + }}; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + if (err) LogError(@"Error binding to address: %@", err); - + if (errPtr) *errPtr = err; - + return result; } @@ -3075,7 +3069,7 @@ - (BOOL)preConnect:(NSError **)errPtr { return NO; } - + if ((flags & kConnecting) || (flags & kDidConnect)) { if (errPtr) @@ -3085,10 +3079,10 @@ - (BOOL)preConnect:(NSError **)errPtr } return NO; } - + BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO; BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO; - + if (isIPv4Disabled && isIPv6Disabled) // Must have IPv4 or IPv6 enabled { if (errPtr) @@ -3098,7 +3092,7 @@ - (BOOL)preConnect:(NSError **)errPtr } return NO; } - + return YES; } @@ -3106,28 +3100,28 @@ - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)e { __block BOOL result = NO; __block NSError *err = nil; - + dispatch_block_t block = ^{ @autoreleasepool { - + // Run through sanity checks. - + if (![self preConnect:&err]) { return_from_block; } - + // Check parameter(s) - + if (host == nil) { NSString *msg = @"The host param is nil. Should be domain name or IP address string."; err = [self badParamError:msg]; - + return_from_block; } - + // Create the socket(s) if needed - + if ((flags & kDidCreateSockets) == 0) { if (![self createSockets:&err]) @@ -3135,51 +3129,51 @@ - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)e return_from_block; } } - + // Create special connect packet - + GCDAsyncUdpSpecialPacket *packet = [[GCDAsyncUdpSpecialPacket alloc] init]; packet->resolveInProgress = YES; - + // Start asynchronous DNS resolve for host:port on background queue - + LogVerbose(@"Dispatching DNS resolve for connect..."); - + [self asyncResolveHost:host port:port withCompletionBlock:^(NSArray *addresses, NSError *error) { - + // The asyncResolveHost:port:: method asynchronously dispatches a task onto the global concurrent queue, // and immediately returns. Once the async resolve task completes, // this block is executed on our socketQueue. - + packet->resolveInProgress = NO; - + packet->addresses = addresses; packet->error = error; - + [self maybeConnect]; }]; - + // Updates flags, add connect packet to send queue, and pump send queue - + flags |= kConnecting; - + [sendQueue addObject:packet]; [self maybeDequeueSend]; - + result = YES; }}; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + if (err) LogError(@"Error connecting to host/port: %@", err); - + if (errPtr) *errPtr = err; - + return result; } @@ -3187,28 +3181,28 @@ - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr { __block BOOL result = NO; __block NSError *err = nil; - + dispatch_block_t block = ^{ @autoreleasepool { - + // Run through sanity checks. - + if (![self preConnect:&err]) { return_from_block; } - + // Check parameter(s) - + if (remoteAddr == nil) { NSString *msg = @"The address param is nil. Should be a valid address."; err = [self badParamError:msg]; - + return_from_block; } - + // Create the socket(s) if needed - + if ((flags & kDidCreateSockets) == 0) { if (![self createSockets:&err]) @@ -3216,37 +3210,37 @@ - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr return_from_block; } } - + // The remoteAddr parameter could be of type NSMutableData. // So we copy it to be safe. - + NSData *address = [remoteAddr copy]; NSArray *addresses = [NSArray arrayWithObject:address]; - + GCDAsyncUdpSpecialPacket *packet = [[GCDAsyncUdpSpecialPacket alloc] init]; packet->addresses = addresses; - + // Updates flags, add connect packet to send queue, and pump send queue - + flags |= kConnecting; - + [sendQueue addObject:packet]; [self maybeDequeueSend]; - + result = YES; }}; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + if (err) LogError(@"Error connecting to address: %@", err); - + if (errPtr) *errPtr = err; - + return result; } @@ -3254,14 +3248,14 @@ - (void)maybeConnect { LogTrace(); NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - - + + BOOL sendQueueReady = [currentSend isKindOfClass:[GCDAsyncUdpSpecialPacket class]]; - + if (sendQueueReady) { GCDAsyncUdpSpecialPacket *connectPacket = (GCDAsyncUdpSpecialPacket *)currentSend; - + if (connectPacket->resolveInProgress) { LogVerbose(@"Waiting for DNS resolve..."); @@ -3276,29 +3270,29 @@ - (void)maybeConnect { NSData *address = nil; NSError *error = nil; - + int addressFamily = [self getAddress:&address error:&error fromAddresses:connectPacket->addresses]; - + // Perform connect - + BOOL result = NO; - + switch (addressFamily) { case AF_INET : result = [self connectWithAddress4:address error:&error]; break; case AF_INET6 : result = [self connectWithAddress6:address error:&error]; break; } - + if (result) { flags |= kDidBind; flags |= kDidConnect; - + cachedConnectedAddress = address; cachedConnectedHost = [[self class] hostFromAddress:address]; cachedConnectedPort = [[self class] portFromAddress:address]; cachedConnectedFamily = addressFamily; - + [self notifyDidConnectToAddress:address]; } else @@ -3306,9 +3300,9 @@ - (void)maybeConnect [self notifyDidNotConnect:error]; } } - + flags &= ~kConnecting; - + [self endCurrentSend]; [self maybeDequeueSend]; } @@ -3319,19 +3313,19 @@ - (BOOL)connectWithAddress4:(NSData *)address4 error:(NSError **)errPtr { LogTrace(); NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + int status = connect(socket4FD, (struct sockaddr *)[address4 bytes], (socklen_t)[address4 length]); if (status != 0) { if (errPtr) *errPtr = [self errnoErrorWithReason:@"Error in connect() function"]; - + return NO; } - + [self closeSocket6]; flags |= kIPv6Deactivated; - + return YES; } @@ -3339,19 +3333,19 @@ - (BOOL)connectWithAddress6:(NSData *)address6 error:(NSError **)errPtr { LogTrace(); NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + int status = connect(socket6FD, (struct sockaddr *)[address6 bytes], (socklen_t)[address6 length]); if (status != 0) { if (errPtr) *errPtr = [self errnoErrorWithReason:@"Error in connect() function"]; - + return NO; } - + [self closeSocket4]; flags |= kIPv4Deactivated; - + return YES; } @@ -3365,7 +3359,7 @@ - (BOOL)preJoin:(NSError **)errPtr { return NO; } - + if (!(flags & kDidBind)) { if (errPtr) @@ -3375,7 +3369,7 @@ - (BOOL)preJoin:(NSError **)errPtr } return NO; } - + if ((flags & kConnecting) || (flags & kDidConnect)) { if (errPtr) @@ -3385,7 +3379,7 @@ - (BOOL)preJoin:(NSError **)errPtr } return NO; } - + return YES; } @@ -3418,109 +3412,109 @@ - (BOOL)performMulticastRequest:(int)requestType { __block BOOL result = NO; __block NSError *err = nil; - + dispatch_block_t block = ^{ @autoreleasepool { - + // Run through sanity checks - + if (![self preJoin:&err]) { return_from_block; } - + // Convert group to address - + NSData *groupAddr4 = nil; NSData *groupAddr6 = nil; - + [self convertNumericHost:group port:0 intoAddress4:&groupAddr4 address6:&groupAddr6]; - + if ((groupAddr4 == nil) && (groupAddr6 == nil)) { NSString *msg = @"Unknown group. Specify valid group IP address."; err = [self badParamError:msg]; - + return_from_block; } - + // Convert interface to address - + NSData *interfaceAddr4 = nil; NSData *interfaceAddr6 = nil; - + [self convertIntefaceDescription:interface port:0 intoAddress4:&interfaceAddr4 address6:&interfaceAddr6]; - + if ((interfaceAddr4 == nil) && (interfaceAddr6 == nil)) { NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; err = [self badParamError:msg]; - + return_from_block; } - + // Perform join - + if ((socket4FD != SOCKET_NULL) && groupAddr4 && interfaceAddr4) { const struct sockaddr_in *nativeGroup = (struct sockaddr_in *)[groupAddr4 bytes]; const struct sockaddr_in *nativeIface = (struct sockaddr_in *)[interfaceAddr4 bytes]; - + struct ip_mreq imreq; imreq.imr_multiaddr = nativeGroup->sin_addr; imreq.imr_interface = nativeIface->sin_addr; - + int status = setsockopt(socket4FD, IPPROTO_IP, requestType, (const void *)&imreq, sizeof(imreq)); if (status != 0) { err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; - + return_from_block; } - + // Using IPv4 only [self closeSocket6]; - + result = YES; } else if ((socket6FD != SOCKET_NULL) && groupAddr6 && interfaceAddr6) { const struct sockaddr_in6 *nativeGroup = (struct sockaddr_in6 *)[groupAddr6 bytes]; - + struct ipv6_mreq imreq; imreq.ipv6mr_multiaddr = nativeGroup->sin6_addr; imreq.ipv6mr_interface = [self indexOfInterfaceAddr6:interfaceAddr6]; - + int status = setsockopt(socket6FD, IPPROTO_IPV6, requestType, (const void *)&imreq, sizeof(imreq)); if (status != 0) { err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; - + return_from_block; } - + // Using IPv6 only [self closeSocket4]; - + result = YES; } else { NSString *msg = @"Socket, group, and interface do not have matching IP versions"; err = [self badParamError:msg]; - + return_from_block; } - + }}; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + if (errPtr) *errPtr = err; - + return result; } @@ -3532,14 +3526,13 @@ - (BOOL)enableReusePort:(BOOL)flag error:(NSError **)errPtr { __block BOOL result = NO; __block NSError *err = nil; - + dispatch_block_t block = ^{ @autoreleasepool { - + if (![self preOp:&err]) { return_from_block; } - if ((flags & kDidCreateSockets) == 0) { if (![self createSockets:&err]) @@ -3547,44 +3540,43 @@ - (BOOL)enableReusePort:(BOOL)flag error:(NSError **)errPtr return_from_block; } } - int value = flag ? 1 : 0; if (socket4FD != SOCKET_NULL) { int error = setsockopt(socket4FD, SOL_SOCKET, SO_REUSEPORT, (const void *)&value, sizeof(value)); - + if (error) { err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; - + return_from_block; } result = YES; } - + if (socket6FD != SOCKET_NULL) { int error = setsockopt(socket6FD, SOL_SOCKET, SO_REUSEPORT, (const void *)&value, sizeof(value)); - + if (error) { err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; - + return_from_block; } result = YES; } - + }}; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + if (errPtr) *errPtr = err; - + return result; } @@ -3596,14 +3588,14 @@ - (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr { __block BOOL result = NO; __block NSError *err = nil; - + dispatch_block_t block = ^{ @autoreleasepool { - + if (![self preOp:&err]) { return_from_block; } - + if ((flags & kDidCreateSockets) == 0) { if (![self createSockets:&err]) @@ -3611,34 +3603,34 @@ - (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr return_from_block; } } - + if (socket4FD != SOCKET_NULL) { int value = flag ? 1 : 0; int error = setsockopt(socket4FD, SOL_SOCKET, SO_BROADCAST, (const void *)&value, sizeof(value)); - + if (error) { err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; - + return_from_block; } result = YES; } - + // IPv6 does not implement broadcast, the ability to send a packet to all hosts on the attached link. // The same effect can be achieved by sending a packet to the link-local all hosts multicast group. - + }}; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + if (errPtr) *errPtr = err; - + return result; } @@ -3654,23 +3646,23 @@ - (void)sendData:(NSData *)data withTag:(long)tag - (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag { LogTrace(); - + if ([data length] == 0) { LogWarn(@"Ignoring attempt to send nil/empty data."); return; } - - - + + + GCDAsyncUdpSendPacket *packet = [[GCDAsyncUdpSendPacket alloc] initWithData:data timeout:timeout tag:tag]; - + dispatch_async(socketQueue, ^{ @autoreleasepool { - + [sendQueue addObject:packet]; [self maybeDequeueSend]; }}); - + } - (void)sendData:(NSData *)data @@ -3680,59 +3672,59 @@ - (void)sendData:(NSData *)data tag:(long)tag { LogTrace(); - + if ([data length] == 0) { LogWarn(@"Ignoring attempt to send nil/empty data."); return; } - + GCDAsyncUdpSendPacket *packet = [[GCDAsyncUdpSendPacket alloc] initWithData:data timeout:timeout tag:tag]; packet->resolveInProgress = YES; - + [self asyncResolveHost:host port:port withCompletionBlock:^(NSArray *addresses, NSError *error) { - + // The asyncResolveHost:port:: method asynchronously dispatches a task onto the global concurrent queue, // and immediately returns. Once the async resolve task completes, // this block is executed on our socketQueue. - + packet->resolveInProgress = NO; - + packet->resolvedAddresses = addresses; packet->resolveError = error; - + if (packet == currentSend) { LogVerbose(@"currentSend - address resolved"); [self doPreSend]; } }]; - + dispatch_async(socketQueue, ^{ @autoreleasepool { - + [sendQueue addObject:packet]; [self maybeDequeueSend]; - + }}); - + } - (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag { LogTrace(); - + if ([data length] == 0) { LogWarn(@"Ignoring attempt to send nil/empty data."); return; } - + GCDAsyncUdpSendPacket *packet = [[GCDAsyncUdpSendPacket alloc] initWithData:data timeout:timeout tag:tag]; packet->addressFamily = [GCDAsyncUdpSocket familyFromAddress:remoteAddr]; packet->address = remoteAddr; - + dispatch_async(socketQueue, ^{ @autoreleasepool { - + [sendQueue addObject:packet]; [self maybeDequeueSend]; }}); @@ -3749,29 +3741,29 @@ - (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock { GCDAsyncUdpSocketSendFilterBlock newFilterBlock = NULL; dispatch_queue_t newFilterQueue = NULL; - + if (filterBlock) { NSAssert(filterQueue, @"Must provide a dispatch_queue in which to run the filter block."); - + newFilterBlock = [filterBlock copy]; newFilterQueue = filterQueue; #if !OS_OBJECT_USE_OBJC dispatch_retain(newFilterQueue); #endif } - + dispatch_block_t block = ^{ - + #if !OS_OBJECT_USE_OBJC if (sendFilterQueue) dispatch_release(sendFilterQueue); #endif - + sendFilterBlock = newFilterBlock; sendFilterQueue = newFilterQueue; sendFilterAsync = isAsynchronous; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -3782,7 +3774,7 @@ - (void)maybeDequeueSend { LogTrace(); NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + // If we don't have a send operation already in progress if (currentSend == nil) { @@ -3796,38 +3788,38 @@ - (void)maybeDequeueSend return; } } - + while ([sendQueue count] > 0) { // Dequeue the next object in the queue currentSend = [sendQueue objectAtIndex:0]; [sendQueue removeObjectAtIndex:0]; - + if ([currentSend isKindOfClass:[GCDAsyncUdpSpecialPacket class]]) { [self maybeConnect]; - + return; // The maybeConnect method, if it connects, will invoke this method again } else if (currentSend->resolveError) { // Notify delegate [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:currentSend->resolveError]; - + // Clear currentSend currentSend = nil; - + continue; } else { // Start preprocessing checks on the send packet [self doPreSend]; - + break; } } - + if ((currentSend == nil) && (flags & kCloseAfterSends)) { [self closeWithError:nil]; @@ -3839,24 +3831,24 @@ - (void)maybeDequeueSend * This method is called after a sendPacket has been dequeued. * It performs various preprocessing checks on the packet, * and queries the sendFilter (if set) to determine if the packet can be sent. - * + * * If the packet passes all checks, it will be passed on to the doSend method. **/ - (void)doPreSend { LogTrace(); - - // + + // // 1. Check for problems with send packet - // - + // + BOOL waitingForResolve = NO; NSError *error = nil; - + if (flags & kDidConnect) { // Connected socket - + if (currentSend->resolveInProgress || currentSend->resolvedAddresses || currentSend->resolveError) { NSString *msg = @"Cannot specify destination of packet for connected socket"; @@ -3871,7 +3863,7 @@ - (void)doPreSend else { // Non-Connected socket - + if (currentSend->resolveInProgress) { // We're waiting for the packet's destination to be resolved. @@ -3891,67 +3883,67 @@ - (void)doPreSend else { // Pick the proper address to use (out of possibly several resolved addresses) - + NSData *address = nil; int addressFamily = AF_UNSPEC; - + addressFamily = [self getAddress:&address error:&error fromAddresses:currentSend->resolvedAddresses]; - + currentSend->address = address; currentSend->addressFamily = addressFamily; } } } - + if (waitingForResolve) { // We're waiting for the packet's destination to be resolved. - + LogVerbose(@"currentSend - waiting for address resolve"); - + if (flags & kSock4CanAcceptBytes) { [self suspendSend4Source]; } if (flags & kSock6CanAcceptBytes) { [self suspendSend6Source]; } - + return; } - + if (error) { // Unable to send packet due to some error. // Notify delegate and move on. - + [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:error]; [self endCurrentSend]; [self maybeDequeueSend]; - + return; } - - // + + // // 2. Query sendFilter (if applicable) - // - + // + if (sendFilterBlock && sendFilterQueue) { // Query sendFilter - + if (sendFilterAsync) { // Scenario 1 of 3 - Need to asynchronously query sendFilter - + currentSend->filterInProgress = YES; GCDAsyncUdpSendPacket *sendPacket = currentSend; - + dispatch_async(sendFilterQueue, ^{ @autoreleasepool { - + BOOL allowed = sendFilterBlock(sendPacket->buffer, sendPacket->address, sendPacket->tag); - + dispatch_async(socketQueue, ^{ @autoreleasepool { - + sendPacket->filterInProgress = NO; if (sendPacket == currentSend) { @@ -3962,7 +3954,7 @@ - (void)doPreSend else { LogVerbose(@"currentSend - silently dropped by sendFilter"); - + [self notifyDidSendDataWithTag:currentSend->tag]; [self endCurrentSend]; [self maybeDequeueSend]; @@ -3974,14 +3966,14 @@ - (void)doPreSend else { // Scenario 2 of 3 - Need to synchronously query sendFilter - + __block BOOL allowed = YES; - + dispatch_sync(sendFilterQueue, ^{ @autoreleasepool { - + allowed = sendFilterBlock(currentSend->buffer, currentSend->address, currentSend->tag); }}); - + if (allowed) { [self doSend]; @@ -3989,7 +3981,7 @@ - (void)doPreSend else { LogVerbose(@"currentSend - silently dropped by sendFilter"); - + [self notifyDidSendDataWithTag:currentSend->tag]; [self endCurrentSend]; [self maybeDequeueSend]; @@ -3999,32 +3991,32 @@ - (void)doPreSend else // if (!sendFilterBlock || !sendFilterQueue) { // Scenario 3 of 3 - No sendFilter. Just go straight into sending. - + [self doSend]; } } /** * This method performs the actual sending of data in the currentSend packet. - * It should only be called if the + * It should only be called if the **/ - (void)doSend { LogTrace(); - + NSAssert(currentSend != nil, @"Invalid logic"); - + // Perform the actual send - + ssize_t result = 0; - + if (flags & kDidConnect) { // Connected socket - + const void *buffer = [currentSend->buffer bytes]; size_t length = (size_t)[currentSend->buffer length]; - + if (currentSend->addressFamily == AF_INET) { result = send(socket4FD, buffer, length, 0); @@ -4039,13 +4031,13 @@ - (void)doSend else { // Non-Connected socket - + const void *buffer = [currentSend->buffer bytes]; size_t length = (size_t)[currentSend->buffer length]; - + const void *dst = [currentSend->address bytes]; socklen_t dstSize = (socklen_t)[currentSend->address length]; - + if (currentSend->addressFamily == AF_INET) { result = sendto(socket4FD, buffer, length, 0, dst, dstSize); @@ -4057,24 +4049,24 @@ - (void)doSend LogVerbose(@"sendto(socket6FD) = %d", result); } } - + // If the socket wasn't bound before, it is now - + if ((flags & kDidBind) == 0) { flags |= kDidBind; } - + // Check the results. - // + // // From the send() & sendto() manpage: - // + // // Upon successful completion, the number of bytes which were sent is returned. // Otherwise, -1 is returned and the global variable errno is set to indicate the error. - + BOOL waitingForSocket = NO; NSError *socketError = nil; - + if (result == 0) { waitingForSocket = YES; @@ -4086,26 +4078,26 @@ - (void)doSend else socketError = [self errnoErrorWithReason:@"Error in send() function."]; } - + if (waitingForSocket) { // Not enough room in the underlying OS socket send buffer. // Wait for a notification of available space. - + LogVerbose(@"currentSend - waiting for socket"); - + if (!(flags & kSock4CanAcceptBytes)) { [self resumeSend4Source]; } if (!(flags & kSock6CanAcceptBytes)) { [self resumeSend6Source]; } - + if ((sendTimer == NULL) && (currentSend->timeout >= 0.0)) { // Unable to send packet right away. // Start timer to timeout the send operation. - + [self setupSendTimerWithTimeout:currentSend->timeout]; } } @@ -4134,7 +4126,7 @@ - (void)endCurrentSend #endif sendTimer = NULL; } - + currentSend = nil; } @@ -4144,7 +4136,7 @@ - (void)endCurrentSend - (void)doSendTimeout { LogTrace(); - + [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:[self sendTimeoutError]]; [self endCurrentSend]; [self maybeDequeueSend]; @@ -4158,18 +4150,18 @@ - (void)setupSendTimerWithTimeout:(NSTimeInterval)timeout { NSAssert(sendTimer == NULL, @"Invalid logic"); NSAssert(timeout >= 0.0, @"Invalid logic"); - + LogTrace(); - + sendTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, socketQueue); - + dispatch_source_set_event_handler(sendTimer, ^{ @autoreleasepool { - + [self doSendTimeout]; }}); - + dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)); - + dispatch_source_set_timer(sendTimer, tt, DISPATCH_TIME_FOREVER, 0); dispatch_resume(sendTimer); } @@ -4181,104 +4173,104 @@ - (void)setupSendTimerWithTimeout:(NSTimeInterval)timeout - (BOOL)receiveOnce:(NSError **)errPtr { LogTrace(); - + __block BOOL result = NO; __block NSError *err = nil; - + dispatch_block_t block = ^{ - + if ((flags & kReceiveOnce) == 0) { if ((flags & kDidCreateSockets) == 0) { NSString *msg = @"Must bind socket before you can receive data. " @"You can do this explicitly via bind, or implicitly via connect or by sending data."; - + err = [self badConfigError:msg]; return_from_block; } - + flags |= kReceiveOnce; // Enable flags &= ~kReceiveContinuous; // Disable - + dispatch_async(socketQueue, ^{ @autoreleasepool { - + [self doReceive]; }}); } - + result = YES; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + if (err) LogError(@"Error in beginReceiving: %@", err); - + if (errPtr) *errPtr = err; - + return result; } - (BOOL)beginReceiving:(NSError **)errPtr { LogTrace(); - + __block BOOL result = NO; __block NSError *err = nil; - + dispatch_block_t block = ^{ - + if ((flags & kReceiveContinuous) == 0) { if ((flags & kDidCreateSockets) == 0) { NSString *msg = @"Must bind socket before you can receive data. " @"You can do this explicitly via bind, or implicitly via connect or by sending data."; - + err = [self badConfigError:msg]; return_from_block; } - + flags |= kReceiveContinuous; // Enable flags &= ~kReceiveOnce; // Disable - + dispatch_async(socketQueue, ^{ @autoreleasepool { - + [self doReceive]; }}); } - + result = YES; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - + if (err) LogError(@"Error in beginReceiving: %@", err); - + if (errPtr) *errPtr = err; - + return result; } - (void)pauseReceiving { LogTrace(); - + dispatch_block_t block = ^{ - + flags &= ~kReceiveOnce; // Disable flags &= ~kReceiveContinuous; // Disable - + if (socket4FDBytesAvailable > 0) { [self suspendReceive4Source]; } @@ -4286,7 +4278,7 @@ - (void)pauseReceiving [self suspendReceive6Source]; } }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -4304,29 +4296,29 @@ - (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock { GCDAsyncUdpSocketReceiveFilterBlock newFilterBlock = NULL; dispatch_queue_t newFilterQueue = NULL; - + if (filterBlock) { NSAssert(filterQueue, @"Must provide a dispatch_queue in which to run the filter block."); - + newFilterBlock = [filterBlock copy]; newFilterQueue = filterQueue; #if !OS_OBJECT_USE_OBJC dispatch_retain(newFilterQueue); #endif } - + dispatch_block_t block = ^{ - + #if !OS_OBJECT_USE_OBJC if (receiveFilterQueue) dispatch_release(receiveFilterQueue); #endif - + receiveFilterBlock = newFilterBlock; receiveFilterQueue = newFilterQueue; receiveFilterAsync = isAsynchronous; }; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -4336,71 +4328,71 @@ - (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock - (void)doReceive { LogTrace(); - + if ((flags & (kReceiveOnce | kReceiveContinuous)) == 0) { LogVerbose(@"Receiving is paused..."); - + if (socket4FDBytesAvailable > 0) { [self suspendReceive4Source]; } if (socket6FDBytesAvailable > 0) { [self suspendReceive6Source]; } - + return; } - + if ((flags & kReceiveOnce) && (pendingFilterOperations > 0)) { LogVerbose(@"Receiving is temporarily paused (pending filter operations)..."); - + if (socket4FDBytesAvailable > 0) { [self suspendReceive4Source]; } if (socket6FDBytesAvailable > 0) { [self suspendReceive6Source]; } - + return; } - + if ((socket4FDBytesAvailable == 0) && (socket6FDBytesAvailable == 0)) { LogVerbose(@"No data available to receive..."); - + if (socket4FDBytesAvailable == 0) { [self resumeReceive4Source]; } if (socket6FDBytesAvailable == 0) { [self resumeReceive6Source]; } - + return; } - + // Figure out if we should receive on socket4 or socket6 - + BOOL doReceive4; - + if (flags & kDidConnect) { // Connected socket - + doReceive4 = (socket4FD != SOCKET_NULL); } else { // Non-Connected socket - + if (socket4FDBytesAvailable > 0) { if (socket6FDBytesAvailable > 0) { // Bytes available on socket4 & socket6 - + doReceive4 = (flags & kFlipFlop) ? YES : NO; - + flags ^= kFlipFlop; // flags = flags xor kFlipFlop; (toggle flip flop bit) } else { @@ -4413,42 +4405,42 @@ - (void)doReceive doReceive4 = NO; } } - + // Perform socket IO - + ssize_t result = 0; - + NSData *data = nil; NSData *addr4 = nil; NSData *addr6 = nil; - + if (doReceive4) { NSAssert(socket4FDBytesAvailable > 0, @"Invalid logic"); LogVerbose(@"Receiving on IPv4"); - + struct sockaddr_in sockaddr4; socklen_t sockaddr4len = sizeof(sockaddr4); - - // #222: GCD does not necessarily return the size of an entire UDP packet + + // #222: GCD does not necessarily return the size of an entire UDP packet // from dispatch_source_get_data(), so we must use the maximum packet size. size_t bufSize = max4ReceiveSize; void *buf = malloc(bufSize); - + result = recvfrom(socket4FD, buf, bufSize, 0, (struct sockaddr *)&sockaddr4, &sockaddr4len); LogVerbose(@"recvfrom(socket4FD) = %i", (int)result); - + if (result > 0) { if ((size_t)result >= socket4FDBytesAvailable) socket4FDBytesAvailable = 0; else socket4FDBytesAvailable -= result; - + if ((size_t)result != bufSize) { buf = realloc(buf, result); } - + data = [NSData dataWithBytesNoCopy:buf length:result freeWhenDone:YES]; addr4 = [NSData dataWithBytes:&sockaddr4 length:sockaddr4len]; } @@ -4463,29 +4455,29 @@ - (void)doReceive { NSAssert(socket6FDBytesAvailable > 0, @"Invalid logic"); LogVerbose(@"Receiving on IPv6"); - + struct sockaddr_in6 sockaddr6; socklen_t sockaddr6len = sizeof(sockaddr6); - - // #222: GCD does not necessarily return the size of an entire UDP packet + + // #222: GCD does not necessarily return the size of an entire UDP packet // from dispatch_source_get_data(), so we must use the maximum packet size. size_t bufSize = max6ReceiveSize; void *buf = malloc(bufSize); - + result = recvfrom(socket6FD, buf, bufSize, 0, (struct sockaddr *)&sockaddr6, &sockaddr6len); LogVerbose(@"recvfrom(socket6FD) -> %i", (int)result); - + if (result > 0) { if ((size_t)result >= socket6FDBytesAvailable) socket6FDBytesAvailable = 0; else socket6FDBytesAvailable -= result; - + if ((size_t)result != bufSize) { buf = realloc(buf, result); } - + data = [NSData dataWithBytesNoCopy:buf length:result freeWhenDone:YES]; addr6 = [NSData dataWithBytes:&sockaddr6 length:sockaddr6len]; } @@ -4496,14 +4488,14 @@ - (void)doReceive free(buf); } } - - + + BOOL waitingForSocket = NO; BOOL notifiedDelegate = NO; BOOL ignored = NO; - + NSError *socketError = nil; - + if (result == 0) { waitingForSocket = YES; @@ -4524,30 +4516,30 @@ - (void)doReceive if (addr6 && ![self isConnectedToAddress6:addr6]) ignored = YES; } - + NSData *addr = (addr4 != nil) ? addr4 : addr6; - + if (!ignored) { if (receiveFilterBlock && receiveFilterQueue) { // Run data through filter, and if approved, notify delegate - + __block id filterContext = nil; __block BOOL allowed = NO; - + if (receiveFilterAsync) { pendingFilterOperations++; dispatch_async(receiveFilterQueue, ^{ @autoreleasepool { - + allowed = receiveFilterBlock(data, addr, &filterContext); - + // Transition back to socketQueue to get the current delegate / delegateQueue dispatch_async(socketQueue, ^{ @autoreleasepool { - + pendingFilterOperations--; - + if (allowed) { [self notifyDidReceiveData:data fromAddress:addr withFilterContext:filterContext]; @@ -4556,7 +4548,7 @@ - (void)doReceive { LogVerbose(@"received packet silently dropped by receiveFilter"); } - + if (flags & kReceiveOnce) { if (allowed) @@ -4579,10 +4571,10 @@ - (void)doReceive else // if (!receiveFilterAsync) { dispatch_sync(receiveFilterQueue, ^{ @autoreleasepool { - + allowed = receiveFilterBlock(data, addr, &filterContext); }}); - + if (allowed) { [self notifyDidReceiveData:data fromAddress:addr withFilterContext:filterContext]; @@ -4602,11 +4594,11 @@ - (void)doReceive } } } - + if (waitingForSocket) { // Wait for a notification of available data. - + if (socket4FDBytesAvailable == 0) { [self resumeReceive4Source]; } @@ -4649,7 +4641,7 @@ - (void)doReceive - (void)doReceiveEOF { LogTrace(); - + [self closeWithError:[self socketClosedError]]; } @@ -4660,26 +4652,26 @@ - (void)doReceiveEOF - (void)closeWithError:(NSError *)error { LogVerbose(@"closeWithError: %@", error); - + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + if (currentSend) [self endCurrentSend]; - + [sendQueue removeAllObjects]; - + // If a socket has been created, we should notify the delegate. BOOL shouldCallDelegate = (flags & kDidCreateSockets) ? YES : NO; - + // Close all sockets, send/receive sources, cfstreams, etc #if TARGET_OS_IPHONE [self removeStreamsFromRunLoop]; [self closeReadAndWriteStreams]; #endif [self closeSockets]; - + // Clear all flags (config remains as is) flags = 0; - + if (shouldCallDelegate) { [self notifyDidCloseWithError:error]; @@ -4689,12 +4681,12 @@ - (void)closeWithError:(NSError *)error - (void)close { LogTrace(); - + dispatch_block_t block = ^{ @autoreleasepool { - + [self closeWithError:nil]; }}; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -4704,17 +4696,17 @@ - (void)close - (void)closeAfterSending { LogTrace(); - + dispatch_block_t block = ^{ @autoreleasepool { - + flags |= kCloseAfterSends; - + if (currentSend == nil && [sendQueue count] == 0) { [self closeWithError:nil]; } }}; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -4736,7 +4728,7 @@ + (void)startListenerThreadIfNeeded { static dispatch_once_t predicate; dispatch_once(&predicate, ^{ - + listenerThread = [[NSThread alloc] initWithTarget:self selector:@selector(listenerThread) object:nil]; @@ -4747,11 +4739,11 @@ + (void)startListenerThreadIfNeeded + (void)listenerThread { @autoreleasepool { - + [[NSThread currentThread] setName:GCDAsyncUdpSocketThreadName]; - + LogInfo(@"ListenerThread: Started"); - + // We can't run the run loop unless it has an associated input source or a timer. // So we'll just create a timer that will never fire - unless the server runs for a decades. [NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow] @@ -4759,9 +4751,9 @@ + (void)listenerThread selector:@selector(ignore:) userInfo:nil repeats:YES]; - + [[NSRunLoop currentRunLoop] run]; - + LogInfo(@"ListenerThread: Stopped"); } } @@ -4770,18 +4762,18 @@ + (void)addStreamListener:(GCDAsyncUdpSocket *)asyncUdpSocket { LogTrace(); NSAssert([NSThread currentThread] == listenerThread, @"Invoked on wrong thread"); - + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); - + if (asyncUdpSocket->readStream4) CFReadStreamScheduleWithRunLoop(asyncUdpSocket->readStream4, runLoop, kCFRunLoopDefaultMode); - + if (asyncUdpSocket->readStream6) CFReadStreamScheduleWithRunLoop(asyncUdpSocket->readStream6, runLoop, kCFRunLoopDefaultMode); - + if (asyncUdpSocket->writeStream4) CFWriteStreamScheduleWithRunLoop(asyncUdpSocket->writeStream4, runLoop, kCFRunLoopDefaultMode); - + if (asyncUdpSocket->writeStream6) CFWriteStreamScheduleWithRunLoop(asyncUdpSocket->writeStream6, runLoop, kCFRunLoopDefaultMode); } @@ -4790,18 +4782,18 @@ + (void)removeStreamListener:(GCDAsyncUdpSocket *)asyncUdpSocket { LogTrace(); NSAssert([NSThread currentThread] == listenerThread, @"Invoked on wrong thread"); - + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); - + if (asyncUdpSocket->readStream4) CFReadStreamUnscheduleFromRunLoop(asyncUdpSocket->readStream4, runLoop, kCFRunLoopDefaultMode); - + if (asyncUdpSocket->readStream6) CFReadStreamUnscheduleFromRunLoop(asyncUdpSocket->readStream6, runLoop, kCFRunLoopDefaultMode); - + if (asyncUdpSocket->writeStream4) CFWriteStreamUnscheduleFromRunLoop(asyncUdpSocket->writeStream4, runLoop, kCFRunLoopDefaultMode); - + if (asyncUdpSocket->writeStream6) CFWriteStreamUnscheduleFromRunLoop(asyncUdpSocket->writeStream6, runLoop, kCFRunLoopDefaultMode); } @@ -4810,7 +4802,7 @@ static void CFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, { @autoreleasepool { GCDAsyncUdpSocket *asyncUdpSocket = (__bridge GCDAsyncUdpSocket *)pInfo; - + switch(type) { case kCFStreamEventOpenCompleted: @@ -4831,23 +4823,23 @@ static void CFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, { error = [asyncUdpSocket socketClosedError]; } - + dispatch_async(asyncUdpSocket->socketQueue, ^{ @autoreleasepool { - + LogCVerbose(@"CFReadStreamCallback - %@", (type == kCFStreamEventErrorOccurred) ? @"Error" : @"EndEncountered"); - + if (stream != asyncUdpSocket->readStream4 && stream != asyncUdpSocket->readStream6 ) { LogCVerbose(@"CFReadStreamCallback - Ignored"); return_from_block; } - + [asyncUdpSocket closeWithError:error]; - + }}); - + break; } default: @@ -4862,7 +4854,7 @@ static void CFWriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType typ { @autoreleasepool { GCDAsyncUdpSocket *asyncUdpSocket = (__bridge GCDAsyncUdpSocket *)pInfo; - + switch(type) { case kCFStreamEventOpenCompleted: @@ -4883,23 +4875,23 @@ static void CFWriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType typ { error = [asyncUdpSocket socketClosedError]; } - + dispatch_async(asyncUdpSocket->socketQueue, ^{ @autoreleasepool { - + LogCVerbose(@"CFWriteStreamCallback - %@", (type == kCFStreamEventErrorOccurred) ? @"Error" : @"EndEncountered"); - + if (stream != asyncUdpSocket->writeStream4 && stream != asyncUdpSocket->writeStream6 ) { LogCVerbose(@"CFWriteStreamCallback - Ignored"); return_from_block; } - + [asyncUdpSocket closeWithError:error]; - + }}); - + break; } default: @@ -4914,25 +4906,25 @@ - (BOOL)createReadAndWriteStreams:(NSError **)errPtr { LogTrace(); NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + NSError *err = nil; - + if (readStream4 || writeStream4 || readStream6 || writeStream6) { // Streams already created return YES; } - + if (socket4FD == SOCKET_NULL && socket6FD == SOCKET_NULL) { err = [self otherError:@"Cannot create streams without a file descriptor"]; goto Failed; } - + // Create streams - + LogVerbose(@"Creating read and write stream(s)..."); - + if (socket4FD != SOCKET_NULL) { CFStreamCreatePairWithSocket(NULL, (CFSocketNativeHandle)socket4FD, &readStream4, &writeStream4); @@ -4942,7 +4934,7 @@ - (BOOL)createReadAndWriteStreams:(NSError **)errPtr goto Failed; } } - + if (socket6FD != SOCKET_NULL) { CFStreamCreatePairWithSocket(NULL, (CFSocketNativeHandle)socket6FD, &readStream6, &writeStream6); @@ -4952,17 +4944,17 @@ - (BOOL)createReadAndWriteStreams:(NSError **)errPtr goto Failed; } } - + // Ensure the CFStream's don't close our underlying socket - + CFReadStreamSetProperty(readStream4, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); CFWriteStreamSetProperty(writeStream4, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); - + CFReadStreamSetProperty(readStream6, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); CFWriteStreamSetProperty(writeStream6, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse); - + return YES; - + Failed: if (readStream4) { @@ -4988,34 +4980,34 @@ - (BOOL)createReadAndWriteStreams:(NSError **)errPtr CFRelease(writeStream6); writeStream6 = NULL; } - + if (errPtr) *errPtr = err; - + return NO; } - (BOOL)registerForStreamCallbacks:(NSError **)errPtr { LogTrace(); - + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(readStream4 || writeStream4 || readStream6 || writeStream6, @"Read/Write streams are null"); - + NSError *err = nil; - + streamContext.version = 0; streamContext.info = (__bridge void *)self; streamContext.retain = nil; streamContext.release = nil; streamContext.copyDescription = nil; - + CFOptionFlags readStreamEvents = kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; CFOptionFlags writeStreamEvents = kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered; - + // readStreamEvents |= (kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable); // writeStreamEvents |= (kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes); - + if (socket4FD != SOCKET_NULL) { if (readStream4 == NULL || writeStream4 == NULL) @@ -5023,17 +5015,17 @@ - (BOOL)registerForStreamCallbacks:(NSError **)errPtr err = [self otherError:@"Read/Write stream4 is null"]; goto Failed; } - + BOOL r1 = CFReadStreamSetClient(readStream4, readStreamEvents, &CFReadStreamCallback, &streamContext); BOOL r2 = CFWriteStreamSetClient(writeStream4, writeStreamEvents, &CFWriteStreamCallback, &streamContext); - + if (!r1 || !r2) { err = [self otherError:@"Error in CFStreamSetClient(), [IPv4]"]; goto Failed; } } - + if (socket6FD != SOCKET_NULL) { if (readStream6 == NULL || writeStream6 == NULL) @@ -5041,19 +5033,19 @@ - (BOOL)registerForStreamCallbacks:(NSError **)errPtr err = [self otherError:@"Read/Write stream6 is null"]; goto Failed; } - + BOOL r1 = CFReadStreamSetClient(readStream6, readStreamEvents, &CFReadStreamCallback, &streamContext); BOOL r2 = CFWriteStreamSetClient(writeStream6, writeStreamEvents, &CFWriteStreamCallback, &streamContext); - + if (!r1 || !r2) { err = [self otherError:@"Error in CFStreamSetClient() [IPv6]"]; goto Failed; } } - + return YES; - + Failed: if (readStream4) { CFReadStreamSetClient(readStream4, kCFStreamEventNone, NULL, NULL); @@ -5067,7 +5059,7 @@ - (BOOL)registerForStreamCallbacks:(NSError **)errPtr if (writeStream6) { CFWriteStreamSetClient(writeStream6, kCFStreamEventNone, NULL, NULL); } - + if (errPtr) *errPtr = err; return NO; } @@ -5075,10 +5067,10 @@ - (BOOL)registerForStreamCallbacks:(NSError **)errPtr - (BOOL)addStreamsToRunLoop:(NSError **)errPtr { LogTrace(); - + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(readStream4 || writeStream4 || readStream6 || writeStream6, @"Read/Write streams are null"); - + if (!(flags & kAddedStreamListener)) { [[self class] startListenerThreadIfNeeded]; @@ -5086,48 +5078,48 @@ - (BOOL)addStreamsToRunLoop:(NSError **)errPtr onThread:listenerThread withObject:self waitUntilDone:YES]; - + flags |= kAddedStreamListener; } - + return YES; } - (BOOL)openStreams:(NSError **)errPtr { LogTrace(); - + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(readStream4 || writeStream4 || readStream6 || writeStream6, @"Read/Write streams are null"); - + NSError *err = nil; - + if (socket4FD != SOCKET_NULL) { BOOL r1 = CFReadStreamOpen(readStream4); BOOL r2 = CFWriteStreamOpen(writeStream4); - + if (!r1 || !r2) { err = [self otherError:@"Error in CFStreamOpen() [IPv4]"]; goto Failed; } } - + if (socket6FD != SOCKET_NULL) { BOOL r1 = CFReadStreamOpen(readStream6); BOOL r2 = CFWriteStreamOpen(writeStream6); - + if (!r1 || !r2) { err = [self otherError:@"Error in CFStreamOpen() [IPv6]"]; goto Failed; } } - + return YES; - + Failed: if (errPtr) *errPtr = err; return NO; @@ -5137,14 +5129,14 @@ - (void)removeStreamsFromRunLoop { LogTrace(); NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); - + if (flags & kAddedStreamListener) { [[self class] performSelector:@selector(removeStreamListener:) onThread:listenerThread withObject:self waitUntilDone:YES]; - + flags &= ~kAddedStreamListener; } } @@ -5152,7 +5144,7 @@ - (void)removeStreamsFromRunLoop - (void)closeReadAndWriteStreams { LogTrace(); - + if (readStream4) { CFReadStreamSetClient(readStream4, kCFStreamEventNone, NULL, NULL); @@ -5188,16 +5180,16 @@ - (void)closeReadAndWriteStreams - (void)applicationWillEnterForeground:(NSNotification *)notification { LogTrace(); - + // If the application was backgrounded, then iOS may have shut down our sockets. // So we take a quick look to see if any of them received an EOF. - + dispatch_block_t block = ^{ @autoreleasepool { - + [self resumeReceive4Source]; [self resumeReceive6Source]; }}; - + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else @@ -5241,7 +5233,7 @@ - (int)socketFD THIS_FILE, THIS_METHOD); return SOCKET_NULL; } - + if (socket4FD != SOCKET_NULL) return socket4FD; else @@ -5256,7 +5248,7 @@ - (int)socket4FD THIS_FILE, THIS_METHOD); return SOCKET_NULL; } - + return socket4FD; } @@ -5268,7 +5260,7 @@ - (int)socket6FD THIS_FILE, THIS_METHOD); return SOCKET_NULL; } - + return socket6FD; } @@ -5282,16 +5274,16 @@ - (CFReadStreamRef)readStream THIS_FILE, THIS_METHOD); return NULL; } - + NSError *err = nil; if (![self createReadAndWriteStreams:&err]) { LogError(@"Error creating CFStream(s): %@", err); return NULL; } - + // Todo... - + if (readStream4) return readStream4; else @@ -5306,14 +5298,14 @@ - (CFWriteStreamRef)writeStream THIS_FILE, THIS_METHOD); return NULL; } - + NSError *err = nil; if (![self createReadAndWriteStreams:&err]) { LogError(@"Error creating CFStream(s): %@", err); return NULL; } - + if (writeStream4) return writeStream4; else @@ -5328,54 +5320,54 @@ - (BOOL)enableBackgroundingOnSockets THIS_FILE, THIS_METHOD); return NO; } - + // Why is this commented out? // See comments below. - + // NSError *err = nil; // if (![self createReadAndWriteStreams:&err]) // { // LogError(@"Error creating CFStream(s): %@", err); // return NO; // } -// +// // LogVerbose(@"Enabling backgrouding on socket"); -// +// // BOOL r1, r2; -// +// // if (readStream4 && writeStream4) // { // r1 = CFReadStreamSetProperty(readStream4, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); // r2 = CFWriteStreamSetProperty(writeStream4, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); -// +// // if (!r1 || !r2) // { // LogError(@"Error setting voip type (IPv4)"); // return NO; // } // } -// +// // if (readStream6 && writeStream6) // { // r1 = CFReadStreamSetProperty(readStream6, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); // r2 = CFWriteStreamSetProperty(writeStream6, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP); -// +// // if (!r1 || !r2) // { // LogError(@"Error setting voip type (IPv6)"); // return NO; // } // } -// +// // return YES; - + // The above code will actually appear to work. // The methods will return YES, and everything will appear fine. - // + // // One tiny problem: the sockets will still get closed when the app gets backgrounded. - // + // // Apple does not officially support backgrounding UDP sockets. - + return NO; } @@ -5388,24 +5380,24 @@ - (BOOL)enableBackgroundingOnSockets + (NSString *)hostFromSockaddr4:(const struct sockaddr_in *)pSockaddr4 { char addrBuf[INET_ADDRSTRLEN]; - + if (inet_ntop(AF_INET, &pSockaddr4->sin_addr, addrBuf, (socklen_t)sizeof(addrBuf)) == NULL) { addrBuf[0] = '\0'; } - + return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding]; } + (NSString *)hostFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6 { char addrBuf[INET6_ADDRSTRLEN]; - + if (inet_ntop(AF_INET6, &pSockaddr6->sin6_addr, addrBuf, (socklen_t)sizeof(addrBuf)) == NULL) { addrBuf[0] = '\0'; } - + return [NSString stringWithCString:addrBuf encoding:NSASCIIStringEncoding]; } @@ -5423,7 +5415,7 @@ + (NSString *)hostFromAddress:(NSData *)address { NSString *host = nil; [self getHost:&host port:NULL family:NULL fromAddress:address]; - + return host; } @@ -5431,7 +5423,7 @@ + (uint16_t)portFromAddress:(NSData *)address { uint16_t port = 0; [self getHost:NULL port:&port family:NULL fromAddress:address]; - + return port; } @@ -5439,7 +5431,7 @@ + (int)familyFromAddress:(NSData *)address { int af = AF_UNSPEC; [self getHost:NULL port:NULL family:&af fromAddress:address]; - + return af; } @@ -5447,7 +5439,7 @@ + (BOOL)isIPv4Address:(NSData *)address { int af = AF_UNSPEC; [self getHost:NULL port:NULL family:&af fromAddress:address]; - + return (af == AF_INET); } @@ -5455,7 +5447,7 @@ + (BOOL)isIPv6Address:(NSData *)address { int af = AF_UNSPEC; [self getHost:NULL port:NULL family:&af fromAddress:address]; - + return (af == AF_INET6); } @@ -5469,17 +5461,17 @@ + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr family:(int *)afPt if ([address length] >= sizeof(struct sockaddr)) { const struct sockaddr *addrX = (const struct sockaddr *)[address bytes]; - + if (addrX->sa_family == AF_INET) { if ([address length] >= sizeof(struct sockaddr_in)) { const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addrX; - + if (hostPtr) *hostPtr = [self hostFromSockaddr4:addr4]; if (portPtr) *portPtr = [self portFromSockaddr4:addr4]; if (afPtr) *afPtr = AF_INET; - + return YES; } } @@ -5488,20 +5480,20 @@ + (BOOL)getHost:(NSString **)hostPtr port:(uint16_t *)portPtr family:(int *)afPt if ([address length] >= sizeof(struct sockaddr_in6)) { const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addrX; - + if (hostPtr) *hostPtr = [self hostFromSockaddr6:addr6]; if (portPtr) *portPtr = [self portFromSockaddr6:addr6]; if (afPtr) *afPtr = AF_INET6; - + return YES; } } } - + if (hostPtr) *hostPtr = nil; if (portPtr) *portPtr = 0; if (afPtr) *afPtr = AF_UNSPEC; - + return NO; } From d9f1ffc9f20516793ad282675b9a0abd602c216d Mon Sep 17 00:00:00 2001 From: Dominique Rau Date: Mon, 24 Dec 2018 02:38:16 +0100 Subject: [PATCH 10/11] Use address instead of host name --- android/src/main/java/com/tradle/react/UdpReceiverTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/tradle/react/UdpReceiverTask.java b/android/src/main/java/com/tradle/react/UdpReceiverTask.java index ff45ce68..75eaa072 100644 --- a/android/src/main/java/com/tradle/react/UdpReceiverTask.java +++ b/android/src/main/java/com/tradle/react/UdpReceiverTask.java @@ -47,7 +47,7 @@ protected Void doInBackground(Pair Date: Fri, 29 Mar 2019 01:35:41 +0100 Subject: [PATCH 11/11] Update react-native-udp.podspec --- react-native-udp.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/react-native-udp.podspec b/react-native-udp.podspec index e396fe06..0375af9b 100644 --- a/react-native-udp.podspec +++ b/react-native-udp.podspec @@ -12,6 +12,6 @@ Pod::Spec.new do |s| s.platform = :ios, "7.0" s.source = { :git => package_json["repository"]["url"].gsub(/(http.*)/).first, :tag => "v#{s.version}" } s.source_files = 'ios/**/*.{h,m}' - s.dependency 'React' - + + s.dependency "React" end