blob: 7bb2801f0f0df236474f6289f2504a19b62dd961 [file] [log] [blame] [edit]
/*
* Copyright (c) 2013-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 "mDNSMacOSX.h"
#include <mdns/system.h>
#include <nw/private.h>
#include "dns_sd_internal.h"
#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
#include "QuerierSupport.h"
#endif
#include "mdns_strict.h"
#define _nw_forget(PTR) \
do \
{ \
if (*(PTR)) \
{ \
nw_release(*(PTR)); \
*(PTR) = NULL; \
} \
} while (0)
//Gets the DNSPolicy from NW PATH EVALUATOR
mDNSexport void mDNSPlatformGetDNSRoutePolicy(DNSQuestion *q)
{
nw_endpoint_t host = NULL;
nw_parameters_t parameters = NULL;
nw_path_evaluator_t evaluator = NULL;
nw_path_t path = NULL;
mDNSBool isBlocked = mDNSfalse;
q->ServiceID = -1; // initialize the ServiceID to default value of -1
// Return for non-unicast DNS queries, or invalid PID.
if (mDNSOpaque16IsZero(q->TargetQID) || (q->pid < 0))
{
goto exit;
}
mDNSs32 service_id;
mDNSu32 client_ifindex, dnspol_ifindex;
mDNSBool isUUIDSet;
char unenc_name[MAX_ESCAPED_DOMAIN_NAME];
ConvertDomainNameToCString(&q->qname, unenc_name);
host = nw_endpoint_create_host(unenc_name, "0");
if (host == NULL)
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
"[Q%u] mDNSPlatformGetDNSRoutePolicy: Query for " PRI_DM_NAME " (" PUB_S "), PID[%d], EUID[%d], ServiceID[%d]"
" host is NULL",
mDNSVal16(q->TargetQID), DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), q->pid, q->euid, q->ServiceID);
goto exit;
}
parameters = nw_parameters_create();
if (parameters == NULL)
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
"[Q%u] mDNSPlatformGetDNSRoutePolicy: Query for " PRI_DM_NAME " (" PUB_S "), PID[%d], EUID[%d], ServiceID[%d]"
" parameters is NULL",
mDNSVal16(q->TargetQID), DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), q->pid, q->euid, q->ServiceID);
goto exit;
}
#if TARGET_OS_WATCH
static xpc_object_t prohibited_interface_subtypes = NULL;
// Companion interface on watchOS does not support DNS, so we don't want path evalution to return it to us.
if (prohibited_interface_subtypes == NULL)
{
prohibited_interface_subtypes = xpc_array_create(NULL, 0);
if (prohibited_interface_subtypes != NULL)
{
xpc_array_set_uint64(prohibited_interface_subtypes, XPC_ARRAY_APPEND, nw_interface_subtype_companion);
}
}
if (prohibited_interface_subtypes == NULL)
{
LogRedact(MDNS_LOG_CATEGORY_UDNS, MDNS_LOG_ERROR,
"mDNSPlatformGetDNSRoutePolicy: DNS Route Policy: prohibited_interface_subtypes returned by xpc_array_create() is NULL");
}
else
{
nw_parameters_set_prohibited_interface_subtypes(parameters, prohibited_interface_subtypes);
}
#endif // TARGET_OS_WATCH
// Check for all the special (negative) internal value interface indices before initializing client_ifindex
if ( (q->InterfaceID == mDNSInterface_Any)
|| (q->InterfaceID == mDNSInterface_LocalOnly)
|| (q->InterfaceID == mDNSInterfaceMark)
|| (q->InterfaceID == mDNSInterface_P2P)
|| (q->InterfaceID == mDNSInterface_BLE)
|| (q->InterfaceID == uDNSInterfaceMark))
{
client_ifindex = 0;
}
else
{
client_ifindex = (mDNSu32)(uintptr_t)q->InterfaceID;
}
if (client_ifindex > 0)
{
nw_interface_t client_intf = nw_interface_create_with_index(client_ifindex);
if (client_intf)
{
nw_parameters_require_interface(parameters, client_intf);
_nw_forget(&client_intf);
}
else
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
"[Q%u] mDNSPlatformGetDNSRoutePolicy: nw_interface_create_with_index() returned NULL for index %u",
mDNSVal16(q->TargetQID), client_ifindex);
}
}
nw_parameters_set_uid(parameters,(uid_t)q->euid);
if (q->pid != 0)
{
nw_parameters_set_pid(parameters, q->pid);
uuid_t uuid;
const OSStatus err = mdns_system_pid_to_uuid(q->pid, uuid);
if (!err)
{
nw_parameters_set_e_proc_uuid(parameters, uuid);
isUUIDSet = mDNStrue;
}
else
{
debugf("mDNSPlatformGetDNSRoutePolicy: proc_pidinfo returned %ld", (long)err);
isUUIDSet = mDNSfalse;
}
}
else
{
nw_parameters_set_e_proc_uuid(parameters, q->uuid);
isUUIDSet = mDNStrue;
}
evaluator = nw_path_create_evaluator_for_endpoint(host, parameters);
if (evaluator == NULL)
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
"[Q%u] mDNSPlatformGetDNSRoutePolicy: Query for " PRI_DM_NAME " (" PUB_S "), PID[%d], EUID[%d], ServiceID[%d]"
" evaluator is NULL",
mDNSVal16(q->TargetQID), DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), q->pid, q->euid, q->ServiceID);
goto exit;
}
_nw_forget(&host);
_nw_forget(&parameters);
path = nw_path_evaluator_copy_path(evaluator);
if (path == NULL)
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
"[Q%u] mDNSPlatformGetDNSRoutePolicy: Query for " PRI_DM_NAME " (" PUB_S "), PID[%d], EUID[%d], ServiceID[%d]"
" path is NULL",
mDNSVal16(q->TargetQID), DM_NAME_PARAM(&q->qname), DNSTypeName(q->qtype), q->pid, q->euid, q->ServiceID);
goto exit;
}
if (nw_path_get_status(path) == nw_path_status_satisfied)
{
service_id = (mDNSs32)nw_path_get_flow_divert_unit(path);
if (service_id != 0)
{
q->ServiceID = service_id;
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
"[Q%u] mDNSPlatformGetDNSRoutePolicy: Query for " PRI_DM_NAME " service ID is set ->service_ID:[%d] ",
mDNSVal16(q->TargetQID), DM_NAME_PARAM(&q->qname), service_id);
}
else
{
nw_interface_t nwpath_intf = nw_path_copy_scoped_interface(path);
if (nwpath_intf != NULL)
{
// Use the new scoped interface given by NW PATH EVALUATOR
dnspol_ifindex = nw_interface_get_index(nwpath_intf);
q->InterfaceID = (mDNSInterfaceID)(uintptr_t)dnspol_ifindex;
_nw_forget(&nwpath_intf);
if (dnspol_ifindex != client_ifindex)
{
LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
"[Q%u] mDNSPlatformGetDNSRoutePolicy: DNS Route Policy has changed the scoped ifindex from [%d] to [%d]",
mDNSVal16(q->TargetQID), client_ifindex, dnspol_ifindex);
}
}
else
{
debugf("mDNSPlatformGetDNSRoutePolicy: Query for %##s (%s), PID[%d], EUID[%d], ServiceID[%d] nw_interface_t nwpath_intf is NULL ", q->qname.c, DNSTypeName(q->qtype), q->pid, q->euid, q->ServiceID);
}
}
}
else if (isUUIDSet && (nw_path_get_status(path) == nw_path_status_unsatisfied) && (nw_path_get_reason(path) != nw_path_reason_no_route))
{
isBlocked = mDNStrue;
}
#if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
// Only set the ResolverUUID if the DNSQuestion isn't blocked and the DNS service selection algorithm isn't being
// overriden with the DNS service specified by ResolverUUID.
if (!isBlocked && !q->OverrideDNSService)
{
uuid_clear(q->ResolverUUID);
if (path != NULL)
{
__block nw_resolver_config_t best_config = NULL;
__block mDNSBool best_config_allows_failover = mDNSfalse;
__block nw_resolver_class_t best_class = nw_resolver_class_default_direct;
nw_path_enumerate_resolver_configs(path,
^bool(nw_resolver_config_t config)
{
const mDNSBool allows_failover = nw_resolver_config_get_allow_failover(config);
const nw_resolver_class_t class = nw_resolver_config_get_class(config);
if (class != nw_resolver_class_default_direct &&
(!allows_failover || !q->IsFailover) &&
(best_class == nw_resolver_class_default_direct || class < best_class))
{
best_class = class;
best_config = config;
best_config_allows_failover = allows_failover;
}
return true;
});
if (best_config != NULL)
{
nw_resolver_config_get_identifier(best_config, q->ResolverUUID);
}
}
if (!uuid_is_null(q->ResolverUUID))
{
Querier_RegisterPathResolver(q->ResolverUUID);
}
}
#endif
exit:
_nw_forget(&host);
_nw_forget(&parameters);
_nw_forget(&path);
_nw_forget(&evaluator);
q->BlockedByPolicy = isBlocked;
}