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