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.
Files changed (117) hide show
  1. package/NOTICE +2 -6
  2. package/README.md +1 -1
  3. package/android/CMakeLists.txt +64 -0
  4. package/android/build.gradle +1 -13
  5. package/android/generated/java/com/balthazargronon/RCTZeroconf/NativeZeroconfSpec.java +96 -0
  6. package/android/generated/jni/CMakeLists.txt +28 -0
  7. package/android/generated/jni/RNZeroconfSpec-generated.cpp +77 -0
  8. package/android/generated/jni/RNZeroconfSpec.h +31 -0
  9. package/android/generated/jni/react/renderer/components/RNZeroconfSpec/RNZeroconfSpecJSI.h +798 -0
  10. package/android/src/main/java/com/balthazargronon/RCTZeroconf/{ZeroconfModule.java → NsdHost.java} +97 -140
  11. package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPayload.java +87 -0
  12. package/android/src/main/java/com/balthazargronon/RCTZeroconf/NsdPromise.java +40 -0
  13. package/android/src/main/java/com/balthazargronon/RCTZeroconf/Zeroconf.java +4 -6
  14. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfNativeSupport.java +60 -0
  15. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfOptions.java +15 -26
  16. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfReactPackage.java +5 -15
  17. package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/NsdServiceImpl.java +90 -96
  18. package/android/src/main/jni/mdnsresponder/mDNSPosix/PosixDaemon.c +19 -1
  19. package/android/src/main/jni/mdnsresponder/mDNSPosix/mDNSPosix.c +104 -1
  20. package/android/src/main/jni/mdnsresponder/mDNSPosix/mDNSPosix.h +7 -0
  21. package/android/src/main/jni/mdnsresponder/mDNSShared/dnssd_clientshim.c +34 -9
  22. package/cpp/Backend.cpp +16 -0
  23. package/cpp/Backend.h +116 -0
  24. package/cpp/ZeroconfModule.cpp +280 -0
  25. package/cpp/ZeroconfModule.h +144 -0
  26. package/cpp/android/NsdBackend.cpp +462 -0
  27. package/cpp/android/NsdBackend.h +61 -0
  28. package/cpp/android/ZeroconfAndroidModule.cpp +49 -0
  29. package/cpp/android/ZeroconfAndroidModule.h +15 -0
  30. package/cpp/apple/DispatchExecutor.cpp +66 -0
  31. package/cpp/apple/DispatchExecutor.h +37 -0
  32. package/cpp/apple/LocalNetworkAccess.h +19 -0
  33. package/cpp/apple/LocalNetworkAccess.mm +87 -0
  34. package/cpp/dnssd/DnssdBackend.cpp +976 -0
  35. package/cpp/dnssd/DnssdBackend.h +114 -0
  36. package/cpp/dnssd/Executor.h +28 -0
  37. package/cpp/embedded/EmbeddedExecutor.cpp +124 -0
  38. package/cpp/embedded/EmbeddedExecutor.h +57 -0
  39. package/ios/ZeroconfOnLoad.mm +34 -0
  40. package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec-generated.mm +140 -0
  41. package/ios/generated/ReactCodegen/RNZeroconfSpec/RNZeroconfSpec.h +189 -0
  42. package/ios/generated/ReactCodegen/RNZeroconfSpecJSI.h +798 -0
  43. package/lib/module/NativeZeroconf.js +15 -0
  44. package/lib/module/NativeZeroconf.js.map +1 -0
  45. package/lib/module/bridge.js +124 -0
  46. package/lib/module/bridge.js.map +1 -0
  47. package/{dist → lib/module}/index.js +149 -149
  48. package/lib/module/index.js.map +1 -0
  49. package/lib/module/package.json +1 -0
  50. package/lib/module/types.js +24 -0
  51. package/lib/module/types.js.map +1 -0
  52. package/lib/typescript/package.json +1 -0
  53. package/lib/typescript/src/NativeZeroconf.d.ts +75 -0
  54. package/lib/typescript/src/NativeZeroconf.d.ts.map +1 -0
  55. package/lib/typescript/src/bridge.d.ts +22 -0
  56. package/lib/typescript/src/bridge.d.ts.map +1 -0
  57. package/lib/typescript/src/index.d.ts +111 -0
  58. package/lib/typescript/src/index.d.ts.map +1 -0
  59. package/lib/typescript/src/types.d.ts +175 -0
  60. package/lib/typescript/src/types.d.ts.map +1 -0
  61. package/package.json +65 -29
  62. package/react-native-zeroconf.podspec +2 -1
  63. package/react-native.config.js +17 -0
  64. package/src/NativeZeroconf.ts +120 -0
  65. package/src/bridge.ts +153 -0
  66. package/src/index.ts +607 -0
  67. package/{index.d.ts → src/types.ts} +5 -99
  68. package/windows/RNZeroconf/RNZeroconf.vcxproj +17 -2
  69. package/windows/RNZeroconf/RNZeroconf.vcxproj.filters +0 -6
  70. package/windows/RNZeroconf/ReactPackageProvider.cpp +23 -20
  71. package/windows/RNZeroconf/WindowsBackend.cpp +156 -0
  72. package/windows/RNZeroconf/WindowsBackend.h +13 -0
  73. package/windows/RNZeroconf/ZeroconfCore.cpp +2 -2
  74. package/windows/RNZeroconf/ZeroconfCore.h +2 -2
  75. package/windows/RNZeroconf/ZeroconfWindowsModule.cpp +22 -0
  76. package/windows/RNZeroconf/ZeroconfWindowsModule.h +14 -0
  77. package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroConfImplFactory.java +0 -56
  78. package/android/src/main/java/com/balthazargronon/RCTZeroconf/dnssd/DnssdImpl.java +0 -654
  79. package/android/src/main/java/com/github/druk/dnssd/BaseListener.java +0 -33
  80. package/android/src/main/java/com/github/druk/dnssd/BrowseListener.java +0 -67
  81. package/android/src/main/java/com/github/druk/dnssd/DNSRecord.java +0 -47
  82. package/android/src/main/java/com/github/druk/dnssd/DNSSD.java +0 -650
  83. package/android/src/main/java/com/github/druk/dnssd/DNSSDEmbedded.java +0 -135
  84. package/android/src/main/java/com/github/druk/dnssd/DNSSDException.java +0 -66
  85. package/android/src/main/java/com/github/druk/dnssd/DNSSDRecordRegistrar.java +0 -55
  86. package/android/src/main/java/com/github/druk/dnssd/DNSSDRegistration.java +0 -54
  87. package/android/src/main/java/com/github/druk/dnssd/DNSSDService.java +0 -35
  88. package/android/src/main/java/com/github/druk/dnssd/DomainListener.java +0 -57
  89. package/android/src/main/java/com/github/druk/dnssd/InternalBrowseListener.java +0 -76
  90. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSD.java +0 -870
  91. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRecordRegistrar.java +0 -30
  92. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDRegistration.java +0 -34
  93. package/android/src/main/java/com/github/druk/dnssd/InternalDNSSDService.java +0 -29
  94. package/android/src/main/java/com/github/druk/dnssd/InternalDomainListener.java +0 -62
  95. package/android/src/main/java/com/github/druk/dnssd/InternalQueryListener.java +0 -61
  96. package/android/src/main/java/com/github/druk/dnssd/InternalRegisterListener.java +0 -51
  97. package/android/src/main/java/com/github/druk/dnssd/InternalResolveListener.java +0 -58
  98. package/android/src/main/java/com/github/druk/dnssd/NSClass.java +0 -16
  99. package/android/src/main/java/com/github/druk/dnssd/NSType.java +0 -58
  100. package/android/src/main/java/com/github/druk/dnssd/QueryListener.java +0 -54
  101. package/android/src/main/java/com/github/druk/dnssd/RegisterListener.java +0 -46
  102. package/android/src/main/java/com/github/druk/dnssd/RegisterRecordListener.java +0 -35
  103. package/android/src/main/java/com/github/druk/dnssd/ResolveListener.java +0 -55
  104. package/android/src/main/java/com/github/druk/dnssd/TXTRecord.java +0 -293
  105. package/android/src/main/jni/Android.mk +0 -73
  106. package/android/src/main/jni/Application.mk +0 -3
  107. package/android/src/main/jni/DNSSD.java.h +0 -290
  108. package/android/src/main/jni/JNISupport.c +0 -1140
  109. package/ios/RNZeroconf/RNNetServiceSerializer.h +0 -31
  110. package/ios/RNZeroconf/RNNetServiceSerializer.m +0 -84
  111. package/ios/RNZeroconf/RNZeroconf.h +0 -17
  112. package/ios/RNZeroconf/RNZeroconf.m +0 -1025
  113. package/ios/RNZeroconf.xcodeproj/project.pbxproj +0 -261
  114. package/ios/RNZeroconf.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  115. package/src/index.js +0 -563
  116. package/windows/RNZeroconf/RNZeroconf.cpp +0 -228
  117. package/windows/RNZeroconf/RNZeroconf.h +0 -70
