1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2011-2020 Apple Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
21 #include <nw/private.h>
24 #ifndef UNICAST_DISABLED
26 extern mDNS mDNSStorage
;
27 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
28 static mDNSBool gDNS64Enabled
= mDNSfalse
;
29 static mDNSBool gDNS64ForceAAAASynthesis
= mDNSfalse
;
30 static nw_nat64_prefix_t gDNS64Prefix
;
33 // Implementation Notes
35 // DNS Proxy listens on port 53 (UDPv4v6 & TCPv4v6) for DNS queries. It handles only
36 // the "Query" opcode of the DNS protocol described in RFC 1035. For all other opcodes, it returns
37 // "Not Implemented" error. The platform interface mDNSPlatformInitDNSProxySkts
38 // sets up the sockets and whenever it receives a packet, it calls ProxyTCPCallback or ProxyUDPCallback
39 // defined here. For TCP socket, the platform does the "accept" and only sends the received packets
40 // on the newly accepted socket. A single UDP socket (per address family) is used to send/recv
41 // requests/responses from all clients. For TCP, there is one socket per request. Hence, there is some
42 // extra state that needs to be disposed at the end.
44 // When a DNS request is received, ProxyCallbackCommon checks for malformed packet etc. and also checks
45 // for duplicates, before creating DNSProxyClient state and starting a question with the "core"
46 // (mDNS_StartQuery). When the callback for the question happens, it gathers all the necessary
47 // resource records, constructs a response and sends it back to the client.
49 // - Question callback is called with only one resource record at a time. We need all the resource
50 // records to construct the response. Hence, we lookup all the records ourselves.
52 // - The response may not fit the client's buffer size. In that case, we need to set the truncate bit
53 // and the client would retry using TCP.
55 // - The client may have set the DNSSEC OK bit in the EDNS0 option and that means we also have to
56 // return the RRSIGs or the NSEC records with the RRSIGs in the Additional section. We need to
57 // ask the "core" to fetch the DNSSEC records and do the validation if the CD bit is not set.
59 // Once the response is sent to the client, the client state is disposed. When there is no response
60 // from the "core", it eventually times out and we will not find any answers in the cache and we send a
61 // "NXDomain" response back. Thus, we don't need any special timers to reap the client state in the case
64 typedef struct DNSProxyClient_struct DNSProxyClient
;
66 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
69 kDNSProxyDNS64State_Initial
= 0, // Initial state.
70 kDNSProxyDNS64State_AAAASynthesis
= 1, // Querying for A record for AAAA record synthesis.
71 kDNSProxyDNS64State_PTRSynthesisTrying
= 2, // Querying for in-addr.arpa PTR record to map from ip6.arpa PTR.
72 kDNSProxyDNS64State_PTRSynthesisSuccess
= 3, // in-addr.arpa PTR query got non-negative non-CNAME answer.
73 kDNSProxyDNS64State_PTRSynthesisNXDomain
= 4 // in-addr.arpa PTR query produced no useful result.
78 struct DNSProxyClient_struct
{
81 mDNSAddr addr
; // Client's IP address
82 mDNSIPPort port
; // Client's port number
83 mDNSOpaque16 msgid
; // DNS msg id
84 mDNSInterfaceID interfaceID
; // Interface on which we received the request
85 void *socket
; // Return socket
86 mDNSBool tcp
; // TCP or UDP ?
87 mDNSOpaque16 requestFlags
; // second 16 bit word in the DNSMessageHeader of the request
88 mDNSu8
*optRR
; // EDNS0 option
89 mDNSu16 optLen
; // Total Length of the EDNS0 option
90 mDNSu16 rcvBufSize
; // How much can the client receive ?
91 void *context
; // Platform context to be disposed if non-NULL
92 domainname qname
; // q->qname can't be used for duplicate check
93 DNSQuestion q
; // as it can change underneath us for CNAMEs
95 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
96 DNSProxyDNS64State dns64state
;
100 #define MIN_DNS_MESSAGE_SIZE 512
101 static DNSProxyClient
*DNSProxyClients
;
103 mDNSlocal
void FreeDNSProxyClient(DNSProxyClient
*pc
)
106 mDNSPlatformMemFree(pc
->optRR
);
107 mDNSPlatformMemFree(pc
);
110 mDNSlocal mDNSBool
ParseEDNS0(DNSProxyClient
*pc
, const mDNSu8
*ptr
, int length
, const mDNSu8
*limit
)
112 if (ptr
+ length
> limit
)
114 LogInfo("ParseEDNS0: Not enough space in the packet");
117 // Skip the root label
119 mDNSu16 rrtype
= (mDNSu16
) ((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
120 if (rrtype
!= kDNSType_OPT
)
122 LogInfo("ParseEDNS0: Not the right type %d", rrtype
);
125 mDNSu16 rrclass
= (mDNSu16
) ((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
127 mDNSu8 rcode
= ptr
[4];
128 mDNSu8 version
= ptr
[5];
129 mDNSu16 flag
= (mDNSu16
) ((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
130 debugf("rrtype is %s, length is %d, rcode %d, version %d, flag 0x%x", DNSTypeName(rrtype
), rrclass
, rcode
, version
, flag
);
132 pc
->rcvBufSize
= rrclass
;
137 mDNSexport mDNSu8
*DNSProxySetAttributes(DNSQuestion
*q
, DNSMessageHeader
*h
, DNSMessage
*msg
, mDNSu8
*ptr
, mDNSu8
*limit
)
139 DNSProxyClient
*pc
= (DNSProxyClient
*)q
->QuestionContext
;
143 h
->flags
= pc
->requestFlags
;
146 if (ptr
+ pc
->optLen
> limit
)
148 LogInfo("DNSProxySetAttributes: Cannot set EDNS0 option start %p, OptLen %d, end %p", ptr
, pc
->optLen
, limit
);
152 mDNSPlatformMemCopy(ptr
, pc
->optRR
, pc
->optLen
);
158 mDNSlocal mDNSu8
*AddEDNS0Option(mDNSu8
*ptr
, mDNSu8
*limit
)
162 if (ptr
+ 11 > limit
)
164 LogInfo("AddEDNS0Option: not enough space");
167 mDNSStorage
.omsg
.h
.numAdditionals
++;
169 ptr
[1] = (mDNSu8
) (kDNSType_OPT
>> 8);
170 ptr
[2] = (mDNSu8
) (kDNSType_OPT
& 0xFF);
171 ptr
[3] = (mDNSu8
) (len
>> 8);
172 ptr
[4] = (mDNSu8
) (len
& 0xFF);
174 ptr
[6] = 0; // version
177 ptr
[9] = 0; // rdlength
178 ptr
[10] = 0; // rdlength
180 debugf("AddEDNS0 option");
185 // Currently RD and CD bit should be copied if present in the request or cleared if
186 // not present in the request. RD bit is normally set in the response and hence the
187 // cache reflects the right value. CD bit behaves differently. If the CD bit is set
188 // the first time, the cache retains it, if it is present in response (assuming the
189 // upstream server does it right). Next time through we should not use the cached
190 // value of the CD bit blindly. It depends on whether it was in the request or not.
191 mDNSlocal mDNSOpaque16
SetResponseFlags(DNSProxyClient
*pc
, const mDNSOpaque16 responseFlags
)
193 mDNSOpaque16 rFlags
= responseFlags
;
195 if (pc
->requestFlags
.b
[0] & kDNSFlag0_RD
)
196 rFlags
.b
[0] |= kDNSFlag0_RD
;
198 rFlags
.b
[0] &= ~kDNSFlag0_RD
;
200 if (pc
->requestFlags
.b
[1] & kDNSFlag1_CD
)
201 rFlags
.b
[1] |= kDNSFlag1_CD
;
203 rFlags
.b
[1] &= ~kDNSFlag1_CD
;
208 mDNSlocal mDNSu8
*AddResourceRecords(DNSProxyClient
*pc
, mDNSu8
**prevptr
, mStatus
*error
)
210 mDNS
*const m
= &mDNSStorage
;
213 int len
= sizeof(DNSMessageHeader
);
214 mDNSu8
*orig
= m
->omsg
.data
;
215 mDNSBool first
= mDNStrue
;
216 mDNSu8
*ptr
= mDNSNULL
;
219 const CacheRecord
*soa
= mDNSNULL
;
220 const CacheRecord
*cname
= mDNSNULL
;
222 domainname tempQName
;
223 mDNSu32 tempQNameHash
;
225 *error
= mStatus_NoError
;
236 limit
= m
->omsg
.data
+ MIN_DNS_MESSAGE_SIZE
;
240 limit
= (pc
->rcvBufSize
> AbsoluteMaxDNSMessageData
? m
->omsg
.data
+ AbsoluteMaxDNSMessageData
: m
->omsg
.data
+ pc
->rcvBufSize
);
245 // For TCP, limit is not determined by EDNS0 but by 16 bit rdlength field and
246 // AbsoluteMaxDNSMessageData is smaller than 64k.
247 limit
= m
->omsg
.data
+ AbsoluteMaxDNSMessageData
;
249 LogInfo("AddResourceRecords: Limit is %d", limit
- m
->omsg
.data
);
251 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
252 if (pc
->dns64state
== kDNSProxyDNS64State_PTRSynthesisSuccess
)
254 // We're going to synthesize a CNAME record to map the originally requested ip6.arpa domain name to the
255 // in-addr.arpa domain name, so we use pc->q.qname, which contains the in-addr.arpa domain name, to get the
256 // in-addr.arpa PTR record.
257 AssignDomainName(&tempQName
, &pc
->q
.qname
);
258 tempQNameHash
= DomainNameHashValue(&tempQName
);
263 AssignDomainName(&tempQName
, &pc
->qname
);
264 tempQNameHash
= DomainNameHashValue(&tempQName
);
268 soa
= cname
= mDNSNULL
;
270 cg
= CacheGroupForName(m
, tempQNameHash
, &tempQName
);
273 LogInfo("AddResourceRecords: CacheGroup not found for %##s", tempQName
.c
);
274 *error
= mStatus_NoSuchRecord
;
277 for (cr
= cg
->members
; cr
; cr
= cr
->next
)
279 if (SameNameCacheRecordAnswersQuestion(cr
, &pc
->q
))
283 // If this is the first time, initialize the header and the question.
284 // This code needs to be here so that we can use the responseFlags from the
286 mDNSOpaque16 responseFlags
= SetResponseFlags(pc
, cr
->responseFlags
);
287 InitializeDNSMessage(&m
->omsg
.h
, pc
->msgid
, responseFlags
);
288 ptr
= putQuestion(&m
->omsg
, m
->omsg
.data
, m
->omsg
.data
+ AbsoluteMaxDNSMessageData
, &pc
->qname
, pc
->qtype
, pc
->q
.qclass
);
291 LogInfo("AddResourceRecords: putQuestion NULL for %##s (%s)", &pc
->qname
.c
, DNSTypeName(pc
->qtype
));
295 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
296 if (pc
->dns64state
== kDNSProxyDNS64State_PTRSynthesisSuccess
)
298 // For the first answer record, synthesize a CNAME record to map the originally requested ip6.arpa
299 // domain name to the in-addr.arpa domain name.
300 // See <https://tools.ietf.org/html/rfc6147#section-5.3.1>.
302 ResourceRecord newRR
;
303 mDNSPlatformMemZero(&newRR
, (mDNSu32
)sizeof(newRR
));
304 newRR
.RecordType
= kDNSRecordTypePacketAns
;
305 newRR
.rrtype
= kDNSType_CNAME
;
306 newRR
.rrclass
= kDNSClass_IN
;
307 newRR
.name
= &pc
->qname
;
308 AssignDomainName(&rdata
.u
.name
, &pc
->q
.qname
);
309 rdata
.MaxRDLength
= (mDNSu32
)sizeof(rdata
.u
);
310 newRR
.rdata
= &rdata
;
311 ptr
= PutResourceRecordTTLWithLimit(&m
->omsg
, ptr
, &m
->omsg
.h
.numAnswers
, &newRR
, 0, limit
);
320 // - For NegativeAnswers there is nothing to add
321 // - If DNSSECOK is set, we also automatically lookup the RRSIGs which
322 // will also be returned. If the client is explicitly looking up
323 // a DNSSEC record (e.g., DNSKEY, DS) we should return the response.
324 // DNSSECOK bit only influences whether we add the RRSIG or not.
325 if (cr
->resrec
.RecordType
!= kDNSRecordTypePacketNegative
)
327 const ResourceRecord
*rr
;
328 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
330 ResourceRecord newRR
;
331 if ((pc
->dns64state
== kDNSProxyDNS64State_AAAASynthesis
) && (cr
->resrec
.rrtype
== kDNSType_A
))
333 struct in_addr addrV4
;
334 struct in6_addr addrV6
;
337 newRR
.rrtype
= kDNSType_AAAA
;
339 rdata
.MaxRDLength
= newRR
.rdlength
;
340 newRR
.rdata
= &rdata
;
342 memcpy(&addrV4
.s_addr
, cr
->resrec
.rdata
->u
.ipv4
.b
, 4);
343 if (nw_nat64_synthesize_v6(&gDNS64Prefix
, &addrV4
, &addrV6
))
345 memcpy(rdata
.u
.ipv6
.b
, addrV6
.s6_addr
, 16);
358 LogInfo("AddResourceRecords: Answering question with %s", RRDisplayString(m
, rr
));
359 ttl
= cr
->resrec
.rroriginalttl
- (now
- cr
->TimeRcvd
) / mDNSPlatformOneSecond
;
360 ptr
= PutResourceRecordTTLWithLimit(&m
->omsg
, ptr
, &m
->omsg
.h
.numAnswers
, rr
, ttl
, limit
);
371 LogInfo("AddResourceRecords: soa set for %s", CRDisplayString(m
,cr
));
374 // If we are using CNAME to answer a question and CNAME is not the type we
375 // are looking for, note down the CNAME record so that we can follow them
376 // later. Before we follow the CNAME, print the RRSIGs and any nsec (wildcard
378 if ((pc
->q
.qtype
!= cr
->resrec
.rrtype
) && cr
->resrec
.rrtype
== kDNSType_CNAME
)
380 LogInfo("AddResourceRecords: cname set for %s", CRDisplayString(m
,cr
));
385 // Along with the nsec records, we also cache the SOA record. For non-DNSSEC question, we need
386 // to send the SOA back. Normally we either cache the SOA record (non-DNSSEC question) pointed
387 // to by "cr->soa" or the NSEC/SOA records along with their RRSIGs (DNSSEC question) pointed to
388 // by "cr->nsec". Two cases:
390 // - if we issue a DNSSEC question followed by non-DNSSEC question for the same name,
391 // we only have the nsec records and we need to filter the SOA record alone for the
392 // non-DNSSEC questions.
394 // - if we issue a non-DNSSEC question followed by DNSSEC question for the same name,
395 // the "core" flushes the cache entry and re-issue the question with EDNS0/DOK bit and
396 // in this case we return all the DNSSEC records we have.
399 LogInfo("AddResourceRecords: SOA Answering question with %s", CRDisplayString(m
, soa
));
400 ptr
= PutResourceRecordTTLWithLimit(&m
->omsg
, ptr
, &m
->omsg
.h
.numAuthorities
, &soa
->resrec
, soa
->resrec
.rroriginalttl
, limit
);
411 AssignDomainName(&tempQName
, &cname
->resrec
.rdata
->u
.name
);
412 tempQNameHash
= DomainNameHashValue(&tempQName
);
417 LogInfo("AddResourceRecords: Did not find any valid ResourceRecords");
418 *error
= mStatus_NoSuchRecord
;
423 ptr
= AddEDNS0Option(ptr
, limit
);
430 // orig = ptr; Commented out to avoid ‘value never read’ error message
432 LogInfo("AddResourceRecord: Added %d bytes to the packet", len
);
436 mDNSlocal
void ProxyClientCallback(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
438 DNSProxyClient
*pc
= question
->QuestionContext
;
439 DNSProxyClient
**ppc
= &DNSProxyClients
;
447 LogInfo("ProxyClientCallback: ResourceRecord %s", RRDisplayString(m
, answer
));
449 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
452 if (pc
->dns64state
== kDNSProxyDNS64State_Initial
)
454 // If we get a negative AAAA answer, then retry the query as an A record query.
455 // See <https://tools.ietf.org/html/rfc6147#section-5.1.6>.
456 if ((answer
->RecordType
== kDNSRecordTypePacketNegative
) && (question
->qtype
== kDNSType_AAAA
) &&
457 (answer
->rrtype
== kDNSType_AAAA
) && (answer
->rrclass
== kDNSClass_IN
))
459 mDNS_StopQuery(m
, question
);
460 pc
->dns64state
= kDNSProxyDNS64State_AAAASynthesis
;
461 question
->qtype
= kDNSType_A
;
462 mDNS_StartQuery(m
, question
);
466 else if (pc
->dns64state
== kDNSProxyDNS64State_PTRSynthesisTrying
)
468 // If we get a non-negative non-CNAME answer, then this is the answer we give to the client.
469 // Otherwise, just respond with NXDOMAIN.
470 // See <https://tools.ietf.org/html/rfc6147#section-5.3.1>.
471 if ((answer
->RecordType
!= kDNSRecordTypePacketNegative
) && (question
->qtype
== kDNSType_PTR
) &&
472 (answer
->rrtype
== kDNSType_PTR
) && (answer
->rrclass
== kDNSClass_IN
))
474 pc
->dns64state
= kDNSProxyDNS64State_PTRSynthesisSuccess
;
478 pc
->dns64state
= kDNSProxyDNS64State_PTRSynthesisNXDomain
;
482 if (pc
->dns64state
== kDNSProxyDNS64State_PTRSynthesisNXDomain
)
484 const mDNSOpaque16 flags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
, kDNSFlag1_RC_NXDomain
} };
485 InitializeDNSMessage(&m
->omsg
.h
, pc
->msgid
, flags
);
486 ptr
= putQuestion(&m
->omsg
, m
->omsg
.data
, m
->omsg
.data
+ AbsoluteMaxDNSMessageData
, &pc
->qname
, pc
->qtype
,
490 LogInfo("ProxyClientCallback: putQuestion NULL for %##s (%s)", &pc
->qname
.c
, DNSTypeName(pc
->qtype
));
496 if ((answer
->RecordType
!= kDNSRecordTypePacketNegative
) && (answer
->rrtype
!= question
->qtype
))
498 // Wait till we get called for the real response
499 LogInfo("ProxyClientCallback: Received %s, not answering yet", RRDisplayString(m
, answer
));
502 ptr
= AddResourceRecords(pc
, &prevptr
, &error
);
505 LogInfo("ProxyClientCallback: AddResourceRecords NULL for %##s (%s)", &pc
->qname
.c
, DNSTypeName(pc
->qtype
));
506 if (error
== mStatus_NoError
&& prevptr
)
508 // No space to add the record. Set the Truncate bit for UDP.
510 // TBD: For TCP, we need to send the rest of the data. But finding out what is left
511 // is harder. We should allocate enough buffer in the first place to send all
515 m
->omsg
.h
.flags
.b
[0] |= kDNSFlag0_TC
;
520 LogInfo("ProxyClientCallback: ERROR!! Not enough space to return in TCP for %##s (%s)", &pc
->qname
.c
, DNSTypeName(pc
->qtype
));
526 mDNSOpaque16 flags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
, kDNSFlag1_RC_ServFail
} };
527 // We could not find the record for some reason. Return a response, so that the client
528 // is not waiting forever.
529 LogInfo("ProxyClientCallback: No response");
530 if (!mDNSOpaque16IsZero(pc
->q
.responseFlags
))
531 flags
= pc
->q
.responseFlags
;
532 InitializeDNSMessage(&m
->omsg
.h
, pc
->msgid
, flags
);
533 ptr
= putQuestion(&m
->omsg
, m
->omsg
.data
, m
->omsg
.data
+ AbsoluteMaxDNSMessageData
, &pc
->qname
, pc
->qtype
, pc
->q
.qclass
);
536 LogInfo("ProxyClientCallback: putQuestion NULL for %##s (%s)", &pc
->qname
.c
, DNSTypeName(pc
->qtype
));
542 debugf("ProxyClientCallback: InterfaceID is %p for response to client", pc
->interfaceID
);
546 mDNSSendDNSMessage(m
, &m
->omsg
, ptr
, pc
->interfaceID
, mDNSNULL
, (UDPSocket
*)pc
->socket
, &pc
->addr
, pc
->port
, mDNSNULL
, mDNSfalse
);
550 mDNSSendDNSMessage(m
, &m
->omsg
, ptr
, pc
->interfaceID
, (TCPSocket
*)pc
->socket
, mDNSNULL
, &pc
->addr
, pc
->port
, mDNSNULL
, mDNSfalse
);
554 mDNS_StopQuery(m
, question
);
556 while (*ppc
&& *ppc
!= pc
)
560 LogMsg("ProxyClientCallback: question %##s (%s) not found", question
->qname
.c
, DNSTypeName(question
->qtype
));
564 mDNSPlatformDisposeProxyContext(pc
->context
);
565 FreeDNSProxyClient(pc
);
568 mDNSlocal
void SendError(void *socket
, DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*dstaddr
,
569 const mDNSIPPort dstport
, const mDNSInterfaceID InterfaceID
, mDNSBool tcp
, void *context
, mDNSu8 rcode
)
571 mDNS
*const m
= &mDNSStorage
;
572 int pktlen
= (int)(end
- (mDNSu8
*)msg
);
574 // RFC 1035 requires that we copy the question back and RFC 2136 is okay with sending nothing
575 // in the body or send back whatever we get for updates. It is easy to return whatever we get
576 // in the question back to the responder. We return as much as we can fit in our standard
578 if (pktlen
> AbsoluteMaxDNSMessageData
)
579 pktlen
= AbsoluteMaxDNSMessageData
;
581 mDNSPlatformMemCopy(&m
->omsg
.h
, &msg
->h
, sizeof(DNSMessageHeader
));
582 m
->omsg
.h
.flags
.b
[0] |= kDNSFlag0_QR_Response
;
583 m
->omsg
.h
.flags
.b
[1] = rcode
;
584 mDNSPlatformMemCopy(m
->omsg
.data
, (mDNSu8
*)&msg
->data
, (pktlen
- sizeof(DNSMessageHeader
)));
588 mDNSSendDNSMessage(m
, &m
->omsg
, (mDNSu8
*)&m
->omsg
+ pktlen
, InterfaceID
, mDNSNULL
, socket
, dstaddr
, dstport
, mDNSNULL
, mDNSfalse
);
592 mDNSSendDNSMessage(m
, &m
->omsg
, (mDNSu8
*)&m
->omsg
+ pktlen
, InterfaceID
, (TCPSocket
*)socket
, mDNSNULL
, dstaddr
, dstport
, mDNSNULL
, mDNSfalse
);
594 mDNSPlatformDisposeProxyContext(context
);
597 mDNSlocal DNSQuestion
*IsDuplicateClient(const mDNSAddr
*const addr
, const mDNSIPPort port
, const mDNSOpaque16 id
,
598 const DNSQuestion
*const question
)
602 for (pc
= DNSProxyClients
; pc
; pc
= pc
->next
)
604 if (mDNSSameAddress(&pc
->addr
, addr
) &&
605 mDNSSameIPPort(pc
->port
, port
) &&
606 mDNSSameOpaque16(pc
->msgid
, id
) &&
607 pc
->qtype
== question
->qtype
&&
608 pc
->q
.qclass
== question
->qclass
&&
609 SameDomainName(&pc
->qname
, &question
->qname
))
611 LogInfo("IsDuplicateClient: Found a duplicate client in the list");
618 mDNSlocal mDNSBool
CheckDNSProxyIpIntf(mDNSInterfaceID InterfaceID
)
620 mDNS
*const m
= &mDNSStorage
;
622 mDNSu32 ip_ifindex
= (mDNSu32
)(unsigned long)InterfaceID
;
624 LogInfo("CheckDNSProxyIpIntf: Check for ifindex[%d] in stored input interface list: [%d] [%d] [%d] [%d] [%d]",
625 ip_ifindex
, m
->dp_ipintf
[0], m
->dp_ipintf
[1], m
->dp_ipintf
[2], m
->dp_ipintf
[3], m
->dp_ipintf
[4]);
629 for (i
= 0; i
< MaxIp
; i
++)
631 if (ip_ifindex
== m
->dp_ipintf
[i
])
636 LogMsg("CheckDNSProxyIpIntf: ifindex[%d] not in stored input interface list: [%d] [%d] [%d] [%d] [%d]",
637 ip_ifindex
, m
->dp_ipintf
[0], m
->dp_ipintf
[1], m
->dp_ipintf
[2], m
->dp_ipintf
[3], m
->dp_ipintf
[4]);
643 mDNSlocal
void ProxyCallbackCommon(void *socket
, DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*const srcaddr
,
644 const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, const mDNSIPPort dstport
, const mDNSInterfaceID InterfaceID
, mDNSBool tcp
, void *context
)
646 mDNS
*const m
= &mDNSStorage
;
649 DNSQuestion q
, *qptr
;
651 const mDNSu8
*optRR
= mDNSNULL
;
653 DNSProxyClient
**ppc
= &DNSProxyClients
;
658 debugf("ProxyCallbackCommon: DNS Query coming from InterfaceID %p", InterfaceID
);
659 // Ignore if the DNS Query is not from a Valid Input InterfaceID
660 if (!CheckDNSProxyIpIntf(InterfaceID
))
662 LogMsg("ProxyCallbackCommon: Rejecting DNS Query coming from InterfaceID %p", InterfaceID
);
666 if ((unsigned)(end
- (mDNSu8
*)msg
) < sizeof(DNSMessageHeader
))
668 debugf("ProxyCallbackCommon: DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), (int)(end
- (mDNSu8
*)msg
));
672 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
673 ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
674 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
675 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
676 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
677 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
679 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
680 if (QR_OP
!= kDNSFlag0_QR_Query
)
682 LogInfo("ProxyCallbackCommon: Not a query(%d) for pkt from %#a:%d", QR_OP
, srcaddr
, mDNSVal16(srcport
));
683 SendError(socket
, msg
, end
, srcaddr
, srcport
, InterfaceID
, tcp
, context
, kDNSFlag1_RC_NotImpl
);
687 if (msg
->h
.numQuestions
!= 1 || msg
->h
.numAnswers
|| msg
->h
.numAuthorities
)
689 LogInfo("ProxyCallbackCommon: Malformed pkt from %#a:%d, Q:%d, An:%d, Au:%d", srcaddr
, mDNSVal16(srcport
),
690 msg
->h
.numQuestions
, msg
->h
.numAnswers
, msg
->h
.numAuthorities
);
691 SendError(socket
, msg
, end
, srcaddr
, srcport
, InterfaceID
, tcp
, context
, kDNSFlag1_RC_FormErr
);
695 ptr
= getQuestion(msg
, ptr
, end
, InterfaceID
, &q
);
698 LogInfo("ProxyCallbackCommon: Question cannot be parsed for pkt from %#a:%d", srcaddr
, mDNSVal16(srcport
));
699 SendError(socket
, msg
, end
, srcaddr
, srcport
, InterfaceID
, tcp
, context
, kDNSFlag1_RC_FormErr
);
704 LogInfo("ProxyCallbackCommon: Question %##s (%s)", q
.qname
.c
, DNSTypeName(q
.qtype
));
706 ptr
= LocateOptRR(msg
, end
, 0);
710 ptr
= skipResourceRecord(msg
, ptr
, end
);
711 // Be liberal and ignore the EDNS0 option if we can't parse it properly
714 LogInfo("ProxyCallbackCommon: EDNS0 cannot be parsed for pkt from %#a:%d, ignoring", srcaddr
, mDNSVal16(srcport
));
718 optLen
= (int)(ptr
- optRR
);
719 LogInfo("ProxyCallbackCommon: EDNS0 opt length %d present in Question %##s (%s)", optLen
, q
.qname
.c
, DNSTypeName(q
.qtype
));
724 LogInfo("ProxyCallbackCommon: EDNS0 opt not present in Question %##s (%s), ptr %p", q
.qname
.c
, DNSTypeName(q
.qtype
), ptr
);
727 qptr
= IsDuplicateClient(srcaddr
, srcport
, msg
->h
.id
, &q
);
730 LogInfo("ProxyCallbackCommon: Found a duplicate for pkt from %#a:%d, ignoring this", srcaddr
, mDNSVal16(srcport
));
733 pc
= (DNSProxyClient
*) mDNSPlatformMemAllocateClear(sizeof(*pc
));
736 LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr
, mDNSVal16(srcport
));
741 pc
->msgid
= msg
->h
.id
;
742 pc
->interfaceID
= InterfaceID
; // input interface
745 pc
->requestFlags
= msg
->h
.flags
;
746 pc
->context
= context
;
747 AssignDomainName(&pc
->qname
, &q
.qname
);
750 if (!ParseEDNS0(pc
, optRR
, optLen
, end
))
752 LogInfo("ProxyCallbackCommon: Invalid EDNS0 option for pkt from %#a:%d, ignoring this", srcaddr
, mDNSVal16(srcport
));
756 pc
->optRR
= (mDNSu8
*) mDNSPlatformMemAllocate(optLen
);
759 LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr
, mDNSVal16(srcport
));
760 FreeDNSProxyClient(pc
);
763 mDNSPlatformMemCopy(pc
->optRR
, optRR
, optLen
);
768 debugf("ProxyCallbackCommon: DNS Query forwarding to interface index %d", m
->dp_opintf
);
769 mDNS_SetupQuestion(&pc
->q
, (mDNSInterfaceID
)(unsigned long)m
->dp_opintf
, &q
.qname
, q
.qtype
, ProxyClientCallback
, pc
);
770 pc
->q
.TimeoutQuestion
= 1;
771 // Set ReturnIntermed so that we get the negative responses
772 pc
->q
.ReturnIntermed
= mDNStrue
;
773 pc
->q
.ProxyQuestion
= mDNStrue
;
774 pc
->q
.responseFlags
= zeroID
;
775 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
776 pc
->qtype
= pc
->q
.qtype
;
779 if (pc
->qtype
== kDNSType_PTR
)
781 struct in6_addr v6Addr
;
782 struct in_addr v4Addr
;
783 if (GetReverseIPv6Addr(&pc
->qname
, v6Addr
.s6_addr
) && nw_nat64_extract_v4(&gDNS64Prefix
, &v6Addr
, &v4Addr
))
785 const mDNSu8
*const a
= (const mDNSu8
*)&v4Addr
.s_addr
;
786 char qnameStr
[MAX_REVERSE_MAPPING_NAME_V4
];
787 mDNS_snprintf(qnameStr
, (mDNSu32
)sizeof(qnameStr
), "%u.%u.%u.%u.in-addr.arpa.", a
[3], a
[2], a
[1], a
[0]);
788 MakeDomainNameFromDNSNameString(&pc
->q
.qname
, qnameStr
);
789 pc
->q
.qnamehash
= DomainNameHashValue(&pc
->q
.qname
);
790 pc
->dns64state
= kDNSProxyDNS64State_PTRSynthesisTrying
;
793 else if ((pc
->qtype
== kDNSType_AAAA
) && gDNS64ForceAAAASynthesis
)
795 pc
->dns64state
= kDNSProxyDNS64State_AAAASynthesis
;
796 pc
->q
.qtype
= kDNSType_A
;
802 ppc
= &((*ppc
)->next
);
805 mDNS_StartQuery(m
, &pc
->q
);
808 mDNSexport
void ProxyUDPCallback(void *socket
, DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*const srcaddr
,
809 const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, const mDNSIPPort dstport
, const mDNSInterfaceID InterfaceID
, void *context
)
811 LogInfo("ProxyUDPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), (int)(end
- (mDNSu8
*)msg
));
812 ProxyCallbackCommon(socket
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
, mDNSfalse
, context
);
815 mDNSexport
void ProxyTCPCallback(void *socket
, DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*const srcaddr
,
816 const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, const mDNSIPPort dstport
, const mDNSInterfaceID InterfaceID
, void *context
)
818 LogInfo("ProxyTCPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), (int)(end
- (mDNSu8
*)msg
));
820 // If the connection was closed from the other side or incoming packet does not match stored input interface list, locate the client
821 // state and free it.
822 if (((end
- (mDNSu8
*)msg
) == 0) || (!CheckDNSProxyIpIntf(InterfaceID
)))
824 DNSProxyClient
**ppc
= &DNSProxyClients
;
825 DNSProxyClient
**prevpc
;
828 while (*ppc
&& (*ppc
)->socket
!= socket
)
835 mDNSPlatformDisposeProxyContext(socket
);
836 LogMsg("ProxyTCPCallback: socket cannot be found");
839 *prevpc
= (*ppc
)->next
;
840 LogInfo("ProxyTCPCallback: free");
841 mDNSPlatformDisposeProxyContext(socket
);
842 FreeDNSProxyClient(*ppc
);
845 ProxyCallbackCommon(socket
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
, mDNStrue
, context
);
848 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
849 mDNSexport
void DNSProxyInit(mDNSu32 IpIfArr
[MaxIp
], mDNSu32 OpIf
, const mDNSu8 IPv6Prefix
[16], int IPv6PrefixBitLen
,
850 mDNSBool forceAAAASynthesis
)
852 mDNSexport
void DNSProxyInit(mDNSu32 IpIfArr
[MaxIp
], mDNSu32 OpIf
)
855 mDNS
*const m
= &mDNSStorage
;
858 // Store DNSProxy Interface fields in mDNS struct
859 for (i
= 0; i
< MaxIp
; i
++)
860 m
->dp_ipintf
[i
] = IpIfArr
[i
];
863 LogInfo("DNSProxyInit Storing interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m
->dp_ipintf
[0],
864 m
->dp_ipintf
[1], m
->dp_ipintf
[2], m
->dp_ipintf
[3], m
->dp_ipintf
[4], m
->dp_opintf
);
865 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
869 mDNSPlatformMemZero(&gDNS64Prefix
, (mDNSu32
)sizeof(gDNS64Prefix
));
870 switch (IPv6PrefixBitLen
)
873 gDNS64Prefix
.length
= nw_nat64_prefix_length_32
;
878 gDNS64Prefix
.length
= nw_nat64_prefix_length_40
;
883 gDNS64Prefix
.length
= nw_nat64_prefix_length_48
;
888 gDNS64Prefix
.length
= nw_nat64_prefix_length_56
;
893 gDNS64Prefix
.length
= nw_nat64_prefix_length_64
;
898 gDNS64Prefix
.length
= nw_nat64_prefix_length_96
;
908 mDNSPlatformMemCopy(gDNS64Prefix
.data
, IPv6Prefix
, copyLen
);
909 gDNS64ForceAAAASynthesis
= forceAAAASynthesis
;
910 gDNS64Enabled
= mDNStrue
;
911 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_INFO
,
912 "DNSProxy using DNS64 IPv6 prefix: " PRI_IPv6_ADDR
"/%d" PUB_S
,
913 IPv6Prefix
, IPv6PrefixBitLen
, gDNS64ForceAAAASynthesis
? "" : " (force AAAA synthesis)");
917 gDNS64Enabled
= mDNSfalse
;
918 gDNS64ForceAAAASynthesis
= mDNSfalse
;
919 LogRedact(MDNS_LOG_CATEGORY_DEFAULT
, MDNS_LOG_ERROR
,
920 "DNSProxy not using invalid DNS64 IPv6 prefix: " PRI_IPv6_ADDR
"/%d", IPv6Prefix
, IPv6PrefixBitLen
);
926 mDNSexport
void DNSProxyTerminate(void)
928 mDNS
*const m
= &mDNSStorage
;
931 // Clear DNSProxy Interface fields from mDNS struct
932 for (i
= 0; i
< MaxIp
; i
++)
936 LogInfo("DNSProxyTerminate Cleared interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m
->dp_ipintf
[0],
937 m
->dp_ipintf
[1], m
->dp_ipintf
[2], m
->dp_ipintf
[3], m
->dp_ipintf
[4], m
->dp_opintf
);
938 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
939 gDNS64Enabled
= mDNSfalse
;
942 #else // UNICAST_DISABLED
944 mDNSexport
void ProxyUDPCallback(void *socket
, DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*const srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, const mDNSIPPort dstport
, const mDNSInterfaceID InterfaceID
, void *context
)
957 mDNSexport
void ProxyTCPCallback(void *socket
, DNSMessage
*const msg
, const mDNSu8
*const end
, const mDNSAddr
*const srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, const mDNSIPPort dstport
, const mDNSInterfaceID InterfaceID
, void *context
)
970 mDNSexport
void DNSProxyInit(mDNSu32 IpIfArr
[MaxIp
], mDNSu32 OpIf
)
975 extern void DNSProxyTerminate(void)
980 #endif // UNICAST_DISABLED