react-native-zeroconf 0.16.1 → 0.17.0
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 +0 -3
- package/README.md +1 -1
- package/android/build.gradle +0 -3
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroConfImplFactory.java +1 -1
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/Zeroconf.java +12 -2
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfModule.java +129 -4
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/ZeroconfOptions.java +72 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/dnssd/DnssdImpl.java +633 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/NsdServiceImpl.java +331 -34
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/nsd/ServiceTypesQuery.java +296 -0
- package/android/src/main/java/com/github/druk/dnssd/DNSSDEmbedded.java +1 -1
- package/android/src/main/jni/mdnsresponder/mDNSShared/dnssd_clientshim.c +59 -22
- package/dist/index.js +234 -48
- package/index.d.ts +101 -6
- package/ios/RNZeroconf/RNZeroconf.m +285 -22
- package/package.json +28 -4
- package/src/index.js +214 -44
- package/windows/ExperimentalFeatures.props +33 -0
- package/windows/NuGet.config +11 -0
- package/windows/RNZeroconf/RNZeroconf.cpp +227 -0
- package/windows/RNZeroconf/RNZeroconf.def +3 -0
- package/windows/RNZeroconf/RNZeroconf.h +70 -0
- package/windows/RNZeroconf/RNZeroconf.rc +0 -0
- package/windows/RNZeroconf/RNZeroconf.vcxproj +150 -0
- package/windows/RNZeroconf/RNZeroconf.vcxproj.filters +50 -0
- package/windows/RNZeroconf/ReactPackageProvider.cpp +20 -0
- package/windows/RNZeroconf/ReactPackageProvider.h +24 -0
- package/windows/RNZeroconf/ReactPackageProvider.idl +9 -0
- package/windows/RNZeroconf/ZeroconfCore.cpp +1042 -0
- package/windows/RNZeroconf/ZeroconfCore.h +165 -0
- package/windows/RNZeroconf/pch.cpp +1 -0
- package/windows/RNZeroconf/pch.h +30 -0
- package/windows/RNZeroconf/resource.h +5 -0
- package/windows/RNZeroconf/targetver.h +8 -0
- package/windows/RNZeroconf.sln +43 -0
- package/android/src/main/java/com/balthazargronon/RCTZeroconf/rx2dnssd/DnssdImpl.java +0 -305
- package/android/src/main/java/com/github/druk/rx2dnssd/BonjourService.java +0 -400
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2BrowseListener.java +0 -55
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2Dnssd.java +0 -127
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2DnssdCommon.java +0 -280
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2DnssdEmbedded.java +0 -31
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2QueryListener.java +0 -70
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2RegisterListener.java +0 -47
- package/android/src/main/java/com/github/druk/rx2dnssd/Rx2ResolveListener.java +0 -51
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Zeroconf on Windows with the DNS-SD functions of windns.h (Windows 10 1809 and later).
|
|
2
|
+
// No React Native dependency: RNZeroconf.cpp bridges it to JavaScript, test/windows/harness.cpp tests it.
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#ifndef UNICODE
|
|
6
|
+
#define UNICODE
|
|
7
|
+
#endif
|
|
8
|
+
#ifndef _UNICODE
|
|
9
|
+
#define _UNICODE
|
|
10
|
+
#endif
|
|
11
|
+
#ifndef WIN32_LEAN_AND_MEAN
|
|
12
|
+
#define WIN32_LEAN_AND_MEAN
|
|
13
|
+
#endif
|
|
14
|
+
#ifndef NOMINMAX
|
|
15
|
+
#define NOMINMAX
|
|
16
|
+
#endif
|
|
17
|
+
|
|
18
|
+
#include <winsock2.h>
|
|
19
|
+
#include <ws2tcpip.h>
|
|
20
|
+
#include <windows.h>
|
|
21
|
+
#include <windns.h>
|
|
22
|
+
|
|
23
|
+
#include <atomic>
|
|
24
|
+
#include <cstdint>
|
|
25
|
+
#include <functional>
|
|
26
|
+
#include <map>
|
|
27
|
+
#include <memory>
|
|
28
|
+
#include <mutex>
|
|
29
|
+
#include <set>
|
|
30
|
+
#include <string>
|
|
31
|
+
#include <utility>
|
|
32
|
+
#include <vector>
|
|
33
|
+
|
|
34
|
+
namespace rnzeroconf {
|
|
35
|
+
|
|
36
|
+
using TxtPairs = std::vector<std::pair<std::wstring, std::wstring>>;
|
|
37
|
+
|
|
38
|
+
struct Service {
|
|
39
|
+
std::wstring name;
|
|
40
|
+
std::wstring fullName;
|
|
41
|
+
std::wstring host;
|
|
42
|
+
uint16_t port = 0;
|
|
43
|
+
std::vector<std::wstring> addresses;
|
|
44
|
+
TxtPairs txt;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// { message, code, domain, serviceName } as on the other platforms.
|
|
48
|
+
// domain "Windows": code is a Win32 / DNS_STATUS number. domain "RNZeroconf": stringCode is set.
|
|
49
|
+
struct Error {
|
|
50
|
+
std::wstring domain;
|
|
51
|
+
int64_t code = 0;
|
|
52
|
+
std::wstring stringCode;
|
|
53
|
+
std::wstring message;
|
|
54
|
+
std::wstring serviceName;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
using ServiceCallback = std::function<void(const Service &)>;
|
|
58
|
+
using ErrorCallback = std::function<void(const Error &)>;
|
|
59
|
+
|
|
60
|
+
// Called from DNS-SD threads. scanId is empty for errors that are not about a scan.
|
|
61
|
+
struct Events {
|
|
62
|
+
std::function<void(const std::string &scanId)> start;
|
|
63
|
+
std::function<void(const std::string &scanId)> stop;
|
|
64
|
+
std::function<void(const std::string &scanId, const std::wstring &name)> found;
|
|
65
|
+
std::function<void(const std::string &scanId, const std::wstring &name)> remove;
|
|
66
|
+
std::function<void(const std::string &scanId, const Service &)> resolved;
|
|
67
|
+
std::function<void(const Service &)> published;
|
|
68
|
+
std::function<void(const Service &)> unpublished;
|
|
69
|
+
std::function<void(const std::string &scanId, const Error &)> error;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
struct Operation;
|
|
73
|
+
struct Browse;
|
|
74
|
+
struct Resolve;
|
|
75
|
+
struct Publication;
|
|
76
|
+
|
|
77
|
+
class Zeroconf {
|
|
78
|
+
public:
|
|
79
|
+
explicit Zeroconf(Events events);
|
|
80
|
+
~Zeroconf();
|
|
81
|
+
|
|
82
|
+
Zeroconf(const Zeroconf &) = delete;
|
|
83
|
+
Zeroconf &operator=(const Zeroconf &) = delete;
|
|
84
|
+
|
|
85
|
+
// type and protocol without underscore ("http", "tcp"). "services._dns-sd" / "udp" lists service types
|
|
86
|
+
void Scan(
|
|
87
|
+
const std::string &scanId,
|
|
88
|
+
const std::wstring &type,
|
|
89
|
+
const std::wstring &protocol,
|
|
90
|
+
const std::wstring &domain,
|
|
91
|
+
const std::wstring &subtype,
|
|
92
|
+
const std::wstring &networkInterface);
|
|
93
|
+
void Stop(const std::string &scanId);
|
|
94
|
+
void StopAll();
|
|
95
|
+
|
|
96
|
+
void Publish(
|
|
97
|
+
const std::wstring &type,
|
|
98
|
+
const std::wstring &protocol,
|
|
99
|
+
const std::wstring &domain,
|
|
100
|
+
const std::wstring &name,
|
|
101
|
+
uint16_t port,
|
|
102
|
+
const TxtPairs &txt,
|
|
103
|
+
const std::wstring &networkInterface,
|
|
104
|
+
ServiceCallback resolve,
|
|
105
|
+
ErrorCallback reject);
|
|
106
|
+
void Unpublish(const std::wstring &name, ServiceCallback resolve, ErrorCallback reject);
|
|
107
|
+
// windns.h has no update: unpublishes and publishes again under the same name
|
|
108
|
+
void Update(const std::wstring &name, const TxtPairs &txt, ServiceCallback resolve, ErrorCallback reject);
|
|
109
|
+
void UnpublishAll();
|
|
110
|
+
|
|
111
|
+
void ResolveService(
|
|
112
|
+
const std::wstring &name,
|
|
113
|
+
const std::wstring &type,
|
|
114
|
+
const std::wstring &protocol,
|
|
115
|
+
const std::wstring &domain,
|
|
116
|
+
const std::wstring &networkInterface,
|
|
117
|
+
double timeoutSeconds,
|
|
118
|
+
ServiceCallback resolve,
|
|
119
|
+
ErrorCallback reject);
|
|
120
|
+
|
|
121
|
+
// Callbacks of windns.h, routed through a registry of live operations
|
|
122
|
+
void OnBrowse(const std::shared_ptr<Browse> &browse, DWORD status, PDNS_RECORD records);
|
|
123
|
+
void OnResolve(const std::shared_ptr<Resolve> &resolve, DWORD status, PDNS_SERVICE_INSTANCE instance);
|
|
124
|
+
void OnRegister(const std::shared_ptr<Publication> &publication, DWORD status, PDNS_SERVICE_INSTANCE instance);
|
|
125
|
+
|
|
126
|
+
private:
|
|
127
|
+
using After = std::vector<std::function<void()>>;
|
|
128
|
+
|
|
129
|
+
void StopLocked(const std::string &scanId, After &after);
|
|
130
|
+
void StartResolve(const std::shared_ptr<Resolve> &resolve, After &after);
|
|
131
|
+
void CancelResolve(const std::shared_ptr<Resolve> &resolve, After &after);
|
|
132
|
+
void PublishAs(
|
|
133
|
+
const std::wstring ®Type,
|
|
134
|
+
const std::wstring &domain,
|
|
135
|
+
const std::wstring &requestedName,
|
|
136
|
+
uint16_t port,
|
|
137
|
+
const TxtPairs &txt,
|
|
138
|
+
ULONG interfaceIndex,
|
|
139
|
+
bool announce,
|
|
140
|
+
ServiceCallback resolve,
|
|
141
|
+
ErrorCallback reject);
|
|
142
|
+
void UnpublishWith(const std::wstring &name, bool announce, ServiceCallback resolve, ErrorCallback reject);
|
|
143
|
+
Service PublishedService(const Publication &publication) const;
|
|
144
|
+
// Service type scans also list the types this app publishes, the network doesn't answer them back
|
|
145
|
+
void SyncLocalType(const std::wstring ®Type, const std::wstring &domain, After &after);
|
|
146
|
+
bool LocallyPublished(const std::wstring &typeKey) const;
|
|
147
|
+
bool InterfaceIndex(const std::wstring &networkInterface, ULONG &index, Error &error) const;
|
|
148
|
+
|
|
149
|
+
Events events_;
|
|
150
|
+
// False once destroyed, for the resolveService() timeout threads
|
|
151
|
+
std::shared_ptr<std::atomic<bool>> alive_;
|
|
152
|
+
std::recursive_mutex mutex_;
|
|
153
|
+
std::map<std::string, std::shared_ptr<Browse>> browses_;
|
|
154
|
+
// Keyed by the published name, which can differ from the requested one
|
|
155
|
+
std::map<std::wstring, std::shared_ptr<Publication>> publications_;
|
|
156
|
+
std::vector<std::shared_ptr<Resolve>> singleResolves_;
|
|
157
|
+
std::wstring hostName_;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Helpers shared with the bridge and the harness
|
|
161
|
+
std::wstring Widen(const std::string &value);
|
|
162
|
+
std::string Narrow(const std::wstring &value);
|
|
163
|
+
std::wstring DescribeStatus(DWORD status);
|
|
164
|
+
|
|
165
|
+
} // namespace rnzeroconf
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#include "pch.h"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// pch.h : include file for standard system include files,
|
|
2
|
+
// or project specific include files that are used frequently, but
|
|
3
|
+
// are changed infrequently
|
|
4
|
+
//
|
|
5
|
+
|
|
6
|
+
#pragma once
|
|
7
|
+
|
|
8
|
+
#include "targetver.h"
|
|
9
|
+
|
|
10
|
+
#define NOMINMAX 1
|
|
11
|
+
#define WIN32_LEAN_AND_MEAN 1
|
|
12
|
+
#define WINRT_LEAN_AND_MEAN 1
|
|
13
|
+
|
|
14
|
+
// Windows Header Files
|
|
15
|
+
#include <windows.h>
|
|
16
|
+
#undef GetCurrentTime
|
|
17
|
+
#include <unknwn.h>
|
|
18
|
+
|
|
19
|
+
// WinRT Header Files
|
|
20
|
+
#include <winrt/base.h>
|
|
21
|
+
#include <CppWinRTIncludes.h>
|
|
22
|
+
#include <winrt/Microsoft.ReactNative.h>
|
|
23
|
+
|
|
24
|
+
// C RunTime Header Files
|
|
25
|
+
#include <malloc.h>
|
|
26
|
+
#include <memory.h>
|
|
27
|
+
#include <stdlib.h>
|
|
28
|
+
#include <tchar.h>
|
|
29
|
+
|
|
30
|
+
// Reference additional headers your project requires here
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
// Including SDKDDKVer.h defines the highest available Windows platform.
|
|
4
|
+
|
|
5
|
+
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
|
|
6
|
+
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
|
|
7
|
+
|
|
8
|
+
#include <SDKDDKVer.h>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
|
|
2
|
+
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
3
|
+
# Visual Studio Version 17
|
|
4
|
+
VisualStudioVersion = 17.3.32929.385
|
|
5
|
+
MinimumVisualStudioVersion = 10.0.40219.1
|
|
6
|
+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RNZeroconf", "RNZeroconf\RNZeroconf.vcxproj", "{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}"
|
|
7
|
+
EndProject
|
|
8
|
+
Global
|
|
9
|
+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
10
|
+
Debug|x64 = Debug|x64
|
|
11
|
+
Debug|x86 = Debug|x86
|
|
12
|
+
Debug|ARM64 = Debug|ARM64
|
|
13
|
+
Release|x64 = Release|x64
|
|
14
|
+
Release|x86 = Release|x86
|
|
15
|
+
Release|ARM64 = Release|ARM64
|
|
16
|
+
EndGlobalSection
|
|
17
|
+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
18
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x64.ActiveCfg = Debug|x64
|
|
19
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x64.Build.0 = Debug|x64
|
|
20
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x64.Deploy.0 = Debug|x64
|
|
21
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x86.ActiveCfg = Debug|Win32
|
|
22
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x86.Build.0 = Debug|Win32
|
|
23
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|x86.Deploy.0 = Debug|Win32
|
|
24
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
|
25
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|ARM64.Build.0 = Debug|ARM64
|
|
26
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Debug|ARM64.Deploy.0 = Debug|ARM64
|
|
27
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x64.ActiveCfg = Release|x64
|
|
28
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x64.Build.0 = Release|x64
|
|
29
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x64.Deploy.0 = Release|x64
|
|
30
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x86.ActiveCfg = Release|Win32
|
|
31
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x86.Build.0 = Release|Win32
|
|
32
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|x86.Deploy.0 = Release|Win32
|
|
33
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|ARM64.ActiveCfg = Release|ARM64
|
|
34
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|ARM64.Build.0 = Release|ARM64
|
|
35
|
+
{9C9B6A6C-3DC5-494E-BABF-9C841E7F0176}.Release|ARM64.Deploy.0 = Release|ARM64
|
|
36
|
+
EndGlobalSection
|
|
37
|
+
GlobalSection(SolutionProperties) = preSolution
|
|
38
|
+
HideSolutionNode = FALSE
|
|
39
|
+
EndGlobalSection
|
|
40
|
+
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
41
|
+
SolutionGuid = {D43FAD39-F619-437D-BB40-04A3982ACB6A}
|
|
42
|
+
EndGlobalSection
|
|
43
|
+
EndGlobal
|
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
package com.balthazargronon.RCTZeroconf.rx2dnssd;
|
|
2
|
-
|
|
3
|
-
import android.annotation.SuppressLint;
|
|
4
|
-
import android.content.Context;
|
|
5
|
-
import android.net.wifi.WifiManager;
|
|
6
|
-
import android.util.Log;
|
|
7
|
-
|
|
8
|
-
import com.balthazargronon.RCTZeroconf.Zeroconf;
|
|
9
|
-
import com.balthazargronon.RCTZeroconf.ZeroconfModule;
|
|
10
|
-
import com.facebook.react.bridge.Promise;
|
|
11
|
-
import com.facebook.react.bridge.ReactApplicationContext;
|
|
12
|
-
import com.facebook.react.bridge.ReadableArray;
|
|
13
|
-
import com.facebook.react.bridge.WritableArray;
|
|
14
|
-
import com.facebook.react.bridge.WritableMap;
|
|
15
|
-
import com.facebook.react.bridge.WritableNativeArray;
|
|
16
|
-
import com.facebook.react.bridge.WritableNativeMap;
|
|
17
|
-
import com.github.druk.dnssd.DNSSDException;
|
|
18
|
-
import com.github.druk.rx2dnssd.BonjourService;
|
|
19
|
-
import com.github.druk.rx2dnssd.Rx2Dnssd;
|
|
20
|
-
import com.github.druk.rx2dnssd.Rx2DnssdEmbedded;
|
|
21
|
-
|
|
22
|
-
import java.net.InetAddress;
|
|
23
|
-
import java.util.ArrayList;
|
|
24
|
-
import java.util.HashMap;
|
|
25
|
-
import java.util.LinkedHashMap;
|
|
26
|
-
import java.util.concurrent.ConcurrentHashMap;
|
|
27
|
-
import java.util.Locale;
|
|
28
|
-
import java.util.Map;
|
|
29
|
-
import java.util.List;
|
|
30
|
-
|
|
31
|
-
import javax.annotation.Nullable;
|
|
32
|
-
|
|
33
|
-
import io.reactivex.Flowable;
|
|
34
|
-
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
35
|
-
import io.reactivex.disposables.Disposable;
|
|
36
|
-
import io.reactivex.schedulers.Schedulers;
|
|
37
|
-
|
|
38
|
-
public class DnssdImpl implements Zeroconf {
|
|
39
|
-
private static final String TAG = "DnssdImpl";
|
|
40
|
-
|
|
41
|
-
private Rx2Dnssd rxDnssd;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
private Map<String, BonjourService> mPublishedServices;
|
|
45
|
-
private Map<String, Disposable> mRegisteredDisposables;
|
|
46
|
-
|
|
47
|
-
// Running browses, keyed by the id of the JS instance that started them
|
|
48
|
-
private final Map<String, Disposable> mBrowses = new ConcurrentHashMap<>();
|
|
49
|
-
|
|
50
|
-
private ZeroconfModule zeroconfModule;
|
|
51
|
-
|
|
52
|
-
private ReactApplicationContext reactApplicationContext;
|
|
53
|
-
private WifiManager.MulticastLock multicastLock;
|
|
54
|
-
|
|
55
|
-
public DnssdImpl(ZeroconfModule zeroconfModule, ReactApplicationContext reactApplicationContext) {
|
|
56
|
-
this.zeroconfModule = zeroconfModule;
|
|
57
|
-
this.reactApplicationContext = reactApplicationContext;
|
|
58
|
-
mPublishedServices = new HashMap<String, BonjourService>();
|
|
59
|
-
mRegisteredDisposables = new HashMap<String, Disposable>();
|
|
60
|
-
rxDnssd = createDnssd(reactApplicationContext);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Creates the Rx2Dnssd implementation.
|
|
65
|
-
* Always uses embedded mDNSResponder since it works across all Android versions.
|
|
66
|
-
* The daemonic version (not bundled) is unreliable on Android as the
|
|
67
|
-
* system daemon at /dev/socket/mdnsd doesn't exist on most devices.
|
|
68
|
-
*/
|
|
69
|
-
private Rx2Dnssd createDnssd(Context context) {
|
|
70
|
-
return new Rx2DnssdEmbedded(context);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
@Override
|
|
74
|
-
public void scan(final String scanId, String type, String protocol, String domain) {
|
|
75
|
-
this.stop(scanId);
|
|
76
|
-
acquireMulticastLock();
|
|
77
|
-
|
|
78
|
-
String serviceType = getServiceType(type, protocol);
|
|
79
|
-
Log.d(TAG, "Starting DNSSD scan " + scanId + " for: " + serviceType);
|
|
80
|
-
|
|
81
|
-
sendScanEvent(ZeroconfModule.EVENT_START, new WritableNativeMap(), scanId);
|
|
82
|
-
|
|
83
|
-
// A service is reported once per network interface, count them so found/remove are emitted once
|
|
84
|
-
final Map<String, Integer> foundInterfaces = new HashMap<>();
|
|
85
|
-
Disposable browse = rxDnssd.browse(serviceType, "local.")
|
|
86
|
-
.doOnNext(bonjourService -> {
|
|
87
|
-
String name = bonjourService.getServiceName();
|
|
88
|
-
Integer count = foundInterfaces.get(name);
|
|
89
|
-
int interfaces = count == null ? 0 : count;
|
|
90
|
-
if (!bonjourService.isLost()) {
|
|
91
|
-
foundInterfaces.put(name, interfaces + 1);
|
|
92
|
-
if (interfaces == 0) {
|
|
93
|
-
sendScanEvent(ZeroconfModule.EVENT_FOUND, serviceNameToMap(bonjourService), scanId);
|
|
94
|
-
}
|
|
95
|
-
} else if (interfaces <= 1) {
|
|
96
|
-
foundInterfaces.remove(name);
|
|
97
|
-
sendScanEvent(ZeroconfModule.EVENT_REMOVE, serviceNameToMap(bonjourService), scanId);
|
|
98
|
-
} else {
|
|
99
|
-
foundInterfaces.put(name, interfaces - 1);
|
|
100
|
-
}
|
|
101
|
-
})
|
|
102
|
-
.filter(bonjourService -> !bonjourService.isLost())
|
|
103
|
-
// Resolve each service independently so a single failure doesn't end the whole browse
|
|
104
|
-
.flatMap(bonjourService -> Flowable.just(bonjourService)
|
|
105
|
-
.compose(rxDnssd.resolve())
|
|
106
|
-
.compose(rxDnssd.queryRecords())
|
|
107
|
-
.onErrorResumeNext((Throwable throwable) -> {
|
|
108
|
-
Log.e(TAG, "Error resolving service: ", throwable);
|
|
109
|
-
sendError(throwable, "Resolving service " + bonjourService.getServiceName() + " failed: ", bonjourService.getServiceName(), scanId);
|
|
110
|
-
return Flowable.empty();
|
|
111
|
-
}))
|
|
112
|
-
.subscribeOn(Schedulers.io())
|
|
113
|
-
.observeOn(AndroidSchedulers.mainThread())
|
|
114
|
-
.subscribe(bonjourService -> {
|
|
115
|
-
// Lost services were filtered out above, a lost service could still come back from resolve
|
|
116
|
-
if (bonjourService.isLost()) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
sendScanEvent(ZeroconfModule.EVENT_RESOLVE, serviceInfoToMap(bonjourService), scanId);
|
|
120
|
-
}, throwable -> {
|
|
121
|
-
Log.e(TAG, "Error browsing services: ", throwable);
|
|
122
|
-
sendError(throwable, "Browsing services failed: ", null, scanId);
|
|
123
|
-
if (mBrowses.remove(scanId) != null) {
|
|
124
|
-
releaseMulticastLock();
|
|
125
|
-
}
|
|
126
|
-
sendScanEvent(ZeroconfModule.EVENT_STOP, new WritableNativeMap(), scanId);
|
|
127
|
-
});
|
|
128
|
-
mBrowses.put(scanId, browse);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private void sendScanEvent(String eventName, WritableMap body, String scanId) {
|
|
132
|
-
body.putString(ZeroconfModule.KEY_SCAN_ID, scanId);
|
|
133
|
-
zeroconfModule.sendEvent(reactApplicationContext, eventName, body);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
private void acquireMulticastLock() {
|
|
137
|
-
if (multicastLock == null) {
|
|
138
|
-
@SuppressLint("WifiManagerLeak") WifiManager wifi = (WifiManager) reactApplicationContext.getSystemService(Context.WIFI_SERVICE);
|
|
139
|
-
multicastLock = wifi.createMulticastLock("multicastLock");
|
|
140
|
-
multicastLock.setReferenceCounted(true);
|
|
141
|
-
}
|
|
142
|
-
multicastLock.acquire();
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private void releaseMulticastLock() {
|
|
146
|
-
if (multicastLock != null && multicastLock.isHeld()) {
|
|
147
|
-
multicastLock.release();
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* DNSSD errors carry their DNSServiceErrorType code, anything else is reported as an exception
|
|
153
|
-
*/
|
|
154
|
-
private void sendError(Throwable throwable, String prefix, @Nullable String serviceName, @Nullable String scanId) {
|
|
155
|
-
if (throwable instanceof DNSSDException) {
|
|
156
|
-
DNSSDException dnssdException = (DNSSDException) throwable;
|
|
157
|
-
zeroconfModule.sendError(ZeroconfModule.ERROR_DOMAIN_DNSSD, dnssdException.getErrorCode(), prefix + throwable.getMessage(), serviceName, scanId);
|
|
158
|
-
} else {
|
|
159
|
-
zeroconfModule.sendError(ZeroconfModule.ERROR_DOMAIN_LIBRARY, ZeroconfModule.ERROR_CODE_EXCEPTION, prefix + throwable.getMessage(), serviceName, scanId);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
private void rejectWith(@Nullable Promise promise, Throwable throwable, String prefix, @Nullable String serviceName) {
|
|
164
|
-
if (throwable instanceof DNSSDException) {
|
|
165
|
-
ZeroconfModule.reject(promise, ZeroconfModule.ERROR_DOMAIN_DNSSD, ((DNSSDException) throwable).getErrorCode(), prefix + throwable.getMessage(), serviceName);
|
|
166
|
-
} else {
|
|
167
|
-
ZeroconfModule.reject(promise, ZeroconfModule.ERROR_DOMAIN_LIBRARY, ZeroconfModule.ERROR_CODE_EXCEPTION, prefix + throwable.getMessage(), serviceName);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
private WritableMap serviceNameToMap(BonjourService serviceInfo) {
|
|
172
|
-
WritableMap service = new WritableNativeMap();
|
|
173
|
-
service.putString(ZeroconfModule.KEY_SERVICE_NAME, serviceInfo.getServiceName());
|
|
174
|
-
return service;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
private String getServiceType(String type, String protocol) {
|
|
178
|
-
return String.format("_%s._%s", type, protocol);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private WritableMap serviceInfoToMap(BonjourService serviceInfo) {
|
|
182
|
-
WritableMap service = new WritableNativeMap();
|
|
183
|
-
service.putString(ZeroconfModule.KEY_SERVICE_NAME, serviceInfo.getServiceName());
|
|
184
|
-
final List<InetAddress> hostList = serviceInfo.getInetAddresses();
|
|
185
|
-
final String hostname = serviceInfo.getHostname();
|
|
186
|
-
final String fullServiceName = hostname != null
|
|
187
|
-
? hostname + serviceInfo.getRegType()
|
|
188
|
-
: serviceInfo.getServiceName();
|
|
189
|
-
service.putString(ZeroconfModule.KEY_SERVICE_HOST, hostname != null ? hostname : serviceInfo.getServiceName());
|
|
190
|
-
|
|
191
|
-
WritableArray addresses = new WritableNativeArray();
|
|
192
|
-
for (InetAddress host : hostList) {
|
|
193
|
-
addresses.pushString(host.getHostAddress());
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
service.putArray(ZeroconfModule.KEY_SERVICE_ADDRESSES, addresses);
|
|
197
|
-
service.putString(ZeroconfModule.KEY_SERVICE_FULL_NAME, fullServiceName);
|
|
198
|
-
service.putInt(ZeroconfModule.KEY_SERVICE_PORT, serviceInfo.getPort());
|
|
199
|
-
|
|
200
|
-
WritableMap txtRecords = new WritableNativeMap();
|
|
201
|
-
|
|
202
|
-
Map<String, String> attributes = serviceInfo.getTxtRecords();
|
|
203
|
-
for (String key : attributes.keySet()) {
|
|
204
|
-
String recordValue = attributes.get(key);
|
|
205
|
-
txtRecords.putString(String.format(Locale.getDefault(), "%s", key), String.format(Locale.getDefault(), "%s", recordValue != null ? recordValue : ""));
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
service.putMap(ZeroconfModule.KEY_SERVICE_TXT, txtRecords);
|
|
209
|
-
|
|
210
|
-
return service;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
@Override
|
|
214
|
-
public void stop(String scanId) {
|
|
215
|
-
Disposable browse = mBrowses.remove(scanId);
|
|
216
|
-
if (browse == null) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
browse.dispose();
|
|
220
|
-
releaseMulticastLock();
|
|
221
|
-
sendScanEvent(ZeroconfModule.EVENT_STOP, new WritableNativeMap(), scanId);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
@Override
|
|
225
|
-
public void stopAll() {
|
|
226
|
-
for (String scanId : new ArrayList<>(mBrowses.keySet())) {
|
|
227
|
-
stop(scanId);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
@Override
|
|
232
|
-
public void unregisterService(String serviceName, @Nullable Promise promise) {
|
|
233
|
-
|
|
234
|
-
BonjourService bs = mPublishedServices.get(serviceName);
|
|
235
|
-
Disposable registerDisposable = mRegisteredDisposables.get(serviceName);
|
|
236
|
-
if (bs == null && registerDisposable == null) {
|
|
237
|
-
ZeroconfModule.reject(promise, ZeroconfModule.ERROR_DOMAIN_LIBRARY, ZeroconfModule.ERROR_CODE_NOT_PUBLISHED, "Service " + serviceName + " is not published", serviceName);
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (bs != null) {
|
|
242
|
-
zeroconfModule.sendEvent(reactApplicationContext, ZeroconfModule.EVENT_UNREGISTERED, serviceInfoToMap(bs));
|
|
243
|
-
mPublishedServices.remove(serviceName);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (registerDisposable != null && !registerDisposable.isDisposed()) {
|
|
247
|
-
registerDisposable.dispose();
|
|
248
|
-
}
|
|
249
|
-
mRegisteredDisposables.remove(serviceName);
|
|
250
|
-
|
|
251
|
-
// Disposing unregisters synchronously
|
|
252
|
-
if (promise != null) {
|
|
253
|
-
promise.resolve(bs != null ? serviceInfoToMap(bs) : null);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
@Override
|
|
258
|
-
public void registerService(String type, String protocol, String domain, String name, int port, ReadableArray txt, Promise promise) {
|
|
259
|
-
BonjourService bs = new BonjourService.Builder(0, 0, name, getServiceType(type, protocol), null)
|
|
260
|
-
.port(port)
|
|
261
|
-
.dnsRecords(getTxtRecordMap(txt))
|
|
262
|
-
.build();
|
|
263
|
-
|
|
264
|
-
// Resolved once, the registration stream can emit again later
|
|
265
|
-
final Promise[] registerPromise = { promise };
|
|
266
|
-
Disposable registerDisposable = rxDnssd.register(bs)
|
|
267
|
-
.subscribeOn(Schedulers.io())
|
|
268
|
-
.observeOn(AndroidSchedulers.mainThread())
|
|
269
|
-
.subscribe(bonjourService -> {
|
|
270
|
-
Log.i(TAG, "Registered service " + bonjourService.toString());
|
|
271
|
-
|
|
272
|
-
mPublishedServices.put(bs.getServiceName(), bs);
|
|
273
|
-
zeroconfModule.sendEvent(reactApplicationContext, ZeroconfModule.EVENT_PUBLISHED, serviceInfoToMap(bonjourService));
|
|
274
|
-
if (registerPromise[0] != null) {
|
|
275
|
-
registerPromise[0].resolve(serviceInfoToMap(bonjourService));
|
|
276
|
-
registerPromise[0] = null;
|
|
277
|
-
}
|
|
278
|
-
}, throwable -> {
|
|
279
|
-
Log.e(TAG, "Error registering service: ", throwable);
|
|
280
|
-
sendError(throwable, "Registering service " + name + " failed: ", name, null);
|
|
281
|
-
rejectWith(registerPromise[0], throwable, "Registering service " + name + " failed: ", name);
|
|
282
|
-
registerPromise[0] = null;
|
|
283
|
-
mRegisteredDisposables.remove(name);
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
mRegisteredDisposables.put(name, registerDisposable);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
@Override
|
|
290
|
-
public void unregisterAllServices() {
|
|
291
|
-
for (String serviceName : new ArrayList<>(mRegisteredDisposables.keySet())) {
|
|
292
|
-
unregisterService(serviceName, null);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
private Map<String, String> getTxtRecordMap(ReadableArray txt) {
|
|
297
|
-
// LinkedHashMap keeps the TXT record order
|
|
298
|
-
Map<String, String> txtMap = new LinkedHashMap<>();
|
|
299
|
-
for (int i = 0; i < txt.size(); i++) {
|
|
300
|
-
ReadableArray pair = txt.getArray(i);
|
|
301
|
-
txtMap.put(pair.getString(0), pair.getString(1));
|
|
302
|
-
}
|
|
303
|
-
return txtMap;
|
|
304
|
-
}
|
|
305
|
-
}
|