Home | History | Annotate | Line # | Download | only in mDNSShared
      1 /*
      2  * Copyright (c) 2020-2024 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  *     https://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  * This file contains code to let mDNSResponder set up the resolver for
     17  * the automatic browsing domain learned from "lb._dns-sd._udp.local PTR"
     18  * query. To setup the resolver, it will get the NS record for the domain
     19  * and the A/AAAA record for the resolver name in the NS record rdata.
     20  * Once it knows about resolver's address, it will set the resolver with
     21  * configuration change to let mDNSResponder add it as a regular DNS resolver.
     22  */
     23 
     24 #include "mDNSFeatures.h" // for MDNSRESPONDER_SUPPORTS(COMMON, LOCAL_DNS_RESOLVER_DISCOVERY)
     25 
     26 #if MDNSRESPONDER_SUPPORTS(COMMON, LOCAL_DNS_RESOLVER_DISCOVERY)
     27 
     28 //======================================================================================================================
     29 // MARK: - Headers
     30 
     31 #include <stdio.h>
     32 #include <mdns/private.h>
     33 #include "mDNSEmbeddedAPI.h"
     34 #include "uds_daemon.h"
     35 #include "bsd_queue.h"			// For SLIST.
     36 #include "discover_resolver.h"
     37 #include "tls-keychain.h"		// For init_tls_cert().
     38 #include "uDNS.h"				// For mDNS_StartQuery_internal() and mDNS_StopQuery_internal().
     39 
     40 
     41 #include "DNSCommon.h"			// For mDNS_Lock() and mDNS_Unlock().
     42 
     43 // for require_*
     44 #if defined(__APPLE__)
     45 	#include <AssertMacros.h>
     46 #elif defined(POSIX_BUILD)
     47 	#include "DebugServices.h"
     48 #else
     49 	#ifndef require
     50 		#define require(assertion, exception_label)					\
     51 			do {													\
     52 				if (!(assertion)) {									\
     53 					goto exception_label;							\
     54 				}													\
     55 			} while (false)
     56 	#endif // #ifndef require
     57 
     58 	#ifndef require_action
     59 		#define require_action(assertion, exception_label, action)	\
     60 			do {													\
     61 				if (!(assertion)) {									\
     62 					{												\
     63 						action;										\
     64 					}												\
     65 					goto exception_label;							\
     66 				}													\
     67 			} while (false)
     68 	#endif // #ifndef require_action
     69 #endif
     70 
     71 #include "mdns_strict.h"
     72 
     73 //======================================================================================================================
     74 // MARK: - Macros
     75 
     76 // Release macros that are used to do reference counting for the discover_resolver_t object.
     77 #define discover_resolver_release(OBJ)															\
     78 	do {																						\
     79 		(OBJ)->ref_count--;																		\
     80 		LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "discover_resolver_t released "	\
     81 			"- ref count after releasing: %u.", (OBJ)->ref_count); 								\
     82 		if ((OBJ)->ref_count == 0) {															\
     83 			_MDNS_STRICT_DISPOSE_TEMPLATE((OBJ), (OBJ)->finalizer);								\
     84 		}																						\
     85 	} while (false)
     86 
     87 #define discover_resolver_forget(PTR)			\
     88 	do {										\
     89 		if (*(PTR)) {							\
     90 			discover_resolver_release(*(PTR));	\
     91 			*(PTR) = mDNSNULL;					\
     92 		}										\
     93 	} while (mDNSfalse)
     94 
     95 //======================================================================================================================
     96 // MARK: - Structures
     97 
     98 // the addresse of the discovered resolver get by resolving the resolver's name
     99 typedef struct resolver_address resolver_address_t;
    100 struct resolver_address {
    101 	mDNSAddr addr;
    102 	resolver_address_t * NULLABLE next;
    103 };
    104 
    105 // the name of the discovered resolver for the given domain.
    106 typedef struct discover_resolver_name discover_resolver_name_t;
    107 struct discover_resolver_name {
    108 	domainname resolver_name; // the discovered resolver name for the given domain
    109 	mDNSInterfaceID NULLABLE interface_id; // the interface id where the response was received
    110 	DNSQuestion ipv4_question; // used to query for IPv4 address of the resolver name
    111 	DNSQuestion ipv6_question; // used to query for IPv6 address of the resolver name
    112 
    113 	resolver_address_t * NULLABLE addresses; // linked list of all addresses for the resolver name
    114 	mDNSs32 next_update_time;				// If non-zero, it indicates the next time when we should update the DNS
    115 											// service that has been previously registered
    116 	discover_resolver_name_t * NULLABLE next;
    117 };
    118 
    119 // the context of resolver discovery.
    120 typedef struct discover_resolver_context discover_resolver_context_t;
    121 struct discover_resolver_context {
    122 	DNSQuestion ns_question; // used to query for the resolver name for the given domain
    123 	discover_resolver_name_t * NULLABLE resolver_names; // linked list of all resolver names for the given domain
    124 };
    125 
    126 // This object is used by the caller to start or stop the resolver discovery.
    127 typedef struct discover_resolver discover_resolver_t;
    128 typedef void (* discover_resolver_finalizer_t)(discover_resolver_t * NULLABLE discover_resolver);
    129 struct discover_resolver {
    130 	domainname						domain;			// The domain that is used to discover the local DNS resolver.
    131 	mDNSs32							next_stop_time;	// If non-zero, indicate when the object should be released.
    132 	mDNSu32							ref_count;		// The reference count. If it is zero, the object will be finalized.
    133 	mDNSu32							use_count;		// The use count that indicates how many clients need this resolver
    134 													// discovery.
    135 
    136 	discover_resolver_context_t *	context;		// The context.
    137 	discover_resolver_finalizer_t	finalizer;		// The finalizer that will be called to invalidate any ongoing
    138 													// activity and free the memory associated with this object, when
    139 													// the reference count becomes 0.
    140 };
    141 
    142 // The single-linked node type that contains discover_resolver_t.
    143 typedef struct discover_resolver_node discover_resolver_node_t;
    144 struct discover_resolver_node {
    145 	SLIST_ENTRY(discover_resolver_node) __entries;
    146 	discover_resolver_t * NULLABLE discover_resolver;
    147 };
    148 
    149 // The single-linked list that contains multiple discover_resolver_t objects.
    150 typedef struct discover_resolver_slist discover_resolver_slist_t;
    151 SLIST_HEAD(discover_resolver_slist, discover_resolver_node);
    152 
    153 //======================================================================================================================
    154 // MARK: - Globals
    155 
    156 extern mDNS mDNSStorage;
    157 static discover_resolver_slist_t * g_discover_resolvers = NULL;
    158 
    159 //======================================================================================================================
    160 // MARK: - Forward Declarations
    161 
    162 discover_resolver_t *
    163 discover_resolver_create(const domainname * NONNULL domain);
    164 
    165 static void
    166 discover_resolver_finalize(discover_resolver_t * NULLABLE discover_resolver);
    167 #define MDNS_DISPOSE_DISCOVER_RESOLVER(obj) _MDNS_STRICT_DISPOSE_TEMPLATE(obj, discover_resolver_finalize)
    168 
    169 static discover_resolver_context_t *
    170 discover_resolver_context_create(void);
    171 
    172 static void
    173 discover_resolver_context_dispose(discover_resolver_context_t * NULLABLE context);
    174 #define MDNS_DISPOSE_DISCOVER_RESOLVER_CONTEXT(obj) _MDNS_STRICT_DISPOSE_TEMPLATE(obj, discover_resolver_context_dispose)
    175 
    176 static bool
    177 discover_resolver_start(discover_resolver_context_t * NULLABLE context, const domainname * NONNULL domain);
    178 
    179 void
    180 discover_resolver_stop(discover_resolver_context_t * NULLABLE context);
    181 
    182 static mDNSs32
    183 _discover_resolver_get_next_dns_service_update_time(void);
    184 
    185 static void
    186 _discover_resolver_update_dns_service(void);
    187 
    188 static mDNSs32
    189 _discover_resolver_get_next_unused_resolver_discovery_stop_time(void);
    190 
    191 static void
    192 _discover_resolver_stop_unused_resolver_discovery(void);
    193 
    194 //======================================================================================================================
    195 // MARK: - Functions
    196 
    197 // discover_resolver_slist_t
    198 
    199 static discover_resolver_slist_t * NULLABLE
    200 discover_resolver_slist_create(void)
    201 {
    202 	discover_resolver_slist_t * const me = mdns_calloc(1, sizeof(*me));
    203 	require(me != NULL, exit);
    204 
    205 	SLIST_INIT(me);
    206 
    207 exit:
    208 	return me;
    209 }
    210 
    211 //======================================================================================================================
    212 
    213 static discover_resolver_node_t *
    214 discover_resolver_slist_add_front(discover_resolver_slist_t * const NONNULL me,
    215 								  discover_resolver_t * const NONNULL discover_resolver)
    216 {
    217 	discover_resolver_node_t * const n = mdns_calloc(1, sizeof(*n));
    218 	require(n != NULL, exit);
    219 
    220 	n->discover_resolver = discover_resolver;
    221 	SLIST_INSERT_HEAD(me, n, __entries);
    222 
    223 exit:
    224 	return n;
    225 }
    226 
    227 //======================================================================================================================
    228 
    229 static bool
    230 discover_resolver_slist_empty(const discover_resolver_slist_t * const NONNULL me)
    231 {
    232 	return SLIST_EMPTY(me);
    233 }
    234 
    235 //======================================================================================================================
    236 
    237 static void
    238 discover_resolver_slist_dispose(discover_resolver_slist_t * const NONNULL me)
    239 {
    240 	discover_resolver_node_t * n;
    241 
    242 	while (!SLIST_EMPTY(me)) {
    243 		n = SLIST_FIRST(me);
    244 		SLIST_REMOVE_HEAD(me, __entries);
    245 		mdns_free(n);
    246 	}
    247 
    248 	discover_resolver_slist_t * temp_me = me;
    249 	mdns_free(temp_me);
    250 }
    251 #define MDNS_DISPOSE_DISCOVER_RESOLVER_SLIST(obj) _MDNS_STRICT_DISPOSE_TEMPLATE(obj, discover_resolver_slist_dispose)
    252 
    253 //======================================================================================================================
    254 
    255 // resolver_address_t
    256 
    257 static resolver_address_t *
    258 resolver_address_create(const void * const NONNULL addr_data, mDNSAddr_Type addr_type)
    259 {
    260 	resolver_address_t * resolver_address = NULL;
    261 
    262 	require(addr_type == mDNSAddrType_IPv4|| addr_type == mDNSAddrType_IPv6, exit);
    263 
    264 	resolver_address = mdns_calloc(1, sizeof(*resolver_address));
    265 	require(resolver_address != NULL, exit);
    266 
    267 	if (addr_type == mDNSAddrType_IPv4) {
    268 		memcpy(&resolver_address->addr.ip.v4, addr_data, sizeof(resolver_address->addr.ip.v4));
    269 	} else { // sa_family == mDNSAddrType_IPv6
    270 		memcpy(&resolver_address->addr.ip.v6, addr_data, sizeof(resolver_address->addr.ip.v6));
    271 	}
    272 
    273 	resolver_address->addr.type = addr_type;
    274 
    275 exit:
    276 	return resolver_address;
    277 }
    278 
    279 //======================================================================================================================
    280 
    281 static void
    282 resolver_address_delete(resolver_address_t * NONNULL to_be_deleted)
    283 {
    284 	resolver_address_t * temp = to_be_deleted;
    285 	mdns_free(temp);
    286 }
    287 
    288 //======================================================================================================================
    289 
    290 static resolver_address_t *
    291 resolver_addresses_add(const void * const NONNULL addr_data, const mDNSAddr_Type addr_type,
    292 	resolver_address_t * NULLABLE * const NONNULL out_addresses)
    293 {
    294 	resolver_address_t * resolver_address = resolver_address_create(addr_data, addr_type);
    295 	require(resolver_address != NULL, exit);
    296 
    297 	if (*out_addresses != NULL) {
    298 		resolver_address->next = *out_addresses;
    299 	}
    300 	*out_addresses = resolver_address;
    301 
    302 exit:
    303 	return resolver_address;
    304 }
    305 
    306 //======================================================================================================================
    307 
    308 static bool
    309 resolver_addresses_remove(const void * const NONNULL addr_data, mDNSAddr_Type addr_type,
    310 	resolver_address_t * NULLABLE * const NONNULL out_addresses)
    311 {
    312 	bool succeeded;
    313 
    314 	require_action(addr_type == mDNSAddrType_IPv4 || addr_type == mDNSAddrType_IPv6, exit, succeeded = false);
    315 
    316 	resolver_address_t * prev = NULL;
    317 	resolver_address_t * current = NULL;
    318 	resolver_address_t * next;
    319 
    320 	for (current = *out_addresses; current != NULL; current = next) {
    321 		bool found = false;
    322 		next = current->next;
    323 		if (current->addr.type != addr_type) {
    324 			continue;
    325 		}
    326 		if (addr_type == mDNSAddrType_IPv4) {
    327 			found = (memcmp(&current->addr.ip.v4, addr_data, sizeof(current->addr.ip.v4)) == 0);
    328 		} else { // sa_family == AF_INET6
    329 			found = (memcmp(&current->addr.ip.v6, addr_data, sizeof(current->addr.ip.v6)) == 0);
    330 		}
    331 		if (found) {
    332 			break;
    333 		}
    334 		prev = current;
    335 	}
    336 
    337 	require_action(current != NULL, exit, succeeded = false);
    338 
    339 	if (prev != NULL) {
    340 		prev->next = current->next;
    341 	} else {
    342 		*out_addresses = current->next;
    343 	}
    344 
    345 	resolver_address_delete(current);
    346 
    347 	succeeded = true;
    348 exit:
    349 	return succeeded;
    350 }
    351 
    352 //======================================================================================================================
    353 
    354 static void
    355 resolver_addresses_remove_all(resolver_address_t * const NONNULL addresses)
    356 {
    357 	resolver_address_t * current, * next;
    358 	for (current = addresses; current != NULL; current = next) {
    359 		next = current->next;
    360 		resolver_address_delete(current);
    361 	}
    362 }
    363 
    364 //======================================================================================================================
    365 
    366 static resolver_address_t *
    367 resolver_addresses_find(const void * const NONNULL addr_data, const mDNSAddr_Type addr_type,
    368 	resolver_address_t * const addresses)
    369 {
    370 	resolver_address_t * current = NULL;
    371 
    372 	require(addr_type == mDNSAddrType_IPv4 || addr_type == mDNSAddrType_IPv6, exit);
    373 
    374 	for (current = addresses; current != NULL; current = current->next) {
    375 		bool found = false;
    376 		if (current->addr.type != mDNSAddrType_IPv6) {
    377 			continue;
    378 		}
    379 		if (addr_type == mDNSAddrType_IPv4) {
    380 			found = (memcmp(&current->addr.ip.v4, addr_data, sizeof(current->addr.ip.v4)) == 0);
    381 		} else { // sa_family == AF_INET6
    382 			found = (memcmp(&current->addr.ip.v6, addr_data, sizeof(current->addr.ip.v6)) == 0);
    383 		}
    384 		if (found) {
    385 			break;
    386 		}
    387 	}
    388 
    389 exit:
    390 	return current;
    391 }
    392 
    393 //======================================================================================================================
    394 // discover_resolver_name_t
    395 
    396 static discover_resolver_name_t *
    397 discover_resolver_name_create(const domainname * const NONNULL resolver_name, const mDNSInterfaceID interface_id)
    398 {
    399 	bool succeeded;
    400 	discover_resolver_name_t * resolver = NULL;
    401 
    402 	resolver = mdns_calloc(1, sizeof(*resolver));
    403 	require_action(resolver != NULL, exit, succeeded = false);
    404 
    405 	AssignDomainName(&resolver->resolver_name, resolver_name);
    406 	resolver->interface_id = interface_id;
    407 
    408 	succeeded = true;
    409 exit:
    410 	if (!succeeded) {
    411 		mdns_free(resolver);
    412 	}
    413 	return resolver;
    414 }
    415 
    416 //======================================================================================================================
    417 
    418 static void
    419 discover_resolver_name_delete(discover_resolver_name_t * NONNULL to_be_deleted)
    420 {
    421 	if (to_be_deleted->addresses != NULL) {
    422 		resolver_addresses_remove_all(to_be_deleted->addresses);
    423 	}
    424 	discover_resolver_name_t * NULLABLE temp_to_be_deleted = to_be_deleted;
    425 	mdns_free(temp_to_be_deleted);
    426 }
    427 
    428 //======================================================================================================================
    429 
    430 static discover_resolver_name_t *
    431 discover_resolver_name_add(const domainname * const name, const mDNSInterfaceID interface_id,
    432 		discover_resolver_name_t * NULLABLE * const NONNULL out_resolver_names)
    433 {
    434 	bool succeeded;
    435 	discover_resolver_name_t * new_resolver_name = NULL;
    436 
    437 	new_resolver_name = discover_resolver_name_create(name, interface_id);
    438 	require_action(new_resolver_name != NULL, exit, succeeded = false);
    439 
    440 	memset(&new_resolver_name->ipv4_question, 0, sizeof(new_resolver_name->ipv4_question));
    441 	memset(&new_resolver_name->ipv6_question, 0, sizeof(new_resolver_name->ipv6_question));
    442 
    443 	if (*out_resolver_names != NULL) {
    444 		new_resolver_name->next = *out_resolver_names;
    445 	}
    446 	*out_resolver_names = new_resolver_name;
    447 
    448 	succeeded = true;
    449 exit:
    450 	if (!succeeded) {
    451 		if (new_resolver_name != NULL) {
    452 			discover_resolver_name_delete(new_resolver_name);
    453 		}
    454 	}
    455 	return new_resolver_name;
    456 }
    457 
    458 //======================================================================================================================
    459 
    460 static bool
    461 discover_resolver_name_remove(const domainname * const resolver_name, const mDNSInterfaceID interface_id,
    462 	discover_resolver_name_t * NULLABLE * const NONNULL out_resolver_names)
    463 {
    464 	bool succeeded;
    465 	discover_resolver_name_t * prev = NULL;
    466 	discover_resolver_name_t * current, * next;
    467 
    468 	for (current = *out_resolver_names; current != NULL; current = next) {
    469 		next = current->next;
    470 		if (SameDomainName(&current->resolver_name, resolver_name) && current->interface_id == interface_id) {
    471 			break;
    472 		}
    473 		prev = current;
    474 	}
    475 
    476 	require_action(current != NULL, exit, succeeded = false);
    477 
    478 	if (prev != NULL) {
    479 		prev->next = current->next;
    480 	} else {
    481 		*out_resolver_names = current->next;
    482 	}
    483 	discover_resolver_name_delete(current);
    484 
    485 	succeeded = true;
    486 exit:
    487 	return succeeded;
    488 }
    489 
    490 //======================================================================================================================
    491 
    492 static discover_resolver_name_t *
    493 discover_resolver_name_find(const domainname * const name, mDNSInterfaceID interface_id,
    494 	discover_resolver_name_t * const NULLABLE resolver_names)
    495 {
    496 	discover_resolver_name_t * resolver_name = NULL;
    497 
    498 	for (resolver_name = resolver_names; resolver_name != NULL; resolver_name = resolver_name->next) {
    499 		if (SameDomainName(&resolver_name->resolver_name, name) && resolver_name->interface_id == interface_id) {
    500 			break;
    501 		}
    502 	}
    503 
    504 	return resolver_name;
    505 }
    506 
    507 //======================================================================================================================
    508 
    509 bool
    510 resolver_discovery_add(const domainname * const NONNULL domain_to_discover, const bool grab_mdns_lock)
    511 {
    512 	bool succeeded;
    513 	discover_resolver_node_t * np;
    514 	discover_resolver_t * discover_resolver_to_retain = NULL;
    515 
    516 	// The list has not been initialized.
    517 	if (g_discover_resolvers == NULL) {
    518 		g_discover_resolvers = discover_resolver_slist_create();
    519 		require_action(g_discover_resolvers != NULL, exit, succeeded = false);
    520 	}
    521 
    522 	// Looking for the existing resolver discovery.
    523 	SLIST_FOREACH(np, g_discover_resolvers, __entries) {
    524 		if (!SameDomainName(&np->discover_resolver->domain, domain_to_discover)) {
    525 			continue;
    526 		}
    527 		discover_resolver_to_retain = np->discover_resolver;
    528 		break;
    529 	}
    530 
    531 	// Increase the use count if it exists.
    532 	if (discover_resolver_to_retain != NULL) {
    533 		discover_resolver_to_retain->use_count++;
    534 
    535 		LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "Use the ongoing resolver discovery -- "
    536 			"domain: " PRI_DM_NAME ", use count: %u", DM_NAME_PARAM(&discover_resolver_to_retain->domain),
    537 			discover_resolver_to_retain->use_count);
    538 
    539 		// Clears the stop time in case we are waiting for stopping it.
    540 		discover_resolver_to_retain->next_stop_time = 0;
    541 		discover_resolver_to_retain = NULL;
    542 		succeeded = true;
    543 		goto exit;
    544 	}
    545 
    546 	// Or create and start a new resolver discovery for the given domain.
    547 	discover_resolver_to_retain = discover_resolver_create(domain_to_discover);
    548 	require_action(discover_resolver_to_retain != NULL, exit, succeeded = false);
    549 
    550 	if (grab_mdns_lock) {
    551 		mDNS_Lock(&mDNSStorage);
    552 	}
    553 	succeeded = discover_resolver_start(discover_resolver_to_retain->context, domain_to_discover);
    554 	if (grab_mdns_lock) {
    555 		mDNS_Unlock(&mDNSStorage);
    556 	}
    557 	require(succeeded, exit);
    558 
    559 	// Add the new one to the list.
    560 	np = discover_resolver_slist_add_front(g_discover_resolvers, discover_resolver_to_retain);
    561 	require_action(np != NULL, exit, succeeded = false);
    562 
    563 	discover_resolver_to_retain->use_count = 1;
    564 
    565 	LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "Start new resolver discovery -- "
    566 		"domain: " PRI_DM_NAME ", use count: %u", DM_NAME_PARAM(&discover_resolver_to_retain->domain),
    567 		discover_resolver_to_retain->use_count);
    568 
    569 	discover_resolver_to_retain = NULL;
    570 
    571 exit:
    572 	discover_resolver_forget(&discover_resolver_to_retain);
    573 
    574 	return succeeded;
    575 }
    576 
    577 //======================================================================================================================
    578 
    579 bool
    580 resolver_discovery_remove(const domainname * const NONNULL domain_to_discover, const bool grab_mdns_lock)
    581 {
    582 	bool succeeded = false;
    583 	discover_resolver_node_t *np, *np_temp;
    584 	mDNS *const m = &mDNSStorage;
    585 
    586 	require_action(g_discover_resolvers != NULL, exit, LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_FAULT,
    587 		"Trying to stop a domain resolver discovery that does not exist - domain: " PRI_DM_NAME ".",
    588 		DM_NAME_PARAM(domain_to_discover)));
    589 
    590 	SLIST_FOREACH_SAFE(np, g_discover_resolvers, __entries, np_temp) {
    591 
    592 		discover_resolver_t *const discover_resolver = np->discover_resolver;
    593 		if (!SameDomainName(&discover_resolver->domain, domain_to_discover)) {
    594 			continue;
    595 		}
    596 
    597 		discover_resolver->use_count--;
    598 
    599 		LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "One less resolver discovery use count -- "
    600 			"domain: " PRI_DM_NAME ", use count: %u", DM_NAME_PARAM(&discover_resolver->domain),
    601 			discover_resolver->use_count);
    602 
    603 		if (discover_resolver->use_count == 0) {
    604 			const mDNSs32 gracePeriodInSeconds = 5;
    605 			const mDNSs32 gracePeriodPlatformTime = gracePeriodInSeconds * mDNSPlatformOneSecond;
    606 
    607 			if (grab_mdns_lock) {
    608 				mDNS_Lock(&mDNSStorage);
    609 			}
    610 
    611 			discover_resolver->next_stop_time = NonZeroTime(m->timenow + gracePeriodPlatformTime);
    612 
    613 			if (grab_mdns_lock) {
    614 				mDNS_Unlock(&mDNSStorage);
    615 			}
    616 
    617 			LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, "Planning to stop the resolver discovery -- "
    618 				"domain: " PRI_DM_NAME ", grace period: %ds", DM_NAME_PARAM(&discover_resolver->domain),
    619 				gracePeriodInSeconds);
    620 		}
    621 
    622 		succeeded = true;
    623 		break;
    624 	}
    625 
    626 	// If the entire list is now empty, delete the list.
    627 	if (discover_resolver_slist_empty(g_discover_resolvers)) {
    628 		MDNS_DISPOSE_DISCOVER_RESOLVER_SLIST(g_discover_resolvers);
    629 	}
    630 
    631 exit:
    632 	return succeeded;
    633 }
    634 
    635 //======================================================================================================================
    636 
    637 mDNSs32
    638 resolver_discovery_get_next_scheduled_event(void)
    639 {
    640 	mDNSs32 next_event = 0;
    641 
    642 	// See if we need to update the registered native DNS service.
    643 	const mDNSs32 next_update_time = _discover_resolver_get_next_dns_service_update_time();
    644 	if ((next_update_time != 0) && ((next_event == 0) || (next_update_time - next_event < 0))) {
    645 		next_event = next_update_time;
    646 	}
    647 
    648 	// See if we need to deregister the native DNS service that has not been used for a while.
    649 	const mDNSs32 next_stop_time = _discover_resolver_get_next_unused_resolver_discovery_stop_time();
    650 	if ((next_stop_time != 0) && ((next_event == 0) || (next_stop_time - next_event < 0))) {
    651 		next_event = next_stop_time;
    652 	}
    653 
    654 	return next_event;
    655 }
    656 
    657 //======================================================================================================================
    658 
    659 void
    660 resolver_discovery_perform_periodic_tasks(void)
    661 {
    662 	_discover_resolver_update_dns_service();
    663 	_discover_resolver_stop_unused_resolver_discovery();
    664 }
    665 
    666 //======================================================================================================================
    667 
    668 bool
    669 dns_question_requires_resolver_discovery(const DNSQuestion * NONNULL q, const domainname ** const out_domain)
    670 {
    671 	if (!q->ForceMCast && !IsRootDomain(Do53_UNICAST_DISCOVERY_DOMAIN)
    672 		&& IsSubdomain(&q->qname, Do53_UNICAST_DISCOVERY_DOMAIN)) {
    673 		*out_domain = Do53_UNICAST_DISCOVERY_DOMAIN;
    674 		return true;
    675 	} else {
    676 		*out_domain = NULL;
    677 		return false;
    678 	}
    679 }
    680 
    681 //======================================================================================================================
    682 
    683 discover_resolver_t * NULLABLE
    684 discover_resolver_create(const domainname * const NONNULL domain)
    685 {
    686 	discover_resolver_t * returned_discover_resolver = NULL;
    687 	discover_resolver_context_t * discover_resolver_context = NULL;
    688 
    689 	discover_resolver_t * discover_resolver = mdns_calloc(1, sizeof(*discover_resolver));
    690 	require(discover_resolver != NULL, exit);
    691 
    692 	AssignDomainName(&discover_resolver->domain, domain);
    693 
    694 	discover_resolver_context = discover_resolver_context_create();
    695 	require(discover_resolver_context != NULL, exit);
    696 
    697 	discover_resolver->context = discover_resolver_context;
    698 	discover_resolver_context = NULL;
    699 
    700 	discover_resolver->finalizer = discover_resolver_finalize;
    701 	discover_resolver->ref_count = 1;
    702 
    703 	returned_discover_resolver = discover_resolver;
    704 	discover_resolver = NULL;
    705 exit:
    706 	MDNS_DISPOSE_DISCOVER_RESOLVER_CONTEXT(discover_resolver_context);
    707 	MDNS_DISPOSE_DISCOVER_RESOLVER(discover_resolver);
    708 	return returned_discover_resolver;
    709 }
    710 
    711 //======================================================================================================================
    712 
    713 static void
    714 discover_resolver_finalize(discover_resolver_t * const NULLABLE discover_resolver)
    715 {
    716 	if (discover_resolver == NULL) {
    717 		return;
    718 	}
    719 
    720 	if (discover_resolver->context != NULL) {
    721 		discover_resolver_stop(discover_resolver->context);
    722 		MDNS_DISPOSE_DISCOVER_RESOLVER_CONTEXT(discover_resolver->context);
    723 	}
    724 
    725 	discover_resolver_t * temp_discover_resolver = discover_resolver;
    726 	mdns_free(temp_discover_resolver);
    727 }
    728 
    729 //======================================================================================================================
    730 // discover_resolver_context_t
    731 
    732 static discover_resolver_context_t *
    733 discover_resolver_context_create(void)
    734 {
    735 	discover_resolver_context_t * const context = mdns_calloc(1, sizeof(*context));
    736 
    737 	memset(&context->ns_question, 0, sizeof(context->ns_question));
    738 	context->resolver_names = NULL;
    739 
    740 	return context;
    741 }
    742 
    743 //======================================================================================================================
    744 
    745 static void
    746 discover_resolver_context_dispose(discover_resolver_context_t * const NULLABLE context)
    747 {
    748 	if (context == NULL) {
    749 		return;
    750 	}
    751 
    752 	if (context->resolver_names != NULL) {
    753 		discover_resolver_name_t * current, * next;
    754 		for (current = context->resolver_names; current != NULL; current = next) {
    755 			next = current->next;
    756 			discover_resolver_name_delete(current);
    757 		}
    758 		context->resolver_names = NULL;
    759 	}
    760 	discover_resolver_context_t * temp_context = context;
    761 	mdns_free(temp_context);
    762 }
    763 
    764 //======================================================================================================================
    765 
    766 static void
    767 discover_resolver_setup_question(DNSQuestion * const NONNULL q, mDNSInterfaceID interface_id,
    768 	const domainname * const NONNULL q_name, uint16_t q_type, bool force_multicast,
    769 	mDNSQuestionCallback * const NULLABLE callback, void * const NONNULL context)
    770 {
    771 	q->InterfaceID = interface_id;
    772 	q->flags = force_multicast ? kDNSServiceFlagsForceMulticast : 0;
    773 	AssignDomainName(&q->qname, q_name);
    774 	q->qtype = q_type;
    775 	q->qclass = kDNSClass_IN;
    776 	q->LongLived = false;
    777 	q->ExpectUnique = false;
    778 	q->ForceMCast = force_multicast;
    779 	q->ReturnIntermed = false;
    780 	q->SuppressUnusable = false;
    781 	q->AppendSearchDomains = false;
    782 	q->TimeoutQuestion = 0;
    783 	q->WakeOnResolve = 0;
    784 	q->UseBackgroundTraffic = false;
    785 	q->pid = mDNSPlatformGetPID();
    786 	q->euid = 0;
    787 	q->QuestionCallback = callback;
    788 	q->QuestionContext = context;
    789 }
    790 
    791 
    792 //======================================================================================================================
    793 
    794 static void
    795 _schedule_dns_service_update(discover_resolver_name_t * const resolver_name)
    796 {
    797 	mDNS * const m = &mDNSStorage;
    798 	mDNS_Lock(m);
    799 	const mDNSs32 time_now = m->timenow;
    800 	mDNS_Unlock(m);
    801 
    802 	// Wait for 0.005s before updating the DNS service.
    803 	const mDNSs32 update_pending_time = mDNSPlatformOneSecond / 200;
    804 	resolver_name->next_update_time = NonZeroTime(time_now + update_pending_time);
    805 }
    806 
    807 //======================================================================================================================
    808 
    809 MDNS_CLOSED_ENUM(_resolver_dns_service_update_result_t, int8_t,
    810 	_resolver_dns_service_update_result_error				= -1,
    811 	_resolver_dns_service_update_result_no_change			= 0,
    812 	_resolver_dns_service_update_result_newly_registered	= 1,
    813 	_resolver_dns_service_update_result_updated				= 2,
    814 	_resolver_dns_service_update_result_deregistered		= 3
    815 );
    816 
    817 static _resolver_dns_service_update_result_t
    818 _native_dns_service_update(const domainname * const domain, discover_resolver_name_t * const resolver_name)
    819 {
    820 	(void)domain;
    821 	(void)resolver_name;
    822 	return _resolver_dns_service_update_result_no_change;
    823 }
    824 
    825 //======================================================================================================================
    826 
    827 static bool
    828 native_dns_service_deregister(discover_resolver_name_t * const NONNULL resolver_name)
    829 {
    830 	(void) resolver_name;
    831 	return false;
    832 }
    833 
    834 //======================================================================================================================
    835 
    836 static void
    837 discover_resolver_addr_query_callback(mDNS * const NONNULL m, DNSQuestion * const NONNULL q,
    838 	const ResourceRecord * const NONNULL answer, const QC_result change_event)
    839 {
    840 	discover_resolver_context_t * context = q->QuestionContext;
    841 	discover_resolver_name_t * resolver_name = NULL;
    842 	const void * const addr_data = answer->rdata->u.data;
    843 	const mDNSAddr_Type addr_type = answer->rrtype == kDNSType_A ? mDNSAddrType_IPv4 : mDNSAddrType_IPv6;
    844 
    845 	mDNS_Lock(m);
    846 	char if_name[64]; // The same size as the ((NetworkInterfaceInfo *)0)->ifname).
    847 	mdns_compile_time_check_local(sizeof(if_name) == sizeof(((NetworkInterfaceInfo *)0)->ifname));
    848 
    849 	const char * const if_name_ptr = InterfaceNameForID(m, answer->InterfaceID);
    850 	if (if_name_ptr) {
    851 		mDNSPlatformStrLCopy(if_name, if_name_ptr, sizeof(if_name));
    852 	} else {
    853 		mDNS_snprintf(if_name, sizeof(if_name), "<ID: %u>", IIDPrintable(answer->InterfaceID));
    854 	}
    855 	mDNS_Unlock(m);
    856 
    857 	bool address_add;
    858 	mDNSAddr addr_changed;
    859 
    860 	mdns_require_quiet(change_event == QC_add ||change_event == QC_rmv, exit);
    861 	mdns_require_quiet(answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA, exit);
    862 	mdns_require_quiet(q->InterfaceID == answer->InterfaceID, exit);
    863 
    864 	// Find the corresponding discover_resolver_name_t that starts this address query.
    865 	resolver_name = discover_resolver_name_find(&q->qname, q->InterfaceID, context->resolver_names);
    866 	mdns_require_quiet(resolver_name, exit);
    867 
    868 	// Try to find if there is existing address in the list.
    869 	resolver_address_t * resolver_addr = resolver_addresses_find(addr_data, addr_type, resolver_name->addresses);
    870 
    871 	if (change_event == QC_add) {
    872 		// Should have no duplicate addresses for the QC_add event.
    873 		mdns_require_quiet(resolver_addr == NULL, exit);
    874 
    875 		// Add the address into list.
    876 		resolver_addr = resolver_addresses_add(addr_data, addr_type, &resolver_name->addresses);
    877 		mdns_require_quiet(resolver_addr != NULL, exit);
    878 		memcpy(&addr_changed, &resolver_addr->addr, sizeof(addr_changed));
    879 
    880 		address_add = true;
    881 	} else {
    882 		// Should be the added address in the list and should not be removed twice.
    883 		mdns_require_quiet(resolver_addr != NULL, exit);
    884 		// Should already have configured resolver.
    885 
    886 		memcpy(&addr_changed, &resolver_addr->addr, sizeof(addr_changed));
    887 
    888 		// Remove the address from the list.
    889 		resolver_addresses_remove(addr_data, addr_type, &resolver_name->addresses);
    890 
    891 		address_add = false;
    892 	}
    893 
    894 	// Schedule new DNS configuration update.
    895 	_schedule_dns_service_update(resolver_name);
    896 
    897 	LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "[Q%u] Resolver " PUB_ADD_RMV " - "
    898 		"browsing domain: " PRI_DM_NAME ", resolver name: " PRI_DM_NAME ", address: " PRI_IP_ADDR
    899 		", interface: " PUB_S ".", mDNSVal16(q->TargetQID), ADD_RMV_PARAM(address_add),
    900 		DM_NAME_PARAM(&context->ns_question.qname), DM_NAME_PARAM(&q->qname), &addr_changed, if_name);
    901 
    902 exit:
    903 	return;
    904 }
    905 
    906 //======================================================================================================================
    907 
    908 static bool
    909 discover_resolver_start_addr_query(discover_resolver_context_t * const NONNULL context,
    910 	discover_resolver_name_t * const NONNULL resolver_name, const domainname * const name)
    911 {
    912 	bool succeeded;
    913 
    914 	DNSQuestion * const ipv4 = &resolver_name->ipv4_question;
    915 	DNSQuestion * const ipv6 = &resolver_name->ipv6_question;
    916 
    917 	// Send address queries as normal
    918 	discover_resolver_setup_question(ipv4, resolver_name->interface_id, name, kDNSType_A, false,
    919 		discover_resolver_addr_query_callback, context);
    920 	discover_resolver_setup_question(ipv6, resolver_name->interface_id, name, kDNSType_AAAA, false,
    921 		discover_resolver_addr_query_callback, context);
    922 
    923 	// discover_resolver_start_addr_query() is called as a callback, therefore, mDNSCore lock is not held.
    924 	// mDNS_StartQuery() must be used here instead of mDNS_StartQuery_internal().
    925 	mStatus mdns_err = mDNS_StartQuery(&mDNSStorage, ipv4);
    926 	require_action(mdns_err == mStatus_NoError, exit, succeeded = false);
    927 
    928 	mdns_err = mDNS_StartQuery(&mDNSStorage, ipv6);
    929 	require_action(mdns_err == mStatus_NoError, exit, succeeded = false);
    930 
    931 	LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "Starting A/AAAA queries for resolver - name: " PRI_DM_NAME,
    932 		DM_NAME_PARAM(name));
    933 
    934 	succeeded = true;
    935 exit:
    936 	return succeeded;
    937 }
    938 
    939 static bool
    940 discover_resolver_stop_addr_query(discover_resolver_name_t * const NONNULL resolver_name)
    941 {
    942 	bool succeeded = true;
    943 
    944 	// Remove the DNS config added by the resolved addresses.
    945 	succeeded = native_dns_service_deregister(resolver_name);
    946 
    947 	// Stop the address queries.
    948 	// discover_resolver_stop_addr_query() is called with lock held, and the two mDNS_StopQuery_internal() below are
    949 	// used to stop the two queries started by mDNS_StartQuery() in discover_resolver_start_addr_query().
    950 	mStatus ipv4_err = mDNS_StopQuery_internal(&mDNSStorage, &resolver_name->ipv4_question);
    951 	mStatus ipv6_err = mDNS_StopQuery_internal(&mDNSStorage, &resolver_name->ipv6_question);
    952 	if (ipv4_err != mStatus_NoError || ipv6_err != mStatus_NoError) {
    953 		succeeded = false;
    954 	}
    955 
    956 	return succeeded;
    957 }
    958 
    959 //======================================================================================================================
    960 
    961 static void
    962 discover_resolver_ns_query_callback(mDNS * const NONNULL UNUSED m, DNSQuestion * const NONNULL q,
    963 	const ResourceRecord * const NONNULL answer, const QC_result change_event)
    964 {
    965 	bool succeeded;
    966 	bool new_resolver_name_created = false;
    967 	discover_resolver_context_t * context = q->QuestionContext;
    968 	const mDNSInterfaceID if_id = answer->InterfaceID;
    969 	const char * const if_name = InterfaceNameForID(&mDNSStorage, if_id);
    970 
    971 	require_action(change_event == QC_add || change_event == QC_rmv, exit, succeeded = false);
    972 
    973 	if (if_id == mDNSInterface_LocalOnly) {
    974 		succeeded = true;
    975 		goto exit;
    976 	}
    977 
    978 	// Find out the corresponding discover_resolver_name_t for resolver name, if exists.
    979 	const domainname * const name = &answer->rdata->u.name;
    980 	discover_resolver_name_t * resolver_name = discover_resolver_name_find(name, if_id, context->resolver_names);
    981 
    982 	if (change_event == QC_add) {
    983 		// If there is existing discover_resolver_name_t, do not start duplicate query.
    984 		if (resolver_name != NULL) {
    985 			succeeded = true;
    986 			goto exit;
    987 		}
    988 
    989 		// Add current name into list.
    990 		resolver_name = discover_resolver_name_add(name, if_id, &context->resolver_names);
    991 		require_action(resolver_name != NULL, exit, succeeded = false);
    992 		new_resolver_name_created = true;
    993 
    994 		LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "Name server found for the browsing domain - "
    995 			PRI_DM_NAME " -NS-> " PRI_DM_NAME ", interface: " PUB_S, DM_NAME_PARAM(&q->qname), DM_NAME_PARAM(name),
    996 			if_name ? if_name : "any");
    997 
    998 		// Start to resolve the resolver name.
    999 		succeeded = discover_resolver_start_addr_query(context, resolver_name, name);
   1000 		require_action(succeeded, exit, false);
   1001 	} else { // change_event == QC_rmv
   1002 		// Should have the added discover_resolver_name_t
   1003 		require_action(resolver_name != NULL, exit, succeeded = false);
   1004 
   1005 		LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "Name server disappeared for the browsing domain - "
   1006 			PRI_DM_NAME " -NS-> " PRI_DM_NAME ", interface: " PUB_S, DM_NAME_PARAM(&q->qname), DM_NAME_PARAM(name),
   1007 			if_name ? if_name : "any");
   1008 
   1009 		// Stop all the address queries started by this resolver name.
   1010 		// Since discover_resolver_ns_query_callback() is always called as a callback function without mDNS_Lock held,
   1011 		// we need to grab the lock explicitly.
   1012 		mDNS_Lock(&mDNSStorage);
   1013 		discover_resolver_stop_addr_query(resolver_name);
   1014 		mDNS_Unlock(&mDNSStorage);
   1015 
   1016 		// Remove the name from the list
   1017 		discover_resolver_name_remove(name, if_id, &context->resolver_names);
   1018 	}
   1019 
   1020 	succeeded = true;
   1021 exit:
   1022 	if (!succeeded) {
   1023 		if (new_resolver_name_created) {
   1024 			discover_resolver_name_remove(name, if_id, &context->resolver_names);
   1025 		}
   1026 	}
   1027 	return;
   1028 }
   1029 
   1030 //======================================================================================================================
   1031 
   1032 static bool
   1033 discover_resolver_start_ns_query(discover_resolver_context_t * const NONNULL context,
   1034 	const domainname * const NONNULL domain)
   1035 {
   1036 	bool succeeded;
   1037 
   1038 	// Start NS query with kDNSServiceFlagsForceMulticast.
   1039 	DNSQuestion * const q = &context->ns_question;
   1040 
   1041 	discover_resolver_setup_question(q, mDNSInterface_Any, domain, kDNSType_NS, true,
   1042 		discover_resolver_ns_query_callback, context);
   1043 
   1044 	LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "Sending NS query to discover name server - "
   1045 		"browsing domain: " PRI_DM_NAME, DM_NAME_PARAM(domain));
   1046 
   1047 	// discover_resolver_start_ns_query() is called with lock held, therefore, mDNS_StartQuery_internal() must be used
   1048 	// here instead of mDNS_StartQuery().
   1049 	mStatus err = mDNS_StartQuery_internal(&mDNSStorage, q);
   1050 	require_action(err == mStatus_NoError, exit, succeeded = false);
   1051 
   1052 	succeeded = true;
   1053 exit:
   1054 	return succeeded;
   1055 }
   1056 
   1057 //======================================================================================================================
   1058 
   1059 static void
   1060 discover_resolver_stop_ns_query(discover_resolver_context_t * const NONNULL context)
   1061 {
   1062 	// Stop all address subqueries.
   1063 	for (discover_resolver_name_t * current = context->resolver_names; current != NULL; current = current->next) {
   1064 		discover_resolver_stop_addr_query(current);
   1065 	}
   1066 
   1067 	// Stop the original NS query.
   1068 	mDNS_StopQuery_internal(&mDNSStorage, &context->ns_question);
   1069 }
   1070 
   1071 //======================================================================================================================
   1072 
   1073 static bool
   1074 discover_resolver_start(discover_resolver_context_t * const NULLABLE context,
   1075 	const domainname * const NONNULL domain)
   1076 {
   1077 	bool succeeded;
   1078 	require_action(context != NULL, exit, succeeded = false);
   1079 
   1080 	LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "Starting the resolver discovery for domain - "
   1081 		"domain: " PRI_DM_NAME, DM_NAME_PARAM(domain));
   1082 	// Start NS query with kDNSServiceFlagsForceMulticast to learn about the browsing domain with mDNS.
   1083 	succeeded = discover_resolver_start_ns_query(context, domain);
   1084 
   1085 exit:
   1086 	return succeeded;
   1087 }
   1088 
   1089 //======================================================================================================================
   1090 
   1091 void
   1092 discover_resolver_stop(discover_resolver_context_t * const NULLABLE context)
   1093 {
   1094 	if (context == NULL) {
   1095 		return;
   1096 	}
   1097 	discover_resolver_stop_ns_query(context);
   1098 }
   1099 
   1100 //======================================================================================================================
   1101 
   1102 static mDNSs32
   1103 _discover_resolver_get_next_dns_service_update_time(void)
   1104 {
   1105 	mDNSs32 next_time = 0;
   1106 	mdns_require_quiet(g_discover_resolvers, exit);
   1107 
   1108 	discover_resolver_node_t *np, *np_temp;
   1109 	SLIST_FOREACH_SAFE(np, g_discover_resolvers, __entries, np_temp) {
   1110 		const discover_resolver_t *const discover_resolver = np->discover_resolver;
   1111 
   1112 		if (discover_resolver == mDNSNULL || discover_resolver->context == mDNSNULL ||
   1113 			discover_resolver->context->resolver_names == mDNSNULL) {
   1114 			continue;
   1115 		}
   1116 
   1117 		const discover_resolver_name_t * const resolver_name = discover_resolver->context->resolver_names;
   1118 		const mDNSs32 next_update_time = resolver_name->next_update_time;
   1119 		if (next_update_time == 0) {
   1120 			continue;
   1121 		}
   1122 
   1123 		if ((next_time == 0) || ((next_update_time - next_time) < 0)) {
   1124 			next_time = next_update_time;
   1125 		}
   1126 	}
   1127 
   1128 exit:
   1129 	return next_time;
   1130 }
   1131 
   1132 //======================================================================================================================
   1133 
   1134 static void
   1135 _discover_resolver_update_dns_service(void)
   1136 {
   1137 	const mDNSs32 time_now = mDNSStorage.timenow;
   1138 	mdns_require_return(g_discover_resolvers);
   1139 
   1140 	discover_resolver_node_t *np, *np_temp;
   1141 	SLIST_FOREACH_SAFE(np, g_discover_resolvers, __entries, np_temp) {
   1142 		discover_resolver_t *const discover_resolver = np->discover_resolver;
   1143 
   1144 		if (discover_resolver == mDNSNULL || discover_resolver->context == mDNSNULL ||
   1145 			discover_resolver->context->resolver_names == mDNSNULL) {
   1146 			continue;
   1147 		}
   1148 
   1149 		discover_resolver_name_t * const resolver_name = discover_resolver->context->resolver_names;
   1150 		const mDNSs32 next_update_time = resolver_name->next_update_time;
   1151 		if ((next_update_time == 0) || ((next_update_time - time_now) > 0)) {
   1152 			continue;
   1153 		}
   1154 
   1155 		const _resolver_dns_service_update_result_t err = _native_dns_service_update(&discover_resolver->domain,
   1156 			resolver_name);
   1157 
   1158 		LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "Discovered local resolver configuration updated"
   1159 			" - name: " PRI_DM_NAME ", result: %d", DM_NAME_PARAM(&discover_resolver->domain), err);
   1160 
   1161 		resolver_name->next_update_time = 0;
   1162 	}
   1163 }
   1164 
   1165 //======================================================================================================================
   1166 
   1167 static mDNSs32
   1168 _discover_resolver_get_next_unused_resolver_discovery_stop_time(void)
   1169 {
   1170 	mDNSs32 next_time = 0;
   1171 	mdns_require_quiet(g_discover_resolvers, exit);
   1172 
   1173 	// Find the next stop time that is closest to us.
   1174 	discover_resolver_node_t *np, *np_temp;
   1175 	SLIST_FOREACH_SAFE(np, g_discover_resolvers, __entries, np_temp) {
   1176 		const discover_resolver_t *const discover_resolver = np->discover_resolver;
   1177 		if (discover_resolver->use_count != 0) {
   1178 			continue;
   1179 		}
   1180 
   1181 		const mDNSs32 next_stop_time = discover_resolver->next_stop_time;
   1182 		if (next_stop_time == 0) {
   1183 			continue;
   1184 		}
   1185 
   1186 		if ((next_time == 0) || (next_time - next_stop_time > 0)) {
   1187 			next_time = next_stop_time;
   1188 		}
   1189 	}
   1190 
   1191 exit:
   1192 	return next_time;
   1193 }
   1194 
   1195 //======================================================================================================================
   1196 
   1197 static void
   1198 _discover_resolver_stop_unused_resolver_discovery(void)
   1199 {
   1200 	const mDNSs32 time_now = mDNSStorage.timenow;
   1201 	mdns_require_return(g_discover_resolvers);
   1202 
   1203 	discover_resolver_node_t *np, *np_temp;
   1204 	SLIST_FOREACH_SAFE(np, g_discover_resolvers, __entries, np_temp) {
   1205 		if (np->discover_resolver->use_count != 0) {
   1206 			continue;
   1207 		}
   1208 
   1209 		const mDNSs32 next_stop_time = np->discover_resolver->next_stop_time;
   1210 		if ((next_stop_time == 0) || (time_now - next_stop_time < 0)) {
   1211 			continue;
   1212 		}
   1213 
   1214 		// Now (next_stop_time <= time_now) indicates that we have passed the next_stop_time.
   1215 		// Therefore, it is time to cancel the resolver discovery.
   1216 		LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, "Stopping the resolver discovery -- "
   1217 			"domain: " PRI_DM_NAME, DM_NAME_PARAM(&np->discover_resolver->domain));
   1218 
   1219 		discover_resolver_forget(&np->discover_resolver);
   1220 		SLIST_REMOVE(g_discover_resolvers, np, discover_resolver_node, __entries);
   1221 		mdns_free(np);
   1222 	}
   1223 }
   1224 
   1225 #else // MDNSRESPONDER_SUPPORTS(COMMON, LOCAL_DNS_RESOLVER_DISCOVERY)
   1226 
   1227 // iso C requires a translation unit to contain at least one declaration
   1228 typedef int __make_iso_c_happy_about_no_declaration;
   1229 
   1230 #endif // MDNSRESPONDER_SUPPORTS(COMMON, LOCAL_DNS_RESOLVER_DISCOVERY)
   1231