| /* |
| * Copyright (c) 2018-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 "ClientRequests.h" |
| |
| #include "DNSCommon.h" |
| #include "uDNS.h" |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) |
| #include "QuerierSupport.h" |
| #endif |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, D2D) |
| #include "D2D.h" |
| #endif |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER) |
| #include "mDNSMacOSX.h" |
| #endif |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, TRACKER_STATE) |
| #include "resolved_cache.h" |
| #endif |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, UNREADY_INTERFACES) |
| #include <dispatch/dispatch.h> |
| #include <net/if.h> |
| #endif |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) |
| #include <WebFilterDNS/WebFilterDNS.h> |
| |
| int WCFIsServerRunning(WCFConnection *conn) __attribute__((weak_import)); |
| int WCFNameResolvesToAddr(WCFConnection *conn, char* domainName, struct sockaddr* address, uid_t userid) __attribute__((weak_import)); |
| int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid_t userid) __attribute__((weak_import)); |
| #endif |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) |
| #include "dnssec.h" |
| #endif |
| |
| #include "mdns_strict.h" |
| |
| #define RecordTypeIsAddress(TYPE) (((TYPE) == kDNSType_A) || ((TYPE) == kDNSType_AAAA)) |
| |
| extern mDNS mDNSStorage; |
| #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) |
| extern domainname ActiveDirectoryPrimaryDomain; |
| #endif |
| |
| // Normally we append search domains only for queries with a single label that are not fully qualified. This can be |
| // overridden to apply search domains for queries (that are not fully qualified) with any number of labels e.g., moon, |
| // moon.cs, moon.cs.be, etc. - Mohan |
| mDNSBool AlwaysAppendSearchDomains = mDNSfalse; |
| |
| // Control enabling optimistic DNS - Phil |
| mDNSBool EnableAllowExpired = mDNStrue; |
| |
| typedef struct |
| { |
| mDNSu32 requestID; |
| const domainname * qname; |
| mDNSu16 qtype; |
| mDNSu16 qclass; |
| mDNSInterfaceID interfaceID; |
| mDNSs32 serviceID; |
| mDNSu32 flags; |
| mDNSBool appendSearchDomains; |
| mDNSs32 effectivePID; |
| const mDNSu8 * effectiveUUID; |
| mDNSu32 peerUID; |
| mDNSBool isInAppBrowserRequest; |
| mDNSBool useAAAAFallback; |
| #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) |
| const mDNSu8 * resolverUUID; |
| mdns_dns_service_id_t customID; |
| mDNSBool needEncryption; |
| mDNSBool useFailover; |
| mDNSBool failoverMode; |
| mDNSBool prohibitEncryptedDNS; |
| mDNSBool overrideDNSService; |
| #endif |
| #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) |
| mdns_audit_token_t peerToken; |
| mdns_audit_token_t delegatorToken; |
| #endif |
| #if MDNSRESPONDER_SUPPORTS(APPLE, LOG_PRIVACY_LEVEL) |
| dnssd_log_privacy_level_t logPrivacyLevel; |
| #endif |
| mDNSBool persistWhenARecordsUnusable; |
| |
| } QueryRecordOpParams; |
| |
| mDNSlocal void QueryRecordOpParamsInit(QueryRecordOpParams *inParams) |
| { |
| mDNSPlatformMemZero(inParams, (mDNSu32)sizeof(*inParams)); |
| inParams->serviceID = -1; |
| } |
| |
| mDNSlocal mStatus QueryRecordOpCreate(QueryRecordOp **outOp); |
| mDNSlocal void QueryRecordOpFree(QueryRecordOp *operation); |
| mDNSlocal mStatus QueryRecordOpStart(QueryRecordOp *inOp, const QueryRecordOpParams *inParams, |
| QueryRecordResultHandler inResultHandler, void *inResultContext); |
| mDNSlocal void QueryRecordOpStop(QueryRecordOp *op); |
| mDNSlocal mDNSBool QueryRecordOpIsMulticast(const QueryRecordOp *op); |
| mDNSlocal void QueryRecordOpCallback(mDNS *m, DNSQuestion *inQuestion, const ResourceRecord *inAnswer, |
| QC_result inAddRecord); |
| mDNSlocal void QueryRecordOpResetHandler(DNSQuestion *inQuestion); |
| mDNSlocal mStatus QueryRecordOpStartQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion); |
| mDNSlocal mStatus QueryRecordOpStopQuestion(DNSQuestion *inQuestion); |
| mDNSlocal mStatus QueryRecordOpRestartUnicastQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion, |
| const domainname *inSearchDomain); |
| mDNSlocal mStatus InterfaceIndexToInterfaceID(mDNSu32 inInterfaceIndex, mDNSInterfaceID *outInterfaceID); |
| mDNSlocal mDNSBool DomainNameIsSingleLabel(const domainname *inName); |
| mDNSlocal mDNSBool StringEndsWithDot(const char *inString); |
| mDNSlocal const domainname * NextSearchDomain(QueryRecordOp *inOp); |
| #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) |
| mDNSlocal mDNSBool DomainNameIsInSearchList(const domainname *domain, mDNSBool inExcludeLocal); |
| #endif |
| #if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) |
| mDNSlocal void NotifyWebContentFilter(const ResourceRecord *inAnswer, uid_t inUID); |
| #endif |
| |
| mDNSexport void GetAddrInfoClientRequestParamsInit(GetAddrInfoClientRequestParams *inParams) |
| { |
| mDNSPlatformMemZero(inParams, (mDNSu32)sizeof(*inParams)); |
| } |
| |
| mDNSexport mStatus GetAddrInfoClientRequestStart(GetAddrInfoClientRequest *inRequest, |
| const GetAddrInfoClientRequestParams *inParams, QueryRecordResultHandler inResultHandler, void *inResultContext) |
| { |
| mStatus err; |
| domainname hostname; |
| mDNSBool appendSearchDomains; |
| mDNSInterfaceID interfaceID; |
| DNSServiceFlags flags; |
| mDNSs32 serviceID; |
| QueryRecordOpParams opParams; |
| |
| if (!MakeDomainNameFromDNSNameString(&hostname, inParams->hostnameStr)) |
| { |
| LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, |
| "[R%u] ERROR: bad hostname '" PRI_S "'", inParams->requestID, inParams->hostnameStr); |
| err = mStatus_BadParamErr; |
| goto exit; |
| } |
| |
| if (inParams->protocols & ~((mDNSu32)(kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6))) |
| { |
| err = mStatus_BadParamErr; |
| goto exit; |
| } |
| |
| flags = inParams->flags; |
| if (inParams->protocols == 0) |
| { |
| flags |= kDNSServiceFlagsSuppressUnusable; |
| inRequest->protocols = kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6; |
| } |
| else |
| { |
| inRequest->protocols = inParams->protocols; |
| } |
| |
| if (flags & kDNSServiceFlagsServiceIndex) |
| { |
| // NOTE: kDNSServiceFlagsServiceIndex flag can only be set for DNSServiceGetAddrInfo() |
| LogInfo("GetAddrInfoClientRequestStart: kDNSServiceFlagsServiceIndex is SET by the client"); |
| |
| // If kDNSServiceFlagsServiceIndex is SET, interpret the interfaceID as the serviceId and set the interfaceID to 0. |
| serviceID = (mDNSs32)inParams->interfaceIndex; |
| interfaceID = mDNSNULL; |
| } |
| else |
| { |
| serviceID = -1; |
| err = InterfaceIndexToInterfaceID(inParams->interfaceIndex, &interfaceID); |
| if (err) goto exit; |
| } |
| inRequest->interfaceID = interfaceID; |
| |
| if (!StringEndsWithDot(inParams->hostnameStr) && (AlwaysAppendSearchDomains || DomainNameIsSingleLabel(&hostname))) |
| { |
| appendSearchDomains = mDNStrue; |
| } |
| else |
| { |
| appendSearchDomains = mDNSfalse; |
| } |
| QueryRecordOpParamsInit(&opParams); |
| opParams.requestID = inParams->requestID; |
| opParams.qname = &hostname; |
| opParams.qclass = kDNSClass_IN; |
| opParams.interfaceID = inRequest->interfaceID; |
| opParams.serviceID = serviceID; |
| opParams.flags = flags; |
| opParams.appendSearchDomains = appendSearchDomains; |
| opParams.effectivePID = inParams->effectivePID; |
| opParams.effectiveUUID = inParams->effectiveUUID; |
| opParams.peerUID = inParams->peerUID; |
| #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) |
| opParams.resolverUUID = inParams->resolverUUID; |
| opParams.customID = inParams->customID; |
| opParams.needEncryption = inParams->needEncryption; |
| opParams.useFailover = inParams->useFailover; |
| opParams.failoverMode = inParams->failoverMode; |
| opParams.prohibitEncryptedDNS = inParams->prohibitEncryptedDNS; |
| #endif |
| #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) |
| opParams.peerToken = inParams->peerToken; |
| opParams.delegatorToken = inParams->delegatorToken; |
| opParams.isInAppBrowserRequest = inParams->isInAppBrowserRequest; |
| #endif |
| #if MDNSRESPONDER_SUPPORTS(APPLE, LOG_PRIVACY_LEVEL) |
| opParams.logPrivacyLevel = inParams->logPrivacyLevel; |
| #endif |
| opParams.persistWhenARecordsUnusable = inParams->persistWhenARecordsUnusable; |
| |
| if (inRequest->protocols & kDNSServiceProtocol_IPv6) |
| { |
| err = QueryRecordOpCreate(&inRequest->op6); |
| if (err) goto exit; |
| |
| opParams.qtype = kDNSType_AAAA; |
| err = QueryRecordOpStart(inRequest->op6, &opParams, inResultHandler, inResultContext); |
| if (err) goto exit; |
| } |
| if (inRequest->protocols & kDNSServiceProtocol_IPv4) |
| { |
| err = QueryRecordOpCreate(&inRequest->op4); |
| if (err) goto exit; |
| |
| opParams.qtype = kDNSType_A; |
| err = QueryRecordOpStart(inRequest->op4, &opParams, inResultHandler, inResultContext); |
| if (err) goto exit; |
| } |
| err = mStatus_NoError; |
| |
| exit: |
| if (err) GetAddrInfoClientRequestStop(inRequest); |
| return err; |
| } |
| |
| mDNSexport void GetAddrInfoClientRequestStop(GetAddrInfoClientRequest *inRequest) |
| { |
| if (inRequest->op4) QueryRecordOpStop(inRequest->op4); |
| if (inRequest->op6) QueryRecordOpStop(inRequest->op6); |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER) |
| { |
| const QueryRecordOp * const op4 = inRequest->op4; |
| const QueryRecordOp * const op6 = inRequest->op6; |
| const DNSQuestion * q4 = mDNSNULL; |
| const DNSQuestion * q6 = mDNSNULL; |
| |
| if (op4) |
| { |
| if (op4->answered) |
| { |
| // If we have a v4 answer and if we timed out prematurely before, provide a trigger to the upper layer so |
| // that it can retry questions if needed. - Mohan |
| q4 = &op4->q; |
| } |
| else if (op4->q.TimeoutQuestion) |
| { |
| // If we are not delivering answers, we may be timing out prematurely. Note down the current state so that |
| // we know to retry when we see a valid response again. - Mohan |
| mDNSPlatformUpdateDNSStatus(&op4->q); |
| } |
| } |
| if (op6) |
| { |
| if (op6->answered) |
| { |
| q6 = &op6->q; |
| } |
| else if (op6->q.TimeoutQuestion) |
| { |
| mDNSPlatformUpdateDNSStatus(&op6->q); |
| } |
| } |
| mDNSPlatformTriggerDNSRetry(q4, q6); |
| } |
| #endif |
| |
| if (inRequest->op4) |
| { |
| QueryRecordOpFree(inRequest->op4); |
| inRequest->op4 = mDNSNULL; |
| } |
| if (inRequest->op6) |
| { |
| QueryRecordOpFree(inRequest->op6); |
| inRequest->op6 = mDNSNULL; |
| } |
| } |
| |
| mDNSexport const domainname * GetAddrInfoClientRequestGetQName(const GetAddrInfoClientRequest *inRequest) |
| { |
| if (inRequest->op4) return &inRequest->op4->q.qname; |
| if (inRequest->op6) return &inRequest->op6->q.qname; |
| return (const domainname *)""; |
| } |
| |
| mDNSexport mDNSBool GetAddrInfoClientRequestIsMulticast(const GetAddrInfoClientRequest *inRequest) |
| { |
| if ((inRequest->op4 && QueryRecordOpIsMulticast(inRequest->op4)) || |
| (inRequest->op6 && QueryRecordOpIsMulticast(inRequest->op6))) |
| { |
| return mDNStrue; |
| } |
| return mDNSfalse; |
| } |
| |
| mDNSexport void QueryRecordClientRequestParamsInit(QueryRecordClientRequestParams *inParams) |
| { |
| mDNSPlatformMemZero(inParams, (mDNSu32)sizeof(*inParams)); |
| } |
| |
| mDNSexport mStatus QueryRecordClientRequestStart(QueryRecordClientRequest *inRequest, |
| const QueryRecordClientRequestParams *inParams, QueryRecordResultHandler inResultHandler, void *inResultContext) |
| { |
| mStatus err; |
| domainname qname; |
| mDNSInterfaceID interfaceID; |
| mDNSBool appendSearchDomains; |
| QueryRecordOpParams opParams; |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) |
| if (inParams->overrideDNSService) |
| { |
| const mdns_audit_token_t token = inParams->peerToken; |
| const mDNSBool entitled = token && mdns_audit_token_is_entitled(token, "com.apple.private.dnssd.resolver-override"); |
| mdns_require_action_quiet(entitled, exit, err = mStatus_NoAuth); |
| mdns_require_action_quiet(inParams->resolverUUID, exit, err = mStatus_BadParamErr); |
| |
| Querier_RegisterPathResolver(inParams->resolverUUID); |
| } |
| #endif |
| err = InterfaceIndexToInterfaceID(inParams->interfaceIndex, &interfaceID); |
| if (err) goto exit; |
| |
| if (!MakeDomainNameFromDNSNameString(&qname, inParams->qnameStr)) |
| { |
| LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, |
| "[R%u] ERROR: bad domain name '" PRI_S "'", inParams->requestID, inParams->qnameStr); |
| err = mStatus_BadParamErr; |
| goto exit; |
| } |
| |
| if (RecordTypeIsAddress(inParams->qtype) && !StringEndsWithDot(inParams->qnameStr) && |
| (AlwaysAppendSearchDomains || DomainNameIsSingleLabel(&qname))) |
| { |
| appendSearchDomains = mDNStrue; |
| } |
| else |
| { |
| appendSearchDomains = mDNSfalse; |
| } |
| QueryRecordOpParamsInit(&opParams); |
| opParams.requestID = inParams->requestID; |
| opParams.qname = &qname; |
| opParams.flags = inParams->flags; |
| opParams.qtype = inParams->qtype; |
| opParams.qclass = inParams->qclass; |
| opParams.interfaceID = interfaceID; |
| opParams.appendSearchDomains = appendSearchDomains; |
| opParams.effectivePID = inParams->effectivePID; |
| opParams.effectiveUUID = inParams->effectiveUUID; |
| opParams.peerUID = inParams->peerUID; |
| #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) |
| opParams.resolverUUID = inParams->resolverUUID; |
| opParams.customID = inParams->customID; |
| opParams.needEncryption = inParams->needEncryption; |
| opParams.useFailover = inParams->useFailover; |
| opParams.failoverMode = inParams->failoverMode; |
| opParams.prohibitEncryptedDNS = inParams->prohibitEncryptedDNS; |
| opParams.overrideDNSService = inParams->overrideDNSService; |
| #endif |
| #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) |
| opParams.peerToken = inParams->peerToken; |
| opParams.delegatorToken = inParams->delegatorToken; |
| opParams.isInAppBrowserRequest = inParams->isInAppBrowserRequest; |
| #endif |
| opParams.useAAAAFallback = inParams->useAAAAFallback; |
| #if MDNSRESPONDER_SUPPORTS(APPLE, LOG_PRIVACY_LEVEL) |
| opParams.logPrivacyLevel = inParams->logPrivacyLevel; |
| #endif |
| |
| err = QueryRecordOpStart(&inRequest->op, &opParams, inResultHandler, inResultContext); |
| |
| exit: |
| if (err) QueryRecordClientRequestStop(inRequest); |
| return err; |
| } |
| |
| mDNSexport void QueryRecordClientRequestStop(QueryRecordClientRequest *inRequest) |
| { |
| QueryRecordOpStop(&inRequest->op); |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER) |
| if (inRequest->op.answered) |
| { |
| DNSQuestion *v4q, *v6q; |
| // If we are receiving positive answers, provide the hint to the upper layer. - Mohan |
| v4q = (inRequest->op.q.qtype == kDNSType_A) ? &inRequest->op.q : mDNSNULL; |
| v6q = (inRequest->op.q.qtype == kDNSType_AAAA) ? &inRequest->op.q : mDNSNULL; |
| mDNSPlatformTriggerDNSRetry(v4q, v6q); |
| } |
| #endif |
| } |
| |
| mDNSexport const domainname * QueryRecordClientRequestGetQName(const QueryRecordClientRequest *inRequest) |
| { |
| return &inRequest->op.q.qname; |
| } |
| |
| mDNSexport mDNSu16 QueryRecordClientRequestGetType(const QueryRecordClientRequest *inRequest) |
| { |
| return inRequest->op.q.qtype; |
| } |
| |
| mDNSexport mDNSBool QueryRecordClientRequestIsMulticast(QueryRecordClientRequest *inRequest) |
| { |
| return (QueryRecordOpIsMulticast(&inRequest->op) ? mDNStrue : mDNSfalse); |
| } |
| |
| mDNSlocal mStatus QueryRecordOpCreate(QueryRecordOp **outOp) |
| { |
| mStatus err; |
| QueryRecordOp *op; |
| |
| op = (QueryRecordOp *) mDNSPlatformMemAllocateClear(sizeof(*op)); |
| if (!op) |
| { |
| err = mStatus_NoMemoryErr; |
| goto exit; |
| } |
| *outOp = op; |
| err = mStatus_NoError; |
| |
| exit: |
| return err; |
| } |
| |
| mDNSlocal void QueryRecordOpFree(QueryRecordOp *operation) |
| { |
| mDNSPlatformMemFree(operation); |
| } |
| |
| mDNSlocal void QueryRecordOpEventHandler(DNSQuestion *const inQuestion, const mDNSQuestionEvent event) |
| { |
| QueryRecordOp *const op = (QueryRecordOp *)inQuestion->QuestionContext; |
| switch (event) |
| { |
| case mDNSQuestionEvent_NoMoreExpiredRecords: |
| if ((inQuestion->ExpRecordPolicy == mDNSExpiredRecordPolicy_UseCached) && op->gotExpiredCNAME) |
| { |
| // If an expired CNAME record was encountered, then rewind back to the original QNAME. |
| QueryRecordOpStopQuestion(inQuestion); |
| LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO, |
| "[R%u->Q%u] Restarting question that got expired CNAMEs -- current name: " PRI_DM_NAME |
| ", original name: " PRI_DM_NAME ", type: " PUB_DNS_TYPE, |
| op->reqID, mDNSVal16(inQuestion->TargetQID), DM_NAME_PARAM(&inQuestion->qname), DM_NAME_PARAM(op->qname), |
| DNS_TYPE_PARAM(inQuestion->qtype)); |
| op->gotExpiredCNAME = mDNSfalse; |
| AssignDomainName(&inQuestion->qname, op->qname); |
| inQuestion->ExpRecordPolicy = mDNSExpiredRecordPolicy_Immortalize; |
| #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) |
| mDNSPlatformMemCopy(inQuestion->ResolverUUID, op->resolverUUID, UUID_SIZE); |
| #endif |
| const domainname *domain = mDNSNULL; |
| // If we're appending search domains, the DNSQuestion needs to be retried without Optimistic DNS, |
| // but with the search domain we just used, so restore the search list index to avoid skipping to |
| // the next search domain. |
| // |
| // Note that when AppendSearchDomains is true, searchListIndex is the index of the next search |
| // domain to try. So if searchListIndex is 0 or negative, that means that we are not currently in |
| // the middle of iterating the search domain list, so no search domain needs to be restored. If |
| // searchListIndex is greater than 0, then we're currently in the middle of iterating through the |
| // search domain list, so the search domain that's currently in effect needs to be restored. |
| if (inQuestion->AppendSearchDomains && (op->searchListIndex > 0)) |
| { |
| op->searchListIndex = op->searchListIndexLast; |
| domain = NextSearchDomain(op); |
| } |
| QueryRecordOpRestartUnicastQuestion(op, inQuestion, domain); |
| } |
| break; |
| |
| MDNS_COVERED_SWITCH_DEFAULT: |
| break; |
| } |
| } |
| |
| #define VALID_MSAD_SRV_TRANSPORT(T) \ |
| (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp")) |
| #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname))) |
| |
| mDNSlocal mStatus QueryRecordOpStart(QueryRecordOp *inOp, const QueryRecordOpParams *inParams, |
| QueryRecordResultHandler inResultHandler, void *inResultContext) |
| { |
| mStatus err; |
| DNSQuestion * const q = &inOp->q; |
| mDNSu32 len; |
| |
| // Save the original qname. |
| |
| len = DomainNameLength(inParams->qname); |
| inOp->qname = (domainname *) mDNSPlatformMemAllocate(len); |
| if (!inOp->qname) |
| { |
| err = mStatus_NoMemoryErr; |
| goto exit; |
| } |
| mDNSPlatformMemCopy(inOp->qname, inParams->qname, len); |
| |
| inOp->interfaceID = inParams->interfaceID; |
| inOp->reqID = inParams->requestID; |
| inOp->resultHandler = inResultHandler; |
| inOp->resultContext = inResultContext; |
| inOp->useAAAAFallback = inParams->useAAAAFallback; |
| #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) |
| inOp->useFailover = inParams->useFailover; |
| inOp->failoverMode = inParams->failoverMode; |
| inOp->prohibitEncryptedDNS = inParams->prohibitEncryptedDNS; |
| inOp->overrideDNSService = inParams->overrideDNSService; |
| inOp->qtype = inParams->qtype; |
| if ((!inOp->prohibitEncryptedDNS || inOp->overrideDNSService) && inParams->resolverUUID) |
| { |
| mDNSPlatformMemCopy(inOp->resolverUUID, inParams->resolverUUID, UUID_SIZE); |
| } |
| #endif |
| #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) |
| mdns_replace(&inOp->peerToken, inParams->peerToken); |
| mdns_replace(&inOp->delegatorToken, inParams->delegatorToken); |
| #endif |
| // Set up DNSQuestion. |
| if (EnableAllowExpired && (inParams->flags & kDNSServiceFlagsAllowExpiredAnswers)) |
| { |
| q->ExpRecordPolicy = mDNSExpiredRecordPolicy_UseCached; |
| } |
| else |
| { |
| q->ExpRecordPolicy = mDNSExpiredRecordPolicy_DoNotUse; |
| } |
| q->ServiceID = inParams->serviceID; |
| #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) |
| q->inAppBrowserRequest = inParams->isInAppBrowserRequest; |
| q->PeerToken = inOp->peerToken; |
| q->DelegatorToken = inOp->delegatorToken; |
| #endif |
| q->InterfaceID = inParams->interfaceID; |
| q->flags = inParams->flags; |
| AssignDomainName(&q->qname, inParams->qname); |
| q->qtype = inParams->qtype; |
| q->qclass = inParams->qclass; |
| q->LongLived = (inParams->flags & kDNSServiceFlagsLongLivedQuery) ? mDNStrue : mDNSfalse; |
| q->ForceMCast = (inParams->flags & kDNSServiceFlagsForceMulticast) ? mDNStrue : mDNSfalse; |
| q->ReturnIntermed = (inParams->flags & kDNSServiceFlagsReturnIntermediates) ? mDNStrue : mDNSfalse; |
| q->SuppressUnusable = (inParams->flags & kDNSServiceFlagsSuppressUnusable) ? mDNStrue : mDNSfalse; |
| q->TimeoutQuestion = (inParams->flags & kDNSServiceFlagsTimeout) ? mDNStrue : mDNSfalse; |
| q->UseBackgroundTraffic = (inParams->flags & kDNSServiceFlagsBackgroundTrafficClass) ? mDNStrue : mDNSfalse; |
| #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) |
| q->enableDNSSEC = dns_service_flags_enables_dnssec(inParams->flags); |
| #endif |
| q->AppendSearchDomains = inParams->appendSearchDomains; |
| q->PersistWhenRecordsUnusable = (inParams->qtype == kDNSType_A) && inParams->persistWhenARecordsUnusable; |
| #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) |
| q->RequireEncryption = inParams->needEncryption; |
| q->CustomID = inParams->customID; |
| q->ProhibitEncryptedDNS = inOp->prohibitEncryptedDNS; |
| q->OverrideDNSService = inOp->overrideDNSService; |
| if (inOp->failoverMode) |
| { |
| q->IsFailover = mDNStrue; |
| // Force a path evaluation if the DNSQuestion isn't interface-scoped. |
| if (!q->InterfaceID) |
| { |
| q->ForcePathEval = mDNStrue; |
| } |
| } |
| mDNSPlatformMemCopy(q->ResolverUUID, inOp->resolverUUID, UUID_SIZE); |
| #endif |
| q->InitialCacheMiss = mDNSfalse; |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, LOG_PRIVACY_LEVEL) |
| q->logPrivacyLevel = inParams->logPrivacyLevel; |
| #endif |
| |
| q->pid = inParams->effectivePID; |
| if (inParams->effectiveUUID) |
| { |
| mDNSPlatformMemCopy(q->uuid, inParams->effectiveUUID, UUID_SIZE); |
| } |
| q->euid = inParams->peerUID; |
| q->request_id = inParams->requestID; |
| q->QuestionCallback = QueryRecordOpCallback; |
| q->ResetHandler = QueryRecordOpResetHandler; |
| q->EventHandler = QueryRecordOpEventHandler; |
| |
| // For single label queries that are not fully qualified, look at /etc/hosts, cache and try search domains before trying |
| // them on the wire as a single label query. - Mohan |
| |
| if (q->AppendSearchDomains && DomainNameIsSingleLabel(inOp->qname)) q->InterfaceID = mDNSInterface_LocalOnly; |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) |
| // Copy the original question to another question that can be used for the legacy unicast dotlocal query, before the |
| // the original question is started and initialized. |
| const mDNSBool startParallelLegacyUnicastDotLocal = ((RecordTypeIsAddress(q->qtype) || VALID_MSAD_SRV(&inOp->q)) && |
| !q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain)); |
| if (startParallelLegacyUnicastDotLocal) |
| { |
| inOp->q2 = mDNSPlatformMemAllocateClear(sizeof(*inOp->q2)); |
| mdns_require_action_quiet(inOp->q2, exit, err = mStatus_NoMemoryErr); |
| *inOp->q2 = *q; |
| } |
| #endif |
| |
| err = QueryRecordOpStartQuestion(inOp, q); |
| if (err) goto exit; |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, D2D) |
| if (callExternalHelpers(q->InterfaceID, &q->qname, q->flags)) |
| { |
| external_start_browsing_for_service(q->InterfaceID, &q->qname, q->qtype, q->flags, q->pid); |
| } |
| #endif |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) |
| if (startParallelLegacyUnicastDotLocal) |
| { |
| DNSQuestion *const q2 = inOp->q2; |
| mdns_require_action_quiet(q2, exit, err = mStatus_BadStateErr); |
| |
| if ((CountLabels(&q2->qname) == 2) && !SameDomainName(&q2->qname, &ActiveDirectoryPrimaryDomain) |
| && !DomainNameIsInSearchList(&q2->qname, mDNSfalse)) |
| { |
| inOp->q2Type = q2->qtype; |
| inOp->q2LongLived = q2->LongLived; |
| inOp->q2ReturnIntermed = q2->ReturnIntermed; |
| inOp->q2TimeoutQuestion = q2->TimeoutQuestion; |
| inOp->q2AppendSearchDomains = q2->AppendSearchDomains; |
| |
| AssignDomainName(&q2->qname, &localdomain); |
| q2->qtype = kDNSType_SOA; |
| q2->ReturnIntermed = mDNStrue; |
| q2->TimeoutQuestion = mDNSfalse; |
| q2->AppendSearchDomains = mDNSfalse; |
| } |
| q2->IsUnicastDotLocal = mDNStrue; |
| // We disable DNS push for this legacy unicast dotlocal query. |
| q2->LongLived = mDNSfalse; |
| |
| LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, |
| "[R%u] QueryRecordOpStart: starting parallel unicast query for " PRI_DM_NAME " " PUB_S, |
| inOp->reqID, DM_NAME_PARAM(&q2->qname), DNSTypeName(q2->qtype)); |
| |
| err = QueryRecordOpStartQuestion(inOp, q2); |
| if (err) goto exit; |
| } |
| #endif |
| err = mStatus_NoError; |
| |
| exit: |
| if (err) QueryRecordOpStop(inOp); |
| return err; |
| } |
| |
| mDNSlocal void QueryRecordOpStop(QueryRecordOp *op) |
| { |
| if (op->q.QuestionContext) |
| { |
| QueryRecordOpStopQuestion(&op->q); |
| #if MDNSRESPONDER_SUPPORTS(APPLE, D2D) |
| if (callExternalHelpers(op->q.InterfaceID, op->qname, op->q.flags)) |
| { |
| external_stop_browsing_for_service(op->q.InterfaceID, &op->q.qname, op->q.qtype, op->q.flags, op->q.pid); |
| } |
| #endif |
| } |
| if (op->qname) |
| { |
| mDNSPlatformMemFree(op->qname); |
| op->qname = mDNSNULL; |
| } |
| #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) |
| if (op->q2) |
| { |
| if (op->q2->QuestionContext) QueryRecordOpStopQuestion(op->q2); |
| mDNSPlatformMemFree(op->q2); |
| op->q2 = mDNSNULL; |
| } |
| #endif |
| #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN) |
| mdns_forget(&op->peerToken); |
| mdns_forget(&op->delegatorToken); |
| #endif |
| } |
| |
| mDNSlocal mDNSBool QueryRecordOpIsMulticast(const QueryRecordOp *op) |
| { |
| return ((mDNSOpaque16IsZero(op->q.TargetQID) && (op->q.ThisQInterval > 0)) ? mDNStrue : mDNSfalse); |
| } |
| |
| // GetTimeNow is a callback-safe alternative to mDNS_TimeNow(), which expects to be called with m->mDNS_busy == 0. |
| mDNSlocal mDNSs32 GetTimeNow(mDNS *m) |
| { |
| mDNSs32 time; |
| mDNS_Lock(m); |
| time = m->timenow; |
| mDNS_Unlock(m); |
| return time; |
| } |
| |
| mDNSlocal void QueryRecordOpCallback(mDNS *m, DNSQuestion *inQuestion, const ResourceRecord *inAnswer, QC_result inAddRecord) |
| { |
| mStatus resultErr; |
| QueryRecordOp *const op = (QueryRecordOp *)inQuestion->QuestionContext; |
| const domainname * domain; |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) |
| if ((inQuestion == op->q2) && (inQuestion->qtype == kDNSType_SOA)) |
| { |
| DNSQuestion * const q2 = op->q2; |
| |
| if (inAnswer->rrtype != kDNSType_SOA) goto exit; |
| QueryRecordOpStopQuestion(q2); |
| |
| // Restore DNSQuestion variables that were modified for the SOA query. |
| |
| q2->qtype = op->q2Type; |
| q2->LongLived = op->q2LongLived; |
| q2->ReturnIntermed = op->q2ReturnIntermed; |
| q2->TimeoutQuestion = op->q2TimeoutQuestion; |
| q2->AppendSearchDomains = op->q2AppendSearchDomains; |
| |
| if (inAnswer->RecordType != kDNSRecordTypePacketNegative) |
| { |
| QueryRecordOpRestartUnicastQuestion(op, q2, mDNSNULL); |
| } |
| else if (q2->AppendSearchDomains) |
| { |
| domain = NextSearchDomain(op); |
| if (domain) QueryRecordOpRestartUnicastQuestion(op, q2, domain); |
| } |
| goto exit; |
| } |
| #endif |
| // The mDNSExpiredRecordPolicy_UseCached policy unconditionally provides us with CNAMEs. So if the client |
| // doesn't want intermediate results, which includes CNAMEs, then don't provide them with CNAMEs unless the |
| // client request was specifically for CNAME records. |
| if (inQuestion->ExpRecordPolicy == mDNSExpiredRecordPolicy_UseCached) |
| { |
| if ((inAddRecord == QC_add) && (inAnswer->rrtype == kDNSType_CNAME)) |
| { |
| if (inAnswer->mortality == Mortality_Ghost) |
| { |
| op->gotExpiredCNAME = mDNStrue; |
| } |
| if (!(inQuestion->ReturnIntermed || (inQuestion->qtype == kDNSType_CNAME))) |
| { |
| goto exit; |
| } |
| } |
| } |
| if (inAddRecord == QC_suppressed) |
| { |
| LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, |
| "[R%u] QueryRecordOpCallback: Suppressed question " PRI_DM_NAME " (" PUB_S ")", |
| op->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype)); |
| |
| resultErr = kDNSServiceErr_NoSuchRecord; |
| } |
| else if (inAnswer->RecordType == kDNSRecordTypePacketNegative) |
| { |
| if (inQuestion->TimeoutQuestion && ((GetTimeNow(m) - inQuestion->StopTime) >= 0)) |
| { |
| LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, |
| "[R%u] QueryRecordOpCallback: Question " PRI_DM_NAME " (" PUB_S ") timing out, InterfaceID %p", |
| op->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype), |
| inQuestion->InterfaceID); |
| resultErr = kDNSServiceErr_Timeout; |
| } |
| else |
| { |
| if (inQuestion->AppendSearchDomains && (op->searchListIndex >= 0) && inAddRecord) |
| { |
| domain = NextSearchDomain(op); |
| if (domain || DomainNameIsSingleLabel(op->qname)) |
| { |
| QueryRecordOpStopQuestion(inQuestion); |
| QueryRecordOpRestartUnicastQuestion(op, inQuestion, domain); |
| goto exit; |
| } |
| } |
| if (op->useAAAAFallback && (inQuestion->qtype == kDNSType_AAAA) && (inAnswer->rcode != kDNSFlag1_RC_NXDomain)) |
| { |
| LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, |
| "[R%u] Restarting question for " PRI_DM_NAME " AAAA record as question for A record (RCODE %d)", |
| op->reqID, DM_NAME_PARAM(&inQuestion->qname), inAnswer->rcode); |
| QueryRecordOpStopQuestion(inQuestion); |
| inQuestion->qtype = kDNSType_A; |
| QueryRecordOpStartQuestion(op, inQuestion); |
| goto exit; |
| } |
| #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER) |
| if (op->useFailover && !inQuestion->IsFailover && inQuestion->dnsservice && |
| mdns_dns_service_allows_failover(inQuestion->dnsservice)) |
| { |
| QueryRecordOpStopQuestion(inQuestion); |
| inQuestion->qtype = op->qtype; // Ensure that the original QTYPE is used in case AAAA fallback was used. |
| inQuestion->IsFailover = mDNStrue; |
| // Force a path evaluation if the DNSQuestion isn't interface-scoped. |
| if (!inQuestion->InterfaceID) |
| { |
| inQuestion->ForcePathEval = mDNStrue; |
| } |
| LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG, |
| "[R%u] Restarting question for " PRI_DM_NAME " (" PUB_S ") due to DNS service failover", |
| op->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype)); |
| domain = mDNSNULL; |
| if (inQuestion->AppendSearchDomains) |
| { |
| op->searchListIndex = 0; // Reset search list usage |
| op->searchListIndexLast = 0; |
| domain = NextSearchDomain(op); |
| } |
| QueryRecordOpRestartUnicastQuestion(op, inQuestion, domain); |
| goto exit; |
| } |
| #endif |
| #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) |
| if (!inAnswer->InterfaceID && IsLocalDomain(inAnswer->name)) |
| { |
| if ((RecordTypeIsAddress(inQuestion->qtype) && (inAnswer->rcode == kDNSFlag1_RC_NoErr)) || |
| DomainNameIsInSearchList(&inQuestion->qname, mDNStrue)) |
| { |
| LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, |
| "[R%u] QueryRecordOpCallback: Question " PRI_DM_NAME " (" PUB_S ") answering local with negative unicast response", |
| op->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype)); |
| } |
| else |
| { |
| goto exit; |
| } |
| } |
| #endif |
| resultErr = kDNSServiceErr_NoSuchRecord; |
| } |
| } |
| else |
| { |
| resultErr = kDNSServiceErr_NoError; |
| } |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER) |
| if ((resultErr != kDNSServiceErr_Timeout) && (inAddRecord == QC_add)) |
| { |
| op->answered = mDNStrue; |
| } |
| #endif |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, TRACKER_STATE) |
| if (resolved_cache_is_enabled() && |
| inAddRecord && |
| !mDNSOpaque16IsZero(inQuestion->TargetQID) && |
| !LocalOnlyOrP2PInterface(inAnswer->InterfaceID) && |
| inAnswer->RecordType != kDNSRecordTypePacketNegative && |
| ((inAnswer->rrtype == kDNSServiceType_A) || |
| (inAnswer->rrtype == kDNSServiceType_AAAA))) |
| { |
| const void *data_ptr; |
| if (inAnswer->rrtype == kDNSServiceType_A) |
| { |
| data_ptr = inAnswer->rdata->u.ipv4.b; |
| } |
| else if (inAnswer->rrtype == kDNSServiceType_AAAA) |
| { |
| data_ptr = inAnswer->rdata->u.ipv6.b; |
| } |
| resolved_cache_append_address(inQuestion, inAnswer->rrtype, data_ptr); |
| } |
| #endif |
| |
| // The result handler is allowed to stop the client request, so it's not safe to touch the DNSQuestion or |
| // the QueryRecordOp unless m->CurrentQuestion still points to this DNSQuestion. |
| #if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) |
| const uid_t euid = inQuestion->euid; |
| #endif |
| if (op->resultHandler) |
| { |
| const mDNSBool expired = (inAddRecord == QC_add) && (op->gotExpiredCNAME || (inAnswer->mortality == Mortality_Ghost)); |
| op->resultHandler(m, inQuestion, inAnswer, expired, inAddRecord, resultErr, op->resultContext); |
| } |
| if (m->CurrentQuestion == inQuestion) |
| { |
| if (resultErr == kDNSServiceErr_Timeout) QueryRecordOpStopQuestion(inQuestion); |
| } |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) |
| NotifyWebContentFilter(inAnswer, euid); |
| #endif |
| |
| exit: |
| return; |
| } |
| |
| mDNSlocal void QueryRecordOpResetHandler(DNSQuestion *inQuestion) |
| { |
| QueryRecordOp *const op = (QueryRecordOp *)inQuestion->QuestionContext; |
| |
| AssignDomainName(&inQuestion->qname, op->qname); |
| if (inQuestion->AppendSearchDomains && DomainNameIsSingleLabel(op->qname)) |
| { |
| inQuestion->InterfaceID = mDNSInterface_LocalOnly; |
| } |
| else |
| { |
| inQuestion->InterfaceID = op->interfaceID; |
| } |
| op->searchListIndex = 0; |
| op->searchListIndexLast = 0; |
| } |
| |
| mDNSlocal mStatus QueryRecordOpStartQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion) |
| { |
| mStatus err; |
| |
| inQuestion->QuestionContext = inOp; |
| err = mDNS_StartQuery(&mDNSStorage, inQuestion); |
| if (err) |
| { |
| LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT, |
| "[R%u] ERROR: QueryRecordOpStartQuestion mDNS_StartQuery for " PRI_DM_NAME " " PUB_S " failed with error %d", |
| inOp->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype), err); |
| inQuestion->QuestionContext = mDNSNULL; |
| } |
| return err; |
| } |
| |
| mDNSlocal mStatus QueryRecordOpStopQuestion(DNSQuestion *inQuestion) |
| { |
| mStatus err; |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, TRACKER_STATE) |
| resolved_cache_delete(inQuestion); |
| #endif |
| err = mDNS_StopQuery(&mDNSStorage, inQuestion); |
| inQuestion->QuestionContext = mDNSNULL; |
| return err; |
| } |
| |
| mDNSlocal mStatus QueryRecordOpRestartUnicastQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion, |
| const domainname *inSearchDomain) |
| { |
| mStatus err; |
| |
| inQuestion->InterfaceID = inOp->interfaceID; |
| AssignDomainName(&inQuestion->qname, inOp->qname); |
| if (inSearchDomain) AppendDomainName(&inQuestion->qname, inSearchDomain); |
| if (SameDomainLabel(LastLabel(&inQuestion->qname), (const mDNSu8 *)&localdomain)) |
| { |
| inQuestion->IsUnicastDotLocal = mDNStrue; |
| } |
| else |
| { |
| inQuestion->IsUnicastDotLocal = mDNSfalse; |
| } |
| err = QueryRecordOpStartQuestion(inOp, inQuestion); |
| return err; |
| } |
| |
| mDNSlocal mStatus InterfaceIndexToInterfaceID(mDNSu32 inInterfaceIndex, mDNSInterfaceID *outInterfaceID) |
| { |
| mStatus err; |
| mDNSInterfaceID interfaceID; |
| |
| interfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, inInterfaceIndex); |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, UNREADY_INTERFACES) |
| // The request is scoped to a specific interface index, but the interface is not currently in our list. |
| if ((inInterfaceIndex != kDNSServiceInterfaceIndexAny) && (interfaceID == mDNSInterface_Any)) |
| { |
| static dispatch_once_t getLoopbackIndexOnce = 0; |
| static mDNSu32 loopbackIndex = 0; |
| |
| dispatch_once(&getLoopbackIndexOnce, |
| ^{ |
| loopbackIndex = if_nametoindex("lo0"); |
| }); |
| |
| // If it's one of the specially defined inteface index values, just return an error. Also, caller should return an |
| // error immediately if lo0 is not configured into the current active interfaces. See <rdar://problem/21967160>. |
| if ((inInterfaceIndex == kDNSServiceInterfaceIndexLocalOnly) || |
| (inInterfaceIndex == kDNSServiceInterfaceIndexUnicast) || |
| (inInterfaceIndex == kDNSServiceInterfaceIndexP2P) || |
| (inInterfaceIndex == kDNSServiceInterfaceIndexBLE) || |
| (inInterfaceIndex == loopbackIndex)) |
| { |
| LogInfo("ERROR: bad interfaceIndex %d", inInterfaceIndex); |
| err = mStatus_BadParamErr; |
| goto exit; |
| } |
| |
| // Otherwise, use the specified interface index value and the request will be applied to that interface when it |
| // comes up. |
| interfaceID = (mDNSInterfaceID)(uintptr_t)inInterfaceIndex; |
| LogInfo("Query pending for interface index %d", inInterfaceIndex); |
| } |
| #endif |
| |
| *outInterfaceID = interfaceID; |
| err = mStatus_NoError; |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, UNREADY_INTERFACES) |
| exit: |
| #endif |
| return err; |
| } |
| |
| mDNSlocal mDNSBool DomainNameIsSingleLabel(const domainname *inName) |
| { |
| const mDNSu8 *const label = inName->c; |
| return (((label[0] != 0) && (label[1 + label[0]] == 0)) ? mDNStrue : mDNSfalse); |
| } |
| |
| mDNSlocal mDNSBool StringEndsWithDot(const char *inString) |
| { |
| const char * ptr; |
| mDNSu32 escapeCount; |
| mDNSBool result; |
| |
| // Loop invariant: escapeCount is the number of consecutive escape characters that immediately precede *ptr. |
| // - If escapeCount is even, then *ptr is immediately preceded by escapeCount / 2 consecutive literal backslash |
| // characters, so *ptr is not escaped. |
| // - If escapeCount is odd, then *ptr is immediately preceded by (escapeCount - 1) / 2 consecutive literal backslash |
| // characters followed by an escape character, so *ptr is escaped. |
| escapeCount = 0; |
| result = mDNSfalse; |
| for (ptr = inString; *ptr != '\0'; ptr++) |
| { |
| if (*ptr == '\\') |
| { |
| escapeCount++; |
| } |
| else |
| { |
| if ((*ptr == '.') && (ptr[1] == '\0')) |
| { |
| if ((escapeCount % 2) == 0) result = mDNStrue; |
| break; |
| } |
| escapeCount = 0; |
| } |
| } |
| return result; |
| } |
| |
| mDNSlocal const domainname * NextSearchDomain(QueryRecordOp *inOp) |
| { |
| const domainname * domain; |
| |
| inOp->searchListIndexLast = inOp->searchListIndex; |
| while ((domain = uDNS_GetNextSearchDomain(inOp->interfaceID, &inOp->searchListIndex, mDNSfalse)) != mDNSNULL) |
| { |
| if ((DomainNameLength(inOp->qname) - 1 + DomainNameLength(domain)) <= MAX_DOMAIN_NAME) break; |
| } |
| if (!domain) inOp->searchListIndex = -1; |
| return domain; |
| } |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL) |
| mDNSlocal mDNSBool DomainNameIsInSearchList(const domainname *inName, mDNSBool inExcludeLocal) |
| { |
| const SearchListElem * item; |
| int labelCount, domainLabelCount; |
| |
| labelCount = CountLabels(inName); |
| for (item = SearchList; item; item = item->next) |
| { |
| if (inExcludeLocal && SameDomainName(&item->domain, &localdomain)) continue; |
| domainLabelCount = CountLabels(&item->domain); |
| if (labelCount >= domainLabelCount) |
| { |
| if (SameDomainName(&item->domain, SkipLeadingLabels(inName, (labelCount - domainLabelCount)))) |
| { |
| return mDNStrue; |
| } |
| } |
| } |
| return mDNSfalse; |
| } |
| #endif |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER) |
| mDNSlocal void NotifyWebContentFilter(const ResourceRecord *inAnswer, uid_t inUID) |
| { |
| if (WCFIsServerRunning) |
| { |
| const mDNS *const m = &mDNSStorage; |
| |
| if (WCFIsServerRunning(m->WCF) && inAnswer->rdlength != 0) |
| { |
| struct sockaddr_storage addr; |
| addr.ss_len = 0; |
| if (inAnswer->rrtype == kDNSType_A || inAnswer->rrtype == kDNSType_AAAA) |
| { |
| if (inAnswer->rrtype == kDNSType_A) |
| { |
| struct sockaddr_in *const sin = (struct sockaddr_in *)&addr; |
| sin->sin_port = 0; |
| // Instead of this stupid call to putRData it would be much simpler to just assign the value in the sensible way, like this: |
| // sin->sin_addr.s_addr = inAnswer->rdata->u.ipv4.NotAnInteger; |
| if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(mDNSv4Addr)), inAnswer)) |
| LogMsg("NotifyWebContentFilter: WCF AF_INET putRData failed"); |
| else |
| { |
| addr.ss_len = sizeof (struct sockaddr_in); |
| addr.ss_family = AF_INET; |
| } |
| } |
| else if (inAnswer->rrtype == kDNSType_AAAA) |
| { |
| struct sockaddr_in6 *const sin6 = (struct sockaddr_in6 *)&addr; |
| sin6->sin6_port = 0; |
| // Instead of this stupid call to putRData it would be much simpler to just assign the value in the sensible way, like this: |
| // sin6->sin6_addr.__u6_addr.__u6_addr32[0] = inAnswer->rdata->u.ipv6.l[0]; |
| // sin6->sin6_addr.__u6_addr.__u6_addr32[1] = inAnswer->rdata->u.ipv6.l[1]; |
| // sin6->sin6_addr.__u6_addr.__u6_addr32[2] = inAnswer->rdata->u.ipv6.l[2]; |
| // sin6->sin6_addr.__u6_addr.__u6_addr32[3] = inAnswer->rdata->u.ipv6.l[3]; |
| if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(mDNSv6Addr)), inAnswer)) |
| LogMsg("NotifyWebContentFilter: WCF AF_INET6 putRData failed"); |
| else |
| { |
| addr.ss_len = sizeof (struct sockaddr_in6); |
| addr.ss_family = AF_INET6; |
| } |
| } |
| if (addr.ss_len) |
| { |
| char name[MAX_ESCAPED_DOMAIN_NAME]; |
| ConvertDomainNameToCString(inAnswer->name, name); |
| |
| debugf("NotifyWebContentFilter: Name %s, uid %u, addr length %d", name, inUID, addr.ss_len); |
| if (WCFNameResolvesToAddr) |
| { |
| WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, inUID); |
| } |
| } |
| } |
| else if (inAnswer->rrtype == kDNSType_CNAME) |
| { |
| domainname cname; |
| char name[MAX_ESCAPED_DOMAIN_NAME]; |
| char cname_cstr[MAX_ESCAPED_DOMAIN_NAME]; |
| |
| if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), inAnswer)) |
| LogMsg("NotifyWebContentFilter: WCF CNAME putRData failed"); |
| else |
| { |
| ConvertDomainNameToCString(inAnswer->name, name); |
| ConvertDomainNameToCString(&cname, cname_cstr); |
| if (WCFNameResolvesToAddr) |
| { |
| WCFNameResolvesToName(m->WCF, name, cname_cstr, inUID); |
| } |
| } |
| } |
| } |
| } |
| } |
| #endif |
| |
| #if MDNSRESPONDER_SUPPORTS(APPLE, POWERLOG_MDNS_REQUESTS) |
| mDNSexport mDNSBool ClientRequestUsesAWDL(const uint32_t ifindex, const DNSServiceFlags flags) |
| { |
| if (ifindex == kDNSServiceInterfaceIndexAny) |
| { |
| return ((flags & kDNSServiceFlagsIncludeAWDL) != 0); |
| } |
| else |
| { |
| return mDNSPlatformInterfaceIsAWDL((mDNSInterfaceID)((uintptr_t)ifindex)); |
| } |
| } |
| #endif |