@@ -505,7 +505,7 @@ mDNSexport int ParseDNSServers(mDNS *m, const char *filePath)
505
505
  {
506
506
  char line[256];
507
507
  char nameserver[16];
508
- char keyword[10];
508
+ char keyword[11]; // "%10s" writes up to 10 characters and the terminator
509
509
  int numOfServers = 0;
510
510
  FILE *fp = fopen(filePath, "r");
511
511
  if (fp == NULL) return -1;
@@ -1333,6 +1333,107 @@ mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m)
1333
1333
  #pragma mark ***** Locking
1334
1334
  #endif
1335
1335
 
1336
+ #ifdef EMBEDDED
1337
+
1338
+ // The embedded build enters mDNS core from several threads: the DNS-SDEmbedded thread
1339
+ // runs mDNS_Execute() and delivers the callbacks, while the DNSService* calls, and in
1340
+ // particular DNSServiceRefDeallocate(), arrive on whatever thread the client uses.
1341
+ // mDNS core relies on mDNSPlatformLock() for mutual exclusion between those, so here it
1342
+ // is a real lock. It is recursive because the core holds it while it calls back into the
1343
+ // client, and the client may call back into the core from there (see mDNS_DropLockBeforeCallback).
1344
+
1345
+ #include <pthread.h>
1346
+
1347
+ static pthread_mutex_t gCoreLock;
1348
+ static pthread_once_t gCoreLockOnce = PTHREAD_ONCE_INIT;
1349
+
1350
+ // The event loop sleeps in select() until the core's next scheduled event. A DNSService* call
1351
+ // from another thread changes that schedule (a new question has to be answered now), so when
1352
+ // such a call releases the lock, the loop is woken through a pipe it listens to. Otherwise the
1353
+ // call would wait for the next timer or the next packet from the network.
1354
+ // The pipe is created together with the lock and lives as long as the process, so that the
1355
+ // write in mDNSPlatformUnlock() can never hit a descriptor another thread has closed and reused.
1356
+ static pthread_t gLoopThread;
1357
+ static int gLoopThreadSet = 0;
1358
+ static int gWakePipe[2] = { -1, -1 };
1359
+
1360
+ static void WakeCallback(int fd, short filter, void *context)
1361
+ {
1362
+ char buffer[64];
1363
+ (void) filter; // Unused
1364
+ (void) context; // Unused
1365
+ while (read(fd, buffer, sizeof(buffer)) > 0) { }
1366
+ }
1367
+
1368
+ static void InitCoreLockAndWakePipe(void)
1369
+ {
1370
+ pthread_mutexattr_t attr;
1371
+ pthread_mutexattr_init(&attr);
1372
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
1373
+ pthread_mutex_init(&gCoreLock, &attr);
1374
+ pthread_mutexattr_destroy(&attr);
1375
+ if (pipe(gWakePipe) == 0)
1376
+ {
1377
+ fcntl(gWakePipe[0], F_SETFL, fcntl(gWakePipe[0], F_GETFL, 0) | O_NONBLOCK);
1378
+ fcntl(gWakePipe[1], F_SETFL, fcntl(gWakePipe[1], F_GETFL, 0) | O_NONBLOCK);
1379
+ }
1380
+ else
1381
+ {
1382
+ gWakePipe[0] = gWakePipe[1] = -1;
1383
+ LogMsg("mDNSPosix: pipe() failed (%d), other threads will not wake the event loop", errno);
1384
+ }
1385
+ }
1386
+
1387
+ mDNSexport void mDNSPosixEmbeddedLoopStarting(void)
1388
+ {
1389
+ mDNSPlatformLock(mDNSNULL);
1390
+ gLoopThread = pthread_self();
1391
+ gLoopThreadSet = 1;
1392
+ if (gWakePipe[0] >= 0)
1393
+ {
1394
+ mStatus err = mDNSPosixAddFDToEventLoop(gWakePipe[0], WakeCallback, mDNSNULL);
1395
+ if (err) LogMsg("mDNSPosix: cannot listen to the wake-up pipe (%d), other threads will not wake the event loop", err);
1396
+ }
1397
+ mDNSPlatformUnlock(mDNSNULL);
1398
+ }
1399
+
1400
+ mDNSexport void mDNSPosixEmbeddedLoopStopping(void)
1401
+ {
1402
+ mDNSPlatformLock(mDNSNULL);
1403
+ if (gWakePipe[0] >= 0)
1404
+ {
1405
+ mDNSPosixRemoveFDFromEventLoop(gWakePipe[0]);
1406
+ WakeCallback(gWakePipe[0], 0, mDNSNULL); // Drain, for the next loop
1407
+ }
1408
+ gLoopThreadSet = 0;
1409
+ mDNSPlatformUnlock(mDNSNULL);
1410
+ }
1411
+
1412
+ mDNSexport void mDNSPosixEmbeddedWakeLoop(void)
1413
+ {
1414
+ if (gWakePipe[1] >= 0)
1415
+ {
1416
+ if (write(gWakePipe[1], "", 1) < 0) { } // A full pipe means the loop is about to wake anyway
1417
+ }
1418
+ }
1419
+
1420
+ mDNSexport void mDNSPlatformLock (const mDNS *const m)
1421
+ {
1422
+ (void) m; // Unused
1423
+ pthread_once(&gCoreLockOnce, InitCoreLockAndWakePipe);
1424
+ pthread_mutex_lock(&gCoreLock);
1425
+ }
1426
+
1427
+ mDNSexport void mDNSPlatformUnlock (const mDNS *const m)
1428
+ {
1429
+ (void) m; // Unused
1430
+ int fromLoopThread = gLoopThreadSet && pthread_equal(pthread_self(), gLoopThread);
1431
+ pthread_mutex_unlock(&gCoreLock);
1432
+ if (!fromLoopThread) mDNSPosixEmbeddedWakeLoop();
1433
+ }
1434
+
1435
+ #else
1436
+
1336
1437
  // On the Posix platform, locking is a no-op because we only ever enter
