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