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