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 
QueryRecordOpParamsInit(QueryRecordOpParams * inParams)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 
GetAddrInfoClientRequestParamsInit(GetAddrInfoClientRequestParams * inParams)123 mDNSexport void GetAddrInfoClientRequestParamsInit(GetAddrInfoClientRequestParams *inParams)
124 {
125 	mDNSPlatformMemZero(inParams, (mDNSu32)sizeof(*inParams));
126 }
127 
GetAddrInfoClientRequestStart(GetAddrInfoClientRequest * inRequest,const GetAddrInfoClientRequestParams * inParams,QueryRecordResultHandler inResultHandler,void * inResultContext)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 
GetAddrInfoClientRequestStop(GetAddrInfoClientRequest * inRequest)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 
GetAddrInfoClientRequestGetQName(const GetAddrInfoClientRequest * inRequest)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 
GetAddrInfoClientRequestIsMulticast(const GetAddrInfoClientRequest * inRequest)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 
QueryRecordClientRequestParamsInit(QueryRecordClientRequestParams * inParams)306 mDNSexport void QueryRecordClientRequestParamsInit(QueryRecordClientRequestParams *inParams)
307 {
308 	mDNSPlatformMemZero(inParams, (mDNSu32)sizeof(*inParams));
309 }
310 
QueryRecordClientRequestStart(QueryRecordClientRequest * inRequest,const QueryRecordClientRequestParams * inParams,QueryRecordResultHandler inResultHandler,void * inResultContext)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 
QueryRecordClientRequestStop(QueryRecordClientRequest * inRequest)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 
QueryRecordClientRequestGetQName(const QueryRecordClientRequest * inRequest)415 mDNSexport const domainname * QueryRecordClientRequestGetQName(const QueryRecordClientRequest *inRequest)
416 {
417     return &inRequest->op.q.qname;
418 }
419 
QueryRecordClientRequestGetType(const QueryRecordClientRequest * inRequest)420 mDNSexport mDNSu16 QueryRecordClientRequestGetType(const QueryRecordClientRequest *inRequest)
421 {
422     return inRequest->op.q.qtype;
423 }
424 
QueryRecordClientRequestIsMulticast(QueryRecordClientRequest * inRequest)425 mDNSexport mDNSBool QueryRecordClientRequestIsMulticast(QueryRecordClientRequest *inRequest)
426 {
427     return (QueryRecordOpIsMulticast(&inRequest->op) ? mDNStrue : mDNSfalse);
428 }
429 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
QueryRecordOpStartForClientRequest(QueryRecordOp * inOp,mDNSu32 inReqID,const domainname * inQName,mDNSu16 inQType,mDNSu16 inQClass,mDNSInterfaceID inInterfaceID,mDNSs32 inServiceID,mDNSu32 inFlags,mDNSBool inAppendSearchDomains,mDNSs32 inPID,const mDNSu8 inUUID[UUID_SIZE],mDNSu32 inUID,const audit_token_t * inPeerAuditTokenPtr,const audit_token_t * inDelegateAuditTokenPtr,const mDNSu8 inResolverUUID[UUID_SIZE],mDNSBool inNeedEncryption,const mdns_dns_service_id_t inCustomID,QueryRecordResultHandler inResultHandler,void * inResultContext)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 
QueryRecordOpStopForClientRequest(QueryRecordOp * op)479 mDNSexport void QueryRecordOpStopForClientRequest(QueryRecordOp *op) {
480     QueryRecordOpStop(op);
481 }
482 
483 #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
484 
QueryRecordOpCreate(QueryRecordOp ** outOp)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 
QueryRecordOpFree(QueryRecordOp * operation)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 
QueryRecordOpStart(QueryRecordOp * inOp,const QueryRecordOpParams * inParams,QueryRecordResultHandler inResultHandler,void * inResultContext)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 
QueryRecordOpStop(QueryRecordOp * op)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 
QueryRecordOpIsMulticast(const QueryRecordOp * op)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.
GetTimeNow(mDNS * m)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 
QueryRecordOpCallback(mDNS * m,DNSQuestion * inQuestion,const ResourceRecord * inAnswer,QC_result inAddRecord)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 
QueryRecordOpResetHandler(DNSQuestion * inQuestion)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 
QueryRecordOpStartQuestion(QueryRecordOp * inOp,DNSQuestion * inQuestion)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 
QueryRecordOpStopQuestion(DNSQuestion * inQuestion)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 
QueryRecordOpRestartUnicastQuestion(QueryRecordOp * inOp,DNSQuestion * inQuestion,const domainname * inSearchDomain)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 
InterfaceIndexToInterfaceID(mDNSu32 inInterfaceIndex,mDNSInterfaceID * outInterfaceID)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 
DomainNameIsSingleLabel(const domainname * inName)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 
StringEndsWithDot(const char * inString)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 
NextSearchDomain(QueryRecordOp * inOp)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)
DomainNameIsInSearchList(const domainname * inName,mDNSBool inExcludeLocal)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)
NotifyWebContentFilter(const ResourceRecord * inAnswer,uid_t inUID)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
1069