]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/BLE.c
mDNSResponder-878.30.4.tar.gz
[apple/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