1337
1438
  // mDNS core on the main thread.
1338
1439
 
@@ -1350,6 +1451,8 @@ mDNSexport void mDNSPlatformUnlock (const mDNS *const m)
1350
1451
  (void) m; // Unused
1351
1452
  }
1352
1453
 
1454
+ #endif // EMBEDDED
1455
+
1353
1456
  #if COMPILER_LIKES_PRAGMA_MARK
1354
1457
  #pragma mark ***** Strings
1355
1458
  #endif
@@ -81,6 +81,13 @@ extern mStatus mDNSPosixListenForSignalInEventLoop( int signum);
81
81
  extern mStatus mDNSPosixIgnoreSignalInEventLoop( int signum);
82
82
  extern mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, sigset_t *pSignalsReceived, mDNSBool *pDataDispatched);
83
83
 
84
+ #ifdef EMBEDDED
85
+ // The embedded build is driven from several threads, see the locking section of mDNSPosix.c.
86
+ extern void mDNSPosixEmbeddedLoopStarting(void); // Call on the event loop thread, before running the loop
87
+ extern void mDNSPosixEmbeddedLoopStopping(void); // Call on the event loop thread, after the loop returned
88
+ extern void mDNSPosixEmbeddedWakeLoop(void); // Make the loop re-read the schedule and stop flags
89
+ #endif
90
+
84
91
  #ifdef __cplusplus
