blob: 6fc40aa59720e12fd386316d97f5e1709fd28cb4 [file] [log] [blame] [edit]
/*
* Copyright (c) 2022-2024 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mDNSFeatures.h"
#if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PUSH)
//======================================================================================================================
// MARK: - Headers
#include "dns_push_discovery.h"
#include "discover_resolver.h"
#include "dns_push_obj_dns_question_member.h"
#include "dns_obj_log.h"
#include "dns_obj_rr.h"
#include "dns_obj_rr_srv.h"
#include "dns_push_obj_context.h"
#include "dns_push_mdns_core.h"
#include "dns_common.h"
#include "DNSCommon.h"
#include "mDNSMacOSX.h"
#include "QuerierSupport.h"
#include "uDNS.h"
#include "dns_assert_macros.h"
#include "mdns_strict.h"
//======================================================================================================================
// MARK: - Local Structures
typedef struct interface_monitor_list_s interface_monitor_list_s;
struct interface_monitor_list_s {
mdns_interface_monitor_t monitor; // The interface monitor object.
size_t use_count; // The number active push questions that use this interface monitor.
interface_monitor_list_s *next; // The next monitor in the list.
};
static interface_monitor_list_s *g_interface_monitors; // The global interface monitor list.
extern mDNS mDNSStorage;
//======================================================================================================================
// MARK: - Local Prototypes
static dispatch_queue_t
_dns_push_discovery_interface_monitor_queue(void);
static DNSQuestion *
_dns_push_discovery_start_soa(mDNS *m, const DNSQuestion *question, dns_push_obj_context_t context);
static DNSQuestion *
_dns_push_discovery_start_srv(mDNS *m, mDNSInterfaceID if_id, dns_push_obj_context_t context);
static void
_dns_push_discovery_stop(mDNS *m, DNSQuestion **q_pptr, dns_push_obj_context_t context, bool hold_lock);
static void
_dns_push_discovery_soa_result_reply(mDNS *m, DNSQuestion *question, const ResourceRecord *answer, QC_result event);
static void
_dns_push_discovery_srv_result_reply(mDNS *m, DNSQuestion *question, const ResourceRecord *answer, QC_result event);
static void
_dns_push_discovery_fallback_to_dns_polling(dns_push_obj_context_t context);
static void
_dns_push_discovery_stop_dns_polling(dns_push_obj_context_t context);
static void
_dns_push_discovery_try_next_qname(mDNS *m, DNSQuestion *question, dns_obj_domain_name_t new_q_name);
static mdns_interface_monitor_t
_dns_push_discovery_start_interface_monitor(uint32_t if_index);
static mdns_interface_monitor_t
_dns_push_discovery_start_mdns_interface_monitor(uint32_t if_index);
static void
_dns_push_discovery_process_interface_changes(mDNS *m, mdns_interface_monitor_t monitor);
static void
_dns_push_discovery_stop_interface_monitor(mdns_interface_monitor_t monitor);
static mdns_dns_service_id_t
_dns_push_discovery_register_push_service(dns_push_obj_context_t context, dns_obj_domain_name_t name,
uint32_t if_index, bool *out_duplicate, dns_obj_error_t *out_error);
static void
_dns_push_discovery_deregister_push_service(dns_push_obj_context_t context);
//======================================================================================================================
// MARK: - Public Functions
dns_obj_error_t
dns_push_handle_question_start(mDNS * const m, DNSQuestion * const question)
{
dns_obj_error_t err;
dns_push_obj_dns_question_member_t dns_question_member = NULL;
dns_push_obj_context_t context = NULL;
mdns_require_action_quiet(!question->enableDNSSEC, exit, err = DNS_OBJ_ERROR_PARAM_ERR);
dns_question_member = dns_push_obj_dns_question_member_create(&err);
mdns_require_action_quiet(dns_question_member != NULL, exit, err = DNS_OBJ_ERROR_NO_MEMORY);
dns_push_obj_replace(&question->dns_push, dns_question_member);
question->LongLived = false; // Disable old DNS push code.
context = dns_push_obj_context_create(question, &err);
mdns_require_noerr_quiet(err, exit);
dns_push_obj_dns_question_member_set_context(dns_question_member, context);
DNSQuestion * const soa_q = _dns_push_discovery_start_soa(m, question, context);
mdns_require_action_quiet(soa_q, exit, err = DNS_OBJ_ERROR_NO_RESOURCES);
dns_push_obj_context_set_discovery_question(context, soa_q);
err = DNS_OBJ_ERROR_NO_ERROR;
exit:
dns_push_obj_forget(&dns_question_member);
dns_push_obj_forget(&context);
if (err) {
dns_push_handle_question_stop(m, question);
}
return err;
}
//======================================================================================================================
void
dns_push_handle_question_stop(mDNS * const m, DNSQuestion * const question)
{
const dns_push_obj_context_t context = dns_question_get_dns_push_context(question);
mdns_require_return(context);
// Stop the SOA or SRV question that is started to determine the authoritative zone and the push capability.
DNSQuestion *discovery_question = dns_push_obj_context_get_discovery_question(context);
if (discovery_question) {
_dns_push_discovery_stop(m, &discovery_question, context, false);
dns_push_obj_context_set_discovery_question(context, NULL);
}
_dns_push_discovery_deregister_push_service(context);
const mdns_interface_monitor_t monitor = dns_push_obj_context_get_interface_monitor(context);
if (monitor) {
_dns_push_discovery_stop_interface_monitor(monitor);
dns_push_obj_context_set_interface_monitor(context, NULL);
}
dns_push_obj_forget(&question->dns_push);
question->LongLived = mDNStrue;
}
//======================================================================================================================
// MARK: - Private Functions
static dispatch_queue_t
_dns_push_discovery_interface_monitor_queue(void)
{
static dispatch_once_t s_once = 0;
static dispatch_queue_t s_queue = NULL;
dispatch_once(&s_once,
^{
s_queue = dispatch_queue_create("com.apple.dns-push.interface-monitor", DISPATCH_QUEUE_SERIAL);
});
return s_queue;
}
static DNSQuestion *
_dns_push_discovery_start_soa(mDNS * const m, const DNSQuestion * const q, const dns_push_obj_context_t context)
{
DNSQuestion *soa_q = NULL;
bool failed = false;
soa_q = mdns_calloc(1, sizeof(*soa_q));
mdns_require_quiet(soa_q, exit);
// Set up a new SOA question for the original question name.
soa_q->InterfaceID = q->InterfaceID;
AssignDomainName(&soa_q->qname, &q->qname);
soa_q->qtype = kDNSServiceType_SOA;
soa_q->qclass = q->qclass;
soa_q->pid = mDNSPlatformGetPID();
soa_q->QuestionCallback = _dns_push_discovery_soa_result_reply;
soa_q->QuestionContext = context;
soa_q->ReturnIntermed = mDNStrue;
// Must not set soa_q->LongLived to true because we are in the process of preparing DNS push. Setting it to true
// will cause infinite recursive call.
soa_q->LongLived = mDNSfalse;
const mStatus q_start_error = mDNS_StartQuery_internal(m, soa_q);
mdns_require_noerr_action_quiet(q_start_error, exit, failed = true);
exit:
if (failed) {
mdns_free(soa_q);
}
return soa_q;
}
//======================================================================================================================
static void
_dns_push_discovery_soa_result_reply(mDNS * const m, DNSQuestion *question, const ResourceRecord * const answer,
const QC_result event)
{
dns_obj_error_t err;
dns_obj_domain_name_t zone_cut = NULL;
dns_obj_domain_name_t next_qname = NULL;
bool stop_discovery = true;
const dns_push_obj_context_t context = ((dns_push_obj_context_t)question->QuestionContext);
// We only handle the add event, for other events we ignore them, for example:
// When the query is suppressed due to no DNS service, we return directly and wait for the DNS service being
// configured by resolver discovery.
if (event != QC_add) {
stop_discovery = false;
goto exit;
}
if (answer->RecordType == kDNSRecordTypePacketNegative) {
// No Data or NXDomain.
// The current name is not an authoritative zone, striping one label off and try the new name (Finished
// below).
} else {
// Positive SOA.
mdns_require_quiet(answer->rrtype == kDNSRecordType_SOA, exit);
zone_cut = dns_obj_domain_name_create_with_labels(answer->name->c, true, &err);
mdns_require_noerr_quiet(err, exit);
}
if (zone_cut != NULL) {
// Have found the zone cut.
dns_push_obj_context_set_authoritative_zone(context, zone_cut);
// Stop SOA query.
_dns_push_discovery_stop(m, &question, context, true);
// Do not touch `question` because question has been freed by `_dns_push_discovery_stop` above.
stop_discovery = false;
mDNS_Lock(m);
const uint32_t if_index = mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, true);
mDNS_Unlock(m);
dns_push_obj_context_set_interface_index(context, if_index);
// Query for the push capability of the server.
DNSQuestion * const srv_question = _dns_push_discovery_start_srv(m, answer->InterfaceID, context);
mdns_require_action_quiet(srv_question, exit, err = DNS_OBJ_ERROR_NO_RESOURCES);
// We have found the zone cut, continue the SRV probing for discovery.
dns_push_obj_context_set_discovery_question(context, srv_question);
} else {
// Have not found the zone cut, try its parent label until root label.
mdns_require_quiet(!IsRootDomain(&question->qname), exit);
const uint8_t * const next_qname_labels = (SecondLabel(&question->qname)->c);
next_qname = dns_obj_domain_name_create_with_labels(next_qname_labels, true, &err);
mdns_require_noerr_quiet(err, exit);
_dns_push_discovery_try_next_qname(m, question, next_qname);
stop_discovery = false;
}
exit:
dns_obj_forget(&zone_cut);
dns_obj_forget(&next_qname);
if (stop_discovery) {
_dns_push_discovery_stop(m, &question, context, true);
}
}
//======================================================================================================================
static void
_dns_push_discovery_srv_result_reply(mDNS * const m, DNSQuestion *question, const ResourceRecord * const answer,
const QC_result event)
{
dns_obj_error_t err;
const dns_push_obj_context_t context = ((dns_push_obj_context_t)question->QuestionContext);
DNSQuestion * const original_q = dns_push_obj_context_get_original_question(context);
const mDNSu16 qid = mDNSVal16(original_q->TargetQID);
mdns_require_quiet(event == QC_add, exit);
if (answer->RecordType == kDNSRecordTypePacketNegative) {
// No Data or NXDomain, the server does not supports DNS push for now, but keep monitoring changes in case the
// network supports it.
log_default("[Q%u] Current network does not support DNS push, falling back to DNS polling -- service ID: %llu",
qid, mdns_dns_service_get_id(question->dnsservice));
_dns_push_discovery_fallback_to_dns_polling(context);
goto exit;
}
const dns_obj_domain_name_t authoritative_zone = dns_push_obj_context_get_authoritative_zone(context);
mdns_require_quiet(authoritative_zone, exit);
mDNS_Lock(m);
const uint32_t if_index = mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, true);
mDNS_Unlock(m);
bool re_registered;
const mdns_dns_service_id_t ident = _dns_push_discovery_register_push_service(context, authoritative_zone,
if_index, &re_registered, &err);
if ((ident == MDNS_DNS_SERVICE_INVALID_ID) && err) {
log_fault("[Q%u] Failed to register push service -- id: %llu"
"authoritative zone: " PUB_DNS_DM_NAME ", interface index: %u, error: " PUB_OS_ERR,
qid, ident, DNS_DM_NAME_PARAM(authoritative_zone), if_index, (long)err);
goto exit;
}
if (re_registered) {
log_default("[Q%u] DNS push discovery finished -- service id: %llu, re registered: " PUB_BOOL,
qid, ident, BOOL_PARAM(re_registered));
} else {
log_default("[Q%u] DNS push discovery finished, using service with SRV name _dns-push-tls._tcp." PUB_DNS_DM_NAME
" -- service id: %llu, re registered: " PUB_BOOL, qid, DNS_DM_NAME_PARAM(authoritative_zone), ident,
BOOL_PARAM(re_registered));
}
// The current network supports DNS push, so stop the discovery.
_dns_push_discovery_stop(m, &question, context, true);
// If the question has been using DNS polling since last update, stop DNS polling because a push service is now
// available.
_dns_push_discovery_stop_dns_polling(context);
exit:
return;
}
//======================================================================================================================
static void
_dns_push_discovery_fallback_to_dns_polling(const dns_push_obj_context_t context)
{
const DNSQuestion *const q = dns_push_obj_context_get_original_question(context);
const mDNSu32 request_id = q->request_id;
const mDNSu16 question_id = mDNSVal16(q->TargetQID);
log_default("[R%u->Q%u] Starting long-lived DNS polling -- "
"polling interval: " STRINGIFY(LLQ_POLL_INTERVAL_MIN) " min", request_id, question_id);
dns_push_obj_context_set_dns_polling_enabled(context, true);
Querier_ProcessDNSServiceChangesAsync(mDNStrue);
}
//======================================================================================================================
static void
_dns_push_discovery_stop_dns_polling(const dns_push_obj_context_t context)
{
const DNSQuestion *const q = dns_push_obj_context_get_original_question(context);
const mDNSu32 request_id = q->request_id;
const mDNSu16 question_id = mDNSVal16(q->TargetQID);
log_default("[R%u->Q%u] Stopping long-lived DNS polling", request_id, question_id);
dns_push_obj_context_set_dns_polling_enabled(context, false);
Querier_ProcessDNSServiceChangesAsync(mDNStrue);
}
//======================================================================================================================
static void
_dns_push_discovery_try_next_qname(mDNS * const m, DNSQuestion * const question, const dns_obj_domain_name_t new_q_name)
{
m->RestartQuestion = question;
mDNS_StopQuery(m, question);
// Must set RestartQuestion back to NULL once the restarting question has been stopped.
m->RestartQuestion = mDNSNULL;
const uint8_t * const labels = dns_obj_domain_name_get_labels(new_q_name);
const size_t labels_length = dns_obj_domain_name_get_length(new_q_name);
require_return(labels_length < sizeof(question->qname.c));
memcpy(question->qname.c, labels, labels_length);
question->qnamehash = DomainNameHashValue(&question->qname);
mDNS_StartQuery(m, question);
}
//======================================================================================================================
static mdns_interface_monitor_t
_dns_push_discovery_start_interface_monitor(const uint32_t if_index)
{
interface_monitor_list_s *monitor_in_list = NULL;
mdns_interface_monitor_t monitor = NULL;
// Search for any existing interface monitor for the same interface.
monitor_in_list = g_interface_monitors;
while (monitor_in_list) {
if (mdns_interface_monitor_get_interface_index(monitor_in_list->monitor) == if_index) {
break;
}
monitor_in_list = monitor_in_list->next;
}
if (monitor_in_list == NULL) {
// If not found, create a new interface monitor and append it.
monitor_in_list = mdns_calloc(1, sizeof(*monitor_in_list));
mdns_require_quiet(monitor_in_list, exit);
// Activate the monitor.
monitor = _dns_push_discovery_start_mdns_interface_monitor(if_index);
mdns_require_quiet(monitor, exit);
monitor_in_list->monitor = monitor;
mdns_retain(monitor_in_list->monitor);
// Add the monitor to the list.
interface_monitor_list_s **pptr = &g_interface_monitors;
while (*pptr) {
pptr = &((*pptr)->next);
}
*pptr = monitor_in_list;
} else {
// If found, retain it directly.
monitor = monitor_in_list->monitor;
mdns_retain(monitor);
}
monitor_in_list->use_count++;
monitor_in_list = NULL;
exit:
mdns_free(monitor_in_list);
return monitor;
}
//======================================================================================================================
static mdns_interface_monitor_t
_dns_push_discovery_start_mdns_interface_monitor(const uint32_t if_index)
{
mdns_interface_monitor_t monitor = NULL;
mdns_interface_monitor_t obj = mdns_interface_monitor_create(if_index);
mdns_require_quiet(obj, exit);
mDNS * const m = &mDNSStorage;
mdns_interface_monitor_set_queue(obj, _dns_push_discovery_interface_monitor_queue());
mdns_retain(obj);
mdns_interface_monitor_set_event_handler(obj,
^(mdns_event_t event, __unused OSStatus error) {
if (event == mdns_event_invalidated) {
mdns_release(obj);
} else if (event == mdns_event_error) {
_dns_push_discovery_process_interface_changes(m, obj);
}
});
mdns_interface_monitor_set_update_handler(obj,
^(const mdns_interface_flags_t update_flags) {
const bool network_changed = ((update_flags & mdns_interface_flag_network) != 0);
// Only restart the push discovery when the network has changed due to interface changes.
if (network_changed) {
_dns_push_discovery_process_interface_changes(m, obj);
}
});
mdns_interface_monitor_activate(obj);
monitor = obj;
obj = NULL;
exit:
mdns_forget(&obj);
return monitor;
}
//======================================================================================================================
static void
_dns_push_discovery_process_interface_changes(mDNS * const m, const mdns_interface_monitor_t monitor)
{
const uint32_t if_index = mdns_interface_monitor_get_interface_index(monitor);
KQueueLock();
mDNS_Lock(m);
const mdns_dns_service_manager_t service_manager = Querier_GetDNSServiceManager();
size_t q_count = 0;
for (const DNSQuestion *q = m->Questions; q; q = q->next) {
q_count++;
}
size_t restarted_push_q_count = 0;
// We are not trying to restart the push question itself here but we are using RestartQuestion pointer to indicate
// that we are restarting push discovery.
m->RestartQuestion = m->Questions;
for (size_t i = 0; (i < q_count) && m->RestartQuestion; i++) {
DNSQuestion *const q = m->RestartQuestion;
const dns_push_obj_dns_question_member_t dns_push = q->dns_push;
const dns_push_obj_context_t context =
dns_push ? dns_push_obj_dns_question_member_get_context(dns_push) : NULL;
const mdns_interface_monitor_t if_monitor =
context ? dns_push_obj_context_get_interface_monitor(context) : NULL;
if (if_monitor && (if_monitor == monitor)) {
// The push question is managed by this interface monitor.
restarted_push_q_count++;
} else {
m->RestartQuestion = q->next;
continue;
}
const mdns_dns_service_id_t service_id = dns_push_obj_context_get_service_id(context);
mdns_dns_service_manager_terminate_discovered_push_service(service_manager, service_id);
// Restart push discovery.
dns_push_handle_question_stop(m, q);
dns_push_handle_question_start(m, q);
if (m->RestartQuestion == q) {
m->RestartQuestion = q->next;
}
}
m->RestartQuestion = mDNSNULL;
log_default("Network changes, restarting all push questions that are related to the changed interface -- "
"if_index: %u, restarted count: %zu", if_index, restarted_push_q_count);
mDNS_Unlock(m);
KQueueUnlock("DNS push interface monitor");
}
//======================================================================================================================
static void
_dns_push_discovery_stop_interface_monitor(const mdns_interface_monitor_t monitor)
{
interface_monitor_list_s **pptr = &g_interface_monitors;
while (*pptr) {
if ((*pptr)->monitor == monitor) {
break;
}
pptr = &((*pptr)->next);
}
mdns_require_return(*pptr);
(*pptr)->use_count--;
if ((*pptr)->use_count == 0) {
// If no active push question is using this interface monitor, then invalidate it completely.
interface_monitor_list_s *if_monitor_to_delete = *pptr;
*pptr = (*pptr)->next;
mdns_interface_monitor_forget(&if_monitor_to_delete->monitor);
mdns_free(if_monitor_to_delete);
}
}
//======================================================================================================================
static DNSQuestion *
_dns_push_discovery_start_srv(mDNS * const m, const mDNSInterfaceID if_id, const dns_push_obj_context_t context)
{
DNSQuestion *srv_q = NULL;
mdns_interface_monitor_t if_monitor = NULL;
bool failed = true;
srv_q = mdns_calloc(1, sizeof(*srv_q));
mdns_require_quiet(srv_q, exit);
const dns_obj_domain_name_t push_srv = dns_push_obj_context_get_push_service_name(context);
mdns_require_quiet(push_srv, exit);
const domainname * const qname = (const domainname *)dns_obj_domain_name_get_labels(push_srv);
AssignDomainName(&srv_q->qname, qname);
const DNSQuestion * const original_question = dns_push_obj_context_get_original_question(context);
mdns_require_quiet(original_question, exit);
srv_q->qtype = kDNSServiceType_SRV;
srv_q->qclass = original_question->qclass;
srv_q->InterfaceID = if_id;
srv_q->pid = mDNSPlatformGetPID();
srv_q->QuestionCallback = _dns_push_discovery_srv_result_reply;
srv_q->QuestionContext = context;
srv_q->ReturnIntermed = mDNStrue;
// Must not set soa_q->LongLived to true because we are in the process of preparing DNS push. Setting it to true
// will cause infinite recursive call.
srv_q->LongLived = mDNSfalse;
// To monitor the network change and restart the discovery process when it happens, start interface monitor.
const uint32_t if_index = dns_push_obj_context_get_interface_index(context);
if_monitor = _dns_push_discovery_start_interface_monitor(if_index);
mdns_require_quiet(if_monitor, exit);
dns_push_obj_context_set_interface_monitor(context, if_monitor);
const mStatus q_start_error = mDNS_StartQuery(m, srv_q);
mdns_require_noerr_quiet(q_start_error, exit);
failed = false;
exit:
if (failed) {
mdns_free(srv_q);
}
mdns_forget(&if_monitor);
return srv_q;
}
//======================================================================================================================
static void
_dns_push_discovery_stop(mDNS * const m, DNSQuestion ** const q_pptr, const dns_push_obj_context_t context,
const bool hold_lock)
{
DNSQuestion *q = *q_pptr;
if (q != dns_push_obj_context_get_discovery_question(context)) {
log_fault("[Q%u] Question being stopped is not the currently active discovery question",
mDNSVal16(q->TargetQID));
}
if (hold_lock) {
mDNS_StopQuery(m, q);
} else {
mDNS_StopQuery_internal(m, q);
}
mdns_free(q);
*q_pptr = mDNSNULL;
dns_push_obj_context_set_discovery_question(context, NULL);
}
//======================================================================================================================
#define kOrphanedPushServiceTimeLimitMs 30 * 1000
static mdns_dns_service_id_t
_dns_push_discovery_register_push_service(const dns_push_obj_context_t context,
const dns_obj_domain_name_t zone, const uint32_t if_index, bool * const out_duplicate,
dns_obj_error_t * const out_error)
{
mdns_dns_service_id_t service_id = MDNS_DNS_SERVICE_INVALID_ID;
dns_obj_error_t err;
dns_obj_domain_name_t dns_push_srv = NULL;
mdns_domain_name_t mdns_dns_push_srv = NULL;
bool duplicate = false;
const mdns_dns_service_manager_t service_manager = Querier_GetDNSServiceManager();
mdns_require_action_quiet(service_manager, exit, err = DNS_OBJ_ERROR_NO_RESOURCES);
static dns_obj_domain_name_t dns_push_service_type = NULL;
if (!dns_push_service_type) {
dns_push_service_type = dns_obj_domain_name_create_with_cstring("_dns-push-tls._tcp", &err);
mdns_require_noerr_quiet(err, exit);
}
dns_push_srv = dns_obj_domain_name_create_concatenation(dns_push_service_type, zone, &err);
mdns_require_noerr_quiet(err, exit);
const uint8_t * const dns_push_srv_labels = dns_obj_domain_name_get_labels(dns_push_srv);
mdns_dns_push_srv = mdns_domain_name_create_with_labels(dns_push_srv_labels, NULL);
mdns_require_action_quiet(mdns_dns_push_srv, exit, err = DNS_OBJ_ERROR_NO_RESOURCES);
OSStatus os_err;
service_id = mdns_dns_service_manager_register_discovered_push_service(service_manager, mdns_dns_push_srv, if_index,
mdns_dns_service_interface_scope_unscoped_and_scoped, kOrphanedPushServiceTimeLimitMs, &duplicate, &os_err);
mdns_require_action_quiet(service_id != MDNS_DNS_SERVICE_INVALID_ID, exit, err = (dns_obj_error_t)os_err);
dns_push_obj_context_set_service_id(context, service_id);
if (duplicate) {
// If the push service has been registered before, then we need to manually trigger a service change so that
// the push question that has finished discovery can start the push connection.
Querier_ProcessDNSServiceChangesAsync(mDNStrue);
}
exit:
mdns_assign(out_duplicate, duplicate);
mdns_assign(out_error, err);
dns_obj_forget(&dns_push_srv);
mdns_forget(&mdns_dns_push_srv);
return service_id;
}
//======================================================================================================================
static void
_dns_push_discovery_deregister_push_service(const dns_push_obj_context_t context)
{
const mdns_dns_service_id_t service_id = dns_push_obj_context_get_service_id(context);
if (service_id != MDNS_DNS_SERVICE_INVALID_ID) {
const mdns_dns_service_manager_t service_manager = Querier_GetDNSServiceManager();
if (service_manager) {
mdns_dns_service_manager_deregister_discovered_push_service(service_manager, service_id);
dns_push_obj_context_set_service_id(context, MDNS_DNS_SERVICE_INVALID_ID);
}
}
}
#endif // MDNSRESPONDER_SUPPORTS(APPLE, DNS_PUSH)