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,296 @@
|
|
|
1
|
+
package com.balthazargronon.RCTZeroconf.nsd;
|
|
2
|
+
|
|
3
|
+
import android.text.TextUtils;
|
|
4
|
+
import android.util.Log;
|
|
5
|
+
|
|
6
|
+
import java.io.ByteArrayOutputStream;
|
|
7
|
+
import java.io.IOException;
|
|
8
|
+
import java.net.DatagramPacket;
|
|
9
|
+
import java.net.Inet4Address;
|
|
10
|
+
import java.net.InetAddress;
|
|
11
|
+
import java.net.InterfaceAddress;
|
|
12
|
+
import java.net.MulticastSocket;
|
|
13
|
+
import java.net.NetworkInterface;
|
|
14
|
+
import java.net.SocketException;
|
|
15
|
+
import java.net.SocketTimeoutException;
|
|
16
|
+
import java.util.ArrayList;
|
|
17
|
+
import java.util.Collections;
|
|
18
|
+
import java.util.HashMap;
|
|
19
|
+
import java.util.List;
|
|
20
|
+
import java.util.Locale;
|
|
21
|
+
import java.util.Map;
|
|
22
|
+
import java.util.Random;
|
|
23
|
+
|
|
24
|
+
import javax.annotation.Nullable;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Lists the service types on the network by querying the PTR records of _services._dns-sd._udp.local.
|
|
28
|
+
* NsdManager can't: on Android 14 and later it reads that name as a subtype of _dns-sd._udp.
|
|
29
|
+
*
|
|
30
|
+
* Queries are sent from an ephemeral port (RFC 6762 legacy unicast), so responders answer directly to
|
|
31
|
+
* this socket and the system's mDNS port 5353 is left alone. Types are queried again periodically, and
|
|
32
|
+
* reported lost when they stop being answered.
|
|
33
|
+
*/
|
|
34
|
+
class ServiceTypesQuery {
|
|
35
|
+
private static final String TAG = "ServiceTypesQuery";
|
|
36
|
+
private static final String QUERY_NAME = "_services._dns-sd._udp.local";
|
|
37
|
+
private static final int TYPE_PTR = 12;
|
|
38
|
+
private static final int CLASS_IN = 1;
|
|
39
|
+
private static final int MDNS_PORT = 5353;
|
|
40
|
+
// Query again after 1, 2 and 4 seconds, then every 10 seconds
|
|
41
|
+
private static final long[] QUERY_DELAYS_MS = { 1000, 1000, 2000 };
|
|
42
|
+
private static final long QUERY_INTERVAL_MS = 10000;
|
|
43
|
+
// Lost after three unanswered queries
|
|
44
|
+
private static final long EXPIRY_MS = 3 * QUERY_INTERVAL_MS + 5000;
|
|
45
|
+
|
|
46
|
+
interface Listener {
|
|
47
|
+
void onTypeFound(String type);
|
|
48
|
+
|
|
49
|
+
void onTypeLost(String type);
|
|
50
|
+
|
|
51
|
+
void onError(String message);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private final Listener listener;
|
|
55
|
+
private final Map<String, Long> lastSeen = new HashMap<>();
|
|
56
|
+
private final int queryId = 1 + new Random().nextInt(0xfffe);
|
|
57
|
+
private volatile boolean running;
|
|
58
|
+
@Nullable private MulticastSocket socket;
|
|
59
|
+
@Nullable private Thread thread;
|
|
60
|
+
|
|
61
|
+
// Network interface name ("wlan0"), or null for the Wi-Fi / Ethernet one
|
|
62
|
+
@Nullable private final String interfaceName;
|
|
63
|
+
|
|
64
|
+
ServiceTypesQuery(@Nullable String interfaceName, Listener listener) {
|
|
65
|
+
this.interfaceName = interfaceName;
|
|
66
|
+
this.listener = listener;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
void start() {
|
|
70
|
+
running = true;
|
|
71
|
+
thread = new Thread(this::run, "ServiceTypesQuery");
|
|
72
|
+
thread.start();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
void stop() {
|
|
76
|
+
running = false;
|
|
77
|
+
MulticastSocket current = socket;
|
|
78
|
+
if (current != null) {
|
|
79
|
+
current.close();
|
|
80
|
+
}
|
|
81
|
+
if (thread != null) {
|
|
82
|
+
thread.interrupt();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private void run() {
|
|
87
|
+
try {
|
|
88
|
+
NetworkInterface networkInterface = interfaceName != null ? NetworkInterface.getByName(interfaceName) : findInterface();
|
|
89
|
+
if (networkInterface == null) {
|
|
90
|
+
listener.onError(interfaceName != null ? "unknown network interface " + interfaceName : "no network interface with IPv4 multicast");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
InetAddress group = InetAddress.getByName("224.0.0.251");
|
|
94
|
+
MulticastSocket multicastSocket = new MulticastSocket(0);
|
|
95
|
+
multicastSocket.setNetworkInterface(networkInterface);
|
|
96
|
+
multicastSocket.setTimeToLive(255);
|
|
97
|
+
multicastSocket.setSoTimeout(500);
|
|
98
|
+
socket = multicastSocket;
|
|
99
|
+
|
|
100
|
+
byte[] query = buildQuery();
|
|
101
|
+
byte[] buffer = new byte[9000];
|
|
102
|
+
int queries = 0;
|
|
103
|
+
long nextQuery = 0;
|
|
104
|
+
while (running) {
|
|
105
|
+
long now = System.currentTimeMillis();
|
|
106
|
+
if (now >= nextQuery) {
|
|
107
|
+
multicastSocket.send(new DatagramPacket(query, query.length, group, MDNS_PORT));
|
|
108
|
+
nextQuery = now + (queries < QUERY_DELAYS_MS.length ? QUERY_DELAYS_MS[queries] : QUERY_INTERVAL_MS);
|
|
109
|
+
queries++;
|
|
110
|
+
expire(now);
|
|
111
|
+
}
|
|
112
|
+
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
|
|
113
|
+
try {
|
|
114
|
+
multicastSocket.receive(packet);
|
|
115
|
+
} catch (SocketTimeoutException e) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
for (Map.Entry<String, Long> answer : parseAnswers(packet.getData(), packet.getLength()).entrySet()) {
|
|
119
|
+
onAnswer(answer.getKey(), answer.getValue(), System.currentTimeMillis());
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch (IOException e) {
|
|
123
|
+
if (running) {
|
|
124
|
+
Log.e(TAG, "Service types query failed", e);
|
|
125
|
+
listener.onError(e.getMessage());
|
|
126
|
+
}
|
|
127
|
+
} finally {
|
|
128
|
+
MulticastSocket current = socket;
|
|
129
|
+
if (current != null) {
|
|
130
|
+
current.close();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private void onAnswer(String type, long ttlSeconds, long now) {
|
|
136
|
+
if (ttlSeconds == 0) {
|
|
137
|
+
// Goodbye record
|
|
138
|
+
if (lastSeen.remove(type) != null) {
|
|
139
|
+
listener.onTypeLost(type);
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (lastSeen.put(type, now) == null) {
|
|
144
|
+
listener.onTypeFound(type);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private void expire(long now) {
|
|
149
|
+
for (String type : new ArrayList<>(lastSeen.keySet())) {
|
|
150
|
+
if (now - lastSeen.get(type) > EXPIRY_MS) {
|
|
151
|
+
lastSeen.remove(type);
|
|
152
|
+
listener.onTypeLost(type);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// The Wi-Fi (or Ethernet) interface: up, multicast, not loopback, with an IPv4 address
|
|
158
|
+
@Nullable
|
|
159
|
+
private static NetworkInterface findInterface() throws SocketException {
|
|
160
|
+
NetworkInterface fallback = null;
|
|
161
|
+
for (NetworkInterface candidate : Collections.list(NetworkInterface.getNetworkInterfaces())) {
|
|
162
|
+
if (!candidate.isUp() || candidate.isLoopback() || !candidate.supportsMulticast() || candidate.isPointToPoint()) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
boolean hasIPv4 = false;
|
|
166
|
+
for (InterfaceAddress address : candidate.getInterfaceAddresses()) {
|
|
167
|
+
hasIPv4 |= address.getAddress() instanceof Inet4Address;
|
|
168
|
+
}
|
|
169
|
+
if (!hasIPv4) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (candidate.getName().startsWith("wlan") || candidate.getName().startsWith("eth")) {
|
|
173
|
+
return candidate;
|
|
174
|
+
}
|
|
175
|
+
if (fallback == null) {
|
|
176
|
+
fallback = candidate;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return fallback;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private byte[] buildQuery() {
|
|
183
|
+
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
184
|
+
// Header: id, flags, 1 question, no records. Legacy unicast queries use a non-zero id
|
|
185
|
+
writeShort(out, queryId);
|
|
186
|
+
writeShort(out, 0);
|
|
187
|
+
writeShort(out, 1);
|
|
188
|
+
writeShort(out, 0);
|
|
189
|
+
writeShort(out, 0);
|
|
190
|
+
writeShort(out, 0);
|
|
191
|
+
for (String label : QUERY_NAME.split("\\.")) {
|
|
192
|
+
out.write(label.length());
|
|
193
|
+
out.write(label.getBytes(), 0, label.length());
|
|
194
|
+
}
|
|
195
|
+
out.write(0);
|
|
196
|
+
writeShort(out, TYPE_PTR);
|
|
197
|
+
writeShort(out, CLASS_IN);
|
|
198
|
+
return out.toByteArray();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private static void writeShort(ByteArrayOutputStream out, int value) {
|
|
202
|
+
out.write((value >> 8) & 0xff);
|
|
203
|
+
out.write(value & 0xff);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Service types ("_http._tcp") and their TTL from the PTR records of _services._dns-sd._udp.local in a response
|
|
208
|
+
*/
|
|
209
|
+
static Map<String, Long> parseAnswers(byte[] data, int length) {
|
|
210
|
+
Map<String, Long> types = new HashMap<>();
|
|
211
|
+
try {
|
|
212
|
+
if (length < 12 || (data[2] & 0x80) == 0) {
|
|
213
|
+
// Not a response
|
|
214
|
+
return types;
|
|
215
|
+
}
|
|
216
|
+
int questions = readShort(data, 4);
|
|
217
|
+
int records = readShort(data, 6) + readShort(data, 8) + readShort(data, 10);
|
|
218
|
+
int offset = 12;
|
|
219
|
+
for (int i = 0; i < questions; i++) {
|
|
220
|
+
offset = skipName(data, offset, length) + 4;
|
|
221
|
+
}
|
|
222
|
+
for (int i = 0; i < records && offset < length; i++) {
|
|
223
|
+
List<String> name = new ArrayList<>();
|
|
224
|
+
offset = readName(data, offset, length, name);
|
|
225
|
+
int type = readShort(data, offset);
|
|
226
|
+
long ttl = ((long) readShort(data, offset + 4) << 16) | readShort(data, offset + 6);
|
|
227
|
+
int dataLength = readShort(data, offset + 8);
|
|
228
|
+
int dataOffset = offset + 10;
|
|
229
|
+
offset = dataOffset + dataLength;
|
|
230
|
+
if (type != TYPE_PTR || !QUERY_NAME.equals(TextUtils.join(".", name).toLowerCase(Locale.ROOT))) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
List<String> target = new ArrayList<>();
|
|
234
|
+
readName(data, dataOffset, length, target);
|
|
235
|
+
if (target.size() >= 2 && target.get(0).startsWith("_")
|
|
236
|
+
&& (target.get(1).equalsIgnoreCase("_tcp") || target.get(1).equalsIgnoreCase("_udp"))) {
|
|
237
|
+
types.put(target.get(0) + "." + target.get(1).toLowerCase(Locale.ROOT), ttl);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
} catch (IndexOutOfBoundsException e) {
|
|
241
|
+
// Truncated or malformed packet, keep what was parsed
|
|
242
|
+
}
|
|
243
|
+
return types;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private static int readShort(byte[] data, int offset) {
|
|
247
|
+
if (offset + 1 >= data.length) {
|
|
248
|
+
throw new IndexOutOfBoundsException();
|
|
249
|
+
}
|
|
250
|
+
return ((data[offset] & 0xff) << 8) | (data[offset + 1] & 0xff);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private static int skipName(byte[] data, int offset, int length) {
|
|
254
|
+
while (offset < length) {
|
|
255
|
+
int labelLength = data[offset] & 0xff;
|
|
256
|
+
if (labelLength == 0) {
|
|
257
|
+
return offset + 1;
|
|
258
|
+
}
|
|
259
|
+
if ((labelLength & 0xc0) == 0xc0) {
|
|
260
|
+
return offset + 2;
|
|
261
|
+
}
|
|
262
|
+
offset += 1 + labelLength;
|
|
263
|
+
}
|
|
264
|
+
throw new IndexOutOfBoundsException();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Reads a name with compression pointers into labels, returns the offset after the name
|
|
268
|
+
private static int readName(byte[] data, int offset, int length, List<String> labels) {
|
|
269
|
+
int end = -1;
|
|
270
|
+
int jumps = 0;
|
|
271
|
+
while (true) {
|
|
272
|
+
if (offset >= length) {
|
|
273
|
+
throw new IndexOutOfBoundsException();
|
|
274
|
+
}
|
|
275
|
+
int labelLength = data[offset] & 0xff;
|
|
276
|
+
if (labelLength == 0) {
|
|
277
|
+
return end >= 0 ? end : offset + 1;
|
|
278
|
+
}
|
|
279
|
+
if ((labelLength & 0xc0) == 0xc0) {
|
|
280
|
+
if (end < 0) {
|
|
281
|
+
end = offset + 2;
|
|
282
|
+
}
|
|
283
|
+
if (++jumps > 20) {
|
|
284
|
+
throw new IndexOutOfBoundsException();
|
|
285
|
+
}
|
|
286
|
+
offset = ((labelLength & 0x3f) << 8) | (data[offset + 1] & 0xff);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (offset + 1 + labelLength > length) {
|
|
290
|
+
throw new IndexOutOfBoundsException();
|
|
291
|
+
}
|
|
292
|
+
labels.add(new String(data, offset + 1, labelLength, java.nio.charset.StandardCharsets.UTF_8));
|
|
293
|
+
offset += 1 + labelLength;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
@@ -22,7 +22,7 @@ import android.os.Looper;
|
|
|
22
22
|
import android.util.Log;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* DNSSD implementation running the embedded mDNSResponder {@link InternalDNSSD}
|
|
26
26
|
*/
|
|
27
27
|
public class DNSSDEmbedded extends DNSSD {
|
|
28
28
|
|
|
@@ -27,6 +27,11 @@
|
|
|
27
27
|
#include "mDNSEmbeddedAPI.h" // The interface we're building on top of
|
|
28
28
|
#include <sys/socket.h>
|
|
29
29
|
#include <netinet/in.h>
|
|
30
|
+
#include <string.h>
|
|
31
|
+
|
|
32
|
+
// react-native-zeroconf: subtype parsing from uds_daemon.c, built into the same library
|
|
33
|
+
extern mDNSs32 ChopSubTypes(char *regtype);
|
|
34
|
+
extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p);
|
|
30
35
|
|
|
31
36
|
extern mDNS mDNSStorage; // We need to pass the address of this storage to the lower-layer functions
|
|
32
37
|
|
|
@@ -242,16 +247,20 @@ DNSServiceErrorType DNSServiceRegister
|
|
|
242
247
|
mDNSIPPort port;
|
|
243
248
|
unsigned int size = sizeof(RDataBody);
|
|
244
249
|
AuthRecord *SubTypes = mDNSNULL;
|
|
245
|
-
|
|
250
|
+
mDNSs32 NumSubTypes = 0;
|
|
251
|
+
// react-native-zeroconf: "_type._tcp,_subtype1,_subtype2" registers subtypes, as with the daemon
|
|
252
|
+
char typeAndSubtypes[MAX_ESCAPED_DOMAIN_NAME];
|
|
246
253
|
mDNS_DirectOP_Register *x;
|
|
247
254
|
(void)flags; // Unused
|
|
248
|
-
(void)interfaceIndex; // Unused
|
|
249
255
|
|
|
250
256
|
// Check parameters
|
|
251
257
|
if (!name) name = "";
|
|
252
258
|
if (!name[0]) n = mDNSStorage.nicelabel;
|
|
253
259
|
else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; }
|
|
254
|
-
if (!regtype || !*regtype ||
|
|
260
|
+
if (!regtype || !*regtype || strlen(regtype) >= sizeof(typeAndSubtypes)) { errormsg = "Bad Service Type"; goto badparam; }
|
|
261
|
+
strcpy(typeAndSubtypes, regtype);
|
|
262
|
+
NumSubTypes = ChopSubTypes(typeAndSubtypes); // Cuts the subtypes off the type
|
|
263
|
+
if (NumSubTypes < 0 || !MakeDomainNameFromDNSNameString(&t, typeAndSubtypes)) { errormsg = "Bad Service Type"; goto badparam; }
|
|
255
264
|
if (!MakeDomainNameFromDNSNameString(&d, (domain && *domain) ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; }
|
|
256
265
|
if (!MakeDomainNameFromDNSNameString(&h, (host && *host ) ? host : "")) { errormsg = "Bad Target Host"; goto badparam; }
|
|
257
266
|
if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; }
|
|
@@ -271,16 +280,22 @@ DNSServiceErrorType DNSServiceRegister
|
|
|
271
280
|
x->autorename = !(flags & kDNSServiceFlagsNoAutoRename);
|
|
272
281
|
x->name = n;
|
|
273
282
|
x->host = h;
|
|
283
|
+
if (NumSubTypes > 0)
|
|
284
|
+
{
|
|
285
|
+
SubTypes = AllocateSubTypes(NumSubTypes, typeAndSubtypes);
|
|
286
|
+
if (!SubTypes) { mDNSPlatformMemFree(x); err = mStatus_BadParamErr; errormsg = "Bad Subtype"; goto fail; }
|
|
287
|
+
}
|
|
274
288
|
|
|
275
289
|
// Do the operation
|
|
290
|
+
// react-native-zeroconf: registers on the requested interface instead of always any
|
|
276
291
|
err = mDNS_RegisterService(&mDNSStorage, &x->s,
|
|
277
292
|
&x->name, &t, &d, // Name, type, domain
|
|
278
293
|
&x->host, port, // Host and port
|
|
279
294
|
txtRecord, txtLen, // TXT data, length
|
|
280
|
-
SubTypes, NumSubTypes, // Subtypes
|
|
281
|
-
|
|
295
|
+
SubTypes, (mDNSu32)NumSubTypes, // Subtypes
|
|
296
|
+
mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex), // Interface ID
|
|
282
297
|
RegCallback, x, 0); // Callback, context, flags
|
|
283
|
-
if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_RegisterService"; goto fail; }
|
|
298
|
+
if (err) { if (SubTypes) mDNSPlatformMemFree(SubTypes); mDNSPlatformMemFree(x); errormsg = "mDNS_RegisterService"; goto fail; }
|
|
284
299
|
|
|
285
300
|
// Succeeded: Wrap up and return
|
|
286
301
|
*sdRef = (DNSServiceRef)x;
|
|
@@ -301,6 +316,14 @@ fail:
|
|
|
301
316
|
// is run against this Extension, it will get a reasonable error code instead of just
|
|
302
317
|
// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link)
|
|
303
318
|
#if !MDNS_BUILDINGSTUBLIBRARY
|
|
319
|
+
// react-native-zeroconf: frees the TXT data replaced by DNSServiceUpdateRecord
|
|
320
|
+
static void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *oldrd, mDNSu16 oldrdlen)
|
|
321
|
+
{
|
|
322
|
+
(void)m; // Unused
|
|
323
|
+
(void)oldrdlen; // Unused
|
|
324
|
+
if (oldrd != &rr->rdatastorage) mDNSPlatformMemFree(oldrd);
|
|
325
|
+
}
|
|
326
|
+
|
|
304
327
|
DNSServiceErrorType DNSServiceAddRecord
|
|
305
328
|
(
|
|
306
329
|
DNSServiceRef sdRef,
|
|
@@ -332,13 +355,23 @@ DNSServiceErrorType DNSServiceUpdateRecord
|
|
|
332
355
|
uint32_t ttl
|
|
333
356
|
)
|
|
334
357
|
{
|
|
335
|
-
|
|
336
|
-
(
|
|
358
|
+
// react-native-zeroconf: updates the primary TXT record of a registration, as uds_daemon.c update_record does
|
|
359
|
+
mDNS_DirectOP_Register *x = (mDNS_DirectOP_Register*)sdRef;
|
|
360
|
+
unsigned int size;
|
|
361
|
+
RData *newrd;
|
|
362
|
+
mStatus err;
|
|
337
363
|
(void)flags; // Unused
|
|
338
|
-
(
|
|
339
|
-
(
|
|
340
|
-
(
|
|
341
|
-
return(
|
|
364
|
+
if (!x || RecordRef || x->disposefn != DNSServiceRegisterDispose) return(kDNSServiceErr_Unsupported);
|
|
365
|
+
size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
|
|
366
|
+
newrd = (RData *)mDNSPlatformMemAllocate(sizeof(RData) - sizeof(RDataBody) + size);
|
|
367
|
+
if (!newrd) return(kDNSServiceErr_NoMemory);
|
|
368
|
+
newrd->MaxRDLength = (mDNSu16)size;
|
|
369
|
+
mDNSPlatformMemCopy(&newrd->u, rdata, rdlen);
|
|
370
|
+
// A TXT record can't be empty, use a single empty string
|
|
371
|
+
if (rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; }
|
|
372
|
+
err = mDNS_Update(&mDNSStorage, &x->s.RR_TXT, ttl, rdlen, newrd, UpdateCallback);
|
|
373
|
+
if (err) mDNSPlatformMemFree(newrd);
|
|
374
|
+
return(err);
|
|
342
375
|
}
|
|
343
376
|
|
|
344
377
|
DNSServiceErrorType DNSServiceRemoveRecord
|
|
@@ -409,11 +442,19 @@ DNSServiceErrorType DNSServiceBrowse
|
|
|
409
442
|
const char *errormsg = "Unknown";
|
|
410
443
|
domainname t, d;
|
|
411
444
|
mDNS_DirectOP_Browse *x;
|
|
445
|
+
// react-native-zeroconf: "_type._tcp,_subtype" browses a subtype, as uds_daemon.c handle_browse_request does
|
|
446
|
+
char typeAndSubtype[MAX_ESCAPED_DOMAIN_NAME];
|
|
447
|
+
mDNSs32 NumSubTypes;
|
|
412
448
|
(void)flags; // Unused
|
|
413
|
-
(void)interfaceIndex; // Unused
|
|
414
449
|
|
|
415
450
|
// Check parameters
|
|
416
|
-
if (!regtype[0] ||
|
|
451
|
+
if (!regtype[0] || strlen(regtype) >= sizeof(typeAndSubtype)) { errormsg = "Illegal regtype"; goto badparam; }
|
|
452
|
+
strcpy(typeAndSubtype, regtype);
|
|
453
|
+
t.c[0] = 0;
|
|
454
|
+
NumSubTypes = ChopSubTypes(typeAndSubtype);
|
|
455
|
+
if (NumSubTypes < 0 || NumSubTypes > 1) { errormsg = "Illegal regtype"; goto badparam; }
|
|
456
|
+
if (NumSubTypes == 1 && !AppendDNSNameString(&t, typeAndSubtype + strlen(typeAndSubtype) + 1)) { errormsg = "Illegal regtype"; goto badparam; }
|
|
457
|
+
if (!typeAndSubtype[0] || !AppendDNSNameString(&t, typeAndSubtype)) { errormsg = "Illegal regtype"; goto badparam; }
|
|
417
458
|
if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Illegal domain"; goto badparam; }
|
|
418
459
|
|
|
419
460
|
// Allocate memory, and handle failure
|
|
@@ -427,7 +468,7 @@ DNSServiceErrorType DNSServiceBrowse
|
|
|
427
468
|
x->q.QuestionContext = x;
|
|
428
469
|
|
|
429
470
|
// Do the operation
|
|
430
|
-
err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d,
|
|
471
|
+
err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex), (flags & kDNSServiceFlagsForceMulticast) != 0, FoundInstance, x);
|
|
431
472
|
if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_StartBrowse"; goto fail; }
|
|
432
473
|
|
|
433
474
|
// Succeeded: Wrap up and return
|
|
@@ -495,8 +536,6 @@ DNSServiceErrorType DNSServiceResolve
|
|
|
495
536
|
mDNS_DirectOP_Resolve *x;
|
|
496
537
|
|
|
497
538
|
(void)flags; // Unused
|
|
498
|
-
(void)interfaceIndex; // Unused
|
|
499
|
-
|
|
500
539
|
// Check parameters
|
|
501
540
|
if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name )) { errormsg = "Bad Instance Name"; goto badparam; }
|
|
502
541
|
if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; }
|
|
@@ -515,7 +554,7 @@ DNSServiceErrorType DNSServiceResolve
|
|
|
515
554
|
x->TXT = mDNSNULL;
|
|
516
555
|
|
|
517
556
|
x->qSRV.ThisQInterval = -1; // So that DNSServiceResolveDispose() knows whether to cancel this question
|
|
518
|
-
x->qSRV.InterfaceID =
|
|
557
|
+
x->qSRV.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
|
|
519
558
|
x->qSRV.Target = zeroAddr;
|
|
520
559
|
AssignDomainName(&x->qSRV.qname, &srv);
|
|
521
560
|
x->qSRV.qtype = kDNSType_SRV;
|
|
@@ -535,7 +574,7 @@ DNSServiceErrorType DNSServiceResolve
|
|
|
535
574
|
x->qSRV.QuestionContext = x;
|
|
536
575
|
|
|
537
576
|
x->qTXT.ThisQInterval = -1; // So that DNSServiceResolveDispose() knows whether to cancel this question
|
|
538
|
-
x->qTXT.InterfaceID =
|
|
577
|
+
x->qTXT.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
|
|
539
578
|
x->qTXT.Target = zeroAddr;
|
|
540
579
|
AssignDomainName(&x->qTXT.qname, &srv);
|
|
541
580
|
x->qTXT.qtype = kDNSType_TXT;
|
|
@@ -653,8 +692,6 @@ DNSServiceErrorType DNSServiceQueryRecord
|
|
|
653
692
|
mDNS_DirectOP_QueryRecord *x;
|
|
654
693
|
|
|
655
694
|
(void)flags; // Unused
|
|
656
|
-
(void)interfaceIndex; // Unused
|
|
657
|
-
|
|
658
695
|
// Allocate memory, and handle failure
|
|
659
696
|
x = (mDNS_DirectOP_QueryRecord *)mDNSPlatformMemAllocate(sizeof(*x));
|
|
660
697
|
if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
|
|
@@ -665,7 +702,7 @@ DNSServiceErrorType DNSServiceQueryRecord
|
|
|
665
702
|
x->context = context;
|
|
666
703
|
|
|
667
704
|
x->q.ThisQInterval = -1; // So that DNSServiceResolveDispose() knows whether to cancel this question
|
|
668
|
-
x->q.InterfaceID =
|
|
705
|
+
x->q.InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
|
|
669
706
|
x->q.Target = zeroAddr;
|
|
670
707
|
MakeDomainNameFromDNSNameString(&x->q.qname, fullname);
|
|
671
708
|
x->q.qtype = rrtype;
|