85
92
  }
86
93
  #endif
@@ -80,6 +80,7 @@ typedef struct
80
80
  {
81
81
  mDNS_DirectOP_Dispose *disposefn;
82
82
  DNSServiceRef aQuery;
83
+ DNSServiceRef aaaaQuery; // react-native-zeroconf: IPv6 addresses too
83
84
  DNSServiceGetAddrInfoReply callback;
84
85
  void *context;
85
86
  } mDNS_DirectOP_GetAddrInfo;
@@ -198,8 +199,9 @@ mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus re
198
199
 
199
200
  if (result == mStatus_NoError)
200
201
  {
202
+ // react-native-zeroconf: kDNSServiceFlagsAdd marks a registration, as with Apple's implementation
201
203
  if (x->callback)
202
- x->callback((DNSServiceRef)x, 0, result, namestr, typestr, domstr, x->context);
204
+ x->callback((DNSServiceRef)x, kDNSServiceFlagsAdd, result, namestr, typestr, domstr, x->context);
203
205
  }
204
206
  else if (result == mStatus_NameConflict)
205
207
  {
@@ -701,10 +703,10 @@ DNSServiceErrorType DNSServiceQueryRecord
701
703
  x->callback = callback;
702
704
  x->context = context;
703
705
 
704
- x->q.ThisQInterval = -1; // So that DNSServiceResolveDispose() knows whether to cancel this question
706
+ x->q.ThisQInterval = -1; // So that DNSServiceQueryRecordDispose() knows whether to cancel this question
705
707
  x->q.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
706
708
  x->q.Target = zeroAddr;
707
- MakeDomainNameFromDNSNameString(&x->q.qname, fullname);
709
+ if (!MakeDomainNameFromDNSNameString(&x->q.qname, fullname)) { mDNSPlatformMemFree(x); err = mStatus_BadParamErr; errormsg = "MakeDomainNameFromDNSNameString"; goto fail; }
708
710
  x->q.qtype = rrtype;
709
711
  x->q.qclass = rrclass;
710
712
  x->q.LongLived = (flags & kDNSServiceFlagsLongLivedQuery) != 0;
@@ -721,7 +723,7 @@ DNSServiceErrorType DNSServiceQueryRecord
721
723
  x->q.QuestionContext = x;
722
724
 
723
725
  err = mDNS_StartQuery(&mDNSStorage, &x->q);
724
- if (err) { DNSServiceResolveDispose((mDNS_DirectOP*)x); errormsg = "mDNS_StartQuery"; goto fail; }
726
+ if (err) { DNSServiceQueryRecordDispose((mDNS_DirectOP*)x); errormsg = "mDNS_StartQuery"; goto fail; }
725
727
 
726
728
  // Succeeded: Wrap up and return
727
729
  *sdRef = (DNSServiceRef)x;
@@ -739,6 +741,7 @@ static void DNSServiceGetAddrInfoDispose(mDNS_DirectOP *op)
739
741
  {
740
742
  mDNS_DirectOP_GetAddrInfo *x = (mDNS_DirectOP_GetAddrInfo*)op;
741
743
  if (x->aQuery) DNSServiceRefDeallocate(x->aQuery);
744
+ if (x->aaaaQuery) DNSServiceRefDeallocate(x->aaaaQuery);
742
745
  mDNSPlatformMemFree(x);
743
746
  }
744
747
 
@@ -757,16 +760,27 @@ static void DNSSD_API DNSServiceGetAddrInfoResponse(
757
760
  {
758
761
  mDNS_DirectOP_GetAddrInfo * x = (mDNS_DirectOP_GetAddrInfo*)inContext;
759
762
  struct sockaddr_in sa4;
763
+ struct sockaddr_in6 sa6;
764
+ const struct sockaddr * sa = (const struct sockaddr *) &sa4;
760
765
 
761
766
  mDNSPlatformMemZero(&sa4, sizeof(sa4));
762
- if (inErrorCode == kDNSServiceErr_NoError && inRRType == kDNSServiceType_A)
767
+ mDNSPlatformMemZero(&sa6, sizeof(sa6));
768
+ if (inErrorCode == kDNSServiceErr_NoError && inRRType == kDNSServiceType_A && inRDLen == 4)
763
769
  {
764
770
  sa4.sin_family = AF_INET;
765
771
  mDNSPlatformMemCopy(&sa4.sin_addr.s_addr, inRData, 4);
766
772
  }
773
+ else if (inErrorCode == kDNSServiceErr_NoError && inRRType == kDNSServiceType_AAAA && inRDLen == 16)
774
+ {
775
+ // react-native-zeroconf: AAAA answers become IPv6 addresses, as with Apple's implementation
776
+ sa6.sin6_family = AF_INET6;
777
+ mDNSPlatformMemCopy(&sa6.sin6_addr, inRData, 16);
778
+ sa6.sin6_scope_id = inInterfaceIndex;
779
+ sa = (const struct sockaddr *) &sa6;
780
+ }
767
781
 
768
782
  x->callback((DNSServiceRef)x, inFlags, inInterfaceIndex, inErrorCode, inFullName,
769
- (const struct sockaddr *) &sa4, inTTL, x->context);
783
+ sa, inTTL, x->context);
770
784
  }
771
785
 
772
786
  DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo(
@@ -791,14 +805,25 @@ DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo(
791
805
  x->callback = inCallback;
792
806
  x->context = inContext;
793
807
  x->aQuery = mDNSNULL;
808
+ x->aaaaQuery = mDNSNULL;
794
809
 
795
810
  // Start the query.
796
811
  // (It would probably be more efficient to code this using mDNS_StartQuery directly,
797
812
  // instead of wrapping DNSServiceQueryRecord, which then unnecessarily allocates
798
813
  // more memory and then just calls through to mDNS_StartQuery. -- SC June 2010)
799
- err = DNSServiceQueryRecord(&x->aQuery, inFlags, inInterfaceIndex, inHostName, kDNSServiceType_A,
800
- kDNSServiceClass_IN, DNSServiceGetAddrInfoResponse, x);
801
- if (err) { DNSServiceGetAddrInfoDispose((mDNS_DirectOP*)x); errormsg = "DNSServiceQueryRecord"; goto fail; }
814
+ // react-native-zeroconf: an A query for IPv4 and an AAAA query for IPv6, both without a protocol
815
+ if (!inProtocol || (inProtocol & kDNSServiceProtocol_IPv4))
816
+ {
817
+ err = DNSServiceQueryRecord(&x->aQuery, inFlags, inInterfaceIndex, inHostName, kDNSServiceType_A,
818
+ kDNSServiceClass_IN, DNSServiceGetAddrInfoResponse, x);
819
+ if (err) { DNSServiceGetAddrInfoDispose((mDNS_DirectOP*)x); errormsg = "DNSServiceQueryRecord"; goto fail; }
820
+ }
821
+ if (!inProtocol || (inProtocol & kDNSServiceProtocol_IPv6))
822
+ {
823
+ err = DNSServiceQueryRecord(&x->aaaaQuery, inFlags, inInterfaceIndex, inHostName, kDNSServiceType_AAAA,
824
+ kDNSServiceClass_IN, DNSServiceGetAddrInfoResponse, x);
825
+ if (err) { DNSServiceGetAddrInfoDispose((mDNS_DirectOP*)x); errormsg = "DNSServiceQueryRecord"; goto fail; }
826
+ }
802
827
 
803
828
  *outRef = (DNSServiceRef)x;
804
829
  return(mStatus_NoError);
@@ -0,0 +1,16 @@
1
+ #include "Backend.h"
2
+
3
+ namespace rnzeroconf {
4
+
5
+ Error LibraryError(const std::string &code, const std::string &message, const std::string &serviceName) {
6
+ return Error{"RNZeroconf", code, message, serviceName};
7
+ }
8
+
9
+ std::string SubtypeLabel(const std::string &subtype) {
10
+ if (subtype.empty() || subtype[0] == '_') {
11
+ return subtype;
12
+ }
13
+ return "_" + subtype;
14
+ }
15
+
16
+ } // namespace rnzeroconf
package/cpp/Backend.h ADDED
@@ -0,0 +1,116 @@
1
+ // What a platform implements: scanning, resolving and publishing on one mDNS stack.
2
+ // No React Native dependency: ZeroconfModule bridges a backend to JavaScript, the native harnesses test it directly.
3
+ // Strings are UTF-8. Callbacks run on the backend's own thread.
4
+ #pragma once
5
+
6
+ #include <cstdint>
7
+ #include <functional>
8
+ #include <optional>
9
+ #include <string>
10
+ #include <utility>
11
+ #include <vector>
12
+
13
+ namespace rnzeroconf {
14
+
15
+ // TXT records as ordered [key, value] pairs
16
+ using TxtPairs = std::vector<std::pair<std::string, std::string>>;
17
+
18
+ struct Service {
19
+ std::string name;
20
+ std::string fullName;
21
+ std::string host;
22
+ uint16_t port = 0;
23
+ std::vector<std::string> addresses;
24
+ TxtPairs txt;
25
+ };
26
+
27
+ // { message, code, domain, serviceName } as JavaScript sees it.
28
+ // The code is a string for domain "RNZeroconf" ('TIMEOUT'), the platform's number otherwise (-65555)
29
+ struct Error {
30
+ std::string domain;
31
+ std::string code;
32
+ std::string message;
33
+ std::string serviceName;
34
+ };
35
+
36
+ Error LibraryError(const std::string &code, const std::string &message, const std::string &serviceName = "");
37
+
38
+ using ServiceCallback = std::function<void(const Service &)>;
39
+ using ErrorCallback = std::function<void(const Error &)>;
40
+
41
+ // Scan events carry the id of the scan they belong to. An error with an empty scanId is not about a scan
42
+ struct Events {
43
+ std::function<void(const std::string &scanId)> start;
44
+ std::function<void(const std::string &scanId)> stop;
45
+ std::function<void(const std::string &scanId, const std::string &name)> found;
46
+ std::function<void(const std::string &scanId, const std::string &name)> remove;
47
+ std::function<void(const std::string &scanId, const Service &)> resolved;
48
+ std::function<void(const Service &)> published;
49
+ std::function<void(const Service &)> unpublished;
50
+ std::function<void(const std::string &scanId, const Error &)> error;
51
+ };
52
+
53
+ struct ScanOptions {
54
+ // "printer" or "_printer", empty for none
55
+ std::string subtype;
56
+ // "en0", empty for every interface
57
+ std::string networkInterface;
58
+ // Each found service is resolved for this long, retried once, then reported with a TIMEOUT error
59
+ double resolveTimeoutSeconds = 5;
60
+ };
61
+
62
+ struct PublishOptions {
63
+ std::vector<std::string> subtypes;
64
+ std::string networkInterface;
65
+ };
66
+
67
+ struct ResolveOptions {
68
+ std::string networkInterface;
69
+ double timeoutSeconds = 5;
70
+ };
71
+
72
+ class Backend {
73
+ public:
74
+ virtual ~Backend() = default;
75
+
76
+ // type and protocol without underscore ("http", "tcp"); "services._dns-sd" / "udp" lists the service types.
77
+ // Starting a scan with the id of a running one replaces it
78
+ virtual void Scan(
79
+ const std::string &scanId,
80
+ const std::string &type,
81
+ const std::string &protocol,
82
+ const std::string &domain,
83
+ const ScanOptions &options) = 0;
84
+ // Stops the scan with this id, or every scan with an empty one
85
+ virtual void Stop(const std::string &scanId) = 0;
86
+
87
+ virtual void Publish(
88
+ const std::string &type,
89
+ const std::string &protocol,
90
+ const std::string &domain,
91
+ const std::string &name,
92
+ uint16_t port,
93
+ const TxtPairs &txt,
94
+ const PublishOptions &options,
95
+ ServiceCallback resolve,
96
+ ErrorCallback reject) = 0;
97
+ virtual void Unpublish(const std::string &name, ServiceCallback resolve, ErrorCallback reject) = 0;
98
+ virtual void Update(const std::string &name, const TxtPairs &txt, ServiceCallback resolve, ErrorCallback reject) = 0;
99
+
100
+ virtual void ResolveService(
101
+ const std::string &name,
102
+ const std::string &type,
103
+ const std::string &protocol,
104
+ const std::string &domain,
105
+ const ResolveOptions &options,
106
+ ServiceCallback resolve,
107
+ ErrorCallback reject) = 0;
108
+
109
+ // Stops the scans and unpublishes the services, when the module goes away
110
+ virtual void Shutdown() = 0;
111
+ };
112
+
113
+ // "printer" or "_printer" -> "_printer", empty stays empty
114
+ std::string SubtypeLabel(const std::string &subtype);
115
+
116
+ } // namespace rnzeroconf
@@ -0,0 +1,280 @@
1
+ #include "ZeroconfModule.h"
2
+
3
+ #include <utility>
4
+
5
+ namespace facebook::react {
6
+
7
+ namespace {
8
+
9
+ rnzeroconf::TxtPairs ToTxt(const std::vector<ZeroconfTxtEntry> &entries) {
10
+ rnzeroconf::TxtPairs txt;
11
+ for (const auto &entry : entries) {
12
+ txt.emplace_back(entry.key, entry.value);
13
+ }
14
+ return txt;
15
+ }
16
+
17
+ ZeroconfService ToJs(const rnzeroconf::Service &service) {
18
+ std::vector<ZeroconfTxtEntry> txt;
19
+ for (const auto &[key, value] : service.txt) {
20
+ txt.push_back(ZeroconfTxtEntry{key, value});
21
+ }
22
+ return ZeroconfService{service.name, service.fullName, service.host, static_cast<double>(service.port), service.addresses, txt};
23
+ }
24
+
25
+ ZeroconfError ToJs(const rnzeroconf::Error &error) {
26
+ return ZeroconfError{error.message, error.code, error.domain, error.serviceName};
27
+ }
28
+
29
+ } // namespace
30
+
31
+ ZeroconfModule::ZeroconfModule(std::shared_ptr<CallInvoker> jsInvoker, ZeroconfPlatform platform)
32
+ : NativeZeroconfCxxSpec(std::move(jsInvoker)), platform_(std::move(platform)), emitter_(std::make_shared<Emitter>()) {
33
+ emitter_->module = this;
34
+ }
35
+
36
+ ZeroconfModule::~ZeroconfModule() {
37
+ {
38
+ std::lock_guard<std::mutex> lock(emitter_->mutex);
39
+ emitter_->module = nullptr;
40
+ }
41
+ {
42
+ std::lock_guard<std::mutex> lock(activityMutex_);
43
+ if (active_ && platform_.setActive) {
44
+ platform_.setActive(false);
45
+ }
46
+ }
47
+ // Stops the scans and unpublishes the services, on reload for example
48
+ std::lock_guard<std::mutex> lock(backendsMutex_);
49
+ for (auto &entry : backends_) {
50
+ entry.second->Shutdown();
51
+ }
52
+ }
53
+
54
+ std::shared_ptr<rnzeroconf::Backend> ZeroconfModule::Backend(const std::string &implType) {
55
+ std::string key = platform_.backendKey ? platform_.backendKey(implType) : implType;
56
+ std::lock_guard<std::mutex> lock(backendsMutex_);
57
+ auto found = backends_.find(key);
58
+ if (found != backends_.end()) {
59
+ return found->second;
60
+ }
61
+ auto backend = platform_.createBackend(key, MakeEvents());
62
+ backends_[key] = backend;
63
+ return backend;
64
+ }
65
+
66
+ rnzeroconf::Events ZeroconfModule::MakeEvents() {
67
+ std::weak_ptr<Emitter> weak = emitter_;
68
+ // Runs an emit while the module exists
69
+ auto with = [weak](const std::function<void(ZeroconfModule &)> &emit) {
70
+ auto emitter = weak.lock();
71
+ if (!emitter) {
72
+ return;
73
+ }
74
+ std::lock_guard<std::mutex> lock(emitter->mutex);
75
+ if (emitter->module) {
76
+ emit(*emitter->module);
77
+ }
78
+ };
79
+
80
+ rnzeroconf::Events events;
81
+ events.start = [with](const std::string &scanId) {
82
+ with([&](ZeroconfModule &module) { module.emitOnStart(ZeroconfScanEvent{scanId}); });
83
+ };
84
+ events.stop = [with](const std::string &scanId) {
85
+ with([&](ZeroconfModule &module) {
86
+ // Also when the scan ended by itself, after an error
87
+ module.Track(Activity::ScanStopped, scanId);
88
+ module.emitOnStop(ZeroconfScanEvent{scanId});
89
+ });
90
+ };
91
+ events.found = [with](const std::string &scanId, const std::string &name) {
92
+ with([&](ZeroconfModule &module) { module.emitOnFound(ZeroconfNameEvent{scanId, name}); });
93
+ };
94
+ events.remove = [with](const std::string &scanId, const std::string &name) {
95
+ with([&](ZeroconfModule &module) { module.emitOnRemove(ZeroconfNameEvent{scanId, name}); });
96
+ };
97
+ events.resolved = [with](const std::string &scanId, const rnzeroconf::Service &service) {
98
+ with([&](ZeroconfModule &module) { module.emitOnResolved(ZeroconfResolvedEvent{scanId, ToJs(service)}); });
99
+ };
100
+ events.published = [with](const rnzeroconf::Service &service) {
101
+ with([&](ZeroconfModule &module) {
102
+ module.Track(Activity::Published, service.name);
103
+ module.emitOnPublished(ToJs(service));
104
+ });
105
+ };
106
+ events.unpublished = [with](const rnzeroconf::Service &service) {
107
+ with([&](ZeroconfModule &module) {
108
+ module.Track(Activity::Unpublished, service.name);
109
+ module.emitOnUnpublished(ToJs(service));
110
+ });
111
+ };
112
+ events.error = [with](const std::string &scanId, const rnzeroconf::Error &error) {
113
+ with([&](ZeroconfModule &module) { module.emitOnError(ZeroconfErrorEvent{scanId, ToJs(error)}); });
114
+ };
115
+ return events;
116
+ }
117
+
118
+ AsyncPromise<ZeroconfResult> ZeroconfModule::Settle(
119
+ jsi::Runtime &rt,
120
+ const std::function<void(rnzeroconf::ServiceCallback, rnzeroconf::ErrorCallback)> &call,
121
+ bool active) {
122
+ AsyncPromise<ZeroconfResult> promise(rt, jsInvoker_);
123
+ // Publishes and resolves need the network until they settle
124
+ std::shared_ptr<bool> settled;
125
+ if (active) {
126
+ Track(Activity::CallStarted);
127
+ settled = std::make_shared<bool>(false);
128
+ }
129
+ std::weak_ptr<Emitter> weak = emitter_;
130
+ auto done = [weak, settled] {
131
+ if (!settled || *settled) {
132
+ return;
133
+ }
134
+ *settled = true;
135
+ if (auto emitter = weak.lock()) {
136
+ std::lock_guard<std::mutex> lock(emitter->mutex);
137
+ if (emitter->module) {
138
+ emitter->module->Track(Activity::CallSettled);
139
+ }
140
+ }
141
+ };
142
+ call(
143
+ [promise, done](const rnzeroconf::Service &service) mutable {
144
+ done();
145
+ promise.resolve(ZeroconfResult{ToJs(service), std::nullopt});
146
+ },
147
+ [promise, done](const rnzeroconf::Error &error) mutable {
148
+ done();
149
+ promise.resolve(ZeroconfResult{std::nullopt, ToJs(error)});
150
+ });
151
+ return promise;
152
+ }
153
+
154
+ void ZeroconfModule::Track(Activity activity, const std::string &key) {
155
+ std::lock_guard<std::mutex> lock(activityMutex_);
156
+ switch (activity) {
157
+ case Activity::ScanStarted:
158
+ runningScans_.insert(key);
159
+ break;
160
+ case Activity::ScanStopped:
161
+ runningScans_.erase(key);
162
+ break;
163
+ case Activity::AllScansStopped:
164
+ runningScans_.clear();
165
+ break;
166
+ case Activity::Published:
167
+ publishedServices_.insert(key);
168
+ break;
169
+ case Activity::Unpublished:
170
+ publishedServices_.erase(key);
171
+ break;
172
+ case Activity::CallStarted:
173
+ callsInFlight_++;
174
+ break;
175
+ case Activity::CallSettled:
176
+ callsInFlight_--;
177
+ break;
178
+ }
179
+ bool active = !runningScans_.empty() || !publishedServices_.empty() || callsInFlight_ > 0;
180
+ if (active != active_) {
181
+ active_ = active;
182
+ if (platform_.setActive) {
183
+ platform_.setActive(active);
184
+ }
185
+ }
186
+ }
187
+
188
+ void ZeroconfModule::scan(
189
+ jsi::Runtime &,
190
+ std::string scanId,
191
+ std::string type,
192
+ std::string protocol,
193
+ std::string domain,
194
+ std::string implType,
195
+ ZeroconfScanOptions options) {
196
+ rnzeroconf::ScanOptions scanOptions;
197
+ scanOptions.resolveTimeoutSeconds = options.resolveTimeout.value_or(5);
198
+ scanOptions.subtype = options.subtype.value_or("");
199
+ scanOptions.networkInterface = options.networkInterface.value_or("");
200
+ Track(Activity::ScanStarted, scanId);
201
+ Backend(implType)->Scan(scanId, type, protocol, domain, scanOptions);
202
+ }
203
+
204
+ void ZeroconfModule::stop(jsi::Runtime &, std::string scanId, std::string implType) {
205
+ Track(scanId.empty() ? Activity::AllScansStopped : Activity::ScanStopped, scanId);
206
+ Backend(implType)->Stop(scanId);
207
+ }
208
+
209
+ AsyncPromise<ZeroconfResult> ZeroconfModule::registerService(
210
+ jsi::Runtime &rt,
211
+ std::string type,
212
+ std::string protocol,
213
+ std::string domain,
214
+ std::string name,
215
+ double port,
216
+ std::vector<ZeroconfTxtEntry> txt,
217
+ std::string implType,
218
+ ZeroconfPublishOptions options) {
219
+ rnzeroconf::PublishOptions publishOptions;
220
+ publishOptions.subtypes = options.subtypes.value_or(std::vector<std::string>{});
221
+ publishOptions.networkInterface = options.networkInterface.value_or("");
222
+ auto backend = Backend(implType);
223
+ return Settle(
224
+ rt,
225
+ [&](rnzeroconf::ServiceCallback resolve, rnzeroconf::ErrorCallback reject) {
226
+ backend->Publish(type, protocol, domain, name, static_cast<uint16_t>(port), ToTxt(txt), publishOptions, std::move(resolve), std::move(reject));
227
+ },
228
+ true);
229
+ }
230
+
231
+ AsyncPromise<ZeroconfResult> ZeroconfModule::updateService(jsi::Runtime &rt, std::string name, std::vector<ZeroconfTxtEntry> txt, std::string implType) {
232
+ auto backend = Backend(implType);
233
+ return Settle(rt, [&](rnzeroconf::ServiceCallback resolve, rnzeroconf::ErrorCallback reject) {
234
+ backend->Update(name, ToTxt(txt), std::move(resolve), std::move(reject));
235
+ });
236
+ }
237
+
238
+ AsyncPromise<ZeroconfResult> ZeroconfModule::unregisterService(jsi::Runtime &rt, std::string name, std::string implType) {
239
+ auto backend = Backend(implType);
240
+ return Settle(rt, [&](rnzeroconf::ServiceCallback resolve, rnzeroconf::ErrorCallback reject) {
241
+ backend->Unpublish(name, std::move(resolve), std::move(reject));
242
+ });
243
+ }
244
+
245
+ AsyncPromise<ZeroconfResult> ZeroconfModule::resolveService(
246
+ jsi::Runtime &rt,
247
+ std::string name,
248
+ std::string type,
249
+ std::string protocol,
250
+ std::string domain,
251
+ std::string implType,
252
+ ZeroconfResolveOptions options) {
253
+ rnzeroconf::ResolveOptions resolveOptions;
254
+ resolveOptions.timeoutSeconds = options.timeout.value_or(5);
255
+ resolveOptions.networkInterface = options.networkInterface.value_or("");
256
+ auto backend = Backend(implType);
257
+ return Settle(
258
+ rt,
259
+ [&](rnzeroconf::ServiceCallback resolve, rnzeroconf::ErrorCallback reject) {
260
+ backend->ResolveService(name, type, protocol, domain, resolveOptions, std::move(resolve), std::move(reject));
261
+ },
262
+ true);
263
+ }
264
+
265
+ AsyncPromise<ZeroconfAccessResult> ZeroconfModule::checkLocalNetworkAccess(jsi::Runtime &rt, std::string type, double timeout) {
266
+ AsyncPromise<ZeroconfAccessResult> promise(rt, jsInvoker_);
267
+ if (!platform_.checkLocalNetworkAccess) {
268
+ // Nothing to grant on this platform
269
+ promise.resolve(ZeroconfAccessResult{std::string("granted"), std::nullopt});
270
+ return promise;
271
+ }
272
+ platform_.checkLocalNetworkAccess(
273
+ type,
274
+ timeout,
275
+ [promise](const std::string &status) mutable { promise.resolve(ZeroconfAccessResult{status, std::nullopt}); },
276
+ [promise](const rnzeroconf::Error &error) mutable { promise.resolve(ZeroconfAccessResult{std::nullopt, ToJs(error)}); });
277
+ return promise;
278
+ }
279
+
280
+ } // namespace facebook::react