Imported Upstream version 878.70.2
[platform/upstream/mdnsresponder.git] / mDNSMacOSX / BLE.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  *
3  * Copyright (c) 2015-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 #if ENABLE_BLE_TRIGGERED_BONJOUR
19
20 #include "mDNSEmbeddedAPI.h"
21 #include "DNSCommon.h"
22 #include "mDNSMacOSX.h"
23 #include "BLE.h"
24 #include "D2D.h"
25
26 #include <dlfcn.h>
27
28 #pragma mark - Browse and Registration Request Handling
29
30 // When set, enables BLE triggered discovery APIs.
31 mDNSBool EnableBLEBasedDiscovery = mDNSfalse;
32
33 // When set, the default mode is to promote all client requests made with 
34 // kDNSServiceInterfaceIndexAny to BLE Triggered Discovery.
35 // Requests to promote will be filtered by either a service type whitelist or
36 // blacklist as noted below.
37 mDNSBool DefaultToBLETriggered = mDNSfalse;
38
39 #define USE_WHITELIST 1
40
41 #if USE_WHITELIST
42
43 // Current list of service types that will have BLE triggers applied by default
44 // when DefaultToBLETriggered is set to true.
45
46 const char * defaultServiceWhitelist[] = {
47     "\x04_ssh",
48     "\x04_smb",
49     "\x04_rfb",
50     "\x04_ipp",
51     "\x05_ipps",
52     "\x08_printer",
53     0
54 };
55
56 // Return true if DefaultToBLETriggered is set and the operation should be
57 // promoted to use BLE triggered discovery by default.
58 bool shouldUseBLE(mDNSInterfaceID interfaceID, DNS_TypeValues rrtype, domainname *serviceType, domainname *domain)
59 {
60     const mDNSu8 ** ptr;
61
62     if (!DefaultToBLETriggered || (interfaceID != mDNSInterface_Any) || !IsLocalDomain(domain))
63         return mDNSfalse;
64
65     // Address records don't have a service type to match on, but we'll trigger them
66     // here to support the case were the DNSServiceQueryRecord() was done using mDNSInterface_Any instead
67     // of the interface that the corresponding SRV record was returned over.
68     if ((rrtype == kDNSType_A) || (rrtype == kDNSType_AAAA))
69             return mDNStrue;
70
71     ptr = (const mDNSu8 **) defaultServiceWhitelist;
72     while (*ptr)
73     {
74         if (SameDomainLabel(*ptr, serviceType->c))
75             return mDNStrue;
76         ptr++;
77     }
78
79     return mDNSfalse;
80 }
81
82 #else // USE_WHITELIST
83
84 // Current list of service types that will NOT have BLE triggers applied by default
85 // when DefaultToBLETriggered is set to true.
86
87 // _airplay and _airdrop discovery already employ BLE based triggering using Apple service specific
88 // BLE beacons.  The rest of the entries here are default browses run in a standard OSX install
89 // that we don't want to have cluttering up the Bloom filter when using the service blacklist approach.
90
91 const char * defaultServiceBlacklist[] = {
92     "\x08_airplay",
93     "\x08_airdrop",
94     "\x05_raop",
95     "\x08_airport",
96     "\x0d_apple-mobdev",
97     "\x06_uscan",
98     "\x07_uscans",
99     "\x08_scanner",
100     "\x0e_apple-mobdev2",
101     "\x04_ipp",
102     "\x05_ipps",
103     "\x07_ippusb",
104     "\x08_printer",
105     "\x0f_pdl-datastream",
106     "\x04_ptp",
107     0
108 };
109
110 // Return true if DefaultToBLETriggered is set and the operation should be
111 // promoted to use BLE triggered discovery by default.
112 bool shouldUseBLE(mDNSInterfaceID interfaceID,  DNS_TypeValues rrtype, domainname *serviceType, domainname *domain)
113 {
114     (void) rrtype;
115     const mDNSu8 ** ptr;
116
117     if (!DefaultToBLETriggered || (interfaceID != mDNSInterface_Any) || !IsLocalDomain(domain))
118         return mDNSfalse;
119
120     ptr = (const mDNSu8 **) defaultServiceBlacklist;
121     while (*ptr)
122     {
123         if (SameDomainLabel(*ptr, serviceType->c))
124             return mDNSfalse;
125         ptr++;
126     }
127
128     return mDNStrue;
129 }
130
131 #endif // USE_WHITELIST
132
133 // Structure for linked list of BLE responses received that match
134 // a given client request.
135 typedef struct matchingResponses 
136 {
137     struct matchingResponses * next;
138     void * response;
139 } matchingResponses_t;
140
141 // Max size of input key generated by DNSNameCompressionBuildLHS() is MAX_DOMAIN_NAME + 3
142 // where the three additional bytes are:
143 // two bytes for DNS_TypeValues and one byte for "compression_packet_v1", the D2D compression version number.
144 #define MAX_KEY_SIZE    MAX_DOMAIN_NAME + 3
145
146 // Initially used for both the browse and registration lists.
147 typedef struct requestList
148 {
149     struct requestList  * next;
150     unsigned int        refCount;
151     domainname          name;
152     mDNSu16             type;
153     DNSServiceFlags     flags;
154     mDNSInterfaceID     InterfaceID;
155     serviceHash_t       browseHash;
156     serviceHash_t       registeredHash;
157     matchingResponses_t * ourResponses;
158     bool                triggeredOnAWDL;
159
160     // The following fields are only used for browse requests currently
161     mDNSu8              key[MAX_KEY_SIZE];
162     size_t              keySize;
163
164     // The following fields are only used for registration requests currently
165     const ResourceRecord    * resourceRecord;
166 } requestList_t;
167
168 // Lists for all DNSServiceBrowse() and DNSServiceRegister() requests using 
169 // BLE beacon based triggering.
170 static requestList_t* BLEBrowseListHead = NULL;
171 static requestList_t* BLERegistrationListHead = NULL;
172
173 // The kDNSServiceFlagsAutoTrigger should only be set for a request that would normally apply to AWDL.
174 #define isAutoTriggerRequest(INTERFACE_INDEX, FLAGS) (    (FLAGS & kDNSServiceFlagsAutoTrigger) \
175                                                        && (   (AWDLInterfaceID && (INTERFACE_INDEX == AWDLInterfaceID)) \
176                                                            || ((INTERFACE_INDEX == kDNSServiceInterfaceIndexAny) && (FLAGS & kDNSServiceFlagsIncludeAWDL))))
177
178 #pragma mark - Manage list of responses that match this request.
179
180 // Return true if any response matches one of our current registrations.
181 mDNSlocal bool responseMatchesRegistrations(void)
182 {
183     requestList_t   *ptr;
184
185     for (ptr = BLERegistrationListHead; ptr; ptr = ptr->next)
186     {
187         if (ptr->ourResponses)
188             return true;
189     }
190     return false;
191 }
192
193 // Return true if the response is already in the list of responses for this client request.
194 mDNSlocal bool inResponseListForRequest(requestList_t *request, void * response)
195 {
196     matchingResponses_t * rp;
197
198     for (rp = request->ourResponses; rp; rp = rp->next)
199         if (rp->response == response)
200             break;
201     
202     return (rp != 0);
203 }
204
205 mDNSlocal void addToResponseListForRequest(requestList_t *request, void * response)
206 {
207     matchingResponses_t *matchingResponse = calloc(1, sizeof(matchingResponses_t));
208
209     if (matchingResponse == NULL)
210     {
211         LogMsg("addToResponseListForRequest: calloc() failed!");
212         return;
213     }
214     matchingResponse->response = response;
215     matchingResponse->next = request->ourResponses;
216     request->ourResponses = matchingResponse;
217 }
218
219 // If response is currently in the list of responses, remove it and return true.
220 // Othewise, return false.
221 mDNSlocal bool removeFromResponseListForRequest(requestList_t *request, void * response)
222 {
223     matchingResponses_t ** nextp;
224     bool responseRemoved = false;
225
226     for (nextp = & request->ourResponses; *nextp; nextp = & (*nextp)->next)
227         if ((*nextp)->response == response)
228             break;
229
230     if (*nextp)
231     {
232         LogInfo("removeFromResponseListForRequest: response no longer matches for  %##s %s ", request->name.c, DNSTypeName(request->type));
233
234         responseRemoved = true;
235         matchingResponses_t *tmp = *nextp;
236         *nextp = (*nextp)->next;
237         free(tmp);
238     }
239     return responseRemoved;
240 }
241
242 // Free all current entries on the response list for this request.
243 mDNSlocal void freeResponseListEntriesForRequest(requestList_t *request)
244 {
245     matchingResponses_t * ptr;
246
247     ptr = request->ourResponses; 
248     while (ptr)
249     {
250         matchingResponses_t * tmp;
251
252         tmp = ptr;
253         ptr = ptr->next;
254         free(tmp);
255     }
256     request->ourResponses = 0;
257 }
258
259 #pragma mark - Manage request lists
260
261 // Return the address of the pointer to the entry, which can either be the address of "listHead"
262 // or the address of the prior entry on the lists "next" pointer.
263 mDNSlocal requestList_t ** findInRequestList(requestList_t ** listHead, const domainname *const name, mDNSu16 type)
264 {
265     requestList_t **ptr = listHead;
266
267     for ( ; *ptr; ptr = &(*ptr)->next)
268         if ((*ptr)->type == type && SameDomainName(&(*ptr)->name, name))
269             break;
270
271     return ptr;
272 }
273
274 mDNSlocal requestList_t * addToRequestList(requestList_t ** listHead, const domainname *const name, mDNSu16 type, DNSServiceFlags flags)
275 {
276     requestList_t **ptr = findInRequestList(listHead, name, type);
277
278     if (!*ptr)
279     {
280         *ptr = mDNSPlatformMemAllocate(sizeof(**ptr));
281         mDNSPlatformMemZero(*ptr, sizeof(**ptr));
282         (*ptr)->type = type;
283         (*ptr)->flags = flags;
284         AssignDomainName(&(*ptr)->name, name);
285     }
286     (*ptr)->refCount += 1;
287
288     LogInfo("addToRequestList: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount);
289
290     return *ptr;
291 }
292
293 mDNSlocal void removeFromRequestList(requestList_t ** listHead, const domainname *const name, mDNSu16 type)
294 {
295     requestList_t **ptr = findInRequestList(listHead, name, type);
296
297     if (!*ptr) { LogMsg("removeFromRequestList: Didn't find %##s %s in list", name->c, DNSTypeName(type)); return; }
298
299     (*ptr)->refCount -= 1;
300
301     LogInfo("removeFromRequestList: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount);
302
303     if (!(*ptr)->refCount)
304     {
305         requestList_t *tmp = *ptr;
306         *ptr = (*ptr)->next;
307         freeResponseListEntriesForRequest(tmp);
308         mDNSPlatformMemFree(tmp);
309     }
310 }
311
312 #pragma mark - Hashing and beacon state 
313
314 // These SipHash routines were copied from CoreUtils-500.9.
315 // We use these when running an mDNSRespnder root on a system that does not
316 // have the SipHash() routine available and exported in CoreUtils.
317 // TODO:  This local copy should be removed once we are no longer running mDNSResponder roots
318 // on systems that do no include CoreUtils-500.9 or newer.
319
320 // Start of code copied from: CoreUtils-500.9
321
322 /*! @group      BitRotates
323     @abstract   Rotates X COUNT bits to the left or right.
324 */
325 #define ROTL( X, N, SIZE )          ( ( (X) << (N) ) | ( (X) >> ( (SIZE) - N ) ) )
326 #define ROTR( X, N, SIZE )          ( ( (X) >> (N) ) | ( (X) << ( (SIZE) - N ) ) )
327
328 #define ROTL64( X, N )              ROTL( (X), (N), 64 )
329 #define ROTR64( X, N )              ROTR( (X), (N), 64 )
330
331     #define ReadLittle64( PTR ) \
332         ( (uint64_t)( \
333               ( (uint64_t)( (uint8_t *)(PTR) )[ 0 ] )           | \
334             ( ( (uint64_t)( (uint8_t *)(PTR) )[ 1 ] ) <<  8 )   | \
335             ( ( (uint64_t)( (uint8_t *)(PTR) )[ 2 ] ) << 16 )   | \
336             ( ( (uint64_t)( (uint8_t *)(PTR) )[ 3 ] ) << 24 )   | \
337             ( ( (uint64_t)( (uint8_t *)(PTR) )[ 4 ] ) << 32 )   | \
338             ( ( (uint64_t)( (uint8_t *)(PTR) )[ 5 ] ) << 40 )   | \
339             ( ( (uint64_t)( (uint8_t *)(PTR) )[ 6 ] ) << 48 )   | \
340             ( ( (uint64_t)( (uint8_t *)(PTR) )[ 7 ] ) << 56 ) ) )
341
342 // Based on <https://131002.net/siphash/>.
343
344 #define SipRound() \
345         do \
346         { \
347                 v0 += v1; v1 = ROTL64( v1, 13 ); v1 ^= v0; v0 = ROTL64( v0, 32 ); \
348                 v2 += v3; v3 = ROTL64( v3, 16 ); v3 ^= v2; \
349                 v0 += v3; v3 = ROTL64( v3, 21 ); v3 ^= v0; \
350                 v2 += v1; v1 = ROTL64( v1, 17 ); v1 ^= v2; v2 = ROTL64( v2, 32 ); \
351                 \
352         }       while( 0 )
353
354 mDNSlocal uint64_t      local_SipHash( const uint8_t inKey[ 16 ], const void *inSrc, size_t inLen )
355 {
356         const uint8_t *                         src  = (const uint8_t *) inSrc;
357         size_t const                            left = inLen % 8;
358         const uint8_t * const           end  = src + ( inLen - left );
359         uint64_t                                        k0, k1, v0, v1, v2, v3, tmp;
360         
361         k0 = ReadLittle64( &inKey[ 0 ] );
362         k1 = ReadLittle64( &inKey[ 8 ] );
363         v0 = k0 ^ UINT64_C( 0x736f6d6570736575 ); // 'somepseu'
364         v1 = k1 ^ UINT64_C( 0x646f72616e646f6d ); // 'dorandom'
365         v2 = k0 ^ UINT64_C( 0x6c7967656e657261 ); // 'lygenera'
366         v3 = k1 ^ UINT64_C( 0x7465646279746573 ); // 'tedbytes'
367         
368         for( ; src != end; src += 8 )
369         {
370                 tmp = ReadLittle64( src );
371                 v3 ^= tmp;
372                 SipRound();
373                 SipRound();
374                 v0 ^= tmp;
375         }
376         
377         tmp = ( (uint64_t)( inLen & 0xFF ) ) << 56;
378         switch( left )
379         {
380                 case 7: tmp |= ( ( (uint64_t) src[ 6 ] ) << 48 );
381                 case 6: tmp |= ( ( (uint64_t) src[ 5 ] ) << 40 );
382                 case 5: tmp |= ( ( (uint64_t) src[ 4 ] ) << 32 );
383                 case 4: tmp |= ( ( (uint64_t) src[ 3 ] ) << 24 );
384                 case 3: tmp |= ( ( (uint64_t) src[ 2 ] ) << 16 );
385                 case 2: tmp |= ( ( (uint64_t) src[ 1 ] ) <<  8 );
386                 case 1: tmp |=   ( (uint64_t) src[ 0 ] );
387                 default: break;
388         }
389         v3 ^= tmp;
390         SipRound();
391         SipRound();
392         v0 ^= tmp;
393         v2 ^= 0xFF;
394         SipRound();
395         SipRound();
396         SipRound();
397         SipRound();
398         return( v0 ^ v1 ^ v2 ^ v3 );
399 }
400
401 // See <https://spc.apple.com/AppleBLEInfo.html#_wifi_tds> for details.
402
403 #define kTDSSipHashKey          ( (const uint8_t *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" )
404 #define kTDSSipHashCount        5
405
406 #define kSizeCString        ( (size_t) -1 )
407
408 // End of code copied from: CoreUtils-500.9
409
410 // Must link symbol from CoreUtils at runtime to avoid cyclic dependency cycles in the build process.
411 static uint64_t (*SipHash_p)( const uint8_t inKey[ 16 ], const void *inSrc, size_t inLen ) = NULL;
412
413 mDNSlocal uint64_t local_TDSBloomFilterMake( uint32_t inBloomCount, const void *inStr, size_t inLen )
414 {
415     uint64_t        bloomFilter = 0, hash;
416     uint8_t         i;
417
418     if( inLen == kSizeCString ) inLen = strlen( (const char *) inStr );
419     if (SipHash_p)
420         hash = SipHash_p( kTDSSipHashKey, inStr, inLen );
421     else
422         hash = local_SipHash( kTDSSipHashKey, inStr, inLen );
423
424     for( i = 0; i < kTDSSipHashCount; ++i )
425     {
426         bloomFilter |= ( UINT64_C( 1 ) << ( hash % inBloomCount ) );
427         hash /= inBloomCount;
428     }
429     return( bloomFilter );
430 }
431
432 mDNSlocal void loadCoreUtils()
433 {
434     static mDNSBool runOnce = mDNSfalse;
435     static void *CoreUtils_p = mDNSNULL;
436     static const char path[] = "/System/Library/PrivateFrameworks/CoreUtils.framework/CoreUtils";
437
438     if (!runOnce)
439     {
440         runOnce = mDNStrue;
441         if (!CoreUtils_p)
442         {
443             CoreUtils_p = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
444             if (!CoreUtils_p)
445             {
446                 LogInfo("loadCoreUtils: dlopen() failed.");
447                 return;
448             }
449         }
450
451         if (!SipHash_p)
452         {
453             SipHash_p = dlsym(CoreUtils_p, "SipHash");
454             if (!SipHash_p)
455             {
456                 LogInfo("loadCoreUtils: load of SipHash symbol failed.");
457                 return;
458             }
459         }
460         LogInfo("loadCoreUtils: found SipHash symbol.");
461     }
462 }
463
464 #define HASH_SIZE    64
465
466 mDNSlocal serviceHash_t BLELabelHash(unsigned char *str, unsigned int length)
467 {
468     loadCoreUtils();
469
470     return local_TDSBloomFilterMake(HASH_SIZE, (const void *) str, (size_t) length);
471 }
472
473
474 // Maximum number of characters in string to hash should be:
475 //  2 for initial "s:" or "p:"
476 // 16 for "_" followed by up to 15 characters of service type
477 //  1 for separating "."
478 //  4 for "_udp" or "_tcp"
479 //  1 for the terminating NULL byte
480 #define MAX_HASH_STRING   (2 + 16 + 1 + 4 + 1)
481
482 // Maximum service name length, including the initial "_"
483 #define MAX_SERVICE_NAME 16
484
485 // Convert the service name and transport protocol to a NULL terminated C string.
486 // stringBuf must point to least (MAX_HASH_STRING - 2) bytes of available space.
487 mDNSlocal bool serviceNameStringFromDomain(const domainname *const domain, mDNSu8 * stringBuf)
488 {
489     mDNSu8       * dst = stringBuf;
490     const mDNSu8 * src = domain->c;
491     mDNSu8         len = *src++;
492
493     if (len == 0 || len > MAX_SERVICE_NAME)
494     {
495         LogInfo("serviceNameStringFromDomain: Invalid name lenght: %d", len);
496         return false;
497     }
498     if (*src != '_')
499     {
500         LogInfo("serviceNameStringFromDomain: service name does not begin with a _");
501         return false;
502     }
503     // Copy the service type
504     while (len--)
505         *dst++ = *src++;
506
507     *dst++ = '.';
508
509     if (!ValidTransportProtocol(src))
510     {
511         LogInfo("serviceNameStringFromDomain: Transport protocol name must be _udp or _tcp");
512         return false;
513     }
514     // copy the transport protocol
515     len = *src++;
516     while (len--)
517         *dst++ = *src++;
518
519     *dst = 0;
520     return true;
521 }
522
523 mDNSlocal bool setBLEServiceHash(const domainname *const domain, requestList_t * ptr)
524 {
525     // Initialize the string with the "s:" for the browser/seeker hash calculation.
526     mDNSu8 stringBuf[MAX_HASH_STRING] = { 's', ':', '\0' };
527
528     // Append the service name and protocol strings to the initial "s:" string.
529     if (!serviceNameStringFromDomain(domain, &stringBuf[2]))
530     {
531         LogInfo("setBLEServiceHash: serviceNameStringFromDomain() failed!");
532         return false;
533     }
534
535     ptr->browseHash =  BLELabelHash(stringBuf, strlen((const char *)stringBuf));
536     LogInfo("setBLEServiceHash: seeker string %s, hashed to 0x%lx", stringBuf, ptr->browseHash);
537
538     // Update string to start with "p:" for registration/provider hash calculation.
539     stringBuf[0] = 'p';
540
541     ptr->registeredHash =  BLELabelHash(stringBuf, strlen((const char *)stringBuf));
542     LogInfo("setBLEServiceHash: provider string %s, hashed to 0x%lx", stringBuf, ptr->registeredHash);
543     if (ptr->browseHash && ptr->registeredHash)
544         return true;
545     else
546         return false;
547 }
548
549 // Indicates we are sending the final beacon with zeroed Bloom filter to let
550 // peers know we are no longer actively seeking or providing any services.
551 bool finalBeacon = false;
552
553 // The last time we walked our response list looking for stale entries.
554 mDNSs32 lastScanForStaleResponses;
555
556 // Forward declaration.
557 mDNSlocal void removeStaleResponses(mDNSs32 currentTime);
558
559 // Interval at which we scan the response lists to remove any stale entries.
560 #define StaleResponseScanInterval 30
561
562 // Called from mDNS_Execute() when NextBLEServiceTime is reached.
563 void serviceBLE(void)
564 {
565     // Note, we can access mDNSStorage.timenow since we are called from mDNS_Execute, 
566     // which initializes that value by calling mDNS_Lock().
567     mDNSs32 currentTime = mDNSStorage.timenow;
568
569     // Initialize if zero.
570     if (!lastScanForStaleResponses)
571         lastScanForStaleResponses = NonZeroTime(currentTime - (StaleResponseScanInterval * mDNSPlatformOneSecond));
572
573     if (finalBeacon)
574     {
575         // We don't expect to do the finalBeacon processing if there are active browse requests,
576         if (BLEBrowseListHead)
577             LogInfo("serviceBLE: finalBeacon set and called with active browse BLE requests ??");
578
579         // or active registrations but we are not in suppress beacons state.
580         if (BLERegistrationListHead && !suppressBeacons)
581             LogInfo("serviceBLE: finalBeacon set and called with active registrations requests, but not in suppress beacons state ??");
582
583         finalBeacon = false;
584         stopBLEBeacon();
585     }
586
587     if (!BLEBrowseListHead && !BLERegistrationListHead)
588     {
589         LogInfo("serviceBLE: no active client requests, disabling service timer");
590         mDNSStorage.NextBLEServiceTime = 0;
591     }
592     else if ((currentTime - lastScanForStaleResponses) >= (StaleResponseScanInterval * mDNSPlatformOneSecond))
593     {
594         removeStaleResponses(currentTime);
595         lastScanForStaleResponses = currentTime;
596         mDNSStorage.NextBLEServiceTime = NonZeroTime(currentTime + (StaleResponseScanInterval * mDNSPlatformOneSecond));
597     }
598 }
599
600 // Initialize the periodic service timer if we have active requests.
601 // The timer is disabled in the next call to serviceBLE() when no requests are active.
602 mDNSlocal void updateServiceTimer()
603 {
604     if (!mDNSStorage.NextBLEServiceTime && (BLEBrowseListHead || BLERegistrationListHead))
605         mDNSStorage.NextBLEServiceTime = NonZeroTime(mDNSStorage.timenow + (StaleResponseScanInterval * mDNSPlatformOneSecond));
606 }
607
608 // Set true when suppressing beacon transmissions for our registrations until we see
609 // a peer beacon indicating a browse for one of our services.
610 bool suppressBeacons = false;
611
612 // Go through all the existing browses and registrations to create the
613 // current Bloom filter value for the BLE beacon.
614 // Update the current scan and beaconing state appropriately.
615 mDNSlocal void updateBeaconAndScanState()
616 {
617     requestList_t   *ptr;
618     serviceHash_t   beaconBloomFilter = 0;
619
620     updateServiceTimer();
621
622     for (ptr = BLEBrowseListHead; ptr; ptr = ptr->next)
623     {
624         beaconBloomFilter |= ptr->browseHash;
625     }
626
627     for (ptr = BLERegistrationListHead; ptr; ptr = ptr->next)
628     {
629         beaconBloomFilter |= ptr->registeredHash;
630     }
631
632     // If only advertising registered services and not browsing, we don't start the beacon transmission 
633     // until we receive a beacon from a peer matching one of our registrations.
634     if (BLERegistrationListHead && !BLEBrowseListHead && !responseMatchesRegistrations())
635     {
636         // If beacons are already suppressed, then no further action to take.
637         if (suppressBeacons)
638             LogInfo("updateBeaconAndScanState: continuing to suppressing beacons");
639         else
640         {
641             LogInfo("updateBeaconAndScanState: suppressing beacons, no peers currently seeking our services");
642             suppressBeacons = true;
643
644             // If currently beaconing, send a beacon for two seconds with a zeroed Bloom filter indicating we are 
645             // no longer browsing  for any services so that any matching auto triggered peer registrations have a 
646             // chance to see our state change.
647             if (currentlyBeaconing())
648                 updateBLEBeacon(0);
649             startBLEScan();
650         }
651     }
652     // If beacons had been suppressed and we no longer have services to advertise, no
653     // need to send a beacon with a zeroed Bloom filter for two seconds, just stop
654     // the scan.
655     else if (suppressBeacons == true && beaconBloomFilter == 0)
656     {
657         suppressBeacons = false;
658         stopBLEScan();
659     }
660     // Update the beacon with the current Bloom filter values.
661     else
662     {
663         suppressBeacons = false;
664         updateBLEBeacon(beaconBloomFilter);
665         // Scan unless the Bloom filter is zero, indicating we are not currently
666         // seeking or providing any services.
667         if (beaconBloomFilter)
668             startBLEScan();
669         else
670             stopBLEScan();
671     }
672 }
673
674 #pragma mark - Peer response handling
675
676 // Structure used to track the beacons received from various peers.
677 typedef struct responseList
678 {
679     struct responseList * next;
680     serviceHash_t       peerBloomFilter;
681     mDNSs32             recievedTime;
682     mDNSEthAddr         peerMac;
683 } responseList_t;
684
685 #define RESPONSE_LIST_NUMBER 8
686 static responseList_t* BLEResponseListHeads[RESPONSE_LIST_NUMBER];
687
688 // Return the address of the pointer to the entry, which can either be the address of the
689 // corresponding BLEResponseListHeads[] entry, or the address of the prior responseList_t entry
690 // on the lists "next" pointer.
691 mDNSlocal responseList_t ** findInResponseList(mDNSEthAddr * ptrToMAC)
692 {
693     // Use the least significant byte of the MAC address as our hash index to find the list.
694     responseList_t **ptr = & BLEResponseListHeads[ptrToMAC->b[5] % RESPONSE_LIST_NUMBER];
695
696     for ( ; *ptr; ptr = &(*ptr)->next)
697     {
698         if (memcmp(&(*ptr)->peerMac, ptrToMAC, sizeof(mDNSEthAddr)) == 0)
699             break;
700     }
701
702     return ptr;
703 }
704
705
706 mDNSlocal responseList_t * addToResponseList(serviceHash_t peerBloomFilter, mDNSEthAddr * ptrToMAC)
707 {
708     responseList_t **ptr = findInResponseList(ptrToMAC);
709
710     if (!*ptr)
711     {
712         *ptr = mDNSPlatformMemAllocate(sizeof(**ptr));
713         mDNSPlatformMemZero(*ptr, sizeof(**ptr));
714         (*ptr)->peerBloomFilter = peerBloomFilter;
715         memcpy(& (*ptr)->peerMac, ptrToMAC, sizeof(mDNSEthAddr));
716     }
717
718     return *ptr;
719 }
720
721 mDNSlocal void removeFromResponseList(mDNSEthAddr * ptrToMAC)
722 {
723     responseList_t **ptr = findInResponseList(ptrToMAC);
724
725     if (!*ptr)
726     {
727         LogMsg("removeFromResponseList: did not find entry for MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
728                 ptrToMAC->b[0], ptrToMAC->b[1], ptrToMAC->b[2], ptrToMAC->b[3], ptrToMAC->b[4], ptrToMAC->b[5]);
729         return;
730     }
731
732     LogInfo("removeFromResponseList: removing entry for MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
733                 ptrToMAC->b[0], ptrToMAC->b[1], ptrToMAC->b[2], ptrToMAC->b[3], ptrToMAC->b[4], ptrToMAC->b[5]);
734
735     responseList_t *tmp = *ptr;
736     *ptr = (*ptr)->next;
737     mDNSPlatformMemFree(tmp);
738 }
739
740 // Free all current entries on the BLE response lists, removing all pointers
741 // to freed structures from the lists.
742 mDNSlocal void clearResponseLists()
743 {
744     responseList_t **ptr;
745
746     for (unsigned int i = 0; i < RESPONSE_LIST_NUMBER; i++)
747     {
748         ptr = & BLEResponseListHeads[i];
749         while (*ptr)
750         {
751             responseList_t * tmp;
752     
753             tmp = *ptr;
754             *ptr = (*ptr)->next;
755             mDNSPlatformMemFree(tmp);
756         }
757     }
758 }
759
760 // Check to see if we have cached a response that matches a service for which we just started a browse or registration.
761 mDNSlocal void checkCachedResponses(requestList_t *browse, requestList_t *registration)
762 {
763     responseList_t *ptr;
764
765     for (unsigned int i = 0; i < RESPONSE_LIST_NUMBER; i++)
766     {
767         for (ptr = BLEResponseListHeads[i]; ptr; ptr = ptr->next)
768         {
769             // For browses, we are looking for responses that have a matching registration
770             // and for registrations we are looking for responses that have a matching browse.
771             if (    (browse && (browse->registeredHash & ptr->peerBloomFilter) == browse->registeredHash)
772                 ||  (registration && (registration->browseHash & ptr->peerBloomFilter) == registration->browseHash))
773             {
774                 // Clear the Bloom filter for the response.
775                 // The next beacon from this peer will update the filter then autoTrigger
776                 // any newly started client requests as appropriate.
777                 ptr->peerBloomFilter = 0;
778             }
779         }
780     }
781 }
782
783 // Define a fixed name to use for the instance name denoting that one or more instances
784 // of a service are being advertised by peers in their BLE beacons.
785 // Name format is: length byte + bytes of name string + two byte pointer to the PTR record name.
786 // See compression_lhs definition in the D2D plugin code for background on 0xc027 DNS name compression pointer value.
787 static Byte  *BLEinstanceValue = (Byte *) "\x11ThresholdInstance\xc0\x27";
788 #define BLEValueSize  strlen((const char *)BLEinstanceValue)
789
790 // Find each local browse that matches the registered service hash in the BLE response.
791 // Called on the CFRunLoop thread while handling a callback from CoreBluetooth.
792 // Caller should hold  KQueueLock().
793 mDNSlocal void findMatchingBrowse(responseList_t *response)
794 {
795     requestList_t *ptr;
796
797     ptr = BLEBrowseListHead;
798     for ( ; ptr; ptr = ptr->next)
799     {
800         // See if we potentially match a corresponding registration in the beacon.
801         // thus, compare using the "registeredHash" of our browse..
802         if ((ptr->registeredHash & response->peerBloomFilter) == ptr->registeredHash) 
803         {
804
805             LogInfo("findMatchingBrowse: Registration in response matched browse for: %##s", ptr->name.c);
806
807             if (inResponseListForRequest(ptr, response))
808             {
809                 LogInfo("findMatchingBrowse: Already on response list for browse: %##s", ptr->name.c);
810
811                 continue;
812             }
813             else
814             {
815                 LogInfo("findMatchingBrowse: Adding to response list for browse: %##s", ptr->name.c);
816
817                 if (ptr->ourResponses == 0)
818                 {
819                     if (isAutoTriggerRequest(ptr->InterfaceID, ptr->flags))
820                     {
821                             LogInfo("findMatchingBrowse: First BLE response, triggering browse for %##s on AWDL", ptr->name.c);
822                             // register with the AWDL D2D plugin, 
823                             internal_start_browsing_for_service(ptr->InterfaceID, & ptr->name, ptr->type, ptr->flags);
824                         ptr->triggeredOnAWDL = true;
825                     }
826
827                         // Browse on mDNSInterface_BLE is used to determine if there are one or more instances of the
828                         // service type discoveryed over BLE.  If this is the first instance, add the psuedo instance defined by BLEinstanceValue.
829                         if (ptr->InterfaceID == mDNSInterface_BLE)
830                         {
831                             xD2DAddToCache(kD2DSuccess, 0, D2DBLETransport, ptr->key, ptr->keySize, BLEinstanceValue, BLEValueSize);
832                         }
833                 }
834                 addToResponseListForRequest(ptr, response);
835             }
836         }
837         else
838         {
839             // If a previous response from this peer had matched the browse, remove that response from the
840             // list now.  If this is the last matching response, remove the corresponding key from the AWDL D2D plugin
841             if (removeFromResponseListForRequest(ptr, response) && (ptr->ourResponses == 0))
842             {
843                 if (ptr->InterfaceID == mDNSInterface_BLE)
844                 {
845                     xD2DRemoveFromCache(kD2DSuccess, 0, D2DBLETransport, ptr->key, ptr->keySize, BLEinstanceValue, BLEValueSize);
846                 }
847
848                 if (isAutoTriggerRequest(ptr->InterfaceID, ptr->flags))
849                 {
850                     LogInfo("findMatchingBrowse: Last BLE response, disabling browse for %##s on AWDL", ptr->name.c);
851                     internal_stop_browsing_for_service(ptr->InterfaceID, & ptr->name, ptr->type, ptr->flags);
852                     ptr->triggeredOnAWDL = false;
853                 }
854             }
855         }
856     }
857 }
858
859 // Find each local registration that matches the service browse hash BLE response Bloom filter.
860 // Called on the CFRunLoop thread while handling a callback from CoreBluetooth.
861 // Caller should hold  KQueueLock().
862 mDNSlocal void findMatchingRegistration(responseList_t *response)
863 {
864     requestList_t   *ptr;
865     bool            matchingPeer;
866
867     ptr = BLERegistrationListHead;
868     for ( ; ptr; ptr = ptr->next)
869     {
870         // See if we potentially match a corresponding browse in the beacon,
871         // thus, compare using the "browseHash" of our registration.
872         if ((ptr->browseHash & response->peerBloomFilter) == ptr->browseHash)
873         {
874             LogInfo("findMatchingRegistration: Incoming browse matched registration for: %##s", ptr->name.c);
875
876             if (inResponseListForRequest(ptr, response))
877             {
878                 LogInfo("findMatchingRegistration: Already on response list for registration: %##s", ptr->name.c);
879
880                 continue;
881             }
882             else
883             {
884                 LogInfo("findMatchingRegistration: Adding to response list for registration: %##s", ptr->name.c);
885
886                 // Also pass the registration to the AWDL D2D plugin if this is the first matching peer browse for
887                 // an auto triggered local registration.
888                 if ((ptr->ourResponses == 0) && isAutoTriggerRequest(ptr->InterfaceID, ptr->flags))
889                 {
890                     LogInfo("findMatchingRegistration: First BLE response, triggering registration for %##s on AWDL", ptr->name.c);
891                     if (ptr->resourceRecord == 0)
892                     {
893                         LogInfo("findMatchingRegistration: resourceRecord pointer is NULL ??");
894                         continue;
895                     }
896
897                     internal_start_advertising_service(ptr->resourceRecord, (ptr->flags | kDNSServiceFlagsIncludeAWDL));
898                     // indicate the registration has been applied to the AWDL interface
899                     ptr->triggeredOnAWDL = true;
900                 }
901
902                 addToResponseListForRequest(ptr, response);
903             }
904         }
905         else
906         {
907             // If a previous response from this peer had matched the browse, remove that response from the
908             // list now.  If this is the last matching response for a local auto triggered registration, 
909             // remove the advertised key/value pairs from the AWDL D2D plugin.
910             if (removeFromResponseListForRequest(ptr, response) && (ptr->ourResponses == 0) && isAutoTriggerRequest(ptr->InterfaceID, ptr->flags))
911             {
912                 LogInfo("findMatchingRegistration: Last BLE response, disabling registration for %##s on AWDL", ptr->name.c);
913
914                 // Restore the saved ARType and call into the AWDL D2D plugin to stop the corresponding record advertisements over AWDL.
915                 internal_stop_advertising_service(ptr->resourceRecord, (ptr->flags | kDNSServiceFlagsIncludeAWDL));
916                 ptr->triggeredOnAWDL = false;
917             }
918         }
919     }
920
921     // If beacons for our registrations had been suppressed, see if we now have a match and need to restart them.
922     matchingPeer = responseMatchesRegistrations();
923     if (suppressBeacons && matchingPeer)
924     {
925         LogInfo("findMatchingRegistration: peer searching for our service, starting beacon transmission");
926         updateBeaconAndScanState();
927
928         if (suppressBeacons == true)
929             LogInfo("findMatchingRegistration: NOTE: suppressBeacons is true after updateBeaconAndScanState() call ??");
930     }
931     // If we have only registrations, but no matching peers, we can suppress beacons until we get a matching peer beacon.
932     else if (!suppressBeacons && !matchingPeer && BLERegistrationListHead && !BLEBrowseListHead)
933     {
934         LogInfo("findMatchingRegistration: no peer beacons match our registrations, suppressing beacon transmission");
935         suppressBeacons = true;
936         stopBLEBeacon();
937     }
938 }
939
940
941 // Time limit before a beacon is aged out of our received list.
942 #define MAX_RESPONSE_AGE 10
943
944 // If we have responses from peers that are more than MAX_RESPONSE_AGE seconds
945 // old, remove them since a peer with active requests should be beaconing multiple
946 // times per second if still within BLE range.
947 mDNSlocal void removeStaleResponses(mDNSs32 currentTime)
948 {
949     responseList_t **ptr;
950
951     for (unsigned int i = 0; i < RESPONSE_LIST_NUMBER; i++)
952     {
953         ptr = & BLEResponseListHeads[i];
954         while (*ptr)
955         {
956             if ((currentTime - (*ptr)->recievedTime) > (MAX_RESPONSE_AGE * mDNSPlatformOneSecond))
957             {
958                 responseList_t * tmp;
959
960                 // Clear the Bloom filter so that it will be removed from any matching response list
961                 // by the following calls.
962                 (*ptr)->peerBloomFilter = 0;
963
964                 LogInfo("removeStaleResponses: clearing stale response from peer MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
965                     (*ptr)->peerMac.b[0], (*ptr)->peerMac.b[1], (*ptr)->peerMac.b[2], (*ptr)->peerMac.b[3], (*ptr)->peerMac.b[4], (*ptr)->peerMac.b[5]);
966
967                 findMatchingBrowse(*ptr);
968                 findMatchingRegistration(*ptr);
969     
970                 // Unlink and free the response structure
971                 tmp = *ptr;
972                 *ptr = (*ptr)->next;
973                 mDNSPlatformMemFree(tmp);
974             }
975             // Move to the next response on this linked list.
976             else
977                 ptr = & (*ptr)->next;
978         }
979     }
980 }
981
982 // Called on CFRunLoop thread during CoreBluetooth beacon response processing.
983 // Thus, must call KQueueLock() prior to calling any core mDNSResponder routines to register records, etc.
984 void responseReceived(serviceHash_t peerBloomFilter, mDNSEthAddr * ptrToMAC)
985 {
986     responseList_t * ptr;
987
988     KQueueLock();
989     mDNS_Lock(& mDNSStorage);   // Must lock to initialize mDNSStorage.timenow
990     
991     ptr = *(findInResponseList(ptrToMAC));
992     if (ptr == 0)
993     {
994         // Only add to list if peer is actively browsing or advertising.
995         if (peerBloomFilter)
996         {
997             LogInfo("responseReceived: First beacon of this type, adding to list");
998             LogInfo("responseReceived: peerBloomFilter = 0x%lx", peerBloomFilter);
999             LogInfo("responseReceived: peer MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
1000                     ptrToMAC->b[0], ptrToMAC->b[1], ptrToMAC->b[2], ptrToMAC->b[3], ptrToMAC->b[4], ptrToMAC->b[5]);
1001
1002             ptr = addToResponseList(peerBloomFilter, ptrToMAC);       
1003             // Update the received time.
1004             ptr->recievedTime =  mDNSStorage.timenow;
1005             // See if we are browsing for any of the peers advertised services.
1006             findMatchingBrowse(ptr);
1007             // See if we have a registration that matches the peer's browse.
1008             findMatchingRegistration(ptr);
1009         }
1010     }
1011     else    // Have an entry from this MAC in the list.
1012     {
1013         // Update the received time.
1014         ptr->recievedTime =  mDNSStorage.timenow;
1015
1016         if (ptr->peerBloomFilter == peerBloomFilter)
1017         {
1018             // A duplicate of a current entry.
1019 #if VERBOSE_BLE_DEBUG
1020             LogInfo("responseReceived: Duplicate of previous beacon, ignoring");
1021             LogInfo("responseReceived: sender MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
1022                     ptrToMAC->b[0], ptrToMAC->b[1], ptrToMAC->b[2], ptrToMAC->b[3], ptrToMAC->b[4], ptrToMAC->b[5]);
1023 #endif // VERBOSE_BLE_DEBUG
1024         }
1025         else
1026         {
1027             LogInfo("responseReceived: Update of previous beacon");
1028             LogInfo("responseReceived: peerBloomFilter = 0x%lx", peerBloomFilter);
1029             LogInfo("responseReceived: sender MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
1030                     ptrToMAC->b[0], ptrToMAC->b[1], ptrToMAC->b[2], ptrToMAC->b[3], ptrToMAC->b[4], ptrToMAC->b[5]);
1031
1032             ptr->peerBloomFilter = peerBloomFilter;
1033             findMatchingBrowse(ptr);
1034             findMatchingRegistration(ptr);
1035         }
1036
1037         // If peer is no longer browsing or advertising, remove from list.
1038         if (peerBloomFilter == 0)
1039         {
1040             LogInfo("responseReceived: Removing peer entry from the list");
1041
1042             removeFromResponseList(ptrToMAC);
1043         }
1044     }
1045
1046     mDNS_Unlock(& mDNSStorage);   // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
1047     KQueueUnlock("BLE responseReceived");
1048 }
1049
1050 #pragma mark - Client request handling
1051
1052 void start_BLE_browse(mDNSInterfaceID InterfaceID, const domainname *const domain, DNS_TypeValues type, DNSServiceFlags flags, mDNSu8 *key, size_t keySize)
1053 {
1054     requestList_t * ptr; 
1055     const domainname *serviceType = domain;
1056
1057     if (!EnableBLEBasedDiscovery)
1058     {
1059         LogMsg("start_BLE_browse: EnableBLEBasedDiscovery disabled");
1060         return;
1061     }
1062
1063     if (keySize > MAX_KEY_SIZE)
1064     {
1065         LogMsg("start_BLE_browse: keySize = %d, maximum allowable is %d", keySize, MAX_KEY_SIZE);
1066         return;
1067     }
1068
1069     // Verify that the request to be auto triggered applies to AWDL, or is using the pseudo interface for
1070     // BLE threshold browsing.
1071     if (!isAutoTriggerRequest(InterfaceID, flags) && (InterfaceID != mDNSInterface_BLE))
1072     {
1073         LogMsg("start_BLE_browse: invalid request: InterfaceID = %d, flags = 0x%x", InterfaceID, flags);
1074         return;
1075     }
1076
1077     // Address records don't have a service type to hash and match on, so pass them directly to the D2D plugin.
1078     if ((type == kDNSType_A) || (type == kDNSType_AAAA))
1079     {
1080         LogInfo("start_BLE_browse: Passing directly to D2D layer: %##s %s", domain->c, DNSTypeName(type));
1081         internal_start_browsing_for_service(InterfaceID, domain, type, flags);
1082         return;
1083     }
1084
1085     // Skip the instance to get to the service type for non PTR records
1086     if (type != kDNSType_PTR)
1087         serviceType = SkipLeadingLabels(domain, 1);
1088
1089     LogInfo("start_BLE_browse: Starting BLE service type browse for: %##s %s", domain->c, DNSTypeName(type));
1090
1091     ptr = addToRequestList(&BLEBrowseListHead, domain, type, flags);
1092
1093     // If equivalent BLE browse is already running, just return.
1094     if (ptr->refCount > 1)
1095     {
1096         LogInfo("start_BLE_browse: Dup of existing BLE browse.");
1097         return;
1098     }
1099
1100     if (!setBLEServiceHash(serviceType, ptr))
1101     {
1102         LogInfo("setBLEServiceHash failed!");
1103         removeFromRequestList(&BLEBrowseListHead, domain, type);
1104         return;
1105     }
1106
1107     // Save these for use in D2D plugin callback logic.
1108     memcpy(ptr->key, key, keySize);
1109     ptr->keySize = keySize;
1110     ptr->InterfaceID = InterfaceID;
1111
1112     mDNS_Lock(& mDNSStorage);   // Must lock to initialize mDNSStorage.timenow.
1113     updateBeaconAndScanState();
1114     mDNS_Unlock(& mDNSStorage); // Updates mDNSStorage.NextScheduledEvent.
1115     checkCachedResponses(ptr, NULL);
1116 }
1117
1118 // Stop the browse.
1119 // Return true if this is the last reference to the browse, false otherwise.
1120 bool stop_BLE_browse(mDNSInterfaceID InterfaceID, const domainname *const domain, DNS_TypeValues type, DNSServiceFlags flags)
1121 {
1122     (void)  flags;   // not used initially
1123     requestList_t * ptr;
1124     bool    lastReference = false;
1125
1126     if (!EnableBLEBasedDiscovery)
1127     {
1128         LogMsg("stop_BLE_browse: EnableBLEBasedDiscovery disabled");
1129         return lastReference;
1130     }
1131
1132     // Address records don't have a service type to hash and match on, so pass them directly to the D2D plugin.
1133     if ((type == kDNSType_A) || (type == kDNSType_AAAA))
1134     {
1135         LogInfo("stop_BLE_browse: Passing directly to D2D layer: %##s %s", domain->c, DNSTypeName(type));
1136         internal_stop_browsing_for_service(InterfaceID, domain, type, flags);
1137         return lastReference;
1138     }
1139
1140     LogInfo("stop_BLE_browse: Stopping BLE service type browse for: %##s %s", domain->c, DNSTypeName(type));
1141
1142     ptr = *(findInRequestList(&BLEBrowseListHead, domain, type));
1143     if (ptr == 0)
1144     {
1145         LogInfo("stop_BLE_browse: No matching browse found.");
1146         return lastReference;
1147     }
1148     
1149     // If this is the last reference for this browse, and it was autoTriggered on AWDL,
1150     // remove the request from the AWDL pluggin.
1151     if (ptr->refCount == 1)
1152     {
1153         lastReference = true;
1154
1155         if (isAutoTriggerRequest(ptr->InterfaceID, ptr->flags) && ptr->triggeredOnAWDL)
1156         {
1157             internal_stop_browsing_for_service(ptr->InterfaceID, & ptr->name, ptr->type, ptr->flags);
1158             ptr->triggeredOnAWDL = false;
1159         }
1160     }
1161
1162     removeFromRequestList(&BLEBrowseListHead, domain, type);
1163
1164     mDNS_Lock(& mDNSStorage);   // Must lock to initialize mDNSStorage.timenow.
1165     if (lastReference)
1166         updateBeaconAndScanState();
1167     mDNS_Unlock(& mDNSStorage); // Updates mDNSStorage.NextScheduledEvent.
1168
1169     // If there are no active browse or registration requests, BLE scanning will be disabled.
1170     // Clear the list of responses received to remove any stale response state.
1171     if (BLEBrowseListHead == NULL && BLERegistrationListHead == 0)
1172         clearResponseLists();
1173
1174     return lastReference;
1175 }
1176
1177 void start_BLE_advertise(const ResourceRecord *const resourceRecord, const domainname *const domain, DNS_TypeValues type, DNSServiceFlags flags)
1178 {
1179     requestList_t * ptr; 
1180     const domainname * serviceType = domain;
1181
1182     if (!EnableBLEBasedDiscovery)
1183     {
1184         LogMsg("start_BLE_advertise: EnableBLEBasedDiscovery disabled");
1185         return;
1186     }
1187
1188     if (resourceRecord == NULL)
1189     {
1190         LogInfo("start_BLE_advertise: NULL resourceRecord for: %##s %s, returning", domain->c, DNSTypeName(type));
1191         return;
1192     }
1193
1194     // Verify that the request to be auto triggered applies to AWDL, or is using the pseudo interface for
1195     // BLE threshold browsing.
1196     if (!isAutoTriggerRequest(resourceRecord->InterfaceID, flags) && (resourceRecord->InterfaceID != mDNSInterface_BLE))
1197     {
1198         LogMsg("start_BLE_advertise: invalid request: InterfaceID = %d, flags = 0x%x", resourceRecord->InterfaceID, flags);
1199         return;
1200     }
1201
1202     LogInfo("start_BLE_advertise: Starting BLE service type advertisement for: %##s %s", domain->c, DNSTypeName(type));
1203
1204     // Skip the instance to get to the service type for non PTR records
1205     if (type != kDNSType_PTR)
1206         serviceType = SkipLeadingLabels(domain, 1);
1207
1208     ptr = addToRequestList(&BLERegistrationListHead, domain, type, flags);
1209
1210     // If equivalent BLE registration is already running, just return.
1211     if (ptr->refCount > 1)
1212     {
1213         LogInfo("start_BLE_advertise: Dup of existing BLE advertisement.");
1214         return;
1215     }
1216
1217     if (!setBLEServiceHash(serviceType, ptr))
1218     {
1219         LogInfo("setBLEServiceHash failed!");
1220         removeFromRequestList(&BLERegistrationListHead, domain, type);
1221         return;
1222     }
1223     ptr->resourceRecord = resourceRecord;
1224     ptr->InterfaceID = resourceRecord->InterfaceID;
1225
1226     mDNS_Lock(& mDNSStorage);   // Must lock to initialize mDNSStorage.timenow.
1227     updateBeaconAndScanState();
1228     mDNS_Unlock(& mDNSStorage); // Updates mDNSStorage.NextScheduledEvent.
1229     checkCachedResponses(NULL, ptr);
1230 }
1231
1232 void stop_BLE_advertise(const domainname *const domain, DNS_TypeValues type, DNSServiceFlags flags)
1233 {
1234     (void)  flags;   // not used initially
1235     requestList_t       * ptr;
1236     bool                lastReference = false;
1237
1238     LogInfo("stop_BLE_advertise: Stopping BLE service type advertisement for: %##s %s", domain->c, DNSTypeName(type));
1239
1240     // Get the request pointer from the indirect pointer returned.
1241     ptr =  *(findInRequestList(&BLERegistrationListHead, domain, type));
1242
1243     if (ptr == 0)
1244     {
1245         LogInfo("stop_BLE_advertise: No matching advertisement found.");
1246         return;
1247     }
1248     
1249     // If this is the last reference for this registration, and it was autoTriggered on AWDL,
1250     // remove the request from the AWDL pluggin.
1251     if (ptr->refCount == 1)
1252     {
1253         lastReference = true;
1254
1255         if (isAutoTriggerRequest(ptr->InterfaceID, ptr->flags) && ptr->triggeredOnAWDL)
1256         {
1257             // And remove the corresponding advertisements from the AWDL D2D plugin if we had previously
1258             // passed this advertisement request to the plugin.
1259             internal_stop_advertising_service(ptr->resourceRecord, (ptr->flags | kDNSServiceFlagsIncludeAWDL));
1260             ptr->triggeredOnAWDL = false;
1261         }
1262     }
1263     removeFromRequestList(&BLERegistrationListHead, domain, type);
1264
1265     mDNS_Lock(& mDNSStorage);   // Must lock to initialize mDNSStorage.timenow.
1266     // If this is the last reference for this registration, update advertising and browsing bits set in the beacon.
1267     if (lastReference)
1268         updateBeaconAndScanState();
1269     mDNS_Unlock(& mDNSStorage); // Updates mDNSStorage.NextScheduledEvent.
1270
1271     // If there are no active browse or registration requests, BLE scanning will be disabled.
1272     // Clear the list of responses received to remove any stale response state.
1273     if (BLEBrowseListHead == NULL && BLERegistrationListHead == 0)
1274         clearResponseLists();
1275 }
1276
1277 #ifdef UNIT_TEST
1278 #pragma mark - Unit test support routines
1279
1280 // These unit test support routines are called from unittests/ framework
1281 // and are not compiled for the mDNSResponder runtime code paths.
1282
1283 #define MAX_ENTRIES 42
1284 #define FAILED      exit(1)
1285
1286 mDNSlocal void BLE_requestListTests(void)
1287 {
1288     const domainname *domainArray[] = { (const domainname*)"\x6" "_test0" "\x4" "_tcp" "\x5" "local",
1289                                         (const domainname*)"\x6" "_test1" "\x4" "_tcp" "\x5" "local",
1290                                         (const domainname*)"\x6" "_test2" "\x4" "_tcp" "\x5" "local",
1291                                         (const domainname*)"\x6" "_test3" "\x4" "_tcp" "\x5" "local",
1292                                         (const domainname*)"\x6" "_test4" "\x4" "_tcp" "\x5" "local",
1293                                       };
1294
1295     mDNSu16         type = kDNSServiceType_PTR;
1296     DNSServiceFlags flags = 0;
1297     requestList_t   * ptr;
1298     void            * response = 0;
1299     int             i;
1300     int             numOfdomains = sizeof(domainArray)/sizeof(domainArray[0]);
1301
1302     printf("BLE_requestListTests() entry:\n");
1303
1304     // Basic request list unit tests.
1305     for (i = 0; i < numOfdomains; i++)
1306     {
1307         ptr = addToRequestList(&BLEBrowseListHead, domainArray[i], type, flags);
1308
1309         if (ptr == NULL)
1310         {
1311             printf("addToRequestList() FAILED:\n");
1312             FAILED;
1313         }
1314     }
1315     for (i = 0; i < numOfdomains; i++)
1316     {
1317         // should now find the entry
1318         if (*(findInRequestList(&BLEBrowseListHead, domainArray[i], type)) == 0)
1319         {
1320             printf("findInRequestList() did not find valid entry FAILED:\n");
1321             FAILED;
1322         }
1323         // but not find an entry with the same domain, but different type
1324         if (*(findInRequestList(&BLEBrowseListHead, domainArray[i], kDNSServiceType_NULL)) != 0)
1325         {
1326             printf("findInRequestList() invalid entry matched FAILED:\n");
1327             FAILED;
1328         }
1329     }
1330     // remove all the entries
1331     for (i = 0; i < numOfdomains; i++)
1332     {
1333         removeFromRequestList(&BLEBrowseListHead, domainArray[i], type);
1334     }
1335     // and sanity check the list is now empty
1336     if (BLEBrowseListHead)
1337     {
1338             printf("BLEBrowseListHead not empty after all entries removed.\n");
1339             FAILED;
1340     }
1341
1342     // Identical request reference count management tests.
1343     // Add identical requests to the list and verify the corresponding refCount is managed correctly
1344     for (i = 0; i < MAX_ENTRIES; i++)
1345     {
1346         ptr = addToRequestList(&BLEBrowseListHead, domainArray[0], type, flags);
1347
1348         if (ptr == NULL)
1349         {
1350             printf("addToRequestList() of duplicate request FAILED:\n");
1351             FAILED;
1352         }
1353     }
1354
1355     if (ptr->refCount != MAX_ENTRIES)
1356     {
1357         printf("refCount = %d, should be %d\n", ptr->refCount, MAX_ENTRIES);
1358         FAILED;
1359     }
1360
1361     // Remove all but one entry
1362     for (i = 0; i < (MAX_ENTRIES - 1); i++)
1363     {
1364         removeFromRequestList(&BLEBrowseListHead, domainArray[0], type);
1365     }
1366     if (ptr->refCount != 1)
1367     {
1368         printf("refCount = %d, should be %d\n", ptr->refCount, 1);
1369         FAILED;
1370     }
1371
1372     // Basic response list unit tests.
1373     // Note that responses per request are not checked for duplicates at this level, so
1374     // we can unit test with the same (NULL) response pointer to add multiple responses.
1375
1376     // add MAX_ENTRIES responses
1377     for (i = 0; i < MAX_ENTRIES; i++)
1378         addToResponseListForRequest(ptr, response);
1379
1380     // remove the responses, counting that MAX_ENTRIES were removed
1381     i = 0;
1382     while (inResponseListForRequest(ptr, response) && removeFromResponseListForRequest(ptr, response))
1383     {
1384         i++;
1385     }
1386     if (i != MAX_ENTRIES)
1387     {
1388         printf("removed %d responses, should have been %d\n", i, MAX_ENTRIES);
1389         FAILED;
1390     }
1391
1392     // response list should be empty at this point
1393     if (ptr->ourResponses)
1394     {
1395         printf("response list should be empty\n");
1396         FAILED;
1397     }
1398
1399     // add MAX_ENTRIES responses
1400     for (i = 0; i < MAX_ENTRIES; i++)
1401         addToResponseListForRequest(ptr, response);
1402
1403     // free them all
1404     freeResponseListEntriesForRequest(ptr);
1405
1406     if (ptr->ourResponses)
1407     {
1408         printf("freeResponseListEntriesForRequest() should have removed all responses\n");
1409         FAILED;
1410     }
1411 }
1412
1413 mDNSlocal mDNSEthAddr etherAddress[] = {
1414     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
1415     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } },
1416     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } },
1417     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 } },
1418     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 } },
1419     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 } },
1420     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06 } },
1421     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x07 } },
1422     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 } },
1423     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x09 } },
1424     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a } },
1425     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b } },
1426     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c } },
1427     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d } },
1428     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e } },
1429     { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f } },
1430 };
1431
1432 mDNSlocal void BLE_responseListTests(void)
1433 {
1434     int             numOfEtherAddresses = sizeof(etherAddress)/sizeof(etherAddress[0]);
1435     int             i;
1436
1437     printf("BLE_responseListTests() entry:\n");
1438
1439     // Just use the index as to generate the peerBloomFilter value to vary it per entry.
1440     for (i = 0; i < numOfEtherAddresses; i++)
1441         (void)addToResponseList(1 << i, &etherAddress[i]);
1442
1443     // Verify all entries are found.
1444     for (i = 0; i < numOfEtherAddresses; i++)
1445     {
1446         if (*(findInResponseList(&etherAddress[i])) == 0)
1447         {
1448             printf("findInResponseList() did not find entry in list\n");
1449             FAILED;
1450         }
1451     }
1452
1453     // Remove all entries.
1454     for (i = 0; i < numOfEtherAddresses; i++)
1455         removeFromResponseList(&etherAddress[i]);
1456         
1457     // Sanity check that all response lists are empty
1458     for (i = 0; i < RESPONSE_LIST_NUMBER; i++)
1459     {
1460         if (BLEResponseListHeads[i])
1461         {
1462             printf("BLEResponseListHeads[%d] not empty after removeFromResponseList() calls \n", i);
1463             FAILED;
1464         }
1465     }
1466
1467     // Add them back again.
1468     for (i = 0; i < numOfEtherAddresses; i++)
1469         (void)addToResponseList(1 << i, &etherAddress[i]);
1470
1471     // And verify that clearResponseLists() clears all entries.
1472     clearResponseLists();
1473     for (i = 0; i < RESPONSE_LIST_NUMBER; i++)
1474     {
1475         if (BLEResponseListHeads[i])
1476         {
1477             printf("BLEResponseListHeads[%d] not empty after clearResponseLists() call\n", i);
1478             FAILED;
1479         }
1480     }
1481 }
1482
1483 void BLE_unitTest(void)
1484 {
1485     BLE_requestListTests();
1486     BLE_responseListTests();
1487     printf("All BLE.c unit tests PASSED.\n");
1488 }
1489
1490 #endif  //  UNIT_TEST
1491
1492 #endif  // ENABLE_BLE_TRIGGERED_BONJOUR