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