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