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