]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/D2D.c
mDNSResponder-1096.0.2.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / D2D.c
1 /*
2 * Copyright (c) 2002-2019 Apple Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "D2D.h"
18 #include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above
19 #include "DNSCommon.h"
20 #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform
21 #include "dns_sd.h" // For mDNSInterface_LocalOnly etc.
22 #include "dns_sd_internal.h"
23 #include "uds_daemon.h"
24 #include "BLE.h"
25
26 D2DStatus D2DInitialize(CFRunLoopRef runLoop, D2DServiceCallback serviceCallback, void* userData) __attribute__((weak_import));
27 D2DStatus D2DRetain(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import));
28 D2DStatus D2DStopAdvertisingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
29 D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import));
30 D2DStatus D2DStartAdvertisingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
31 D2DStatus D2DStartBrowsingForKeyOnTransport(const Byte *key, const size_t keySize, D2DTransportType transport) __attribute__((weak_import));
32 D2DStatus D2DStopBrowsingForKeyOnTransport(const Byte *key, const size_t keySize, D2DTransportType transport) __attribute__((weak_import));
33 void D2DStartResolvingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
34 void D2DStopResolvingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import));
35 D2DStatus D2DTerminate(void) __attribute__((weak_import));
36
37 #pragma mark - D2D Support
38
39 mDNSexport void D2D_start_advertising_interface(NetworkInterfaceInfo *interface)
40 {
41 // AWDL wants the address and reverse address PTR record communicated
42 // via the D2D interface layer.
43 if (interface->InterfaceID == AWDLInterfaceID)
44 {
45 // only log if we have a valid record to start advertising
46 if (interface->RR_A.resrec.RecordType || interface->RR_PTR.resrec.RecordType)
47 LogInfo("D2D_start_advertising_interface: %s", interface->ifname);
48
49 if (interface->RR_A.resrec.RecordType)
50 external_start_advertising_service(&interface->RR_A.resrec, 0);
51 if (interface->RR_PTR.resrec.RecordType)
52 external_start_advertising_service(&interface->RR_PTR.resrec, 0);
53 }
54 }
55
56 mDNSexport void D2D_stop_advertising_interface(NetworkInterfaceInfo *interface)
57 {
58 if (interface->InterfaceID == AWDLInterfaceID)
59 {
60 // only log if we have a valid record to stop advertising
61 if (interface->RR_A.resrec.RecordType || interface->RR_PTR.resrec.RecordType)
62 LogInfo("D2D_stop_advertising_interface: %s", interface->ifname);
63
64 if (interface->RR_A.resrec.RecordType)
65 external_stop_advertising_service(&interface->RR_A.resrec, 0);
66 if (interface->RR_PTR.resrec.RecordType)
67 external_stop_advertising_service(&interface->RR_PTR.resrec, 0);
68 }
69 }
70
71 // If record would have been advertised to the D2D plugin layer, stop that advertisement.
72 mDNSexport void D2D_stop_advertising_record(AuthRecord *ar)
73 {
74 DNSServiceFlags flags = deriveD2DFlagsFromAuthRecType(ar->ARType);
75 if (callExternalHelpers(ar->resrec.InterfaceID, ar->resrec.name, flags))
76 {
77 external_stop_advertising_service(&ar->resrec, flags);
78 }
79 }
80
81 // If record should be advertised to the D2D plugin layer, start that advertisement.
82 mDNSexport void D2D_start_advertising_record(AuthRecord *ar)
83 {
84 DNSServiceFlags flags = deriveD2DFlagsFromAuthRecType(ar->ARType);
85 if (callExternalHelpers(ar->resrec.InterfaceID, ar->resrec.name, flags))
86 {
87 external_start_advertising_service(&ar->resrec, flags);
88 }
89 }
90
91 // Name compression items for fake packet version number 1
92 static const mDNSu8 compression_packet_v1 = 0x01;
93
94 static DNSMessage compression_base_msg = { { {{0}}, {{0}}, 2, 0, 0, 0 }, "\x04_tcp\x05local\x00\x00\x0C\x00\x01\x04_udp\xC0\x11\x00\x0C\x00\x01" };
95 static mDNSu8 *const compression_limit = (mDNSu8 *) &compression_base_msg + sizeof(DNSMessage);
96 static mDNSu8 *const compression_lhs = (mDNSu8 *const) compression_base_msg.data + 27;
97
98 mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
99
100 typedef struct D2DRecordListElem
101 {
102 struct D2DRecordListElem *next;
103 D2DServiceInstance instanceHandle;
104 D2DTransportType transportType;
105 AuthRecord ar; // must be last in the structure to accomodate extra space
106 // allocated for large records.
107 } D2DRecordListElem;
108
109 static D2DRecordListElem *D2DRecords = NULL; // List of records returned with D2DServiceFound events
110
111 typedef struct D2DBrowseListElem
112 {
113 struct D2DBrowseListElem *next;
114 domainname name;
115 mDNSu16 type;
116 unsigned int refCount;
117 } D2DBrowseListElem;
118
119 D2DBrowseListElem* D2DBrowseList = NULL;
120
121 mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
122 {
123 ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
124 ptr[1] = (mDNSu8)((val ) & 0xFF);
125 return ptr + sizeof(mDNSu16);
126 }
127
128 mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
129 {
130 ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
131 ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
132 ptr[2] = (mDNSu8)((val >> 8) & 0xFF);
133 ptr[3] = (mDNSu8)((val ) & 0xFF);
134 return ptr + sizeof(mDNSu32);
135 }
136
137 mDNSlocal void DomainnameToLower(const domainname * const in, domainname * const out)
138 {
139 const mDNSu8 * const start = (const mDNSu8 * const)in;
140 mDNSu8 *ptr = (mDNSu8*)start;
141 while(*ptr)
142 {
143 mDNSu8 c = *ptr;
144 out->c[ptr-start] = *ptr;
145 ptr++;
146 for (; c; c--,ptr++) out->c[ptr-start] = mDNSIsUpperCase(*ptr) ? (*ptr - 'A' + 'a') : *ptr;
147 }
148 out->c[ptr-start] = *ptr;
149 }
150
151 mDNSlocal mDNSu8 * DNSNameCompressionBuildLHS(const domainname* typeDomain, DNS_TypeValues qtype)
152 {
153 mDNSu8 *ptr = putDomainNameAsLabels(&compression_base_msg, compression_lhs, compression_limit, typeDomain);
154 if (!ptr) return ptr;
155 *ptr = (qtype >> 8) & 0xff;
156 ptr += 1;
157 *ptr = qtype & 0xff;
158 ptr += 1;
159 *ptr = compression_packet_v1;
160 return ptr + 1;
161 }
162
163 mDNSlocal mDNSu8 * DNSNameCompressionBuildRHS(mDNSu8 *start, const ResourceRecord *const resourceRecord)
164 {
165 return putRData(&compression_base_msg, start, compression_limit, resourceRecord);
166 }
167
168 mDNSlocal void PrintHelper(const char *const tag, mDNSu8 *lhs, mDNSu16 lhs_len, mDNSu8 *rhs, mDNSu16 rhs_len)
169 {
170 if (mDNS_LoggingEnabled)
171 {
172 LogDebug("%s: LHS: (%d bytes) %.*H", tag, lhs_len, lhs_len, lhs);
173 if (rhs) LogDebug("%s: RHS: (%d bytes) %.*H", tag, rhs_len, rhs_len, rhs);
174 }
175 }
176
177 mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
178 {
179 (void)m; // unused
180 if (result == mStatus_MemFree)
181 {
182 D2DRecordListElem **ptr = &D2DRecords;
183 D2DRecordListElem *tmp;
184 while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next;
185 if (!*ptr) { LogMsg("FreeD2DARElemCallback: Could not find in D2DRecords: %s", ARDisplayString(m, rr)); return; }
186 LogInfo("FreeD2DARElemCallback: Found in D2DRecords: %s", ARDisplayString(m, rr));
187 tmp = *ptr;
188 *ptr = (*ptr)->next;
189 // Just because we stoppped browsing, doesn't mean we should tear down the PAN connection.
190 mDNSPlatformMemFree(tmp);
191 }
192 }
193
194 mDNSexport void external_connection_release(const domainname *instance)
195 {
196 (void) instance;
197 D2DRecordListElem *ptr = D2DRecords;
198
199 for ( ; ptr ; ptr = ptr->next)
200 {
201 if ((ptr->ar.resrec.rrtype == kDNSServiceType_PTR) &&
202 SameDomainName(&ptr->ar.rdatastorage.u.name, instance))
203 {
204 LogInfo("external_connection_release: Calling D2DRelease(instanceHandle = %p, transportType = %d",
205 ptr->instanceHandle, ptr->transportType);
206 if (D2DRelease) D2DRelease(ptr->instanceHandle, ptr->transportType);
207 }
208 }
209 }
210
211 mDNSlocal void xD2DClearCache(const domainname *regType, DNS_TypeValues qtype)
212 {
213 D2DRecordListElem *ptr = D2DRecords;
214 for ( ; ptr ; ptr = ptr->next)
215 {
216 if ((ptr->ar.resrec.rrtype == qtype) && SameDomainName(&ptr->ar.namestorage, regType))
217 {
218 LogInfo("xD2DClearCache: Clearing cache record and deregistering %s", ARDisplayString(&mDNSStorage, &ptr->ar));
219 mDNS_Deregister(&mDNSStorage, &ptr->ar);
220 }
221 }
222 }
223
224 mDNSlocal D2DBrowseListElem ** D2DFindInBrowseList(const domainname *const name, mDNSu16 type)
225 {
226 D2DBrowseListElem **ptr = &D2DBrowseList;
227
228 for ( ; *ptr; ptr = &(*ptr)->next)
229 if ((*ptr)->type == type && SameDomainName(&(*ptr)->name, name))
230 break;
231
232 return ptr;
233 }
234
235 mDNSlocal unsigned int D2DBrowseListRefCount(const domainname *const name, mDNSu16 type)
236 {
237 D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type);
238 return *ptr ? (*ptr)->refCount : 0;
239 }
240
241 mDNSlocal void D2DBrowseListRetain(const domainname *const name, mDNSu16 type)
242 {
243 D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type);
244
245 if (!*ptr)
246 {
247 *ptr = (D2DBrowseListElem *) mDNSPlatformMemAllocateClear(sizeof(**ptr));
248 (*ptr)->type = type;
249 AssignDomainName(&(*ptr)->name, name);
250 }
251 (*ptr)->refCount += 1;
252
253 LogInfo("D2DBrowseListRetain: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount);
254 }
255
256 // Returns true if found in list, false otherwise
257 mDNSlocal bool D2DBrowseListRelease(const domainname *const name, mDNSu16 type)
258 {
259 D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type);
260
261 if (!*ptr) { LogMsg("D2DBrowseListRelease: Didn't find %##s %s in list", name->c, DNSTypeName(type)); return false; }
262
263 (*ptr)->refCount -= 1;
264
265 LogInfo("D2DBrowseListRelease: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount);
266
267 if (!(*ptr)->refCount)
268 {
269 D2DBrowseListElem *tmp = *ptr;
270 *ptr = (*ptr)->next;
271 mDNSPlatformMemFree(tmp);
272 }
273 return true;
274 }
275
276 mDNSlocal mStatus xD2DParse(const mDNSu8 * const lhs, const mDNSu16 lhs_len, const mDNSu8 * const rhs, const mDNSu16 rhs_len, D2DRecordListElem **D2DListp)
277 {
278 mDNS *const m = &mDNSStorage;
279
280 // Sanity check that key array (lhs) has one domain name, followed by the record type and single byte D2D
281 // plugin protocol version number.
282 // Note, we don't have a DNSMessage pointer at this point, so just pass in the lhs value as the lower bound
283 // of the input bytes we are processing. skipDomainName() does not try to follow name compression pointers,
284 // so it is safe to pass it the key byte array since it will stop parsing the DNS name and return a pointer
285 // to the byte after the first name compression pointer it encounters.
286 const mDNSu8 *keyp = skipDomainName((const DNSMessage *const) lhs, lhs, lhs + lhs_len);
287
288 // There should be 3 bytes remaining in a valid key,
289 // two for the DNS record type, and one for the D2D protocol version number.
290 if (keyp == NULL || (keyp + 3 != (lhs + lhs_len)))
291 {
292 LogInfo("xD2DParse: Could not parse DNS name in key");
293 return mStatus_Incompatible;
294 }
295 keyp += 2; // point to D2D compression packet format version byte
296 if (*keyp != compression_packet_v1)
297 {
298 LogInfo("xD2DParse: Invalid D2D packet version: %d", *keyp);
299 return mStatus_Incompatible;
300 }
301
302 if (mDNS_LoggingEnabled)
303 {
304 const int len = (int)(compression_lhs - (mDNSu8*)&compression_base_msg);
305 LogInfo("xD2DParse: Static Bytes: (%d bytes) %.*H", len, len, &compression_base_msg);
306 }
307
308 mDNSu8 *ptr = compression_lhs; // pointer to the end of our fake packet
309
310 // Check to make sure we're not going to go past the end of the DNSMessage data
311 // 7 = 2 for CLASS (-1 for our version) + 4 for TTL + 2 for RDLENGTH
312 if (ptr + lhs_len - 7 + rhs_len >= compression_limit) return mStatus_NoMemoryErr;
313
314 // Copy the LHS onto our fake wire packet
315 mDNSPlatformMemCopy(ptr, lhs, lhs_len);
316 ptr += lhs_len - 1;
317
318 // Check the 'fake packet' version number, to ensure that we know how to decompress this data
319 if (*ptr != compression_packet_v1) return mStatus_Incompatible;
320
321 // two bytes of CLASS
322 ptr = putVal16(ptr, kDNSClass_IN | kDNSClass_UniqueRRSet);
323
324 // four bytes of TTL
325 ptr = putVal32(ptr, 120);
326
327 // Copy the RHS length into the RDLENGTH of our fake wire packet
328 ptr = putVal16(ptr, rhs_len);
329
330 // Copy the RHS onto our fake wire packet
331 mDNSPlatformMemCopy(ptr, rhs, rhs_len);
332 ptr += rhs_len;
333
334 if (mDNS_LoggingEnabled)
335 {
336 const int len = (int)(ptr - compression_lhs);
337 LogInfo("xD2DParse: Our Bytes (%d bytes): %.*H", len, len, compression_lhs);
338 }
339
340 ptr = (mDNSu8 *) GetLargeResourceRecord(m, &compression_base_msg, compression_lhs, ptr, mDNSInterface_Any, kDNSRecordTypePacketAns, &m->rec);
341 if (!ptr || m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative)
342 {
343 LogMsg("xD2DParse: failed to get large RR");
344 m->rec.r.resrec.RecordType = 0;
345 return mStatus_UnknownErr;
346 }
347 else
348 {
349 LogInfo("xD2DParse: got rr: %s", CRDisplayString(m, &m->rec.r));
350 }
351
352 *D2DListp = (D2DRecordListElem *) mDNSPlatformMemAllocateClear(sizeof(D2DRecordListElem) + (m->rec.r.resrec.rdlength <= sizeof(RDataBody) ? 0 : m->rec.r.resrec.rdlength - sizeof(RDataBody)));
353 if (!*D2DListp) return mStatus_NoMemoryErr;
354
355 AuthRecord *rr = &(*D2DListp)->ar;
356 mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_P2P, m->rec.r.resrec.rrtype, 7200, kDNSRecordTypeShared, AuthRecordP2P, FreeD2DARElemCallback, NULL);
357 AssignDomainName(&rr->namestorage, &m->rec.namestorage);
358 rr->resrec.rdlength = m->rec.r.resrec.rdlength;
359 rr->resrec.rdata->MaxRDLength = m->rec.r.resrec.rdlength;
360 mDNSPlatformMemCopy(rr->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, m->rec.r.resrec.rdlength);
361 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
362 SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us
363
364 m->rec.r.resrec.RecordType = 0; // Mark m->rec as no longer in use
365
366 return mStatus_NoError;
367 }
368
369 mDNSexport void xD2DAddToCache(D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
370 {
371 mDNS *const m = &mDNSStorage;
372 if (result == kD2DSuccess)
373 {
374 if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DAddToCache: NULL Byte * passed in or length == 0"); return; }
375
376 mStatus err;
377 D2DRecordListElem *ptr = NULL;
378
379 err = xD2DParse((const mDNSu8 * const)key, (const mDNSu16)keySize, (const mDNSu8 * const)value, (const mDNSu16)valueSize, &ptr);
380 if (err)
381 {
382 LogMsg("xD2DAddToCache: xD2DParse returned error: %d", err);
383 PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize);
384 if (ptr)
385 mDNSPlatformMemFree(ptr);
386 return;
387 }
388
389 #if ENABLE_BLE_TRIGGERED_BONJOUR
390 // If the record was created based on a BLE beacon, update the interface index to indicate
391 // this and thus match BLE specific queries.
392 if (transportType == D2DBLETransport)
393 ptr->ar.resrec.InterfaceID = mDNSInterface_BLE;
394 #endif // ENABLE_BLE_TRIGGERED_BONJOUR
395
396 err = mDNS_Register(m, &ptr->ar);
397 if (err)
398 {
399 LogMsg("xD2DAddToCache: mDNS_Register returned error %d for %s", err, ARDisplayString(m, &ptr->ar));
400 mDNSPlatformMemFree(ptr);
401 return;
402 }
403
404 LogInfo("xD2DAddToCache: mDNS_Register succeeded for %s", ARDisplayString(m, &ptr->ar));
405 ptr->instanceHandle = instanceHandle;
406 ptr->transportType = transportType;
407 ptr->next = D2DRecords;
408 D2DRecords = ptr;
409 }
410 else
411 LogMsg("xD2DAddToCache: Unexpected result %d", result);
412 }
413
414 mDNSlocal D2DRecordListElem * xD2DFindInList(const Byte *const key, const size_t keySize, const Byte *const value, const size_t valueSize)
415 {
416 D2DRecordListElem *ptr = D2DRecords;
417 D2DRecordListElem *arptr = NULL;
418
419 if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DFindInList: NULL Byte * passed in or length == 0"); return NULL; }
420
421 mStatus err = xD2DParse((const mDNSu8 *const)key, (const mDNSu16)keySize, (const mDNSu8 *const)value, (const mDNSu16)valueSize, &arptr);
422 if (err)
423 {
424 LogMsg("xD2DFindInList: xD2DParse returned error: %d", err);
425 PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize);
426 if (arptr)
427 mDNSPlatformMemFree(arptr);
428 return NULL;
429 }
430
431 while (ptr)
432 {
433 if (IdenticalResourceRecord(&arptr->ar.resrec, &ptr->ar.resrec)) break;
434 ptr = ptr->next;
435 }
436
437 if (!ptr) LogMsg("xD2DFindInList: Could not find in D2DRecords: %s", ARDisplayString(&mDNSStorage, &arptr->ar));
438 mDNSPlatformMemFree(arptr);
439 return ptr;
440 }
441
442 mDNSexport void xD2DRemoveFromCache(D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
443 {
444 (void)transportType; // We don't care about this, yet.
445 (void)instanceHandle; // We don't care about this, yet.
446
447 if (result == kD2DSuccess)
448 {
449 D2DRecordListElem *ptr = xD2DFindInList(key, keySize, value, valueSize);
450 if (ptr)
451 {
452 LogInfo("xD2DRemoveFromCache: Remove from cache: %s", ARDisplayString(&mDNSStorage, &ptr->ar));
453 mDNS_Deregister(&mDNSStorage, &ptr->ar);
454 }
455 }
456 else
457 LogMsg("xD2DRemoveFromCache: Unexpected result %d", result);
458 }
459
460 mDNSlocal void xD2DServiceResolved(D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
461 {
462 (void)key;
463 (void)keySize;
464 (void)value;
465 (void)valueSize;
466
467 if (result == kD2DSuccess)
468 {
469 LogInfo("xD2DServiceResolved: Starting up PAN connection for %p", instanceHandle);
470 if (D2DRetain) D2DRetain(instanceHandle, transportType);
471 }
472 else LogMsg("xD2DServiceResolved: Unexpected result %d", result);
473 }
474
475 mDNSlocal void xD2DRetainHappened(D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
476 {
477 (void)instanceHandle;
478 (void)transportType;
479 (void)key;
480 (void)keySize;
481 (void)value;
482 (void)valueSize;
483
484 if (result == kD2DSuccess) LogInfo("xD2DRetainHappened: Opening up PAN connection for %p", instanceHandle);
485 else LogMsg("xD2DRetainHappened: Unexpected result %d", result);
486 }
487
488 mDNSlocal void xD2DReleaseHappened(D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize)
489 {
490 (void)instanceHandle;
491 (void)transportType;
492 (void)key;
493 (void)keySize;
494 (void)value;
495 (void)valueSize;
496
497 if (result == kD2DSuccess) LogInfo("xD2DReleaseHappened: Closing PAN connection for %p", instanceHandle);
498 else LogMsg("xD2DReleaseHappened: Unexpected result %d", result);
499 }
500
501 mDNSlocal void xD2DServiceCallback(D2DServiceEvent event, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize, void *userData)
502 {
503 const char *eventString = "unknown";
504
505 KQueueLock();
506
507 if (keySize > 0xFFFF) LogMsg("xD2DServiceCallback: keySize too large: %u", keySize);
508 if (valueSize > 0xFFFF) LogMsg("xD2DServiceCallback: valueSize too large: %u", valueSize);
509
510 switch (event)
511 {
512 case D2DServiceFound:
513 eventString = "D2DServiceFound";
514 break;
515 case D2DServiceLost:
516 eventString = "D2DServiceLost";
517 break;
518 case D2DServiceResolved:
519 eventString = "D2DServiceResolved";
520 break;
521 case D2DServiceRetained:
522 eventString = "D2DServiceRetained";
523 break;
524 case D2DServiceReleased:
525 eventString = "D2DServiceReleased";
526 break;
527 default:
528 break;
529 }
530
531 LogInfo("xD2DServiceCallback: event=%s result=%d instanceHandle=%p transportType=%d LHS=%p (%u) RHS=%p (%u) userData=%p", eventString, result, instanceHandle, transportType, key, keySize, value, valueSize, userData);
532 PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize);
533
534 switch (event)
535 {
536 case D2DServiceFound:
537 xD2DAddToCache(result, instanceHandle, transportType, key, keySize, value, valueSize);
538 break;
539 case D2DServiceLost:
540 xD2DRemoveFromCache(result, instanceHandle, transportType, key, keySize, value, valueSize);
541 break;
542 case D2DServiceResolved:
543 xD2DServiceResolved(result, instanceHandle, transportType, key, keySize, value, valueSize);
544 break;
545 case D2DServiceRetained:
546 xD2DRetainHappened(result, instanceHandle, transportType, key, keySize, value, valueSize);
547 break;
548 case D2DServiceReleased:
549 xD2DReleaseHappened(result, instanceHandle, transportType, key, keySize, value, valueSize);
550 break;
551 default:
552 break;
553 }
554
555 // Need to tickle the main kqueue loop to potentially handle records we removed or added.
556 KQueueUnlock("xD2DServiceCallback");
557 }
558
559 // Map interface index and flags to a specific D2D transport type or D2DTransportMax if all plugins
560 // should be called.
561 // When D2DTransportMax is returned, if a specific transport should not be called, *excludedTransportType
562 // will be set to the excluded transport value, otherwise, it will be set to D2DTransportMax.
563 // If the return value is not D2DTransportMax, excludedTransportType is undefined.
564
565 mDNSlocal D2DTransportType xD2DInterfaceToTransportType(mDNSInterfaceID InterfaceID, DNSServiceFlags flags, D2DTransportType * excludedTransportType)
566 {
567 NetworkInterfaceInfoOSX *info;
568
569 // Default exludes the D2DAWDLTransport when D2DTransportMax is returned.
570 *excludedTransportType = D2DAWDLTransport;
571
572 // Call all D2D plugins when both kDNSServiceFlagsIncludeP2P and kDNSServiceFlagsIncludeAWDL are set.
573 if ((flags & kDNSServiceFlagsIncludeP2P) && (flags & kDNSServiceFlagsIncludeAWDL))
574 {
575 LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (including AWDL) since both kDNSServiceFlagsIncludeP2P and kDNSServiceFlagsIncludeAWDL are set");
576 *excludedTransportType = D2DTransportMax;
577 return D2DTransportMax;
578 }
579 // Call all D2D plugins (exlcluding AWDL) when only kDNSServiceFlagsIncludeP2P is set.
580 else if (flags & kDNSServiceFlagsIncludeP2P)
581 {
582 LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (excluding AWDL) since only kDNSServiceFlagsIncludeP2P is set");
583 return D2DTransportMax;
584 }
585 // Call AWDL D2D plugin when only kDNSServiceFlagsIncludeAWDL is set.
586 else if (flags & kDNSServiceFlagsIncludeAWDL)
587 {
588 LogInfo("xD2DInterfaceToTransportType: returning D2DAWDLTransport since only kDNSServiceFlagsIncludeAWDL is set");
589 return D2DAWDLTransport;
590 }
591
592 if (InterfaceID == mDNSInterface_P2P)
593 {
594 LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (excluding AWDL) for interface index mDNSInterface_P2P");
595 return D2DTransportMax;
596 }
597
598 // Compare to cached AWDL interface ID.
599 if (AWDLInterfaceID && (InterfaceID == AWDLInterfaceID))
600 {
601 LogInfo("xD2DInterfaceToTransportType: returning D2DAWDLTransport for interface index %d", InterfaceID);
602 return D2DAWDLTransport;
603 }
604
605 info = IfindexToInterfaceInfoOSX(InterfaceID);
606 if (info == NULL)
607 {
608 LogInfo("xD2DInterfaceToTransportType: Invalid interface index %d", InterfaceID);
609 return D2DTransportMax;
610 }
611
612 // Recognize AirDrop specific p2p* interface based on interface name.
613 if (strncmp(info->ifinfo.ifname, "p2p", 3) == 0)
614 {
615 LogInfo("xD2DInterfaceToTransportType: returning D2DWifiPeerToPeerTransport for interface index %d", InterfaceID);
616 return D2DWifiPeerToPeerTransport;
617 }
618
619 // Currently there is no way to identify Bluetooth interface by name,
620 // since they use "en*" based name strings.
621
622 LogInfo("xD2DInterfaceToTransportType: returning default D2DTransportMax for interface index %d", InterfaceID);
623 return D2DTransportMax;
624 }
625
626 mDNSexport void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags)
627 {
628 #if ENABLE_BLE_TRIGGERED_BONJOUR
629 // BLE support currently not handled by a D2D plugin
630 if (applyToBLE(InterfaceID, flags))
631 {
632 domainname lower;
633
634 DomainnameToLower(typeDomain, &lower);
635 // pass in the key and keySize
636 mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype);
637 start_BLE_browse(InterfaceID, &lower, qtype, flags, compression_lhs, end - compression_lhs);
638 }
639 else
640 #endif // ENABLE_BLE_TRIGGERED_BONJOUR
641 internal_start_browsing_for_service(InterfaceID, typeDomain, qtype, flags);
642 }
643
644 mDNSexport void internal_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags)
645 {
646 domainname lower;
647
648 DomainnameToLower(typeDomain, &lower);
649
650 if (!D2DBrowseListRefCount(&lower, qtype))
651 {
652 D2DTransportType transportType, excludedTransport;
653
654 LogInfo("%s: Starting browse for: %##s %s", __func__, lower.c, DNSTypeName(qtype));
655 mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype);
656 PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0);
657
658 transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport);
659 if (transportType == D2DTransportMax)
660 {
661 D2DTransportType i;
662 for (i = 0; i < D2DTransportMax; i++)
663 {
664 if (i == excludedTransport) continue;
665 if (D2DStartBrowsingForKeyOnTransport) D2DStartBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, i);
666 }
667 }
668 else
669 {
670 if (D2DStartBrowsingForKeyOnTransport) D2DStartBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, transportType);
671 }
672 }
673 D2DBrowseListRetain(&lower, qtype);
674 }
675
676 mDNSexport void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags)
677 {
678 #if ENABLE_BLE_TRIGGERED_BONJOUR
679 // BLE support currently not handled by a D2D plugin
680 if (applyToBLE(InterfaceID, flags))
681 {
682 domainname lower;
683
684 // If this is the last instance of this browse, clear any cached records recieved for it.
685 // We are not guaranteed to get a D2DServiceLost event for all key, value pairs cached over BLE.
686 DomainnameToLower(typeDomain, &lower);
687 if (stop_BLE_browse(InterfaceID, &lower, qtype, flags))
688 xD2DClearCache(&lower, qtype);
689 }
690 else
691 #endif // ENABLE_BLE_TRIGGERED_BONJOUR
692 internal_stop_browsing_for_service(InterfaceID, typeDomain, qtype, flags);
693 }
694
695 mDNSexport void internal_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags)
696 {
697 domainname lower;
698
699 DomainnameToLower(typeDomain, &lower);
700
701 // If found in list and this is the last reference to this browse, remove the key from the D2D plugins.
702 if (D2DBrowseListRelease(&lower, qtype) && !D2DBrowseListRefCount(&lower, qtype))
703 {
704 D2DTransportType transportType, excludedTransport;
705
706 LogInfo("%s: Stopping browse for: %##s %s", __func__, lower.c, DNSTypeName(qtype));
707 mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype);
708 PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0);
709
710 transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport);
711 if (transportType == D2DTransportMax)
712 {
713 D2DTransportType i;
714 for (i = 0; i < D2DTransportMax; i++)
715 {
716 if (i == excludedTransport) continue;
717 if (D2DStopBrowsingForKeyOnTransport) D2DStopBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, i);
718 }
719 }
720 else
721 {
722 if (D2DStopBrowsingForKeyOnTransport) D2DStopBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, transportType);
723 }
724
725 // The D2D driver may not generate the D2DServiceLost event for this key after
726 // the D2DStopBrowsingForKey*() call above. So, we flush the key from the D2D
727 // record cache now.
728 xD2DClearCache(&lower, qtype);
729 }
730 }
731
732 mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags)
733 {
734 #if ENABLE_BLE_TRIGGERED_BONJOUR
735 if (applyToBLE(resourceRecord->InterfaceID, flags))
736 {
737 domainname lower;
738
739 DomainnameToLower(resourceRecord->name, &lower);
740 start_BLE_advertise(resourceRecord, &lower, resourceRecord->rrtype, flags);
741 }
742 else
743 #endif // ENABLE_BLE_TRIGGERED_BONJOUR
744 internal_start_advertising_service(resourceRecord, flags);
745 }
746
747 mDNSexport void internal_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags)
748 {
749 domainname lower;
750 mDNSu8 *rhs = NULL;
751 mDNSu8 *end = NULL;
752 D2DTransportType transportType, excludedTransport;
753 DomainnameToLower(resourceRecord->name, &lower);
754
755 LogInfo("%s: %s", __func__, RRDisplayString(&mDNSStorage, resourceRecord));
756
757 // For SRV records, update packet filter if p2p interface already exists, otherwise,
758 // if will be updated when we get the KEV_DL_IF_ATTACHED event for the interface.
759 if (resourceRecord->rrtype == kDNSType_SRV)
760 mDNSUpdatePacketFilter(NULL);
761
762 rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype);
763 end = DNSNameCompressionBuildRHS(rhs, resourceRecord);
764 PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
765
766 transportType = xD2DInterfaceToTransportType(resourceRecord->InterfaceID, flags, & excludedTransport);
767 if (transportType == D2DTransportMax)
768 {
769 D2DTransportType i;
770 for (i = 0; i < D2DTransportMax; i++)
771 {
772 if (i == excludedTransport) continue;
773 if (D2DStartAdvertisingPairOnTransport) D2DStartAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
774 }
775 }
776 else
777 {
778 if (D2DStartAdvertisingPairOnTransport) D2DStartAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
779 }
780 }
781
782 mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags)
783 {
784 #if ENABLE_BLE_TRIGGERED_BONJOUR
785 // BLE support currently not handled by a D2D plugin
786 if (applyToBLE(resourceRecord->InterfaceID, flags))
787 {
788 domainname lower;
789
790 DomainnameToLower(resourceRecord->name, &lower);
791 stop_BLE_advertise(&lower, resourceRecord->rrtype, flags);
792 }
793 else
794 #endif // ENABLE_BLE_TRIGGERED_BONJOUR
795 internal_stop_advertising_service(resourceRecord, flags);
796 }
797
798 mDNSexport void internal_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags)
799 {
800 domainname lower;
801 mDNSu8 *rhs = NULL;
802 mDNSu8 *end = NULL;
803 D2DTransportType transportType, excludedTransport;
804 DomainnameToLower(resourceRecord->name, &lower);
805
806 LogInfo("%s: %s", __func__, RRDisplayString(&mDNSStorage, resourceRecord));
807
808 // For SRV records, update packet filter if p2p interface already exists, otherwise,
809 // For SRV records, update packet filter to to remove this port from list
810 if (resourceRecord->rrtype == kDNSType_SRV)
811 mDNSUpdatePacketFilter(resourceRecord);
812
813 rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype);
814 end = DNSNameCompressionBuildRHS(rhs, resourceRecord);
815 PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
816
817 transportType = xD2DInterfaceToTransportType(resourceRecord->InterfaceID, flags, & excludedTransport);
818 if (transportType == D2DTransportMax)
819 {
820 D2DTransportType i;
821 for (i = 0; i < D2DTransportMax; i++)
822 {
823 if (i == excludedTransport) continue;
824 if (D2DStopAdvertisingPairOnTransport) D2DStopAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
825 }
826 }
827 else
828 {
829 if (D2DStopAdvertisingPairOnTransport) D2DStopAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
830 }
831 }
832
833 mDNSexport void external_start_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags)
834 {
835 domainname lower;
836 mDNSu8 *rhs = NULL;
837 mDNSu8 *end = NULL;
838 mDNSBool AWDL_used = false; // whether AWDL was used for this resolve
839 D2DTransportType transportType, excludedTransport;
840 DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower);
841
842 LogInfo("external_start_resolving_service: %##s", fqdn->c);
843 rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR);
844 end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn);
845 PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
846
847 transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport);
848 if (transportType == D2DTransportMax)
849 {
850 // Resolving over all the transports, except for excludedTransport if set.
851 D2DTransportType i;
852 for (i = 0; i < D2DTransportMax; i++)
853 {
854 if (i == excludedTransport) continue;
855 if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
856
857 if (i == D2DAWDLTransport)
858 AWDL_used = true;
859 }
860 }
861 else
862 {
863 // Resolving over one specific transport.
864 if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
865
866 if (transportType == D2DAWDLTransport)
867 AWDL_used = true;
868 }
869
870 // AWDL wants the SRV and TXT record queries communicated over the D2D interface.
871 // We only want these records going to AWDL, so use AWDLInterfaceID as the
872 // interface and don't set any other flags.
873 if (AWDL_used && AWDLInterfaceID)
874 {
875 LogInfo("external_start_resolving_service: browse for TXT and SRV over AWDL");
876 external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, 0);
877 external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, 0);
878 }
879 }
880
881 mDNSexport void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags)
882 {
883 domainname lower;
884 mDNSu8 *rhs = NULL;
885 mDNSu8 *end = NULL;
886 mDNSBool AWDL_used = false; // whether AWDL was used for this resolve
887 D2DTransportType transportType, excludedTransport;
888 DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower);
889
890 LogInfo("external_stop_resolving_service: %##s", fqdn->c);
891 rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR);
892 end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn);
893 PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs);
894
895 transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport);
896 if (transportType == D2DTransportMax)
897 {
898 D2DTransportType i;
899 for (i = 0; i < D2DTransportMax; i++)
900 {
901 if (i == excludedTransport) continue;
902 if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i);
903
904 if (i == D2DAWDLTransport)
905 AWDL_used = true;
906 }
907 }
908 else
909 {
910 if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType);
911
912 if (transportType == D2DAWDLTransport)
913 AWDL_used = true;
914 }
915
916 // AWDL wants the SRV and TXT record queries communicated over the D2D interface.
917 // We only want these records going to AWDL, so use AWDLInterfaceID as the
918 // interface and don't set any other flags.
919 if (AWDL_used && AWDLInterfaceID)
920 {
921 LogInfo("external_stop_resolving_service: stop browse for TXT and SRV on AWDL");
922 external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, 0);
923 external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, 0);
924 }
925 }
926
927 mDNSexport mDNSBool callExternalHelpers(mDNSInterfaceID InterfaceID, const domainname *const domain, DNSServiceFlags flags)
928 {
929 // Only call D2D layer routines if request applies to a D2D interface and the domain is "local".
930 if ( (((InterfaceID == mDNSInterface_Any) && (flags & (kDNSServiceFlagsIncludeP2P | kDNSServiceFlagsIncludeAWDL | kDNSServiceFlagsAutoTrigger)))
931 || mDNSPlatformInterfaceIsD2D(InterfaceID) || (InterfaceID == mDNSInterface_BLE))
932 && IsLocalDomain(domain))
933 {
934 return mDNStrue;
935 }
936 else
937 return mDNSfalse;
938 }
939
940 // Used to derive the original D2D specific flags specified by the client in the registration
941 // when we don't have access to the original flag (kDNSServiceFlags*) values.
942 mDNSexport mDNSu32 deriveD2DFlagsFromAuthRecType(AuthRecType authRecType)
943 {
944 mDNSu32 flags = 0;
945 if ((authRecType == AuthRecordAnyIncludeP2P) || (authRecType == AuthRecordAnyIncludeAWDLandP2P))
946 flags |= kDNSServiceFlagsIncludeP2P;
947 else if ((authRecType == AuthRecordAnyIncludeAWDL) || (authRecType == AuthRecordAnyIncludeAWDLandP2P))
948 flags |= kDNSServiceFlagsIncludeAWDL;
949 return flags;
950 }
951
952 void initializeD2DPlugins(mDNS *const m)
953 {
954 // We only initialize if mDNSCore successfully initialized.
955 if (D2DInitialize)
956 {
957 D2DStatus ds = D2DInitialize(CFRunLoopGetMain(), xD2DServiceCallback, m);
958 if (ds != kD2DSuccess)
959 LogMsg("D2DInitialiize failed: %d", ds);
960 else
961 LogMsg("D2DInitialize succeeded");
962 }
963 }
964
965 void terminateD2DPlugins(void)
966 {
967 if (D2DTerminate)
968 {
969 D2DStatus ds = D2DTerminate();
970 if (ds != kD2DSuccess)
971 LogMsg("D2DTerminate failed: %d", ds);
972 else
973 LogMsg("D2DTerminate succeeded");
974 }
975 }
976
977 #ifdef UNIT_TEST
978 #pragma mark - Unit test support routines
979
980 // These unit test support routines are called from unittests/ framework
981 // and are not compiled for the mDNSResponder runtime code paths.
982
983 void D2D_unitTest(void)
984 {
985 }
986
987 #endif // UNIT_TEST