react-native-zeroconf 0.17.4 → 1.0.0-rc.2

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.
Files changed (115) hide show
  1. package/NOTICE +2 -6
  2. package/Package.swift +19 -3
  3. package/README.md +1 -3
  4. package/android/CMakeLists.txt +64 -0
  5. package/android/build.gradle +1 -17
  6. package/android/generated/java/com/balthazargronon/RCTZeroconf/NativeZeroconfSpec.java +96 -0
  7. package/android/generated/jni/CMakeLists.txt +28 -0
  8. package/android/generated/jni/RNZeroconfSpec-generated.cpp +77 -0
  9. package/android/generated/jni/RNZeroconfSpec.h +31 -0
  10. package/android/generated/jni/react/renderer/components/RNZeroconfSpec/RNZeroconfSpecJSI.h +798 -0
  11. package/android/src/main/java/com/balthazargronon/RCTZeroconf/{ZeroconfModule.java → NsdHost.java} +97 -140
  12. package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPayload.java +87 -0
  13. package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPromise.java +40 -0
  14. package/android/src/main/java/com/balthazargronon/RCTZeroconf/Zeroconf.java +4 -6
  15. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfNativeSupport.java +60 -0
  16. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfOptions.java +15 -26
  17. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfReactPackage.java +5 -15
  18. package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/NsdServiceImpl.java +90 -96
  19. package/android/src/main/jni/mdnsresponder/mDNSShared/dnssd_clientshim.c +31 -6
  20. package/cpp/Backend.cpp +16 -0
  21. package/cpp/Backend.h +116 -0
  22. package/cpp/ZeroconfModule.cpp +280 -0
  23. package/cpp/ZeroconfModule.h +144 -0
  24. package/cpp/android/NsdBackend.cpp +462 -0
  25. package/cpp/android/NsdBackend.h +61 -0
  26. package/cpp/android/ZeroconfAndroidModule.cpp +49 -0
  27. package/cpp/android/ZeroconfAndroidModule.h +15 -0
  28. package/cpp/apple/DispatchExecutor.cpp +66 -0
  29. package/cpp/apple/DispatchExecutor.h +37 -0
  30. package/cpp/apple/LocalNetworkAccess.h +19 -0
  31. package/cpp/apple/LocalNetworkAccess.mm +87 -0
  32. package/cpp/dnssd/DnssdBackend.cpp +976 -0
  33. package/cpp/dnssd/DnssdBackend.h +114 -0
  34. package/cpp/dnssd/Executor.h +28 -0
  35. package/cpp/embedded/EmbeddedExecutor.cpp +124 -0
  36. package/cpp/embedded/EmbeddedExecutor.h +57 -0
  37. package/ios/ZeroconfOnLoad.mm +34 -0
  38. package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec-generated.mm +140 -0
  39. package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec.h +189 -0
  40. package/ios/generated/ReactCodegen/RNZeroconfSpecJSI.h +798 -0
  41. package/lib/module/NativeZeroconf.js +15 -0
  42. package/lib/module/NativeZeroconf.js.map +1 -0
  43. package/lib/module/bridge.js +124 -0
  44. package/lib/module/bridge.js.map +1 -0
  45. package/{dist → lib/module}/index.js +149 -149
  46. package/lib/module/index.js.map +1 -0
  47. package/lib/module/package.json +1 -0
  48. package/lib/module/types.js +24 -0
  49. package/lib/module/types.js.map +1 -0
  50. package/lib/typescript/package.json +1 -0
  51. package/lib/typescript/src/NativeZeroconf.d.ts +75 -0
  52. package/lib/typescript/src/NativeZeroconf.d.ts.map +1 -0
  53. package/lib/typescript/src/bridge.d.ts +22 -0
  54. package/lib/typescript/src/bridge.d.ts.map +1 -0
  55. package/lib/typescript/src/index.d.ts +111 -0
  56. package/lib/typescript/src/index.d.ts.map +1 -0
  57. package/lib/typescript/src/types.d.ts +175 -0
  58. package/lib/typescript/src/types.d.ts.map +1 -0
  59. package/package.json +64 -29
  60. package/react-native-zeroconf.podspec +2 -1
  61. package/react-native.config.js +17 -0
  62. package/src/NativeZeroconf.ts +120 -0
  63. package/src/bridge.ts +153 -0
  64. package/src/index.ts +607 -0
  65. package/{index.d.ts → src/types.ts} +5 -99
  66. package/windows/RNZeroconf/RNZeroconf.vcxproj +17 -2
  67. package/windows/RNZeroconf/RNZeroconf.vcxproj.filters +0 -6
  68. package/windows/RNZeroconf/ReactPackageProvider.cpp +23 -20
  69. package/windows/RNZeroconf/WindowsBackend.cpp +156 -0
  70. package/windows/RNZeroconf/WindowsBackend.h +13 -0
  71. package/windows/RNZeroconf/ZeroconfCore.cpp +2 -2
  72. package/windows/RNZeroconf/ZeroconfCore.h +2 -2
  73. package/windows/RNZeroconf/ZeroconfWindowsModule.cpp +22 -0
  74. package/windows/RNZeroconf/ZeroconfWindowsModule.h +14 -0
  75. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroConfImplFactory.java +0 -56
  76. package/android/src/main/java/com/balthazargronon/RCTZeroconf/dnssd/DnssdImpl.java +0 -654
  77. package/android/src/main/java/com/github/druk/dnssd/BaseListener.java +0 -33
  78. package/android/src/main/java/com/github/druk/dnssd/BrowseListener.java +0 -67
  79. package/android/src/main/java/com/github/druk/dnssd/DNSRecord.java +0 -47
  80. package/android/src/main/java/com/github/druk/dnssd/DNSSD.java +0 -681
  81. package/android/src/main/java/com/github/druk/dnssd/DNSSDEmbedded.java +0 -198
  82. package/android/src/main/java/com/github/druk/dnssd/DNSSDException.java +0 -66
  83. package/android/src/main/java/com/github/druk/dnssd/DNSSDRecordRegistrar.java +0 -55
  84. package/android/src/main/java/com/github/druk/dnssd/DNSSDRegistration.java +0 -54
  85. package/android/src/main/java/com/github/druk/dnssd/DNSSDService.java +0 -35
  86. package/android/src/main/java/com/github/druk/dnssd/DomainListener.java +0 -57
  87. package/android/src/main/java/com/github/druk/dnssd/InternalBrowseListener.java +0 -76
  88. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSD.java +0 -870
  89. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRecordRegistrar.java +0 -30
  90. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRegistration.java +0 -34
  91. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDService.java +0 -29
  92. package/android/src/main/java/com/github/druk/dnssd/InternalDomainListener.java +0 -62
  93. package/android/src/main/java/com/github/druk/dnssd/InternalQueryListener.java +0 -61
  94. package/android/src/main/java/com/github/druk/dnssd/InternalRegisterListener.java +0 -51
  95. package/android/src/main/java/com/github/druk/dnssd/InternalResolveListener.java +0 -58
  96. package/android/src/main/java/com/github/druk/dnssd/NSClass.java +0 -16
  97. package/android/src/main/java/com/github/druk/dnssd/NSType.java +0 -58
  98. package/android/src/main/java/com/github/druk/dnssd/QueryListener.java +0 -54
  99. package/android/src/main/java/com/github/druk/dnssd/RegisterListener.java +0 -46
  100. package/android/src/main/java/com/github/druk/dnssd/RegisterRecordListener.java +0 -35
  101. package/android/src/main/java/com/github/druk/dnssd/ResolveListener.java +0 -55
  102. package/android/src/main/java/com/github/druk/dnssd/TXTRecord.java +0 -293
  103. package/android/src/main/jni/Android.mk +0 -73
  104. package/android/src/main/jni/Application.mk +0 -3
  105. package/android/src/main/jni/DNSSD.java.h +0 -290
  106. package/android/src/main/jni/JNISupport.c +0 -1221
  107. package/ios/RNZeroconf/RNNetServiceSerializer.h +0 -31
  108. package/ios/RNZeroconf/RNNetServiceSerializer.m +0 -84
  109. package/ios/RNZeroconf/RNZeroconf.h +0 -17
  110. package/ios/RNZeroconf/RNZeroconf.m +0 -1025
  111. package/ios/RNZeroconf.xcodeproj/project.pbxproj +0 -261
  112. package/ios/RNZeroconf.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  113. package/src/index.js +0 -563
  114. package/windows/RNZeroconf/RNZeroconf.cpp +0 -228
  115. 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