react-native-zeroconf 0.17.2 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +2 -6
- package/README.md +1 -1
- package/android/CMakeLists.txt +64 -0
- package/android/build.gradle +1 -13
- package/android/generated/java/com/balthazargronon/RCTZeroconf/NativeZeroconfSpec.java +96 -0
- package/android/generated/jni/CMakeLists.txt +28 -0
- package/android/generated/jni/RNZeroconfSpec-generated.cpp +77 -0
- package/android/generated/jni/RNZeroconfSpec.h +31 -0
- package/android/generated/jni/react/renderer/components/RNZeroconfSpec/RNZeroconfSpecJSI.h +798 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/{ZeroconfModule.java → NsdHost.java} +97 -140
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPayload.java +87 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPromise.java +40 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/Zeroconf.java +4 -6
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfNativeSupport.java +60 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfOptions.java +15 -26
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfReactPackage.java +5 -15
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/NsdServiceImpl.java +90 -96
- package/android/src/main/jni/mdnsresponder/mDNSPosix/PosixDaemon.c +19 -1
- package/android/src/main/jni/mdnsresponder/mDNSPosix/mDNSPosix.c +104 -1
- package/android/src/main/jni/mdnsresponder/mDNSPosix/mDNSPosix.h +7 -0
- package/android/src/main/jni/mdnsresponder/mDNSShared/dnssd_clientshim.c +34 -9
- package/cpp/Backend.cpp +16 -0
- package/cpp/Backend.h +116 -0
- package/cpp/ZeroconfModule.cpp +280 -0
- package/cpp/ZeroconfModule.h +144 -0
- package/cpp/android/NsdBackend.cpp +462 -0
- package/cpp/android/NsdBackend.h +61 -0
- package/cpp/android/ZeroconfAndroidModule.cpp +49 -0
- package/cpp/android/ZeroconfAndroidModule.h +15 -0
- package/cpp/apple/DispatchExecutor.cpp +66 -0
- package/cpp/apple/DispatchExecutor.h +37 -0
- package/cpp/apple/LocalNetworkAccess.h +19 -0
- package/cpp/apple/LocalNetworkAccess.mm +87 -0
- package/cpp/dnssd/DnssdBackend.cpp +976 -0
- package/cpp/dnssd/DnssdBackend.h +114 -0
- package/cpp/dnssd/Executor.h +28 -0
- package/cpp/embedded/EmbeddedExecutor.cpp +124 -0
- package/cpp/embedded/EmbeddedExecutor.h +57 -0
- package/ios/ZeroconfOnLoad.mm +34 -0
- package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec-generated.mm +140 -0
- package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec.h +189 -0
- package/ios/generated/ReactCodegen/RNZeroconfSpecJSI.h +798 -0
- package/lib/module/NativeZeroconf.js +15 -0
- package/lib/module/NativeZeroconf.js.map +1 -0
- package/lib/module/bridge.js +124 -0
- package/lib/module/bridge.js.map +1 -0
- package/{dist → lib/module}/index.js +149 -149
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +24 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeZeroconf.d.ts +75 -0
- package/lib/typescript/src/NativeZeroconf.d.ts.map +1 -0
- package/lib/typescript/src/bridge.d.ts +22 -0
- package/lib/typescript/src/bridge.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +111 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +175 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +65 -29
- package/react-native-zeroconf.podspec +2 -1
- package/react-native.config.js +17 -0
- package/src/NativeZeroconf.ts +120 -0
- package/src/bridge.ts +153 -0
- package/src/index.ts +607 -0
- package/{index.d.ts → src/types.ts} +5 -99
- package/windows/RNZeroconf/RNZeroconf.vcxproj +17 -2
- package/windows/RNZeroconf/RNZeroconf.vcxproj.filters +0 -6
- package/windows/RNZeroconf/ReactPackageProvider.cpp +23 -20
- package/windows/RNZeroconf/WindowsBackend.cpp +156 -0
- package/windows/RNZeroconf/WindowsBackend.h +13 -0
- package/windows/RNZeroconf/ZeroconfCore.cpp +2 -2
- package/windows/RNZeroconf/ZeroconfCore.h +2 -2
- package/windows/RNZeroconf/ZeroconfWindowsModule.cpp +22 -0
- package/windows/RNZeroconf/ZeroconfWindowsModule.h +14 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroConfImplFactory.java +0 -56
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/dnssd/DnssdImpl.java +0 -654
- package/android/src/main/java/com/github/druk/dnssd/BaseListener.java +0 -33
- package/android/src/main/java/com/github/druk/dnssd/BrowseListener.java +0 -67
- package/android/src/main/java/com/github/druk/dnssd/DNSRecord.java +0 -47
- package/android/src/main/java/com/github/druk/dnssd/DNSSD.java +0 -650
- package/android/src/main/java/com/github/druk/dnssd/DNSSDEmbedded.java +0 -135
- package/android/src/main/java/com/github/druk/dnssd/DNSSDException.java +0 -66
- package/android/src/main/java/com/github/druk/dnssd/DNSSDRecordRegistrar.java +0 -55
- package/android/src/main/java/com/github/druk/dnssd/DNSSDRegistration.java +0 -54
- package/android/src/main/java/com/github/druk/dnssd/DNSSDService.java +0 -35
- package/android/src/main/java/com/github/druk/dnssd/DomainListener.java +0 -57
- package/android/src/main/java/com/github/druk/dnssd/InternalBrowseListener.java +0 -76
- package/android/src/main/java/com/github/druk/dnssd/InternalDNSSD.java +0 -870
- package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRecordRegistrar.java +0 -30
- package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRegistration.java +0 -34
- package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDService.java +0 -29
- package/android/src/main/java/com/github/druk/dnssd/InternalDomainListener.java +0 -62
- package/android/src/main/java/com/github/druk/dnssd/InternalQueryListener.java +0 -61
- package/android/src/main/java/com/github/druk/dnssd/InternalRegisterListener.java +0 -51
- package/android/src/main/java/com/github/druk/dnssd/InternalResolveListener.java +0 -58
- package/android/src/main/java/com/github/druk/dnssd/NSClass.java +0 -16
- package/android/src/main/java/com/github/druk/dnssd/NSType.java +0 -58
- package/android/src/main/java/com/github/druk/dnssd/QueryListener.java +0 -54
- package/android/src/main/java/com/github/druk/dnssd/RegisterListener.java +0 -46
- package/android/src/main/java/com/github/druk/dnssd/RegisterRecordListener.java +0 -35
- package/android/src/main/java/com/github/druk/dnssd/ResolveListener.java +0 -55
- package/android/src/main/java/com/github/druk/dnssd/TXTRecord.java +0 -293
- package/android/src/main/jni/Android.mk +0 -73
- package/android/src/main/jni/Application.mk +0 -3
- package/android/src/main/jni/DNSSD.java.h +0 -290
- package/android/src/main/jni/JNISupport.c +0 -1140
- package/ios/RNZeroconf/RNNetServiceSerializer.h +0 -31
- package/ios/RNZeroconf/RNNetServiceSerializer.m +0 -84
- package/ios/RNZeroconf/RNZeroconf.h +0 -17
- package/ios/RNZeroconf/RNZeroconf.m +0 -1025
- package/ios/RNZeroconf.xcodeproj/project.pbxproj +0 -261
- package/ios/RNZeroconf.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/src/index.js +0 -563
- package/windows/RNZeroconf/RNZeroconf.cpp +0 -228
- package/windows/RNZeroconf/RNZeroconf.h +0 -70
|
@@ -1,1025 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// RNZeroconf.m
|
|
3
|
-
// RNZeroconf
|
|
4
|
-
//
|
|
5
|
-
// Created by Balthazar Gronon on 25/10/2015.
|
|
6
|
-
// Copyright © 2016 Balthazar Gronon MIT
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
#import "RNZeroconf.h"
|
|
10
|
-
#import "RNNetServiceSerializer.h"
|
|
11
|
-
#import <Network/Network.h>
|
|
12
|
-
#import <dns_sd.h>
|
|
13
|
-
#import <net/if.h>
|
|
14
|
-
|
|
15
|
-
// dns_sd callbacks are delivered on the main queue, and refs must be deallocated on it
|
|
16
|
-
#define RNZ_QUEUE dispatch_get_main_queue()
|
|
17
|
-
|
|
18
|
-
#pragma mark - State
|
|
19
|
-
|
|
20
|
-
@class RNZScan;
|
|
21
|
-
|
|
22
|
-
// A found service being resolved: DNSServiceResolve for host, port and TXT, then DNSServiceGetAddrInfo for addresses.
|
|
23
|
-
// Once resolved, the address query and a TXT record query stay open, and changes are emitted again.
|
|
24
|
-
// Without a scan, it is a single resolveService() call settling a promise.
|
|
25
|
-
@interface RNZResolve : NSObject
|
|
26
|
-
@property (nonatomic, weak) RNZeroconf *module;
|
|
27
|
-
@property (nonatomic, weak) RNZScan *scan;
|
|
28
|
-
@property (nonatomic, copy) RCTPromiseResolveBlock promiseResolve;
|
|
29
|
-
@property (nonatomic, copy) RCTPromiseRejectBlock promiseReject;
|
|
30
|
-
@property (nonatomic, assign) uint32_t interfaceIndex;
|
|
31
|
-
@property (nonatomic, assign) NSTimeInterval timeoutSeconds;
|
|
32
|
-
@property (nonatomic, assign) DNSServiceRef txtRef;
|
|
33
|
-
@property (nonatomic, assign) BOOL emitted;
|
|
34
|
-
@property (nonatomic, strong) NSDictionary *lastEmitted;
|
|
35
|
-
@property (nonatomic, copy) NSString *name;
|
|
36
|
-
@property (nonatomic, copy) NSString *regtype;
|
|
37
|
-
@property (nonatomic, copy) NSString *domain;
|
|
38
|
-
@property (nonatomic, assign) DNSServiceRef resolveRef;
|
|
39
|
-
@property (nonatomic, assign) DNSServiceRef addressRef;
|
|
40
|
-
@property (nonatomic, copy) NSString *host;
|
|
41
|
-
@property (nonatomic, assign) uint16_t port;
|
|
42
|
-
@property (nonatomic, strong) NSDictionary *txt;
|
|
43
|
-
@property (nonatomic, strong) NSMutableOrderedSet<NSString *> *addresses;
|
|
44
|
-
@property (nonatomic, assign) BOOL retried;
|
|
45
|
-
@property (nonatomic, assign) BOOL finished;
|
|
46
|
-
@property (nonatomic, strong) dispatch_block_t timeoutBlock;
|
|
47
|
-
@property (nonatomic, strong) dispatch_block_t emitBlock;
|
|
48
|
-
@end
|
|
49
|
-
|
|
50
|
-
@implementation RNZResolve
|
|
51
|
-
@end
|
|
52
|
-
|
|
53
|
-
// One browse, keyed by the id of the JS instance that started it, several can run at once
|
|
54
|
-
@interface RNZScan : NSObject
|
|
55
|
-
@property (nonatomic, weak) RNZeroconf *module;
|
|
56
|
-
@property (nonatomic, copy) NSString *scanId;
|
|
57
|
-
@property (nonatomic, assign) DNSServiceRef browseRef;
|
|
58
|
-
@property (nonatomic, assign) NSTimeInterval resolveTimeoutSeconds;
|
|
59
|
-
@property (nonatomic, assign) uint32_t interfaceIndex;
|
|
60
|
-
// Services are reported once per network interface, count them so found/remove are emitted once
|
|
61
|
-
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *foundInterfaces;
|
|
62
|
-
@property (nonatomic, strong) NSMutableDictionary<NSString *, RNZResolve *> *resolvingServices;
|
|
63
|
-
// Browsing _services._dns-sd._udp: results are service types, reported as "_http._tcp" and not resolved
|
|
64
|
-
@property (nonatomic, assign) BOOL typesOnly;
|
|
65
|
-
@end
|
|
66
|
-
|
|
67
|
-
@implementation RNZScan
|
|
68
|
-
@end
|
|
69
|
-
|
|
70
|
-
// A service registered with DNSServiceRegister
|
|
71
|
-
@interface RNZPublication : NSObject
|
|
72
|
-
@property (nonatomic, weak) RNZeroconf *module;
|
|
73
|
-
@property (nonatomic, copy) NSString *name;
|
|
74
|
-
@property (nonatomic, copy) NSString *regtype;
|
|
75
|
-
@property (nonatomic, copy) NSString *domain;
|
|
76
|
-
@property (nonatomic, assign) uint16_t port;
|
|
77
|
-
@property (nonatomic, strong) NSDictionary *txt;
|
|
78
|
-
@property (nonatomic, assign) DNSServiceRef ref;
|
|
79
|
-
@property (nonatomic, copy) RCTPromiseResolveBlock resolve;
|
|
80
|
-
@property (nonatomic, copy) RCTPromiseRejectBlock reject;
|
|
81
|
-
@end
|
|
82
|
-
|
|
83
|
-
@implementation RNZPublication
|
|
84
|
-
@end
|
|
85
|
-
|
|
86
|
-
@interface RNZeroconf ()
|
|
87
|
-
|
|
88
|
-
@property (nonatomic, strong, readonly) NSMutableDictionary<NSString *, RNZScan *> *scans;
|
|
89
|
-
// Registered, keyed by the name actually used (it can differ from the requested one)
|
|
90
|
-
@property (nonatomic, strong, readonly) NSMutableDictionary<NSString *, RNZPublication *> *publishedServices;
|
|
91
|
-
// Waiting for the register callback
|
|
92
|
-
@property (nonatomic, strong, readonly) NSMutableSet<RNZPublication *> *pendingPublications;
|
|
93
|
-
// resolveService() calls in flight
|
|
94
|
-
@property (nonatomic, strong, readonly) NSMutableSet<RNZResolve *> *singleResolves;
|
|
95
|
-
|
|
96
|
-
- (void) handleBrowse:(RNZScan *)scan flags:(DNSServiceFlags)flags error:(DNSServiceErrorType)error name:(const char *)name regtype:(const char *)regtype domain:(const char *)domain;
|
|
97
|
-
- (void) handleResolve:(RNZResolve *)resolve error:(DNSServiceErrorType)error host:(const char *)host port:(uint16_t)port txtLength:(uint16_t)txtLength txt:(const unsigned char *)txt;
|
|
98
|
-
- (void) handleAddress:(RNZResolve *)resolve flags:(DNSServiceFlags)flags error:(DNSServiceErrorType)error address:(const struct sockaddr *)address;
|
|
99
|
-
- (void) handleTXT:(RNZResolve *)resolve flags:(DNSServiceFlags)flags error:(DNSServiceErrorType)error length:(uint16_t)length data:(const void *)data;
|
|
100
|
-
- (void) handleRegister:(RNZPublication *)publication flags:(DNSServiceFlags)flags error:(DNSServiceErrorType)error name:(const char *)name;
|
|
101
|
-
|
|
102
|
-
@end
|
|
103
|
-
|
|
104
|
-
#pragma mark - dns_sd callbacks
|
|
105
|
-
|
|
106
|
-
static void RNZBrowseReply(DNSServiceRef ref, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType error,
|
|
107
|
-
const char *name, const char *regtype, const char *domain, void *context)
|
|
108
|
-
{
|
|
109
|
-
RNZScan *scan = (__bridge RNZScan *)context;
|
|
110
|
-
[scan.module handleBrowse:scan flags:flags error:error name:name regtype:regtype domain:domain];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
static void RNZResolveReply(DNSServiceRef ref, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType error,
|
|
114
|
-
const char *fullName, const char *host, uint16_t port, uint16_t txtLength,
|
|
115
|
-
const unsigned char *txt, void *context)
|
|
116
|
-
{
|
|
117
|
-
RNZResolve *resolve = (__bridge RNZResolve *)context;
|
|
118
|
-
[resolve.module handleResolve:resolve error:error host:host port:ntohs(port) txtLength:txtLength txt:txt];
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
static void RNZAddressReply(DNSServiceRef ref, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType error,
|
|
122
|
-
const char *host, const struct sockaddr *address, uint32_t ttl, void *context)
|
|
123
|
-
{
|
|
124
|
-
RNZResolve *resolve = (__bridge RNZResolve *)context;
|
|
125
|
-
[resolve.module handleAddress:resolve flags:flags error:error address:address];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
static void RNZTXTReply(DNSServiceRef ref, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType error,
|
|
129
|
-
const char *fullName, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata,
|
|
130
|
-
uint32_t ttl, void *context)
|
|
131
|
-
{
|
|
132
|
-
RNZResolve *resolve = (__bridge RNZResolve *)context;
|
|
133
|
-
[resolve.module handleTXT:resolve flags:flags error:error length:rdlen data:rdata];
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
static void RNZRegisterReply(DNSServiceRef ref, DNSServiceFlags flags, DNSServiceErrorType error,
|
|
137
|
-
const char *name, const char *regtype, const char *domain, void *context)
|
|
138
|
-
{
|
|
139
|
-
RNZPublication *publication = (__bridge RNZPublication *)context;
|
|
140
|
-
[publication.module handleRegister:publication flags:flags error:error name:name];
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Deallocates on the next turn of the queue, for refs whose callback may currently be running.
|
|
144
|
-
// keepAlive keeps the callback context alive until then.
|
|
145
|
-
static void RNZDeallocateLater(DNSServiceRef ref, id keepAlive)
|
|
146
|
-
{
|
|
147
|
-
if (ref == NULL) {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
dispatch_async(RNZ_QUEUE, ^{
|
|
151
|
-
DNSServiceRefDeallocate(ref);
|
|
152
|
-
(void)keepAlive;
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
@implementation RNZeroconf
|
|
157
|
-
|
|
158
|
-
@synthesize bridge = _bridge;
|
|
159
|
-
|
|
160
|
-
RCT_EXPORT_MODULE()
|
|
161
|
-
|
|
162
|
-
- (dispatch_queue_t)methodQueue
|
|
163
|
-
{
|
|
164
|
-
return RNZ_QUEUE;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
+ (BOOL)requiresMainQueueSetup
|
|
168
|
-
{
|
|
169
|
-
return YES;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
- (instancetype) init
|
|
173
|
-
{
|
|
174
|
-
self = [super init];
|
|
175
|
-
|
|
176
|
-
if (self) {
|
|
177
|
-
_scans = [[NSMutableDictionary alloc] init];
|
|
178
|
-
_publishedServices = [[NSMutableDictionary alloc] init];
|
|
179
|
-
_pendingPublications = [[NSMutableSet alloc] init];
|
|
180
|
-
_singleResolves = [[NSMutableSet alloc] init];
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return self;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
#pragma mark - Scan
|
|
187
|
-
|
|
188
|
-
// options: resolveTimeout (seconds), subtype, networkInterface
|
|
189
|
-
RCT_EXPORT_METHOD(scan:(NSString *)scanId
|
|
190
|
-
type:(NSString *)type
|
|
191
|
-
protocol:(NSString *)protocol
|
|
192
|
-
domain:(NSString *)domain
|
|
193
|
-
options:(NSDictionary *)options)
|
|
194
|
-
{
|
|
195
|
-
[self stopScan:scanId];
|
|
196
|
-
|
|
197
|
-
RNZScan *scan = [[RNZScan alloc] init];
|
|
198
|
-
scan.module = self;
|
|
199
|
-
scan.scanId = scanId;
|
|
200
|
-
double resolveTimeout = [options[@"resolveTimeout"] respondsToSelector:@selector(doubleValue)] ? [options[@"resolveTimeout"] doubleValue] : 0;
|
|
201
|
-
scan.resolveTimeoutSeconds = resolveTimeout > 0 ? resolveTimeout : 5.0;
|
|
202
|
-
scan.foundInterfaces = [[NSMutableDictionary alloc] init];
|
|
203
|
-
scan.resolvingServices = [[NSMutableDictionary alloc] init];
|
|
204
|
-
|
|
205
|
-
uint32_t interfaceIndex = kDNSServiceInterfaceIndexAny;
|
|
206
|
-
NSDictionary *interfaceError = [self interfaceIndexForName:options[@"networkInterface"] index:&interfaceIndex];
|
|
207
|
-
if (interfaceError) {
|
|
208
|
-
[self sendError:interfaceError scan:scan];
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
scan.interfaceIndex = interfaceIndex;
|
|
212
|
-
|
|
213
|
-
NSString *regtype = [NSString stringWithFormat:@"_%@._%@", type, protocol];
|
|
214
|
-
scan.typesOnly = [regtype isEqualToString:@"_services._dns-sd._udp"];
|
|
215
|
-
// "_ipp._tcp,_printer" browses the _printer subtype
|
|
216
|
-
NSString *browseType = regtype;
|
|
217
|
-
NSString *subtype = [RNZeroconf subtypeLabel:options[@"subtype"]];
|
|
218
|
-
if (subtype) {
|
|
219
|
-
browseType = [NSString stringWithFormat:@"%@,%@", regtype, subtype];
|
|
220
|
-
}
|
|
221
|
-
DNSServiceRef ref = NULL;
|
|
222
|
-
DNSServiceErrorType error = DNSServiceBrowse(&ref, 0, interfaceIndex, browseType.UTF8String,
|
|
223
|
-
domain.length > 0 ? domain.UTF8String : NULL,
|
|
224
|
-
RNZBrowseReply, (__bridge void *)scan);
|
|
225
|
-
if (error != kDNSServiceErr_NoError) {
|
|
226
|
-
[self sendError:[self browseErrorWithCode:error scan:scan] scan:scan];
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
DNSServiceSetDispatchQueue(ref, RNZ_QUEUE);
|
|
230
|
-
scan.browseRef = ref;
|
|
231
|
-
self.scans[scanId] = scan;
|
|
232
|
-
[self sendEvent:@"RNZeroconfStart" scan:scan body:@{}];
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Stops the scan with this id, or every scan without one
|
|
236
|
-
RCT_EXPORT_METHOD(stop:(NSString *)scanId)
|
|
237
|
-
{
|
|
238
|
-
if (scanId.length > 0) {
|
|
239
|
-
[self stopScan:scanId];
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
for (NSString *key in self.scans.allKeys) {
|
|
243
|
-
[self stopScan:key];
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
- (void) stopScan:(NSString *)scanId
|
|
248
|
-
{
|
|
249
|
-
RNZScan *scan = self.scans[scanId];
|
|
250
|
-
if (!scan) {
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
[self.scans removeObjectForKey:scanId];
|
|
254
|
-
|
|
255
|
-
for (RNZResolve *resolve in scan.resolvingServices.allValues) {
|
|
256
|
-
[self cancelResolve:resolve];
|
|
257
|
-
}
|
|
258
|
-
[scan.resolvingServices removeAllObjects];
|
|
259
|
-
[scan.foundInterfaces removeAllObjects];
|
|
260
|
-
|
|
261
|
-
if (scan.browseRef) {
|
|
262
|
-
DNSServiceRefDeallocate(scan.browseRef);
|
|
263
|
-
scan.browseRef = NULL;
|
|
264
|
-
}
|
|
265
|
-
[self sendEvent:@"RNZeroconfStop" scan:scan body:@{}];
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
- (void) handleBrowse:(RNZScan *)scan flags:(DNSServiceFlags)flags error:(DNSServiceErrorType)error name:(const char *)name regtype:(const char *)regtype domain:(const char *)domain
|
|
269
|
-
{
|
|
270
|
-
if (scan.browseRef == NULL) {
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
if (error != kDNSServiceErr_NoError) {
|
|
274
|
-
[self sendError:[self browseErrorWithCode:error scan:scan] scan:scan];
|
|
275
|
-
// The browse can't continue after an error
|
|
276
|
-
RNZDeallocateLater(scan.browseRef, scan);
|
|
277
|
-
scan.browseRef = NULL;
|
|
278
|
-
if (self.scans[scan.scanId] == scan) {
|
|
279
|
-
[self stopScan:scan.scanId];
|
|
280
|
-
}
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
NSString *serviceName = name ? [NSString stringWithUTF8String:name] : nil;
|
|
285
|
-
if (!serviceName) {
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
if (scan.typesOnly) {
|
|
289
|
-
// name is "_http" and regtype "_tcp.local.", the service type is "_http._tcp"
|
|
290
|
-
NSString *protocol = [[NSString stringWithUTF8String:regtype] componentsSeparatedByString:@"."].firstObject;
|
|
291
|
-
serviceName = [NSString stringWithFormat:@"%@.%@", serviceName, protocol];
|
|
292
|
-
}
|
|
293
|
-
NSInteger interfaces = scan.foundInterfaces[serviceName].integerValue;
|
|
294
|
-
|
|
295
|
-
if (flags & kDNSServiceFlagsAdd) {
|
|
296
|
-
scan.foundInterfaces[serviceName] = @(interfaces + 1);
|
|
297
|
-
if (interfaces == 0) {
|
|
298
|
-
[self sendEvent:@"RNZeroconfFound" scan:scan body:@{ kRNServiceKeysName: serviceName }];
|
|
299
|
-
if (scan.typesOnly) {
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
[self startResolve:serviceName regtype:[NSString stringWithUTF8String:regtype] domain:[NSString stringWithUTF8String:domain] scan:scan];
|
|
303
|
-
}
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (interfaces > 1) {
|
|
308
|
-
scan.foundInterfaces[serviceName] = @(interfaces - 1);
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
[scan.foundInterfaces removeObjectForKey:serviceName];
|
|
312
|
-
|
|
313
|
-
// Stop any pending resolve for the removed service
|
|
314
|
-
RNZResolve *resolve = scan.resolvingServices[serviceName];
|
|
315
|
-
if (resolve) {
|
|
316
|
-
[self cancelResolve:resolve];
|
|
317
|
-
[scan.resolvingServices removeObjectForKey:serviceName];
|
|
318
|
-
}
|
|
319
|
-
[self sendEvent:@"RNZeroconfRemove" scan:scan body:@{ kRNServiceKeysName: serviceName }];
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
#pragma mark - Resolve
|
|
323
|
-
|
|
324
|
-
- (void) startResolve:(NSString *)name regtype:(NSString *)regtype domain:(NSString *)domain scan:(RNZScan *)scan
|
|
325
|
-
{
|
|
326
|
-
RNZResolve *resolve = [[RNZResolve alloc] init];
|
|
327
|
-
resolve.module = self;
|
|
328
|
-
resolve.scan = scan;
|
|
329
|
-
resolve.name = name;
|
|
330
|
-
resolve.regtype = regtype;
|
|
331
|
-
resolve.domain = domain;
|
|
332
|
-
resolve.interfaceIndex = scan.interfaceIndex;
|
|
333
|
-
resolve.timeoutSeconds = scan.resolveTimeoutSeconds;
|
|
334
|
-
scan.resolvingServices[name] = resolve;
|
|
335
|
-
[self resolve:resolve];
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
- (void) resolve:(RNZResolve *)resolve
|
|
339
|
-
{
|
|
340
|
-
resolve.finished = NO;
|
|
341
|
-
resolve.addresses = [[NSMutableOrderedSet alloc] init];
|
|
342
|
-
|
|
343
|
-
DNSServiceRef ref = NULL;
|
|
344
|
-
DNSServiceErrorType error = DNSServiceResolve(&ref, 0, resolve.interfaceIndex, resolve.name.UTF8String,
|
|
345
|
-
resolve.regtype.UTF8String, resolve.domain.UTF8String,
|
|
346
|
-
RNZResolveReply, (__bridge void *)resolve);
|
|
347
|
-
if (error != kDNSServiceErr_NoError) {
|
|
348
|
-
[self failResolve:resolve error:[self errorWithCode:error action:@"Resolving service" serviceName:resolve.name]];
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
DNSServiceSetDispatchQueue(ref, RNZ_QUEUE);
|
|
352
|
-
resolve.resolveRef = ref;
|
|
353
|
-
|
|
354
|
-
__weak RNZeroconf *weakSelf = self;
|
|
355
|
-
__weak RNZResolve *weakResolve = resolve;
|
|
356
|
-
resolve.timeoutBlock = dispatch_block_create(0, ^{
|
|
357
|
-
[weakSelf resolveTimedOut:weakResolve];
|
|
358
|
-
});
|
|
359
|
-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(resolve.timeoutSeconds * NSEC_PER_SEC)), RNZ_QUEUE, resolve.timeoutBlock);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
- (void) resolveTimedOut:(RNZResolve *)resolve
|
|
363
|
-
{
|
|
364
|
-
if (!resolve || resolve.finished || resolve.emitted || ![self isActiveResolve:resolve]) {
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
[self cancelResolve:resolve];
|
|
368
|
-
|
|
369
|
-
// Slow devices can time out, retry once before reporting the error (#186)
|
|
370
|
-
if (!resolve.retried) {
|
|
371
|
-
resolve.retried = YES;
|
|
372
|
-
[self resolve:resolve];
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
[self failResolve:resolve error:@{
|
|
377
|
-
@"message": [NSString stringWithFormat:@"Resolving service %@ failed: timed out", resolve.name],
|
|
378
|
-
@"code": @"TIMEOUT",
|
|
379
|
-
@"domain": @"RNZeroconf",
|
|
380
|
-
@"serviceName": resolve.name,
|
|
381
|
-
}];
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
- (void) handleResolve:(RNZResolve *)resolve error:(DNSServiceErrorType)error host:(const char *)host port:(uint16_t)port txtLength:(uint16_t)txtLength txt:(const unsigned char *)txt
|
|
385
|
-
{
|
|
386
|
-
if (resolve.finished || resolve.resolveRef == NULL) {
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
if (error != kDNSServiceErr_NoError) {
|
|
390
|
-
[self failResolve:resolve error:[self errorWithCode:error action:@"Resolving service" serviceName:resolve.name]];
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Only the first answer is needed
|
|
395
|
-
RNZDeallocateLater(resolve.resolveRef, resolve);
|
|
396
|
-
resolve.resolveRef = NULL;
|
|
397
|
-
|
|
398
|
-
resolve.host = host ? [NSString stringWithUTF8String:host] : nil;
|
|
399
|
-
resolve.port = port;
|
|
400
|
-
resolve.txt = [RNNetServiceSerializer dictionaryFromTXTRecord:txt length:txtLength];
|
|
401
|
-
|
|
402
|
-
DNSServiceRef ref = NULL;
|
|
403
|
-
DNSServiceErrorType addressError = DNSServiceGetAddrInfo(&ref, 0, resolve.interfaceIndex,
|
|
404
|
-
kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6,
|
|
405
|
-
host, RNZAddressReply, (__bridge void *)resolve);
|
|
406
|
-
if (addressError != kDNSServiceErr_NoError) {
|
|
407
|
-
[self failResolve:resolve error:[self errorWithCode:addressError action:@"Resolving service" serviceName:resolve.name]];
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
DNSServiceSetDispatchQueue(ref, RNZ_QUEUE);
|
|
411
|
-
resolve.addressRef = ref;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
- (void) handleAddress:(RNZResolve *)resolve flags:(DNSServiceFlags)flags error:(DNSServiceErrorType)error address:(const struct sockaddr *)address
|
|
415
|
-
{
|
|
416
|
-
if (resolve.finished || resolve.addressRef == NULL) {
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
if (error != kDNSServiceErr_NoError) {
|
|
420
|
-
// No record for one of the address families is not a failure
|
|
421
|
-
if (error == kDNSServiceErr_NoSuchRecord) {
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
[self failResolve:resolve error:[self errorWithCode:error action:@"Resolving service" serviceName:resolve.name]];
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
NSString *string = [RNNetServiceSerializer stringFromAddress:address];
|
|
429
|
-
if (string && (flags & kDNSServiceFlagsAdd)) {
|
|
430
|
-
[resolve.addresses addObject:string];
|
|
431
|
-
} else if (string) {
|
|
432
|
-
[resolve.addresses removeObject:string];
|
|
433
|
-
}
|
|
434
|
-
if (flags & kDNSServiceFlagsMoreComing) {
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
[self scheduleEmit:resolve];
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// Follows the TXT record of a resolved service
|
|
441
|
-
- (void) watchTXT:(RNZResolve *)resolve
|
|
442
|
-
{
|
|
443
|
-
char fullName[kDNSServiceMaxDomainName];
|
|
444
|
-
if (DNSServiceConstructFullName(fullName, resolve.name.UTF8String, resolve.regtype.UTF8String, resolve.domain.UTF8String) != kDNSServiceErr_NoError) {
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
DNSServiceRef ref = NULL;
|
|
448
|
-
if (DNSServiceQueryRecord(&ref, 0, resolve.interfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN,
|
|
449
|
-
RNZTXTReply, (__bridge void *)resolve) != kDNSServiceErr_NoError) {
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
DNSServiceSetDispatchQueue(ref, RNZ_QUEUE);
|
|
453
|
-
resolve.txtRef = ref;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
- (void) handleTXT:(RNZResolve *)resolve flags:(DNSServiceFlags)flags error:(DNSServiceErrorType)error length:(uint16_t)length data:(const void *)data
|
|
457
|
-
{
|
|
458
|
-
if (resolve.finished || resolve.txtRef == NULL || error != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd)) {
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
resolve.txt = [RNNetServiceSerializer dictionaryFromTXTRecord:data length:length];
|
|
462
|
-
[self scheduleEmit:resolve];
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// IPv4 and IPv6 answers can arrive in separate batches, wait briefly before emitting
|
|
466
|
-
- (void) scheduleEmit:(RNZResolve *)resolve
|
|
467
|
-
{
|
|
468
|
-
if (resolve.emitBlock) {
|
|
469
|
-
dispatch_block_cancel(resolve.emitBlock);
|
|
470
|
-
}
|
|
471
|
-
__weak RNZeroconf *weakSelf = self;
|
|
472
|
-
__weak RNZResolve *weakResolve = resolve;
|
|
473
|
-
resolve.emitBlock = dispatch_block_create(0, ^{
|
|
474
|
-
[weakSelf emitResolve:weakResolve];
|
|
475
|
-
});
|
|
476
|
-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), RNZ_QUEUE, resolve.emitBlock);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
- (void) emitResolve:(RNZResolve *)resolve
|
|
480
|
-
{
|
|
481
|
-
if (!resolve || resolve.finished || resolve.addresses.count == 0 || ![self isActiveResolve:resolve]) {
|
|
482
|
-
return;
|
|
483
|
-
}
|
|
484
|
-
NSDictionary *body = @{
|
|
485
|
-
kRNServiceKeysName: resolve.name,
|
|
486
|
-
kRNServiceKeysFullName: [NSString stringWithFormat:@"%@%@.", resolve.host ?: @"", resolve.regtype],
|
|
487
|
-
kRNServiceKeysHost: resolve.host ?: @"",
|
|
488
|
-
kRNServiceKeysPort: @(resolve.port),
|
|
489
|
-
kRNServiceKeysAddresses: resolve.addresses.array,
|
|
490
|
-
kRNServiceTxtRecords: resolve.txt ?: @{},
|
|
491
|
-
};
|
|
492
|
-
|
|
493
|
-
if (resolve.promiseResolve) {
|
|
494
|
-
RCTPromiseResolveBlock promiseResolve = resolve.promiseResolve;
|
|
495
|
-
[self endSingleResolve:resolve];
|
|
496
|
-
promiseResolve(body);
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
if (!resolve.emitted) {
|
|
501
|
-
resolve.emitted = YES;
|
|
502
|
-
if (resolve.timeoutBlock) {
|
|
503
|
-
dispatch_block_cancel(resolve.timeoutBlock);
|
|
504
|
-
resolve.timeoutBlock = nil;
|
|
505
|
-
}
|
|
506
|
-
[self watchTXT:resolve];
|
|
507
|
-
}
|
|
508
|
-
// Emitted again only when something changed
|
|
509
|
-
if ([body isEqualToDictionary:resolve.lastEmitted]) {
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
resolve.lastEmitted = body;
|
|
513
|
-
[self sendEvent:@"RNZeroconfResolved" scan:resolve.scan body:body];
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
- (void) failResolve:(RNZResolve *)resolve error:(NSDictionary *)error
|
|
517
|
-
{
|
|
518
|
-
if (resolve.promiseReject) {
|
|
519
|
-
RCTPromiseRejectBlock promiseReject = resolve.promiseReject;
|
|
520
|
-
[self endSingleResolve:resolve];
|
|
521
|
-
[self reject:promiseReject error:error];
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
[self cancelResolve:resolve];
|
|
525
|
-
if (resolve.scan.resolvingServices[resolve.name] == resolve) {
|
|
526
|
-
[resolve.scan.resolvingServices removeObjectForKey:resolve.name];
|
|
527
|
-
}
|
|
528
|
-
[self sendError:error scan:resolve.scan];
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
- (BOOL) isActiveResolve:(RNZResolve *)resolve
|
|
532
|
-
{
|
|
533
|
-
if (resolve.promiseResolve) {
|
|
534
|
-
return [self.singleResolves containsObject:resolve];
|
|
535
|
-
}
|
|
536
|
-
return resolve.scan.resolvingServices[resolve.name] == resolve;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
- (void) endSingleResolve:(RNZResolve *)resolve
|
|
540
|
-
{
|
|
541
|
-
[self cancelResolve:resolve];
|
|
542
|
-
resolve.promiseResolve = nil;
|
|
543
|
-
resolve.promiseReject = nil;
|
|
544
|
-
[self.singleResolves removeObject:resolve];
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// resolveService(): resolves one service by name without scanning. options: timeout (seconds), networkInterface
|
|
548
|
-
RCT_EXPORT_METHOD(resolveService:(NSString *)name
|
|
549
|
-
type:(NSString *)type
|
|
550
|
-
protocol:(NSString *)protocol
|
|
551
|
-
domain:(NSString *)domain
|
|
552
|
-
options:(NSDictionary *)options
|
|
553
|
-
resolve:(RCTPromiseResolveBlock)resolve
|
|
554
|
-
reject:(RCTPromiseRejectBlock)reject)
|
|
555
|
-
{
|
|
556
|
-
uint32_t interfaceIndex = kDNSServiceInterfaceIndexAny;
|
|
557
|
-
NSDictionary *interfaceError = [self interfaceIndexForName:options[@"networkInterface"] index:&interfaceIndex];
|
|
558
|
-
if (interfaceError) {
|
|
559
|
-
[self reject:reject error:interfaceError];
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
double timeout = [options[@"timeout"] respondsToSelector:@selector(doubleValue)] ? [options[@"timeout"] doubleValue] : 0;
|
|
563
|
-
|
|
564
|
-
RNZResolve *single = [[RNZResolve alloc] init];
|
|
565
|
-
single.module = self;
|
|
566
|
-
single.name = name;
|
|
567
|
-
single.regtype = [NSString stringWithFormat:@"_%@._%@", type, protocol];
|
|
568
|
-
single.domain = domain.length > 0 ? domain : @"local.";
|
|
569
|
-
single.interfaceIndex = interfaceIndex;
|
|
570
|
-
single.timeoutSeconds = timeout > 0 ? timeout : 5.0;
|
|
571
|
-
single.promiseResolve = resolve;
|
|
572
|
-
single.promiseReject = reject;
|
|
573
|
-
// The timeout is the caller's, no retry as for scans
|
|
574
|
-
single.retried = YES;
|
|
575
|
-
[self.singleResolves addObject:single];
|
|
576
|
-
[self resolve:single];
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// Stops everything in flight for a resolve, safe to call from its own callbacks
|
|
580
|
-
- (void) cancelResolve:(RNZResolve *)resolve
|
|
581
|
-
{
|
|
582
|
-
resolve.finished = YES;
|
|
583
|
-
if (resolve.timeoutBlock) {
|
|
584
|
-
dispatch_block_cancel(resolve.timeoutBlock);
|
|
585
|
-
resolve.timeoutBlock = nil;
|
|
586
|
-
}
|
|
587
|
-
if (resolve.emitBlock) {
|
|
588
|
-
dispatch_block_cancel(resolve.emitBlock);
|
|
589
|
-
resolve.emitBlock = nil;
|
|
590
|
-
}
|
|
591
|
-
RNZDeallocateLater(resolve.resolveRef, resolve);
|
|
592
|
-
resolve.resolveRef = NULL;
|
|
593
|
-
RNZDeallocateLater(resolve.addressRef, resolve);
|
|
594
|
-
resolve.addressRef = NULL;
|
|
595
|
-
RNZDeallocateLater(resolve.txtRef, resolve);
|
|
596
|
-
resolve.txtRef = NULL;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
#pragma mark - Publish
|
|
600
|
-
|
|
601
|
-
RCT_EXPORT_METHOD(registerService:(NSString *)type
|
|
602
|
-
protocol:(NSString *)protocol
|
|
603
|
-
domain:(NSString *)domain
|
|
604
|
-
name:(NSString *)name
|
|
605
|
-
port:(int)port
|
|
606
|
-
txt:(NSArray<NSArray<NSString *> *> *)txt
|
|
607
|
-
options:(NSDictionary *)options
|
|
608
|
-
resolve:(RCTPromiseResolveBlock)resolve
|
|
609
|
-
reject:(RCTPromiseRejectBlock)reject)
|
|
610
|
-
{
|
|
611
|
-
uint32_t interfaceIndex = kDNSServiceInterfaceIndexAny;
|
|
612
|
-
NSDictionary *interfaceError = [self interfaceIndexForName:options[@"networkInterface"] index:&interfaceIndex];
|
|
613
|
-
if (interfaceError) {
|
|
614
|
-
[self sendError:interfaceError];
|
|
615
|
-
[self reject:reject error:interfaceError];
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
NSMutableArray<NSString *> *tooLong = [NSMutableArray array];
|
|
620
|
-
NSData *txtRecord = [RNNetServiceSerializer TXTRecordFromPairs:txt ?: @[] tooLong:tooLong];
|
|
621
|
-
for (NSString *key in tooLong) {
|
|
622
|
-
[self sendError:@{
|
|
623
|
-
@"message": [NSString stringWithFormat:@"TXT record entry %@ is longer than 255 bytes", key],
|
|
624
|
-
@"code": @"TXT_ENTRY_TOO_LONG",
|
|
625
|
-
@"domain": @"RNZeroconf",
|
|
626
|
-
@"serviceName": name,
|
|
627
|
-
}];
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
// dns_sd lets the same app register a name twice, rename like Bonjour does on conflicts
|
|
631
|
-
NSString *availableName = name;
|
|
632
|
-
for (NSInteger suffix = 2; [self isNameInUse:availableName]; suffix++) {
|
|
633
|
-
availableName = [NSString stringWithFormat:@"%@ (%ld)", name, (long)suffix];
|
|
634
|
-
}
|
|
635
|
-
name = availableName;
|
|
636
|
-
|
|
637
|
-
RNZPublication *publication = [[RNZPublication alloc] init];
|
|
638
|
-
publication.module = self;
|
|
639
|
-
publication.name = name;
|
|
640
|
-
publication.regtype = [NSString stringWithFormat:@"_%@._%@", type, protocol];
|
|
641
|
-
publication.domain = domain.length > 0 ? domain : @"local.";
|
|
642
|
-
publication.port = (uint16_t)port;
|
|
643
|
-
publication.txt = [RNNetServiceSerializer dictionaryFromTXTRecord:txtRecord.bytes length:(uint16_t)txtRecord.length];
|
|
644
|
-
publication.resolve = resolve;
|
|
645
|
-
publication.reject = reject;
|
|
646
|
-
|
|
647
|
-
// "_ipp._tcp,_printer,_color" registers the subtypes too
|
|
648
|
-
NSMutableString *registerType = [publication.regtype mutableCopy];
|
|
649
|
-
NSArray *subtypes = [options[@"subtypes"] isKindOfClass:[NSArray class]] ? options[@"subtypes"] : @[];
|
|
650
|
-
for (id subtype in subtypes) {
|
|
651
|
-
NSString *label = [RNZeroconf subtypeLabel:subtype];
|
|
652
|
-
if (label) {
|
|
653
|
-
[registerType appendFormat:@",%@", label];
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
DNSServiceRef ref = NULL;
|
|
658
|
-
DNSServiceErrorType error = DNSServiceRegister(&ref, 0, interfaceIndex, name.UTF8String,
|
|
659
|
-
registerType.UTF8String,
|
|
660
|
-
domain.length > 0 ? domain.UTF8String : NULL,
|
|
661
|
-
NULL, htons((uint16_t)port),
|
|
662
|
-
(uint16_t)txtRecord.length, txtRecord.length > 0 ? txtRecord.bytes : NULL,
|
|
663
|
-
RNZRegisterReply, (__bridge void *)publication);
|
|
664
|
-
if (error != kDNSServiceErr_NoError) {
|
|
665
|
-
NSDictionary *errorInfo = [self errorWithCode:error action:@"Publishing service" serviceName:name];
|
|
666
|
-
[self sendError:errorInfo];
|
|
667
|
-
[self reject:reject error:errorInfo];
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
DNSServiceSetDispatchQueue(ref, RNZ_QUEUE);
|
|
671
|
-
publication.ref = ref;
|
|
672
|
-
[self.pendingPublications addObject:publication];
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
- (void) handleRegister:(RNZPublication *)publication flags:(DNSServiceFlags)flags error:(DNSServiceErrorType)error name:(const char *)name
|
|
676
|
-
{
|
|
677
|
-
if (publication.ref == NULL) {
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
if (error != kDNSServiceErr_NoError) {
|
|
681
|
-
NSDictionary *errorInfo = [self errorWithCode:error action:@"Publishing service" serviceName:publication.name];
|
|
682
|
-
[self sendError:errorInfo];
|
|
683
|
-
[self.pendingPublications removeObject:publication];
|
|
684
|
-
if (self.publishedServices[publication.name] == publication) {
|
|
685
|
-
[self.publishedServices removeObjectForKey:publication.name];
|
|
686
|
-
}
|
|
687
|
-
RNZDeallocateLater(publication.ref, publication);
|
|
688
|
-
publication.ref = NULL;
|
|
689
|
-
if (publication.reject) {
|
|
690
|
-
[self reject:publication.reject error:errorInfo];
|
|
691
|
-
}
|
|
692
|
-
publication.resolve = nil;
|
|
693
|
-
publication.reject = nil;
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
696
|
-
if (!(flags & kDNSServiceFlagsAdd)) {
|
|
697
|
-
return;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// The name can differ from the requested one when it was already taken
|
|
701
|
-
NSString *registeredName = (name ? [NSString stringWithUTF8String:name] : nil) ?: publication.name;
|
|
702
|
-
if (self.publishedServices[publication.name] == publication) {
|
|
703
|
-
[self.publishedServices removeObjectForKey:publication.name];
|
|
704
|
-
}
|
|
705
|
-
publication.name = registeredName;
|
|
706
|
-
self.publishedServices[registeredName] = publication;
|
|
707
|
-
[self.pendingPublications removeObject:publication];
|
|
708
|
-
|
|
709
|
-
NSDictionary *serviceInfo = [self publicationInfo:publication];
|
|
710
|
-
[self sendEvent:@"RNZeroconfServiceRegistered" body:serviceInfo];
|
|
711
|
-
if (publication.resolve) {
|
|
712
|
-
publication.resolve(serviceInfo);
|
|
713
|
-
}
|
|
714
|
-
publication.resolve = nil;
|
|
715
|
-
publication.reject = nil;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
RCT_EXPORT_METHOD(unregisterService:(NSString *)serviceName
|
|
719
|
-
resolve:(RCTPromiseResolveBlock)resolve
|
|
720
|
-
reject:(RCTPromiseRejectBlock)reject)
|
|
721
|
-
{
|
|
722
|
-
RNZPublication *publication = self.publishedServices[serviceName];
|
|
723
|
-
if (!publication) {
|
|
724
|
-
[self reject:reject error:@{
|
|
725
|
-
@"message": [NSString stringWithFormat:@"Service %@ is not published", serviceName],
|
|
726
|
-
@"code": @"NOT_PUBLISHED",
|
|
727
|
-
@"domain": @"RNZeroconf",
|
|
728
|
-
@"serviceName": serviceName,
|
|
729
|
-
}];
|
|
730
|
-
return;
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
// Deallocating the ref unregisters the service
|
|
734
|
-
[self.publishedServices removeObjectForKey:serviceName];
|
|
735
|
-
DNSServiceRefDeallocate(publication.ref);
|
|
736
|
-
publication.ref = NULL;
|
|
737
|
-
|
|
738
|
-
NSDictionary *serviceInfo = [self publicationInfo:publication];
|
|
739
|
-
[self sendEvent:@"RNZeroconfServiceUnregistered" body:serviceInfo];
|
|
740
|
-
resolve(serviceInfo);
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// Replaces the TXT record of a published service
|
|
744
|
-
RCT_EXPORT_METHOD(updateService:(NSString *)serviceName
|
|
745
|
-
txt:(NSArray<NSArray<NSString *> *> *)txt
|
|
746
|
-
resolve:(RCTPromiseResolveBlock)resolve
|
|
747
|
-
reject:(RCTPromiseRejectBlock)reject)
|
|
748
|
-
{
|
|
749
|
-
RNZPublication *publication = self.publishedServices[serviceName];
|
|
750
|
-
if (!publication) {
|
|
751
|
-
[self reject:reject error:@{
|
|
752
|
-
@"message": [NSString stringWithFormat:@"Service %@ is not published", serviceName],
|
|
753
|
-
@"code": @"NOT_PUBLISHED",
|
|
754
|
-
@"domain": @"RNZeroconf",
|
|
755
|
-
@"serviceName": serviceName,
|
|
756
|
-
}];
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
NSMutableArray<NSString *> *tooLong = [NSMutableArray array];
|
|
761
|
-
NSData *txtRecord = [RNNetServiceSerializer TXTRecordFromPairs:txt ?: @[] tooLong:tooLong];
|
|
762
|
-
for (NSString *key in tooLong) {
|
|
763
|
-
[self sendError:@{
|
|
764
|
-
@"message": [NSString stringWithFormat:@"TXT record entry %@ is longer than 255 bytes", key],
|
|
765
|
-
@"code": @"TXT_ENTRY_TOO_LONG",
|
|
766
|
-
@"domain": @"RNZeroconf",
|
|
767
|
-
@"serviceName": serviceName,
|
|
768
|
-
}];
|
|
769
|
-
}
|
|
770
|
-
// An empty TXT record is a single empty string
|
|
771
|
-
static const uint8_t emptyTXT = 0;
|
|
772
|
-
DNSServiceErrorType error = DNSServiceUpdateRecord(publication.ref, NULL, 0,
|
|
773
|
-
txtRecord.length > 0 ? (uint16_t)txtRecord.length : 1,
|
|
774
|
-
txtRecord.length > 0 ? txtRecord.bytes : &emptyTXT, 0);
|
|
775
|
-
if (error != kDNSServiceErr_NoError) {
|
|
776
|
-
[self reject:reject error:[self errorWithCode:error action:@"Updating service" serviceName:serviceName]];
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
publication.txt = [RNNetServiceSerializer dictionaryFromTXTRecord:txtRecord.bytes length:(uint16_t)txtRecord.length];
|
|
780
|
-
resolve([self publicationInfo:publication]);
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
// "printer" or "_printer" -> "_printer"
|
|
784
|
-
+ (NSString *) subtypeLabel:(id)subtype
|
|
785
|
-
{
|
|
786
|
-
if (![subtype isKindOfClass:[NSString class]] || [subtype length] == 0) {
|
|
787
|
-
return nil;
|
|
788
|
-
}
|
|
789
|
-
return [subtype hasPrefix:@"_"] ? subtype : [@"_" stringByAppendingString:subtype];
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
// The index of a network interface by name ("en0"), kDNSServiceInterfaceIndexAny without one
|
|
793
|
-
- (NSDictionary *) interfaceIndexForName:(id)name index:(uint32_t *)index
|
|
794
|
-
{
|
|
795
|
-
*index = kDNSServiceInterfaceIndexAny;
|
|
796
|
-
if (![name isKindOfClass:[NSString class]] || [name length] == 0) {
|
|
797
|
-
return nil;
|
|
798
|
-
}
|
|
799
|
-
unsigned int found = if_nametoindex([name UTF8String]);
|
|
800
|
-
if (found == 0) {
|
|
801
|
-
return @{
|
|
802
|
-
@"message": [NSString stringWithFormat:@"Unknown network interface %@", name],
|
|
803
|
-
@"code": @"UNKNOWN_INTERFACE",
|
|
804
|
-
@"domain": @"RNZeroconf",
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
|
-
*index = found;
|
|
808
|
-
return nil;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
- (BOOL) isNameInUse:(NSString *)name
|
|
812
|
-
{
|
|
813
|
-
if (self.publishedServices[name]) {
|
|
814
|
-
return YES;
|
|
815
|
-
}
|
|
816
|
-
for (RNZPublication *publication in self.pendingPublications) {
|
|
817
|
-
if ([publication.name isEqualToString:name]) {
|
|
818
|
-
return YES;
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
return NO;
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
- (NSDictionary *) publicationInfo:(RNZPublication *)publication
|
|
825
|
-
{
|
|
826
|
-
return @{
|
|
827
|
-
kRNServiceKeysName: publication.name,
|
|
828
|
-
kRNServiceKeysFullName: [NSString stringWithFormat:@"%@.%@.%@", publication.name, publication.regtype, publication.domain],
|
|
829
|
-
kRNServiceKeysPort: @(publication.port),
|
|
830
|
-
kRNServiceKeysAddresses: @[],
|
|
831
|
-
kRNServiceTxtRecords: publication.txt ?: @{},
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
#pragma mark - Local Network access
|
|
836
|
-
|
|
837
|
-
/**
|
|
838
|
-
* Resolves 'granted', 'denied' or 'unknown'. There is no API to read the Local Network permission,
|
|
839
|
-
* so this advertises a temporary Bonjour service and browses for it: seeing it means access is granted,
|
|
840
|
-
* a browser waiting with kDNSServiceErr_PolicyDenied until the timeout means it is denied.
|
|
841
|
-
* The type must be listed in NSBonjourServices, defaults to the first one. It can show the permission prompt.
|
|
842
|
-
*/
|
|
843
|
-
RCT_EXPORT_METHOD(checkLocalNetworkAccess:(NSString *)type
|
|
844
|
-
timeout:(double)timeout
|
|
845
|
-
resolve:(RCTPromiseResolveBlock)resolve
|
|
846
|
-
reject:(RCTPromiseRejectBlock)reject)
|
|
847
|
-
{
|
|
848
|
-
NSString *serviceType = type;
|
|
849
|
-
if (serviceType.length == 0) {
|
|
850
|
-
NSArray *declared = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSBonjourServices"];
|
|
851
|
-
serviceType = [declared isKindOfClass:[NSArray class]] ? declared.firstObject : nil;
|
|
852
|
-
}
|
|
853
|
-
if (serviceType.length == 0) {
|
|
854
|
-
[self reject:reject error:@{
|
|
855
|
-
@"message": @"No service type to check with, add one to NSBonjourServices in Info.plist",
|
|
856
|
-
@"code": @"MISSING_BONJOUR_SERVICES",
|
|
857
|
-
@"domain": @"RNZeroconf",
|
|
858
|
-
}];
|
|
859
|
-
return;
|
|
860
|
-
}
|
|
861
|
-
// "_http._tcp." -> "_http._tcp"
|
|
862
|
-
if ([serviceType hasSuffix:@"."]) {
|
|
863
|
-
serviceType = [serviceType substringToIndex:serviceType.length - 1];
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
NSString *name = [NSString stringWithFormat:@"rnzeroconf-%@", [[NSUUID UUID].UUIDString substringToIndex:8]];
|
|
867
|
-
dispatch_queue_t queue = RNZ_QUEUE;
|
|
868
|
-
|
|
869
|
-
nw_parameters_t parameters = nw_parameters_create_secure_tcp(NW_PARAMETERS_DISABLE_PROTOCOL, NW_PARAMETERS_DEFAULT_CONFIGURATION);
|
|
870
|
-
nw_listener_t listener = nw_listener_create(parameters);
|
|
871
|
-
nw_listener_set_advertise_descriptor(listener, nw_advertise_descriptor_create_bonjour_service(name.UTF8String, serviceType.UTF8String, "local."));
|
|
872
|
-
nw_listener_set_queue(listener, queue);
|
|
873
|
-
nw_listener_set_new_connection_handler(listener, ^(nw_connection_t connection) {
|
|
874
|
-
nw_connection_cancel(connection);
|
|
875
|
-
});
|
|
876
|
-
|
|
877
|
-
nw_browse_descriptor_t descriptor = nw_browse_descriptor_create_bonjour_service(serviceType.UTF8String, "local.");
|
|
878
|
-
nw_browser_t browser = nw_browser_create(descriptor, nw_parameters_create_secure_tcp(NW_PARAMETERS_DISABLE_PROTOCOL, NW_PARAMETERS_DEFAULT_CONFIGURATION));
|
|
879
|
-
nw_browser_set_queue(browser, queue);
|
|
880
|
-
|
|
881
|
-
__block BOOL finished = NO;
|
|
882
|
-
__block BOOL policyDenied = NO;
|
|
883
|
-
void (^finish)(NSString *) = ^(NSString *result) {
|
|
884
|
-
if (finished) {
|
|
885
|
-
return;
|
|
886
|
-
}
|
|
887
|
-
finished = YES;
|
|
888
|
-
nw_browser_cancel(browser);
|
|
889
|
-
nw_listener_cancel(listener);
|
|
890
|
-
resolve(result);
|
|
891
|
-
};
|
|
892
|
-
|
|
893
|
-
nw_browser_set_browse_results_changed_handler(browser, ^(nw_browse_result_t oldResult, nw_browse_result_t newResult, bool batchComplete) {
|
|
894
|
-
nw_endpoint_t endpoint = newResult ? nw_browse_result_copy_endpoint(newResult) : nil;
|
|
895
|
-
const char *found = endpoint ? nw_endpoint_get_bonjour_service_name(endpoint) : NULL;
|
|
896
|
-
if (found && strcmp(found, name.UTF8String) == 0) {
|
|
897
|
-
finish(@"granted");
|
|
898
|
-
}
|
|
899
|
-
});
|
|
900
|
-
nw_browser_set_state_changed_handler(browser, ^(nw_browser_state_t state, nw_error_t error) {
|
|
901
|
-
if (error && nw_error_get_error_domain(error) == nw_error_domain_dns) {
|
|
902
|
-
// Stays waiting while the prompt is shown, only conclude on timeout
|
|
903
|
-
policyDenied = nw_error_get_error_code(error) == kDNSServiceErr_PolicyDenied;
|
|
904
|
-
} else if (state == nw_browser_state_ready) {
|
|
905
|
-
policyDenied = NO;
|
|
906
|
-
}
|
|
907
|
-
});
|
|
908
|
-
|
|
909
|
-
nw_listener_start(listener);
|
|
910
|
-
nw_browser_start(browser);
|
|
911
|
-
|
|
912
|
-
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((timeout > 0 ? timeout : 5.0) * NSEC_PER_SEC)), queue, ^{
|
|
913
|
-
finish(policyDenied ? @"denied" : @"unknown");
|
|
914
|
-
});
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
#pragma mark - Teardown
|
|
918
|
-
|
|
919
|
-
// Called when the bridge is torn down (e.g. reload): stop scanning and unpublish services
|
|
920
|
-
- (void) invalidate
|
|
921
|
-
{
|
|
922
|
-
[self stop:nil];
|
|
923
|
-
for (RNZPublication *publication in self.publishedServices.allValues) {
|
|
924
|
-
DNSServiceRefDeallocate(publication.ref);
|
|
925
|
-
publication.ref = NULL;
|
|
926
|
-
}
|
|
927
|
-
[self.publishedServices removeAllObjects];
|
|
928
|
-
for (RNZPublication *publication in self.pendingPublications) {
|
|
929
|
-
DNSServiceRefDeallocate(publication.ref);
|
|
930
|
-
publication.ref = NULL;
|
|
931
|
-
}
|
|
932
|
-
[self.pendingPublications removeAllObjects];
|
|
933
|
-
for (RNZResolve *single in [self.singleResolves allObjects]) {
|
|
934
|
-
[self cancelResolve:single];
|
|
935
|
-
}
|
|
936
|
-
[self.singleResolves removeAllObjects];
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
#pragma mark - Events and errors
|
|
940
|
-
|
|
941
|
-
- (void) sendEvent:(NSString *)name body:(id)body
|
|
942
|
-
{
|
|
943
|
-
[self.bridge.eventDispatcher sendDeviceEventWithName:name body:body];
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
// Scan events carry the id of the scan they belong to
|
|
947
|
-
- (void) sendEvent:(NSString *)name scan:(RNZScan *)scan body:(NSDictionary *)body
|
|
948
|
-
{
|
|
949
|
-
NSMutableDictionary *withScan = [body mutableCopy];
|
|
950
|
-
withScan[@"scanId"] = scan.scanId ?: @"";
|
|
951
|
-
[self sendEvent:name body:withScan];
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
- (void) sendError:(NSDictionary *)error scan:(RNZScan *)scan
|
|
955
|
-
{
|
|
956
|
-
NSMutableDictionary *withScan = [error mutableCopy];
|
|
957
|
-
withScan[@"scanId"] = scan.scanId ?: @"";
|
|
958
|
-
[self sendError:withScan];
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
// Emits an error event as { message, code, domain, serviceName }
|
|
962
|
-
- (void) sendError:(NSDictionary *)error
|
|
963
|
-
{
|
|
964
|
-
[self sendEvent:@"RNZeroconfError" body:error];
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// Rejects a promise with the same { message, code, domain, serviceName } shape as error events, in userInfo
|
|
968
|
-
- (void) reject:(RCTPromiseRejectBlock)reject error:(NSDictionary *)error
|
|
969
|
-
{
|
|
970
|
-
id code = error[@"code"];
|
|
971
|
-
NSError *nsError = [NSError errorWithDomain:error[@"domain"]
|
|
972
|
-
code:[code isKindOfClass:[NSNumber class]] ? [code integerValue] : 0
|
|
973
|
-
userInfo:error];
|
|
974
|
-
reject([NSString stringWithFormat:@"%@", code], error[@"message"], nsError);
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
- (NSDictionary *) browseErrorWithCode:(DNSServiceErrorType)code scan:(RNZScan *)scan
|
|
978
|
-
{
|
|
979
|
-
if (!scan.typesOnly) {
|
|
980
|
-
return [self errorWithCode:code action:@"Browsing services" serviceName:nil];
|
|
981
|
-
}
|
|
982
|
-
if (code == kDNSServiceErr_NoAuth) {
|
|
983
|
-
// Browsing for service types requires the multicast entitlement (Apple's Local Network Privacy FAQ)
|
|
984
|
-
return @{
|
|
985
|
-
@"message": @"Listing service types failed: not authorized, it requires the com.apple.developer.networking.multicast entitlement",
|
|
986
|
-
@"code": @(code),
|
|
987
|
-
@"domain": @"DNSSD",
|
|
988
|
-
};
|
|
989
|
-
}
|
|
990
|
-
return [self errorWithCode:code action:@"Listing service types" serviceName:nil];
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
- (NSDictionary *) errorWithCode:(DNSServiceErrorType)code action:(NSString *)action serviceName:(NSString *)serviceName
|
|
994
|
-
{
|
|
995
|
-
NSString *subject = serviceName ? [NSString stringWithFormat:@"%@ %@", action, serviceName] : action;
|
|
996
|
-
NSMutableDictionary *error = [@{
|
|
997
|
-
@"message": [NSString stringWithFormat:@"%@ failed: %@", subject, [RNZeroconf describeError:code]],
|
|
998
|
-
@"code": @(code),
|
|
999
|
-
@"domain": @"DNSSD",
|
|
1000
|
-
} mutableCopy];
|
|
1001
|
-
if (serviceName) {
|
|
1002
|
-
error[@"serviceName"] = serviceName;
|
|
1003
|
-
}
|
|
1004
|
-
return error;
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
// Readable description of a DNSServiceErrorType
|
|
1008
|
-
+ (NSString *) describeError:(DNSServiceErrorType)code
|
|
1009
|
-
{
|
|
1010
|
-
switch (code) {
|
|
1011
|
-
case kDNSServiceErr_NoAuth: return @"not authorized, add the service type to NSBonjourServices in Info.plist";
|
|
1012
|
-
case kDNSServiceErr_PolicyDenied: return @"Local Network access denied";
|
|
1013
|
-
case kDNSServiceErr_NameConflict: return @"name already in use";
|
|
1014
|
-
case kDNSServiceErr_BadParam: return @"bad parameter";
|
|
1015
|
-
case kDNSServiceErr_NoSuchName: return @"no such name";
|
|
1016
|
-
case kDNSServiceErr_NoSuchRecord: return @"no such record";
|
|
1017
|
-
case kDNSServiceErr_ServiceNotRunning: return @"mDNSResponder is not running";
|
|
1018
|
-
case kDNSServiceErr_Timeout: return @"timed out";
|
|
1019
|
-
case kDNSServiceErr_NoMemory: return @"out of memory";
|
|
1020
|
-
case kDNSServiceErr_Unsupported: return @"unsupported";
|
|
1021
|
-
default: return [NSString stringWithFormat:@"error %d", code];
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
@end
|