| /* |
| * 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(¶meters); |
| |
| 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(¶meters); |
| _nw_forget(&path); |
| _nw_forget(&evaluator); |
| q->BlockedByPolicy = isBlocked; |
| } |