Home | History | Annotate | Line # | Download | only in mDNSShared
      1 /*
      2  * Copyright (c) 2018-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 
     17 #include "ClientRequests.h"
     18 
     19 #include "DNSCommon.h"
     20 #include "uDNS.h"
     21 
     22 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
     23 #include "QuerierSupport.h"
     24 #endif
     25 
     26 #if MDNSRESPONDER_SUPPORTS(APPLE, D2D)
     27 #include "D2D.h"
     28 #endif
     29 
     30 #if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER)
     31 #include "mDNSMacOSX.h"
     32 #endif
     33 
     34 #if MDNSRESPONDER_SUPPORTS(APPLE, TRACKER_STATE)
     35 #include "resolved_cache.h"
     36 #endif
     37 
     38 #if MDNSRESPONDER_SUPPORTS(APPLE, UNREADY_INTERFACES)
     39 #include <dispatch/dispatch.h>
     40 #include <net/if.h>
     41 #endif
     42 
     43 #if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER)
     44 #include <WebFilterDNS/WebFilterDNS.h>
     45 
     46 int WCFIsServerRunning(WCFConnection *conn) __attribute__((weak_import));
     47 int WCFNameResolvesToAddr(WCFConnection *conn, char* domainName, struct sockaddr* address, uid_t userid) __attribute__((weak_import));
     48 int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid_t userid) __attribute__((weak_import));
     49 #endif
     50 
     51 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
     52 #include "dnssec.h"
     53 #endif
     54 
     55 #include "mdns_strict.h"
     56 
     57 #define RecordTypeIsAddress(TYPE)   (((TYPE) == kDNSType_A) || ((TYPE) == kDNSType_AAAA))
     58 
     59 extern mDNS mDNSStorage;
     60 #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
     61 extern domainname ActiveDirectoryPrimaryDomain;
     62 #endif
     63 
     64 // Normally we append search domains only for queries with a single label that are not fully qualified. This can be
     65 // overridden to apply search domains for queries (that are not fully qualified) with any number of labels e.g., moon,
     66 // moon.cs, moon.cs.be, etc. - Mohan
     67 mDNSBool AlwaysAppendSearchDomains = mDNSfalse;
     68 
     69 // Control enabling optimistic DNS - Phil
     70 mDNSBool EnableAllowExpired = mDNStrue;
     71 
     72 typedef struct
     73 {
     74     mDNSu32                 requestID;
     75     const domainname *      qname;
     76     mDNSu16                 qtype;
     77     mDNSu16                 qclass;
     78     mDNSInterfaceID         interfaceID;
     79     mDNSs32                 serviceID;
     80     mDNSu32                 flags;
     81     mDNSBool                appendSearchDomains;
     82     mDNSs32                 effectivePID;
     83     const mDNSu8 *          effectiveUUID;
     84     mDNSu32                 peerUID;
     85     mDNSBool                isInAppBrowserRequest;
     86     mDNSBool                useAAAAFallback;
     87 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
     88     const mDNSu8 *          resolverUUID;
     89 	mdns_dns_service_id_t	customID;
     90     mDNSBool                needEncryption;
     91     mDNSBool                useFailover;
     92     mDNSBool                failoverMode;
     93     mDNSBool                prohibitEncryptedDNS;
     94     mDNSBool                overrideDNSService;
     95 #endif
     96 #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN)
     97     mdns_audit_token_t      peerToken;
     98     mdns_audit_token_t      delegatorToken;
     99 #endif
    100 #if MDNSRESPONDER_SUPPORTS(APPLE, LOG_PRIVACY_LEVEL)
    101     dnssd_log_privacy_level_t logPrivacyLevel;
    102 #endif
    103     mDNSBool                persistWhenARecordsUnusable;
    104 
    105 }   QueryRecordOpParams;
    106 
    107 mDNSlocal void QueryRecordOpParamsInit(QueryRecordOpParams *inParams)
    108 {
    109 	mDNSPlatformMemZero(inParams, (mDNSu32)sizeof(*inParams));
    110     inParams->serviceID = -1;
    111 }
    112 
    113 mDNSlocal mStatus QueryRecordOpCreate(QueryRecordOp **outOp);
    114 mDNSlocal void QueryRecordOpFree(QueryRecordOp *operation);
    115 mDNSlocal mStatus QueryRecordOpStart(QueryRecordOp *inOp, const QueryRecordOpParams *inParams,
    116     QueryRecordResultHandler inResultHandler, void *inResultContext);
    117 mDNSlocal void QueryRecordOpStop(QueryRecordOp *op);
    118 mDNSlocal mDNSBool QueryRecordOpIsMulticast(const QueryRecordOp *op);
    119 mDNSlocal void QueryRecordOpCallback(mDNS *m, DNSQuestion *inQuestion, const ResourceRecord *inAnswer,
    120     QC_result inAddRecord);
    121 mDNSlocal void QueryRecordOpResetHandler(DNSQuestion *inQuestion);
    122 mDNSlocal mStatus QueryRecordOpStartQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion);
    123 mDNSlocal mStatus QueryRecordOpStopQuestion(DNSQuestion *inQuestion);
    124 mDNSlocal mStatus QueryRecordOpRestartUnicastQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion,
    125     const domainname *inSearchDomain);
    126 mDNSlocal mStatus InterfaceIndexToInterfaceID(mDNSu32 inInterfaceIndex, mDNSInterfaceID *outInterfaceID);
    127 mDNSlocal mDNSBool DomainNameIsSingleLabel(const domainname *inName);
    128 mDNSlocal mDNSBool StringEndsWithDot(const char *inString);
    129 mDNSlocal const domainname * NextSearchDomain(QueryRecordOp *inOp);
    130 #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
    131 mDNSlocal mDNSBool DomainNameIsInSearchList(const domainname *domain, mDNSBool inExcludeLocal);
    132 #endif
    133 #if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER)
    134 mDNSlocal void NotifyWebContentFilter(const ResourceRecord *inAnswer, uid_t inUID);
    135 #endif
    136 
    137 mDNSexport void GetAddrInfoClientRequestParamsInit(GetAddrInfoClientRequestParams *inParams)
    138 {
    139 	mDNSPlatformMemZero(inParams, (mDNSu32)sizeof(*inParams));
    140 }
    141 
    142 mDNSexport mStatus GetAddrInfoClientRequestStart(GetAddrInfoClientRequest *inRequest,
    143     const GetAddrInfoClientRequestParams *inParams, QueryRecordResultHandler inResultHandler, void *inResultContext)
    144 {
    145     mStatus             err;
    146     domainname          hostname;
    147     mDNSBool            appendSearchDomains;
    148     mDNSInterfaceID     interfaceID;
    149     DNSServiceFlags     flags;
    150     mDNSs32             serviceID;
    151     QueryRecordOpParams opParams;
    152 
    153     if (!MakeDomainNameFromDNSNameString(&hostname, inParams->hostnameStr))
    154     {
    155         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
    156                "[R%u] ERROR: bad hostname '" PRI_S "'", inParams->requestID, inParams->hostnameStr);
    157         err = mStatus_BadParamErr;
    158         goto exit;
    159     }
    160 
    161     if (inParams->protocols & ~((mDNSu32)(kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6)))
    162     {
    163         err = mStatus_BadParamErr;
    164         goto exit;
    165     }
    166 
    167     flags = inParams->flags;
    168     if (inParams->protocols == 0)
    169     {
    170         flags |= kDNSServiceFlagsSuppressUnusable;
    171         inRequest->protocols = kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6;
    172     }
    173     else
    174     {
    175         inRequest->protocols = inParams->protocols;
    176     }
    177 
    178     if (flags & kDNSServiceFlagsServiceIndex)
    179     {
    180         // NOTE: kDNSServiceFlagsServiceIndex flag can only be set for DNSServiceGetAddrInfo()
    181         LogInfo("GetAddrInfoClientRequestStart: kDNSServiceFlagsServiceIndex is SET by the client");
    182 
    183         // If kDNSServiceFlagsServiceIndex is SET, interpret the interfaceID as the serviceId and set the interfaceID to 0.
    184         serviceID   = (mDNSs32)inParams->interfaceIndex;
    185         interfaceID = mDNSNULL;
    186     }
    187     else
    188     {
    189         serviceID = -1;
    190         err = InterfaceIndexToInterfaceID(inParams->interfaceIndex, &interfaceID);
    191         if (err) goto exit;
    192     }
    193     inRequest->interfaceID = interfaceID;
    194 
    195     if (!StringEndsWithDot(inParams->hostnameStr) && (AlwaysAppendSearchDomains || DomainNameIsSingleLabel(&hostname)))
    196     {
    197         appendSearchDomains = mDNStrue;
    198     }
    199     else
    200     {
    201         appendSearchDomains = mDNSfalse;
    202     }
    203     QueryRecordOpParamsInit(&opParams);
    204     opParams.requestID              = inParams->requestID;
    205     opParams.qname                  = &hostname;
    206     opParams.qclass                 = kDNSClass_IN;
    207     opParams.interfaceID            = inRequest->interfaceID;
    208     opParams.serviceID              = serviceID;
    209     opParams.flags                  = flags;
    210     opParams.appendSearchDomains    = appendSearchDomains;
    211     opParams.effectivePID           = inParams->effectivePID;
    212     opParams.effectiveUUID          = inParams->effectiveUUID;
    213     opParams.peerUID                = inParams->peerUID;
    214 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
    215     opParams.resolverUUID           = inParams->resolverUUID;
    216     opParams.customID               = inParams->customID;
    217     opParams.needEncryption         = inParams->needEncryption;
    218     opParams.useFailover            = inParams->useFailover;
    219     opParams.failoverMode           = inParams->failoverMode;
    220     opParams.prohibitEncryptedDNS   = inParams->prohibitEncryptedDNS;
    221 #endif
    222 #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN)
    223     opParams.peerToken              = inParams->peerToken;
    224     opParams.delegatorToken         = inParams->delegatorToken;
    225     opParams.isInAppBrowserRequest  = inParams->isInAppBrowserRequest;
    226 #endif
    227 #if MDNSRESPONDER_SUPPORTS(APPLE, LOG_PRIVACY_LEVEL)
    228     opParams.logPrivacyLevel        = inParams->logPrivacyLevel;
    229 #endif
    230     opParams.persistWhenARecordsUnusable = inParams->persistWhenARecordsUnusable;
    231 
    232     if (inRequest->protocols & kDNSServiceProtocol_IPv6)
    233     {
    234         err = QueryRecordOpCreate(&inRequest->op6);
    235         if (err) goto exit;
    236 
    237         opParams.qtype = kDNSType_AAAA;
    238         err = QueryRecordOpStart(inRequest->op6, &opParams, inResultHandler, inResultContext);
    239         if (err) goto exit;
    240     }
    241     if (inRequest->protocols & kDNSServiceProtocol_IPv4)
    242     {
    243         err = QueryRecordOpCreate(&inRequest->op4);
    244         if (err) goto exit;
    245 
    246         opParams.qtype = kDNSType_A;
    247         err = QueryRecordOpStart(inRequest->op4, &opParams, inResultHandler, inResultContext);
    248         if (err) goto exit;
    249     }
    250     err = mStatus_NoError;
    251 
    252 exit:
    253     if (err) GetAddrInfoClientRequestStop(inRequest);
    254     return err;
    255 }
    256 
    257 mDNSexport void GetAddrInfoClientRequestStop(GetAddrInfoClientRequest *inRequest)
    258 {
    259     if (inRequest->op4) QueryRecordOpStop(inRequest->op4);
    260     if (inRequest->op6) QueryRecordOpStop(inRequest->op6);
    261 
    262 #if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER)
    263     {
    264         const QueryRecordOp * const     op4 = inRequest->op4;
    265         const QueryRecordOp * const     op6 = inRequest->op6;
    266         const DNSQuestion *             q4  = mDNSNULL;
    267         const DNSQuestion *             q6  = mDNSNULL;
    268 
    269         if (op4)
    270         {
    271             if (op4->answered)
    272             {
    273                 // If we have a v4 answer and if we timed out prematurely before, provide a trigger to the upper layer so
    274                 // that it can retry questions if needed. - Mohan
    275                 q4 = &op4->q;
    276             }
    277             else if (op4->q.TimeoutQuestion)
    278             {
    279                 // If we are not delivering answers, we may be timing out prematurely. Note down the current state so that
    280                 // we know to retry when we see a valid response again. - Mohan
    281                 mDNSPlatformUpdateDNSStatus(&op4->q);
    282             }
    283         }
    284         if (op6)
    285         {
    286             if (op6->answered)
    287             {
    288                 q6 = &op6->q;
    289             }
    290             else if (op6->q.TimeoutQuestion)
    291             {
    292                 mDNSPlatformUpdateDNSStatus(&op6->q);
    293             }
    294         }
    295         mDNSPlatformTriggerDNSRetry(q4, q6);
    296     }
    297 #endif
    298 
    299     if (inRequest->op4)
    300     {
    301         QueryRecordOpFree(inRequest->op4);
    302         inRequest->op4 = mDNSNULL;
    303     }
    304     if (inRequest->op6)
    305     {
    306         QueryRecordOpFree(inRequest->op6);
    307         inRequest->op6 = mDNSNULL;
    308     }
    309 }
    310 
    311 mDNSexport const domainname * GetAddrInfoClientRequestGetQName(const GetAddrInfoClientRequest *inRequest)
    312 {
    313     if (inRequest->op4) return &inRequest->op4->q.qname;
    314     if (inRequest->op6) return &inRequest->op6->q.qname;
    315     return (const domainname *)"";
    316 }
    317 
    318 mDNSexport mDNSBool GetAddrInfoClientRequestIsMulticast(const GetAddrInfoClientRequest *inRequest)
    319 {
    320     if ((inRequest->op4 && QueryRecordOpIsMulticast(inRequest->op4)) ||
    321         (inRequest->op6 && QueryRecordOpIsMulticast(inRequest->op6)))
    322     {
    323         return mDNStrue;
    324     }
    325     return mDNSfalse;
    326 }
    327 
    328 mDNSexport void QueryRecordClientRequestParamsInit(QueryRecordClientRequestParams *inParams)
    329 {
    330 	mDNSPlatformMemZero(inParams, (mDNSu32)sizeof(*inParams));
    331 }
    332 
    333 mDNSexport mStatus QueryRecordClientRequestStart(QueryRecordClientRequest *inRequest,
    334     const QueryRecordClientRequestParams *inParams, QueryRecordResultHandler inResultHandler, void *inResultContext)
    335 {
    336     mStatus             err;
    337     domainname          qname;
    338     mDNSInterfaceID     interfaceID;
    339     mDNSBool            appendSearchDomains;
    340     QueryRecordOpParams opParams;
    341 
    342 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
    343     if (inParams->overrideDNSService)
    344     {
    345         const mdns_audit_token_t token = inParams->peerToken;
    346         const mDNSBool entitled = token && mdns_audit_token_is_entitled(token, "com.apple.private.dnssd.resolver-override");
    347         mdns_require_action_quiet(entitled, exit, err = mStatus_NoAuth);
    348         mdns_require_action_quiet(inParams->resolverUUID, exit, err = mStatus_BadParamErr);
    349 
    350         Querier_RegisterPathResolver(inParams->resolverUUID);
    351     }
    352 #endif
    353     err = InterfaceIndexToInterfaceID(inParams->interfaceIndex, &interfaceID);
    354     if (err) goto exit;
    355 
    356     if (!MakeDomainNameFromDNSNameString(&qname, inParams->qnameStr))
    357     {
    358         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
    359                "[R%u] ERROR: bad domain name '" PRI_S "'", inParams->requestID, inParams->qnameStr);
    360         err = mStatus_BadParamErr;
    361         goto exit;
    362     }
    363 
    364     if (RecordTypeIsAddress(inParams->qtype) && !StringEndsWithDot(inParams->qnameStr) &&
    365         (AlwaysAppendSearchDomains || DomainNameIsSingleLabel(&qname)))
    366     {
    367         appendSearchDomains = mDNStrue;
    368     }
    369     else
    370     {
    371         appendSearchDomains = mDNSfalse;
    372     }
    373     QueryRecordOpParamsInit(&opParams);
    374     opParams.requestID              = inParams->requestID;
    375     opParams.qname                  = &qname;
    376     opParams.flags                  = inParams->flags;
    377     opParams.qtype                  = inParams->qtype;
    378     opParams.qclass                 = inParams->qclass;
    379     opParams.interfaceID            = interfaceID;
    380     opParams.appendSearchDomains    = appendSearchDomains;
    381     opParams.effectivePID           = inParams->effectivePID;
    382     opParams.effectiveUUID          = inParams->effectiveUUID;
    383     opParams.peerUID                = inParams->peerUID;
    384 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
    385     opParams.resolverUUID           = inParams->resolverUUID;
    386     opParams.customID               = inParams->customID;
    387     opParams.needEncryption         = inParams->needEncryption;
    388     opParams.useFailover            = inParams->useFailover;
    389     opParams.failoverMode           = inParams->failoverMode;
    390     opParams.prohibitEncryptedDNS   = inParams->prohibitEncryptedDNS;
    391     opParams.overrideDNSService     = inParams->overrideDNSService;
    392 #endif
    393 #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN)
    394     opParams.peerToken              = inParams->peerToken;
    395     opParams.delegatorToken         = inParams->delegatorToken;
    396     opParams.isInAppBrowserRequest  = inParams->isInAppBrowserRequest;
    397 #endif
    398     opParams.useAAAAFallback   = inParams->useAAAAFallback;
    399 #if MDNSRESPONDER_SUPPORTS(APPLE, LOG_PRIVACY_LEVEL)
    400     opParams.logPrivacyLevel   = inParams->logPrivacyLevel;
    401 #endif
    402 
    403     err = QueryRecordOpStart(&inRequest->op, &opParams, inResultHandler, inResultContext);
    404 
    405 exit:
    406     if (err) QueryRecordClientRequestStop(inRequest);
    407     return err;
    408 }
    409 
    410 mDNSexport void QueryRecordClientRequestStop(QueryRecordClientRequest *inRequest)
    411 {
    412     QueryRecordOpStop(&inRequest->op);
    413 
    414 #if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER)
    415     if (inRequest->op.answered)
    416     {
    417         DNSQuestion *v4q, *v6q;
    418         // If we are receiving positive answers, provide the hint to the upper layer. - Mohan
    419         v4q = (inRequest->op.q.qtype == kDNSType_A)    ? &inRequest->op.q : mDNSNULL;
    420         v6q = (inRequest->op.q.qtype == kDNSType_AAAA) ? &inRequest->op.q : mDNSNULL;
    421         mDNSPlatformTriggerDNSRetry(v4q, v6q);
    422     }
    423 #endif
    424 }
    425 
    426 mDNSexport const domainname * QueryRecordClientRequestGetQName(const QueryRecordClientRequest *inRequest)
    427 {
    428     return &inRequest->op.q.qname;
    429 }
    430 
    431 mDNSexport mDNSu16 QueryRecordClientRequestGetType(const QueryRecordClientRequest *inRequest)
    432 {
    433     return inRequest->op.q.qtype;
    434 }
    435 
    436 mDNSexport mDNSBool QueryRecordClientRequestIsMulticast(QueryRecordClientRequest *inRequest)
    437 {
    438     return (QueryRecordOpIsMulticast(&inRequest->op) ? mDNStrue : mDNSfalse);
    439 }
    440 
    441 mDNSlocal mStatus QueryRecordOpCreate(QueryRecordOp **outOp)
    442 {
    443     mStatus err;
    444     QueryRecordOp *op;
    445 
    446     op = (QueryRecordOp *) mDNSPlatformMemAllocateClear(sizeof(*op));
    447     if (!op)
    448     {
    449         err = mStatus_NoMemoryErr;
    450         goto exit;
    451     }
    452     *outOp = op;
    453     err = mStatus_NoError;
    454 
    455 exit:
    456     return err;
    457 }
    458 
    459 mDNSlocal void QueryRecordOpFree(QueryRecordOp *operation)
    460 {
    461     mDNSPlatformMemFree(operation);
    462 }
    463 
    464 mDNSlocal void QueryRecordOpEventHandler(DNSQuestion *const inQuestion, const mDNSQuestionEvent event)
    465 {
    466     QueryRecordOp *const op = (QueryRecordOp *)inQuestion->QuestionContext;
    467     switch (event)
    468     {
    469         case mDNSQuestionEvent_NoMoreExpiredRecords:
    470             if ((inQuestion->ExpRecordPolicy == mDNSExpiredRecordPolicy_UseCached) && op->gotExpiredCNAME)
    471             {
    472                 // If an expired CNAME record was encountered, then rewind back to the original QNAME.
    473                 QueryRecordOpStopQuestion(inQuestion);
    474                 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO,
    475                     "[R%u->Q%u] Restarting question that got expired CNAMEs -- current name: " PRI_DM_NAME
    476                     ", original name: " PRI_DM_NAME ", type: " PUB_DNS_TYPE,
    477                     op->reqID, mDNSVal16(inQuestion->TargetQID), DM_NAME_PARAM(&inQuestion->qname), DM_NAME_PARAM(op->qname),
    478                     DNS_TYPE_PARAM(inQuestion->qtype));
    479                 op->gotExpiredCNAME = mDNSfalse;
    480                 AssignDomainName(&inQuestion->qname, op->qname);
    481                 inQuestion->ExpRecordPolicy = mDNSExpiredRecordPolicy_Immortalize;
    482             #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
    483                 mDNSPlatformMemCopy(inQuestion->ResolverUUID, op->resolverUUID, UUID_SIZE);
    484             #endif
    485                 const domainname *domain = mDNSNULL;
    486                 // If we're appending search domains, the DNSQuestion needs to be retried without Optimistic DNS,
    487                 // but with the search domain we just used, so restore the search list index to avoid skipping to
    488                 // the next search domain.
    489                 //
    490                 // Note that when AppendSearchDomains is true, searchListIndex is the index of the next search
    491                 // domain to try. So if searchListIndex is 0 or negative, that means that we are not currently in
    492                 // the middle of iterating the search domain list, so no search domain needs to be restored. If
    493                 // searchListIndex is greater than 0, then we're currently in the middle of iterating through the
    494                 // search domain list, so the search domain that's currently in effect needs to be restored.
    495                 if (inQuestion->AppendSearchDomains && (op->searchListIndex > 0))
    496                 {
    497                     op->searchListIndex = op->searchListIndexLast;
    498                     domain = NextSearchDomain(op);
    499                 }
    500                 QueryRecordOpRestartUnicastQuestion(op, inQuestion, domain);
    501             }
    502             break;
    503 
    504         MDNS_COVERED_SWITCH_DEFAULT:
    505             break;
    506     }
    507 }
    508 
    509 #define VALID_MSAD_SRV_TRANSPORT(T) \
    510     (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp"))
    511 #define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname)))
    512 
    513 mDNSlocal mStatus QueryRecordOpStart(QueryRecordOp *inOp, const QueryRecordOpParams *inParams,
    514     QueryRecordResultHandler inResultHandler, void *inResultContext)
    515 {
    516     mStatus                 err;
    517     DNSQuestion * const     q = &inOp->q;
    518     mDNSu32                 len;
    519 
    520     // Save the original qname.
    521 
    522     len = DomainNameLength(inParams->qname);
    523     inOp->qname = (domainname *) mDNSPlatformMemAllocate(len);
    524     if (!inOp->qname)
    525     {
    526         err = mStatus_NoMemoryErr;
    527         goto exit;
    528     }
    529     mDNSPlatformMemCopy(inOp->qname, inParams->qname, len);
    530 
    531     inOp->interfaceID          = inParams->interfaceID;
    532     inOp->reqID                = inParams->requestID;
    533     inOp->resultHandler        = inResultHandler;
    534     inOp->resultContext        = inResultContext;
    535     inOp->useAAAAFallback      = inParams->useAAAAFallback;
    536 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
    537     inOp->useFailover          = inParams->useFailover;
    538     inOp->failoverMode         = inParams->failoverMode;
    539     inOp->prohibitEncryptedDNS = inParams->prohibitEncryptedDNS;
    540     inOp->overrideDNSService   = inParams->overrideDNSService;
    541     inOp->qtype                = inParams->qtype;
    542     if ((!inOp->prohibitEncryptedDNS || inOp->overrideDNSService) && inParams->resolverUUID)
    543     {
    544         mDNSPlatformMemCopy(inOp->resolverUUID, inParams->resolverUUID, UUID_SIZE);
    545     }
    546 #endif
    547 #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN)
    548     mdns_replace(&inOp->peerToken, inParams->peerToken);
    549     mdns_replace(&inOp->delegatorToken, inParams->delegatorToken);
    550 #endif
    551     // Set up DNSQuestion.
    552     if (EnableAllowExpired && (inParams->flags & kDNSServiceFlagsAllowExpiredAnswers))
    553     {
    554         q->ExpRecordPolicy     = mDNSExpiredRecordPolicy_UseCached;
    555     }
    556     else
    557     {
    558         q->ExpRecordPolicy     = mDNSExpiredRecordPolicy_DoNotUse;
    559     }
    560     q->ServiceID                  = inParams->serviceID;
    561 #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN)
    562     q->inAppBrowserRequest        = inParams->isInAppBrowserRequest;
    563     q->PeerToken                  = inOp->peerToken;
    564     q->DelegatorToken             = inOp->delegatorToken;
    565 #endif
    566     q->InterfaceID                = inParams->interfaceID;
    567     q->flags                      = inParams->flags;
    568     AssignDomainName(&q->qname, inParams->qname);
    569     q->qtype                      = inParams->qtype;
    570     q->qclass                     = inParams->qclass;
    571     q->LongLived                  = (inParams->flags & kDNSServiceFlagsLongLivedQuery)         ? mDNStrue : mDNSfalse;
    572     q->ForceMCast                 = (inParams->flags & kDNSServiceFlagsForceMulticast)         ? mDNStrue : mDNSfalse;
    573     q->ReturnIntermed             = (inParams->flags & kDNSServiceFlagsReturnIntermediates)    ? mDNStrue : mDNSfalse;
    574     q->SuppressUnusable           = (inParams->flags & kDNSServiceFlagsSuppressUnusable)       ? mDNStrue : mDNSfalse;
    575     q->TimeoutQuestion            = (inParams->flags & kDNSServiceFlagsTimeout)                ? mDNStrue : mDNSfalse;
    576     q->UseBackgroundTraffic       = (inParams->flags & kDNSServiceFlagsBackgroundTrafficClass) ? mDNStrue : mDNSfalse;
    577 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
    578     q->enableDNSSEC               = dns_service_flags_enables_dnssec(inParams->flags);
    579 #endif
    580     q->AppendSearchDomains        = inParams->appendSearchDomains;
    581     q->PersistWhenRecordsUnusable = (inParams->qtype == kDNSType_A) && inParams->persistWhenARecordsUnusable;
    582 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
    583     q->RequireEncryption          = inParams->needEncryption;
    584     q->CustomID                   = inParams->customID;
    585     q->ProhibitEncryptedDNS       = inOp->prohibitEncryptedDNS;
    586     q->OverrideDNSService         = inOp->overrideDNSService;
    587     if (inOp->failoverMode)
    588     {
    589         q->IsFailover = mDNStrue;
    590         // Force a path evaluation if the DNSQuestion isn't interface-scoped.
    591         if (!q->InterfaceID)
    592         {
    593             q->ForcePathEval = mDNStrue;
    594         }
    595     }
    596     mDNSPlatformMemCopy(q->ResolverUUID, inOp->resolverUUID, UUID_SIZE);
    597 #endif
    598     q->InitialCacheMiss     = mDNSfalse;
    599 
    600 #if MDNSRESPONDER_SUPPORTS(APPLE, LOG_PRIVACY_LEVEL)
    601     q->logPrivacyLevel      = inParams->logPrivacyLevel;
    602 #endif
    603 
    604     q->pid              = inParams->effectivePID;
    605     if (inParams->effectiveUUID)
    606     {
    607         mDNSPlatformMemCopy(q->uuid, inParams->effectiveUUID, UUID_SIZE);
    608     }
    609     q->euid             = inParams->peerUID;
    610     q->request_id       = inParams->requestID;
    611     q->QuestionCallback = QueryRecordOpCallback;
    612     q->ResetHandler     = QueryRecordOpResetHandler;
    613     q->EventHandler     = QueryRecordOpEventHandler;
    614 
    615     // For single label queries that are not fully qualified, look at /etc/hosts, cache and try search domains before trying
    616     // them on the wire as a single label query. - Mohan
    617 
    618     if (q->AppendSearchDomains && DomainNameIsSingleLabel(inOp->qname)) q->InterfaceID = mDNSInterface_LocalOnly;
    619 
    620 #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
    621     // Copy the original question to another question that can be used for the legacy unicast dotlocal query, before the
    622     // the original question is started and initialized.
    623     const mDNSBool startParallelLegacyUnicastDotLocal = ((RecordTypeIsAddress(q->qtype) || VALID_MSAD_SRV(&inOp->q)) &&
    624         !q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain));
    625     if (startParallelLegacyUnicastDotLocal)
    626     {
    627         inOp->q2 = mDNSPlatformMemAllocateClear(sizeof(*inOp->q2));
    628         mdns_require_action_quiet(inOp->q2, exit, err = mStatus_NoMemoryErr);
    629         *inOp->q2 = *q;
    630     }
    631 #endif
    632 
    633     err = QueryRecordOpStartQuestion(inOp, q);
    634     if (err) goto exit;
    635 
    636 #if MDNSRESPONDER_SUPPORTS(APPLE, D2D)
    637     if (callExternalHelpers(q->InterfaceID, &q->qname, q->flags))
    638     {
    639         external_start_browsing_for_service(q->InterfaceID, &q->qname, q->qtype, q->flags, q->pid);
    640     }
    641 #endif
    642 
    643 #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
    644     if (startParallelLegacyUnicastDotLocal)
    645     {
    646         DNSQuestion *const q2 = inOp->q2;
    647         mdns_require_action_quiet(q2, exit, err = mStatus_BadStateErr);
    648 
    649         if ((CountLabels(&q2->qname) == 2) && !SameDomainName(&q2->qname, &ActiveDirectoryPrimaryDomain)
    650             && !DomainNameIsInSearchList(&q2->qname, mDNSfalse))
    651         {
    652             inOp->q2Type                = q2->qtype;
    653             inOp->q2LongLived           = q2->LongLived;
    654             inOp->q2ReturnIntermed      = q2->ReturnIntermed;
    655             inOp->q2TimeoutQuestion     = q2->TimeoutQuestion;
    656             inOp->q2AppendSearchDomains = q2->AppendSearchDomains;
    657 
    658             AssignDomainName(&q2->qname, &localdomain);
    659             q2->qtype                   = kDNSType_SOA;
    660             q2->ReturnIntermed          = mDNStrue;
    661             q2->TimeoutQuestion         = mDNSfalse;
    662             q2->AppendSearchDomains     = mDNSfalse;
    663         }
    664         q2->IsUnicastDotLocal = mDNStrue;
    665         // We disable DNS push for this legacy unicast dotlocal query.
    666         q2->LongLived = mDNSfalse;
    667 
    668         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
    669                "[R%u] QueryRecordOpStart: starting parallel unicast query for " PRI_DM_NAME " " PUB_S,
    670                inOp->reqID, DM_NAME_PARAM(&q2->qname), DNSTypeName(q2->qtype));
    671 
    672         err = QueryRecordOpStartQuestion(inOp, q2);
    673         if (err) goto exit;
    674     }
    675 #endif
    676     err = mStatus_NoError;
    677 
    678 exit:
    679     if (err) QueryRecordOpStop(inOp);
    680     return err;
    681 }
    682 
    683 mDNSlocal void QueryRecordOpStop(QueryRecordOp *op)
    684 {
    685     if (op->q.QuestionContext)
    686     {
    687         QueryRecordOpStopQuestion(&op->q);
    688 #if MDNSRESPONDER_SUPPORTS(APPLE, D2D)
    689         if (callExternalHelpers(op->q.InterfaceID, op->qname, op->q.flags))
    690         {
    691             external_stop_browsing_for_service(op->q.InterfaceID, &op->q.qname, op->q.qtype, op->q.flags, op->q.pid);
    692         }
    693 #endif
    694     }
    695     if (op->qname)
    696     {
    697         mDNSPlatformMemFree(op->qname);
    698         op->qname = mDNSNULL;
    699     }
    700 #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
    701     if (op->q2)
    702     {
    703         if (op->q2->QuestionContext) QueryRecordOpStopQuestion(op->q2);
    704         mDNSPlatformMemFree(op->q2);
    705         op->q2 = mDNSNULL;
    706     }
    707 #endif
    708 #if MDNSRESPONDER_SUPPORTS(APPLE, AUDIT_TOKEN)
    709     mdns_forget(&op->peerToken);
    710     mdns_forget(&op->delegatorToken);
    711 #endif
    712 }
    713 
    714 mDNSlocal mDNSBool QueryRecordOpIsMulticast(const QueryRecordOp *op)
    715 {
    716     return ((mDNSOpaque16IsZero(op->q.TargetQID) && (op->q.ThisQInterval > 0)) ? mDNStrue : mDNSfalse);
    717 }
    718 
    719 // GetTimeNow is a callback-safe alternative to mDNS_TimeNow(), which expects to be called with m->mDNS_busy == 0.
    720 mDNSlocal mDNSs32 GetTimeNow(mDNS *m)
    721 {
    722     mDNSs32 time;
    723     mDNS_Lock(m);
    724     time = m->timenow;
    725     mDNS_Unlock(m);
    726     return time;
    727 }
    728 
    729 mDNSlocal void QueryRecordOpCallback(mDNS *m, DNSQuestion *inQuestion, const ResourceRecord *inAnswer, QC_result inAddRecord)
    730 {
    731     mStatus                     resultErr;
    732     QueryRecordOp *const        op = (QueryRecordOp *)inQuestion->QuestionContext;
    733     const domainname *          domain;
    734 
    735 #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
    736     if ((inQuestion == op->q2) && (inQuestion->qtype == kDNSType_SOA))
    737     {
    738         DNSQuestion * const     q2 = op->q2;
    739 
    740         if (inAnswer->rrtype != kDNSType_SOA) goto exit;
    741         QueryRecordOpStopQuestion(q2);
    742 
    743         // Restore DNSQuestion variables that were modified for the SOA query.
    744 
    745         q2->qtype               = op->q2Type;
    746         q2->LongLived           = op->q2LongLived;
    747         q2->ReturnIntermed      = op->q2ReturnIntermed;
    748         q2->TimeoutQuestion     = op->q2TimeoutQuestion;
    749         q2->AppendSearchDomains = op->q2AppendSearchDomains;
    750 
    751         if (inAnswer->RecordType != kDNSRecordTypePacketNegative)
    752         {
    753             QueryRecordOpRestartUnicastQuestion(op, q2, mDNSNULL);
    754         }
    755         else if (q2->AppendSearchDomains)
    756         {
    757             domain = NextSearchDomain(op);
    758             if (domain) QueryRecordOpRestartUnicastQuestion(op, q2, domain);
    759         }
    760         goto exit;
    761     }
    762 #endif
    763     // The mDNSExpiredRecordPolicy_UseCached policy unconditionally provides us with CNAMEs. So if the client
    764     // doesn't want intermediate results, which includes CNAMEs, then don't provide them with CNAMEs unless the
    765     // client request was specifically for CNAME records.
    766     if (inQuestion->ExpRecordPolicy == mDNSExpiredRecordPolicy_UseCached)
    767     {
    768         if ((inAddRecord == QC_add) && (inAnswer->rrtype == kDNSType_CNAME))
    769         {
    770             if (inAnswer->mortality == Mortality_Ghost)
    771             {
    772                 op->gotExpiredCNAME = mDNStrue;
    773             }
    774             if (!(inQuestion->ReturnIntermed || (inQuestion->qtype == kDNSType_CNAME)))
    775             {
    776                 goto exit;
    777             }
    778         }
    779     }
    780     if (inAddRecord == QC_suppressed)
    781     {
    782         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
    783                "[R%u] QueryRecordOpCallback: Suppressed question " PRI_DM_NAME " (" PUB_S ")",
    784                op->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype));
    785 
    786         resultErr = kDNSServiceErr_NoSuchRecord;
    787     }
    788     else if (inAnswer->RecordType == kDNSRecordTypePacketNegative)
    789     {
    790         if (inQuestion->TimeoutQuestion && ((GetTimeNow(m) - inQuestion->StopTime) >= 0))
    791         {
    792             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
    793                    "[R%u] QueryRecordOpCallback: Question " PRI_DM_NAME " (" PUB_S ") timing out, InterfaceID %p",
    794                    op->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype),
    795                    inQuestion->InterfaceID);
    796             resultErr = kDNSServiceErr_Timeout;
    797         }
    798         else
    799         {
    800             if (inQuestion->AppendSearchDomains && (op->searchListIndex >= 0) && inAddRecord)
    801             {
    802                 domain = NextSearchDomain(op);
    803                 if (domain || DomainNameIsSingleLabel(op->qname))
    804                 {
    805                     QueryRecordOpStopQuestion(inQuestion);
    806                     QueryRecordOpRestartUnicastQuestion(op, inQuestion, domain);
    807                     goto exit;
    808                 }
    809             }
    810             if (op->useAAAAFallback && (inQuestion->qtype == kDNSType_AAAA) && (inAnswer->rcode != kDNSFlag1_RC_NXDomain))
    811             {
    812                 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
    813                     "[R%u] Restarting question for " PRI_DM_NAME " AAAA record as question for A record (RCODE %d)",
    814                     op->reqID, DM_NAME_PARAM(&inQuestion->qname), inAnswer->rcode);
    815                 QueryRecordOpStopQuestion(inQuestion);
    816                 inQuestion->qtype = kDNSType_A;
    817                 QueryRecordOpStartQuestion(op, inQuestion);
    818                 goto exit;
    819             }
    820 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
    821             if (op->useFailover && !inQuestion->IsFailover && inQuestion->dnsservice &&
    822                 mdns_dns_service_allows_failover(inQuestion->dnsservice))
    823             {
    824                 QueryRecordOpStopQuestion(inQuestion);
    825                 inQuestion->qtype = op->qtype; // Ensure that the original QTYPE is used in case AAAA fallback was used.
    826                 inQuestion->IsFailover = mDNStrue;
    827                 // Force a path evaluation if the DNSQuestion isn't interface-scoped.
    828                 if (!inQuestion->InterfaceID)
    829                 {
    830                     inQuestion->ForcePathEval = mDNStrue;
    831                 }
    832                 LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
    833                     "[R%u] Restarting question for " PRI_DM_NAME " (" PUB_S ") due to DNS service failover",
    834                     op->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype));
    835                 domain = mDNSNULL;
    836                 if (inQuestion->AppendSearchDomains)
    837                 {
    838                     op->searchListIndex = 0; // Reset search list usage
    839                     op->searchListIndexLast = 0;
    840                     domain = NextSearchDomain(op);
    841                 }
    842                 QueryRecordOpRestartUnicastQuestion(op, inQuestion, domain);
    843                 goto exit;
    844             }
    845 #endif
    846 #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
    847             if (!inAnswer->InterfaceID && IsLocalDomain(inAnswer->name))
    848             {
    849                 if ((RecordTypeIsAddress(inQuestion->qtype) && (inAnswer->rcode == kDNSFlag1_RC_NoErr)) ||
    850                     DomainNameIsInSearchList(&inQuestion->qname, mDNStrue))
    851                 {
    852                     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
    853                            "[R%u] QueryRecordOpCallback: Question " PRI_DM_NAME " (" PUB_S ") answering local with negative unicast response",
    854                            op->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype));
    855                 }
    856                 else
    857                 {
    858                     goto exit;
    859                 }
    860             }
    861 #endif
    862             resultErr = kDNSServiceErr_NoSuchRecord;
    863         }
    864     }
    865     else
    866     {
    867         resultErr = kDNSServiceErr_NoError;
    868     }
    869 
    870 #if MDNSRESPONDER_SUPPORTS(APPLE, REACHABILITY_TRIGGER)
    871     if ((resultErr != kDNSServiceErr_Timeout) && (inAddRecord == QC_add))
    872     {
    873         op->answered = mDNStrue;
    874     }
    875 #endif
    876 
    877 #if MDNSRESPONDER_SUPPORTS(APPLE, TRACKER_STATE)
    878     if (resolved_cache_is_enabled()                             &&
    879         inAddRecord                                             &&
    880         !mDNSOpaque16IsZero(inQuestion->TargetQID)              &&
    881         !LocalOnlyOrP2PInterface(inAnswer->InterfaceID)         &&
    882         inAnswer->RecordType != kDNSRecordTypePacketNegative    &&
    883         ((inAnswer->rrtype == kDNSServiceType_A)        ||
    884          (inAnswer->rrtype == kDNSServiceType_AAAA)))
    885     {
    886         const void *data_ptr;
    887         if (inAnswer->rrtype == kDNSServiceType_A)
    888         {
    889             data_ptr = inAnswer->rdata->u.ipv4.b;
    890         }
    891         else if (inAnswer->rrtype == kDNSServiceType_AAAA)
    892         {
    893             data_ptr = inAnswer->rdata->u.ipv6.b;
    894         }
    895         resolved_cache_append_address(inQuestion, inAnswer->rrtype, data_ptr);
    896     }
    897 #endif
    898 
    899     // The result handler is allowed to stop the client request, so it's not safe to touch the DNSQuestion or
    900     // the QueryRecordOp unless m->CurrentQuestion still points to this DNSQuestion.
    901 #if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER)
    902     const uid_t euid = inQuestion->euid;
    903 #endif
    904     if (op->resultHandler)
    905     {
    906         const mDNSBool expired = (inAddRecord == QC_add) && (op->gotExpiredCNAME || (inAnswer->mortality == Mortality_Ghost));
    907         op->resultHandler(m, inQuestion, inAnswer, expired, inAddRecord, resultErr, op->resultContext);
    908     }
    909     if (m->CurrentQuestion == inQuestion)
    910     {
    911         if (resultErr == kDNSServiceErr_Timeout) QueryRecordOpStopQuestion(inQuestion);
    912     }
    913 
    914 #if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER)
    915     NotifyWebContentFilter(inAnswer, euid);
    916 #endif
    917 
    918 exit:
    919     return;
    920 }
    921 
    922 mDNSlocal void QueryRecordOpResetHandler(DNSQuestion *inQuestion)
    923 {
    924     QueryRecordOp *const        op = (QueryRecordOp *)inQuestion->QuestionContext;
    925 
    926     AssignDomainName(&inQuestion->qname, op->qname);
    927     if (inQuestion->AppendSearchDomains && DomainNameIsSingleLabel(op->qname))
    928     {
    929         inQuestion->InterfaceID = mDNSInterface_LocalOnly;
    930     }
    931     else
    932     {
    933         inQuestion->InterfaceID = op->interfaceID;
    934     }
    935     op->searchListIndex = 0;
    936     op->searchListIndexLast = 0;
    937 }
    938 
    939 mDNSlocal mStatus QueryRecordOpStartQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion)
    940 {
    941     mStatus     err;
    942 
    943     inQuestion->QuestionContext = inOp;
    944     err = mDNS_StartQuery(&mDNSStorage, inQuestion);
    945     if (err)
    946     {
    947         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEFAULT,
    948                "[R%u] ERROR: QueryRecordOpStartQuestion mDNS_StartQuery for " PRI_DM_NAME " " PUB_S " failed with error %d",
    949                inOp->reqID, DM_NAME_PARAM(&inQuestion->qname), DNSTypeName(inQuestion->qtype), err);
    950         inQuestion->QuestionContext = mDNSNULL;
    951     }
    952     return err;
    953 }
    954 
    955 mDNSlocal mStatus QueryRecordOpStopQuestion(DNSQuestion *inQuestion)
    956 {
    957     mStatus     err;
    958 
    959 #if MDNSRESPONDER_SUPPORTS(APPLE, TRACKER_STATE)
    960     resolved_cache_delete(inQuestion);
    961 #endif
    962     err = mDNS_StopQuery(&mDNSStorage, inQuestion);
    963     inQuestion->QuestionContext = mDNSNULL;
    964     return err;
    965 }
    966 
    967 mDNSlocal mStatus QueryRecordOpRestartUnicastQuestion(QueryRecordOp *inOp, DNSQuestion *inQuestion,
    968     const domainname *inSearchDomain)
    969 {
    970     mStatus     err;
    971 
    972     inQuestion->InterfaceID = inOp->interfaceID;
    973     AssignDomainName(&inQuestion->qname, inOp->qname);
    974     if (inSearchDomain) AppendDomainName(&inQuestion->qname, inSearchDomain);
    975     if (SameDomainLabel(LastLabel(&inQuestion->qname), (const mDNSu8 *)&localdomain))
    976     {
    977         inQuestion->IsUnicastDotLocal = mDNStrue;
    978     }
    979     else
    980     {
    981         inQuestion->IsUnicastDotLocal = mDNSfalse;
    982     }
    983     err = QueryRecordOpStartQuestion(inOp, inQuestion);
    984     return err;
    985 }
    986 
    987 mDNSlocal mStatus InterfaceIndexToInterfaceID(mDNSu32 inInterfaceIndex, mDNSInterfaceID *outInterfaceID)
    988 {
    989     mStatus             err;
    990     mDNSInterfaceID     interfaceID;
    991 
    992     interfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, inInterfaceIndex);
    993 
    994 #if MDNSRESPONDER_SUPPORTS(APPLE, UNREADY_INTERFACES)
    995     // The request is scoped to a specific interface index, but the interface is not currently in our list.
    996     if ((inInterfaceIndex != kDNSServiceInterfaceIndexAny) && (interfaceID == mDNSInterface_Any))
    997     {
    998         static dispatch_once_t      getLoopbackIndexOnce = 0;
    999         static mDNSu32              loopbackIndex = 0;
   1000 
   1001         dispatch_once(&getLoopbackIndexOnce,
   1002         ^{
   1003             loopbackIndex = if_nametoindex("lo0");
   1004         });
   1005 
   1006         // If it's one of the specially defined inteface index values, just return an error. Also, caller should return an
   1007         // error immediately if lo0 is not configured into the current active interfaces. See <rdar://problem/21967160>.
   1008         if ((inInterfaceIndex == kDNSServiceInterfaceIndexLocalOnly) ||
   1009             (inInterfaceIndex == kDNSServiceInterfaceIndexUnicast)   ||
   1010             (inInterfaceIndex == kDNSServiceInterfaceIndexP2P)       ||
   1011             (inInterfaceIndex == kDNSServiceInterfaceIndexBLE)       ||
   1012             (inInterfaceIndex == loopbackIndex))
   1013         {
   1014             LogInfo("ERROR: bad interfaceIndex %d", inInterfaceIndex);
   1015             err = mStatus_BadParamErr;
   1016             goto exit;
   1017         }
   1018 
   1019         // Otherwise, use the specified interface index value and the request will be applied to that interface when it
   1020         // comes up.
   1021         interfaceID = (mDNSInterfaceID)(uintptr_t)inInterfaceIndex;
   1022         LogInfo("Query pending for interface index %d", inInterfaceIndex);
   1023     }
   1024 #endif
   1025 
   1026     *outInterfaceID = interfaceID;
   1027     err = mStatus_NoError;
   1028 
   1029 #if MDNSRESPONDER_SUPPORTS(APPLE, UNREADY_INTERFACES)
   1030 exit:
   1031 #endif
   1032     return err;
   1033 }
   1034 
   1035 mDNSlocal mDNSBool DomainNameIsSingleLabel(const domainname *inName)
   1036 {
   1037     const mDNSu8 *const     label = inName->c;
   1038     return (((label[0] != 0) && (label[1 + label[0]] == 0)) ? mDNStrue : mDNSfalse);
   1039 }
   1040 
   1041 mDNSlocal mDNSBool StringEndsWithDot(const char *inString)
   1042 {
   1043     const char *        ptr;
   1044     mDNSu32             escapeCount;
   1045     mDNSBool            result;
   1046 
   1047     // Loop invariant: escapeCount is the number of consecutive escape characters that immediately precede *ptr.
   1048     // - If escapeCount is even, then *ptr is immediately preceded by escapeCount / 2 consecutive literal backslash
   1049     //   characters, so *ptr is not escaped.
   1050     // - If escapeCount is odd, then *ptr is immediately preceded by (escapeCount - 1) / 2 consecutive literal backslash
   1051     //   characters followed by an escape character, so *ptr is escaped.
   1052     escapeCount = 0;
   1053     result = mDNSfalse;
   1054     for (ptr = inString; *ptr != '\0'; ptr++)
   1055     {
   1056         if (*ptr == '\\')
   1057         {
   1058             escapeCount++;
   1059         }
   1060         else
   1061         {
   1062             if ((*ptr == '.') && (ptr[1] == '\0'))
   1063             {
   1064                 if ((escapeCount % 2) == 0) result = mDNStrue;
   1065                 break;
   1066             }
   1067             escapeCount = 0;
   1068         }
   1069     }
   1070     return result;
   1071 }
   1072 
   1073 mDNSlocal const domainname * NextSearchDomain(QueryRecordOp *inOp)
   1074 {
   1075     const domainname *      domain;
   1076 
   1077     inOp->searchListIndexLast = inOp->searchListIndex;
   1078     while ((domain = uDNS_GetNextSearchDomain(inOp->interfaceID, &inOp->searchListIndex, mDNSfalse)) != mDNSNULL)
   1079     {
   1080         if ((DomainNameLength(inOp->qname) - 1 + DomainNameLength(domain)) <= MAX_DOMAIN_NAME) break;
   1081     }
   1082     if (!domain) inOp->searchListIndex = -1;
   1083     return domain;
   1084 }
   1085 
   1086 #if MDNSRESPONDER_SUPPORTS(APPLE, UNICAST_DOTLOCAL)
   1087 mDNSlocal mDNSBool DomainNameIsInSearchList(const domainname *inName, mDNSBool inExcludeLocal)
   1088 {
   1089     const SearchListElem *      item;
   1090     int                         labelCount, domainLabelCount;
   1091 
   1092     labelCount = CountLabels(inName);
   1093     for (item = SearchList; item; item = item->next)
   1094     {
   1095         if (inExcludeLocal && SameDomainName(&item->domain, &localdomain)) continue;
   1096         domainLabelCount = CountLabels(&item->domain);
   1097         if (labelCount >= domainLabelCount)
   1098         {
   1099             if (SameDomainName(&item->domain, SkipLeadingLabels(inName, (labelCount - domainLabelCount))))
   1100             {
   1101                 return mDNStrue;
   1102             }
   1103         }
   1104     }
   1105     return mDNSfalse;
   1106 }
   1107 #endif
   1108 
   1109 #if MDNSRESPONDER_SUPPORTS(APPLE, WEB_CONTENT_FILTER)
   1110 mDNSlocal void NotifyWebContentFilter(const ResourceRecord *inAnswer, uid_t inUID)
   1111 {
   1112     if (WCFIsServerRunning)
   1113     {
   1114 		const mDNS *const m = &mDNSStorage;
   1115 
   1116         if (WCFIsServerRunning(m->WCF) && inAnswer->rdlength != 0)
   1117         {
   1118 			struct sockaddr_storage addr;
   1119 			addr.ss_len = 0;
   1120 			if (inAnswer->rrtype == kDNSType_A || inAnswer->rrtype == kDNSType_AAAA)
   1121 			{
   1122 				if (inAnswer->rrtype == kDNSType_A)
   1123 				{
   1124 					struct sockaddr_in *const sin = (struct sockaddr_in *)&addr;
   1125 					sin->sin_port = 0;
   1126 					// Instead of this stupid call to putRData it would be much simpler to just assign the value in the sensible way, like this:
   1127 					// sin->sin_addr.s_addr = inAnswer->rdata->u.ipv4.NotAnInteger;
   1128 					if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(mDNSv4Addr)), inAnswer))
   1129 						LogMsg("NotifyWebContentFilter: WCF AF_INET putRData failed");
   1130 					else
   1131 					{
   1132 						addr.ss_len = sizeof (struct sockaddr_in);
   1133 						addr.ss_family = AF_INET;
   1134 					}
   1135 				}
   1136 				else if (inAnswer->rrtype == kDNSType_AAAA)
   1137 				{
   1138 					struct sockaddr_in6 *const sin6 = (struct sockaddr_in6 *)&addr;
   1139 					sin6->sin6_port = 0;
   1140 					// Instead of this stupid call to putRData it would be much simpler to just assign the value in the sensible way, like this:
   1141 					// sin6->sin6_addr.__u6_addr.__u6_addr32[0] = inAnswer->rdata->u.ipv6.l[0];
   1142 					// sin6->sin6_addr.__u6_addr.__u6_addr32[1] = inAnswer->rdata->u.ipv6.l[1];
   1143 					// sin6->sin6_addr.__u6_addr.__u6_addr32[2] = inAnswer->rdata->u.ipv6.l[2];
   1144 					// sin6->sin6_addr.__u6_addr.__u6_addr32[3] = inAnswer->rdata->u.ipv6.l[3];
   1145 					if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(mDNSv6Addr)), inAnswer))
   1146 						LogMsg("NotifyWebContentFilter: WCF AF_INET6 putRData failed");
   1147 					else
   1148 					{
   1149 						addr.ss_len = sizeof (struct sockaddr_in6);
   1150 						addr.ss_family = AF_INET6;
   1151 					}
   1152 				}
   1153 				if (addr.ss_len)
   1154 				{
   1155         			char name[MAX_ESCAPED_DOMAIN_NAME];
   1156         			ConvertDomainNameToCString(inAnswer->name, name);
   1157 
   1158 					debugf("NotifyWebContentFilter: Name %s, uid %u, addr length %d", name, inUID, addr.ss_len);
   1159 					if (WCFNameResolvesToAddr)
   1160 					{
   1161 						WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, inUID);
   1162 					}
   1163 				}
   1164 			}
   1165 			else if (inAnswer->rrtype == kDNSType_CNAME)
   1166 			{
   1167 				domainname cname;
   1168         		char name[MAX_ESCAPED_DOMAIN_NAME];
   1169 				char cname_cstr[MAX_ESCAPED_DOMAIN_NAME];
   1170 
   1171 				if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), inAnswer))
   1172 					LogMsg("NotifyWebContentFilter: WCF CNAME putRData failed");
   1173 				else
   1174 				{
   1175         			ConvertDomainNameToCString(inAnswer->name, name);
   1176 					ConvertDomainNameToCString(&cname, cname_cstr);
   1177 					if (WCFNameResolvesToAddr)
   1178 					{
   1179 						WCFNameResolvesToName(m->WCF, name, cname_cstr, inUID);
   1180 					}
   1181 				}
   1182 			}
   1183         }
   1184     }
   1185 }
   1186 #endif
   1187 
   1188 #if MDNSRESPONDER_SUPPORTS(APPLE, POWERLOG_MDNS_REQUESTS)
   1189 mDNSexport mDNSBool ClientRequestUsesAWDL(const uint32_t ifindex, const DNSServiceFlags flags)
   1190 {
   1191     if (ifindex == kDNSServiceInterfaceIndexAny)
   1192     {
   1193         return ((flags & kDNSServiceFlagsIncludeAWDL) != 0);
   1194     }
   1195     else
   1196     {
   1197         return mDNSPlatformInterfaceIsAWDL((mDNSInterfaceID)((uintptr_t)ifindex));
   1198     }
   1199 }
   1200 #endif
   1201