]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSCore/dnsproxy.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / mDNSCore / dnsproxy.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2011-2020 Apple Inc. All rights reserved.
4 *
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
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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.
16 */
17
18 #include "dnsproxy.h"
19
20 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
21 #include <nw/private.h>
22 #endif
23
24 #ifndef UNICAST_DISABLED
25
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;
31 #endif
32
33 // Implementation Notes
34 //
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.
43 //
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.
48 //
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.
51 //
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.
54 //
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.
58 //
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
62 // of errors.
63
64 typedef struct DNSProxyClient_struct DNSProxyClient;
65
66 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
67 typedef enum
68 {
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.
74
75 } DNSProxyDNS64State;
76 #endif
77
78 struct DNSProxyClient_struct {
79
80 DNSProxyClient *next;
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
94 mDNSu16 qtype;
95 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
96 DNSProxyDNS64State dns64state;
97 #endif
98 };
99
100 #define MIN_DNS_MESSAGE_SIZE 512
101 static DNSProxyClient *DNSProxyClients;
102
103 mDNSlocal void FreeDNSProxyClient(DNSProxyClient *pc)
104 {
105 if (pc->optRR)
106 mDNSPlatformMemFree(pc->optRR);
107 mDNSPlatformMemFree(pc);
108 }
109
110 mDNSlocal mDNSBool ParseEDNS0(DNSProxyClient *pc, const mDNSu8 *ptr, int length, const mDNSu8 *limit)
111 {
112 if (ptr + length > limit)
113 {
114 LogInfo("ParseEDNS0: Not enough space in the packet");
115 return mDNSfalse;
116 }
117 // Skip the root label
118 ptr++;
119 mDNSu16 rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]);
120 if (rrtype != kDNSType_OPT)
121 {
122 LogInfo("ParseEDNS0: Not the right type %d", rrtype);
123 return mDNSfalse;
124 }
125 mDNSu16 rrclass = (mDNSu16) ((mDNSu16)ptr[2] << 8 | ptr[3]);
126 #if MDNS_DEBUGMSGS
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);
131 #endif
132 pc->rcvBufSize = rrclass;
133
134 return mDNStrue;
135 }
136
137 mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit)
138 {
139 DNSProxyClient *pc = (DNSProxyClient *)q->QuestionContext;
140
141 (void) msg;
142
143 h->flags = pc->requestFlags;
144 if (pc->optRR)
145 {
146 if (ptr + pc->optLen > limit)
147 {
148 LogInfo("DNSProxySetAttributes: Cannot set EDNS0 option start %p, OptLen %d, end %p", ptr, pc->optLen, limit);
149 return ptr;
150 }
151 h->numAdditionals++;
152 mDNSPlatformMemCopy(ptr, pc->optRR, pc->optLen);
153 ptr += pc->optLen;
154 }
155 return ptr;
156 }
157
158 mDNSlocal mDNSu8 *AddEDNS0Option(mDNSu8 *ptr, mDNSu8 *limit)
159 {
160 int len = 4096;
161
162 if (ptr + 11 > limit)
163 {
164 LogInfo("AddEDNS0Option: not enough space");
165 return mDNSNULL;
166 }
167 mDNSStorage.omsg.h.numAdditionals++;
168 ptr[0] = 0;
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);
173 ptr[5] = 0; // rcode
174 ptr[6] = 0; // version
175 ptr[7] = 0;
176 ptr[8] = 0; // flags
177 ptr[9] = 0; // rdlength
178 ptr[10] = 0; // rdlength
179
180 debugf("AddEDNS0 option");
181
182 return (ptr + 11);
183 }
184
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)
192 {
193 mDNSOpaque16 rFlags = responseFlags;
194
195 if (pc->requestFlags.b[0] & kDNSFlag0_RD)
196 rFlags.b[0] |= kDNSFlag0_RD;
197 else
198 rFlags.b[0] &= ~kDNSFlag0_RD;
199
200 if (pc->requestFlags.b[1] & kDNSFlag1_CD)
201 rFlags.b[1] |= kDNSFlag1_CD;
202 else
203 rFlags.b[1] &= ~kDNSFlag1_CD;
204
205 return rFlags;
206 }
207
208 mDNSlocal mDNSu8 *AddResourceRecords(DNSProxyClient *pc, mDNSu8 **prevptr, mStatus *error)
209 {
210 mDNS *const m = &mDNSStorage;
211 CacheGroup *cg;
212 CacheRecord *cr;
213 int len = sizeof(DNSMessageHeader);
214 mDNSu8 *orig = m->omsg.data;
215 mDNSBool first = mDNStrue;
216 mDNSu8 *ptr = mDNSNULL;
217 mDNSs32 now;
218 mDNSs32 ttl;
219 const CacheRecord *soa = mDNSNULL;
220 const CacheRecord *cname = mDNSNULL;
221 mDNSu8 *limit;
222 domainname tempQName;
223 mDNSu32 tempQNameHash;
224
225 *error = mStatus_NoError;
226 *prevptr = mDNSNULL;
227
228 mDNS_Lock(m);
229 now = m->timenow;
230 mDNS_Unlock(m);
231
232 if (!pc->tcp)
233 {
234 if (!pc->rcvBufSize)
235 {
236 limit = m->omsg.data + MIN_DNS_MESSAGE_SIZE;
237 }
238 else
239 {
240 limit = (pc->rcvBufSize > AbsoluteMaxDNSMessageData ? m->omsg.data + AbsoluteMaxDNSMessageData : m->omsg.data + pc->rcvBufSize);
241 }
242 }
243 else
244 {
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;
248 }
249 LogInfo("AddResourceRecords: Limit is %d", limit - m->omsg.data);
250
251 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
252 if (pc->dns64state == kDNSProxyDNS64State_PTRSynthesisSuccess)
253 {
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);
259 }
260 else
261 #endif
262 {
263 AssignDomainName(&tempQName, &pc->qname);
264 tempQNameHash = DomainNameHashValue(&tempQName);
265 }
266
267 again:
268 soa = cname = mDNSNULL;
269
270 cg = CacheGroupForName(m, tempQNameHash, &tempQName);
271 if (!cg)
272 {
273 LogInfo("AddResourceRecords: CacheGroup not found for %##s", tempQName.c);
274 *error = mStatus_NoSuchRecord;
275 return mDNSNULL;
276 }
277 for (cr = cg->members; cr; cr = cr->next)
278 {
279 if (SameNameCacheRecordAnswersQuestion(cr, &pc->q))
280 {
281 if (first)
282 {
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
285 // cache record
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);
289 if (!ptr)
290 {
291 LogInfo("AddResourceRecords: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->qtype));
292 return mDNSNULL;
293 }
294 first = mDNSfalse;
295 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
296 if (pc->dns64state == kDNSProxyDNS64State_PTRSynthesisSuccess)
297 {
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>.
301 RData rdata;
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);
312 if (!ptr)
313 {
314 *prevptr = orig;
315 return mDNSNULL;
316 }
317 }
318 #endif
319 }
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)
326 {
327 const ResourceRecord *rr;
328 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
329 RData rdata;
330 ResourceRecord newRR;
331 if ((pc->dns64state == kDNSProxyDNS64State_AAAASynthesis) && (cr->resrec.rrtype == kDNSType_A))
332 {
333 struct in_addr addrV4;
334 struct in6_addr addrV6;
335
336 newRR = cr->resrec;
337 newRR.rrtype = kDNSType_AAAA;
338 newRR.rdlength = 16;
339 rdata.MaxRDLength = newRR.rdlength;
340 newRR.rdata = &rdata;
341
342 memcpy(&addrV4.s_addr, cr->resrec.rdata->u.ipv4.b, 4);
343 if (nw_nat64_synthesize_v6(&gDNS64Prefix, &addrV4, &addrV6))
344 {
345 memcpy(rdata.u.ipv6.b, addrV6.s6_addr, 16);
346 rr = &newRR;
347 }
348 else
349 {
350 continue;
351 }
352 }
353 else
354 #endif
355 {
356 rr = &cr->resrec;
357 }
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);
361 if (!ptr)
362 {
363 *prevptr = orig;
364 return mDNSNULL;
365 }
366 len += (ptr - orig);
367 orig = ptr;
368 }
369 if (cr->soa)
370 {
371 LogInfo("AddResourceRecords: soa set for %s", CRDisplayString(m ,cr));
372 soa = cr->soa;
373 }
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
377 // expanded) if any.
378 if ((pc->q.qtype != cr->resrec.rrtype) && cr->resrec.rrtype == kDNSType_CNAME)
379 {
380 LogInfo("AddResourceRecords: cname set for %s", CRDisplayString(m ,cr));
381 cname = cr;
382 }
383 }
384 }
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:
389 //
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.
393 //
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.
397 if (soa)
398 {
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);
401 if (!ptr)
402 {
403 *prevptr = orig;
404 return mDNSNULL;
405 }
406 len += (ptr - orig);
407 orig = ptr;
408 }
409 if (cname)
410 {
411 AssignDomainName(&tempQName, &cname->resrec.rdata->u.name);
412 tempQNameHash = DomainNameHashValue(&tempQName);
413 goto again;
414 }
415 if (!ptr)
416 {
417 LogInfo("AddResourceRecords: Did not find any valid ResourceRecords");
418 *error = mStatus_NoSuchRecord;
419 return mDNSNULL;
420 }
421 if (pc->rcvBufSize)
422 {
423 ptr = AddEDNS0Option(ptr, limit);
424 if (!ptr)
425 {
426 *prevptr = orig;
427 return mDNSNULL;
428 }
429 len += (ptr - orig);
430 // orig = ptr; Commented out to avoid ‘value never read’ error message
431 }
432 LogInfo("AddResourceRecord: Added %d bytes to the packet", len);
433 return ptr;
434 }
435
436 mDNSlocal void ProxyClientCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
437 {
438 DNSProxyClient *pc = question->QuestionContext;
439 DNSProxyClient **ppc = &DNSProxyClients;
440 mDNSu8 *ptr;
441 mDNSu8 *prevptr;
442 mStatus error;
443
444 if (!AddRecord)
445 return;
446
447 LogInfo("ProxyClientCallback: ResourceRecord %s", RRDisplayString(m, answer));
448
449 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS_PROXY_DNS64)
450 if (gDNS64Enabled)
451 {
452 if (pc->dns64state == kDNSProxyDNS64State_Initial)
453 {
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))
458 {
459 mDNS_StopQuery(m, question);
460 pc->dns64state = kDNSProxyDNS64State_AAAASynthesis;
461 question->qtype = kDNSType_A;
462 mDNS_StartQuery(m, question);
463 return;
464 }
465 }
466 else if (pc->dns64state == kDNSProxyDNS64State_PTRSynthesisTrying)
467 {
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))
473 {
474 pc->dns64state = kDNSProxyDNS64State_PTRSynthesisSuccess;
475 }
476 else
477 {
478 pc->dns64state = kDNSProxyDNS64State_PTRSynthesisNXDomain;
479 }
480 }
481 }
482 if (pc->dns64state == kDNSProxyDNS64State_PTRSynthesisNXDomain)
483 {
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,
487 pc->q.qclass);
488 if (!ptr)
489 {
490 LogInfo("ProxyClientCallback: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->qtype));
491 }
492 }
493 else
494 #endif
495 {
496 if ((answer->RecordType != kDNSRecordTypePacketNegative) && (answer->rrtype != question->qtype))
497 {
498 // Wait till we get called for the real response
499 LogInfo("ProxyClientCallback: Received %s, not answering yet", RRDisplayString(m, answer));
500 return;
501 }
502 ptr = AddResourceRecords(pc, &prevptr, &error);
503 if (!ptr)
504 {
505 LogInfo("ProxyClientCallback: AddResourceRecords NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->qtype));
506 if (error == mStatus_NoError && prevptr)
507 {
508 // No space to add the record. Set the Truncate bit for UDP.
509 //
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
512 // of the data.
513 if (!pc->tcp)
514 {
515 m->omsg.h.flags.b[0] |= kDNSFlag0_TC;
516 ptr = prevptr;
517 }
518 else
519 {
520 LogInfo("ProxyClientCallback: ERROR!! Not enough space to return in TCP for %##s (%s)", &pc->qname.c, DNSTypeName(pc->qtype));
521 ptr = prevptr;
522 }
523 }
524 else
525 {
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);
534 if (!ptr)
535 {
536 LogInfo("ProxyClientCallback: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->qtype));
537 goto done;
538 }
539 }
540 }
541 }
542 debugf("ProxyClientCallback: InterfaceID is %p for response to client", pc->interfaceID);
543
544 if (!pc->tcp)
545 {
546 mDNSSendDNSMessage(m, &m->omsg, ptr, pc->interfaceID, mDNSNULL, (UDPSocket *)pc->socket, &pc->addr, pc->port, mDNSNULL, mDNSfalse);
547 }
548 else
549 {
550 mDNSSendDNSMessage(m, &m->omsg, ptr, pc->interfaceID, (TCPSocket *)pc->socket, mDNSNULL, &pc->addr, pc->port, mDNSNULL, mDNSfalse);
551 }
552
553 done:
554 mDNS_StopQuery(m, question);
555
556 while (*ppc && *ppc != pc)
557 ppc=&(*ppc)->next;
558 if (!*ppc)
559 {
560 LogMsg("ProxyClientCallback: question %##s (%s) not found", question->qname.c, DNSTypeName(question->qtype));
561 return;
562 }
563 *ppc = pc->next;
564 mDNSPlatformDisposeProxyContext(pc->context);
565 FreeDNSProxyClient(pc);
566 }
567
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)
570 {
571 mDNS *const m = &mDNSStorage;
572 int pktlen = (int)(end - (mDNSu8 *)msg);
573
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
577 // output packet.
578 if (pktlen > AbsoluteMaxDNSMessageData)
579 pktlen = AbsoluteMaxDNSMessageData;
580
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)));
585
586 if (!tcp)
587 {
588 mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, InterfaceID, mDNSNULL, socket, dstaddr, dstport, mDNSNULL, mDNSfalse);
589 }
590 else
591 {
592 mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, InterfaceID, (TCPSocket *)socket, mDNSNULL, dstaddr, dstport, mDNSNULL, mDNSfalse);
593 }
594 mDNSPlatformDisposeProxyContext(context);
595 }
596
597 mDNSlocal DNSQuestion *IsDuplicateClient(const mDNSAddr *const addr, const mDNSIPPort port, const mDNSOpaque16 id,
598 const DNSQuestion *const question)
599 {
600 DNSProxyClient *pc;
601
602 for (pc = DNSProxyClients; pc; pc = pc->next)
603 {
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))
610 {
611 LogInfo("IsDuplicateClient: Found a duplicate client in the list");
612 return(&pc->q);
613 }
614 }
615 return(mDNSNULL);
616 }
617
618 mDNSlocal mDNSBool CheckDNSProxyIpIntf(mDNSInterfaceID InterfaceID)
619 {
620 mDNS *const m = &mDNSStorage;
621 int i;
622 mDNSu32 ip_ifindex = (mDNSu32)(unsigned long)InterfaceID;
623
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]);
626
627 if (ip_ifindex > 0)
628 {
629 for (i = 0; i < MaxIp; i++)
630 {
631 if (ip_ifindex == m->dp_ipintf[i])
632 return mDNStrue;
633 }
634 }
635
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]);
638
639 return mDNSfalse;
640
641 }
642
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)
645 {
646 mDNS *const m = &mDNSStorage;
647 mDNSu8 QR_OP;
648 const mDNSu8 *ptr;
649 DNSQuestion q, *qptr;
650 DNSProxyClient *pc;
651 const mDNSu8 *optRR = mDNSNULL;
652 int optLen = 0;
653 DNSProxyClient **ppc = &DNSProxyClients;
654
655 (void) dstaddr;
656 (void) dstport;
657
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))
661 {
662 LogMsg("ProxyCallbackCommon: Rejecting DNS Query coming from InterfaceID %p", InterfaceID);
663 return;
664 }
665
666 if ((unsigned)(end - (mDNSu8 *)msg) < sizeof(DNSMessageHeader))
667 {
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));
669 return;
670 }
671
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]);
678
679 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
680 if (QR_OP != kDNSFlag0_QR_Query)
681 {
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);
684 return;
685 }
686
687 if (msg->h.numQuestions != 1 || msg->h.numAnswers || msg->h.numAuthorities)
688 {
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);
692 return;
693 }
694 ptr = msg->data;
695 ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
696 if (!ptr)
697 {
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);
700 return;
701 }
702 else
703 {
704 LogInfo("ProxyCallbackCommon: Question %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
705 }
706 ptr = LocateOptRR(msg, end, 0);
707 if (ptr)
708 {
709 optRR = ptr;
710 ptr = skipResourceRecord(msg, ptr, end);
711 // Be liberal and ignore the EDNS0 option if we can't parse it properly
712 if (!ptr)
713 {
714 LogInfo("ProxyCallbackCommon: EDNS0 cannot be parsed for pkt from %#a:%d, ignoring", srcaddr, mDNSVal16(srcport));
715 }
716 else
717 {
718 optLen = (int)(ptr - optRR);
719 LogInfo("ProxyCallbackCommon: EDNS0 opt length %d present in Question %##s (%s)", optLen, q.qname.c, DNSTypeName(q.qtype));
720 }
721 }
722 else
723 {
724 LogInfo("ProxyCallbackCommon: EDNS0 opt not present in Question %##s (%s), ptr %p", q.qname.c, DNSTypeName(q.qtype), ptr);
725 }
726
727 qptr = IsDuplicateClient(srcaddr, srcport, msg->h.id, &q);
728 if (qptr)
729 {
730 LogInfo("ProxyCallbackCommon: Found a duplicate for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
731 return;
732 }
733 pc = (DNSProxyClient *) mDNSPlatformMemAllocateClear(sizeof(*pc));
734 if (!pc)
735 {
736 LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
737 return;
738 }
739 pc->addr = *srcaddr;
740 pc->port = srcport;
741 pc->msgid = msg->h.id;
742 pc->interfaceID = InterfaceID; // input interface
743 pc->socket = socket;
744 pc->tcp = tcp;
745 pc->requestFlags = msg->h.flags;
746 pc->context = context;
747 AssignDomainName(&pc->qname, &q.qname);
748 if (optRR)
749 {
750 if (!ParseEDNS0(pc, optRR, optLen, end))
751 {
752 LogInfo("ProxyCallbackCommon: Invalid EDNS0 option for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
753 }
754 else
755 {
756 pc->optRR = (mDNSu8 *) mDNSPlatformMemAllocate(optLen);
757 if (!pc->optRR)
758 {
759 LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport));
760 FreeDNSProxyClient(pc);
761 return;
762 }
763 mDNSPlatformMemCopy(pc->optRR, optRR, optLen);
764 pc->optLen = optLen;
765 }
766 }
767
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;
777 if (gDNS64Enabled)
778 {
779 if (pc->qtype == kDNSType_PTR)
780 {
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))
784 {
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;
791 }
792 }
793 else if ((pc->qtype == kDNSType_AAAA) && gDNS64ForceAAAASynthesis)
794 {
795 pc->dns64state = kDNSProxyDNS64State_AAAASynthesis;
796 pc->q.qtype = kDNSType_A;
797 }
798 }
799 #endif
800
801 while (*ppc)
802 ppc = &((*ppc)->next);
803 *ppc = pc;
804
805 mDNS_StartQuery(m, &pc->q);
806 }
807
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)
810 {
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);
813 }
814
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)
817 {
818 LogInfo("ProxyTCPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), (int)(end - (mDNSu8 *)msg));
819
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)))
823 {
824 DNSProxyClient **ppc = &DNSProxyClients;
825 DNSProxyClient **prevpc;
826
827 prevpc = ppc;
828 while (*ppc && (*ppc)->socket != socket)
829 {
830 prevpc = ppc;
831 ppc=&(*ppc)->next;
832 }
833 if (!*ppc)
834 {
835 mDNSPlatformDisposeProxyContext(socket);
836 LogMsg("ProxyTCPCallback: socket cannot be found");
837 return;
838 }
839 *prevpc = (*ppc)->next;
840 LogInfo("ProxyTCPCallback: free");
841 mDNSPlatformDisposeProxyContext(socket);
842 FreeDNSProxyClient(*ppc);
843 return;
844 }
845 ProxyCallbackCommon(socket, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNStrue, context);
846 }
847
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)
851 #else
852 mDNSexport void DNSProxyInit(mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf)
853 #endif
854 {
855 mDNS *const m = &mDNSStorage;
856 int i;
857
858 // Store DNSProxy Interface fields in mDNS struct
859 for (i = 0; i < MaxIp; i++)
860 m->dp_ipintf[i] = IpIfArr[i];
861 m->dp_opintf = OpIf;
862
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)
866 if (IPv6Prefix)
867 {
868 mDNSu32 copyLen;
869 mDNSPlatformMemZero(&gDNS64Prefix, (mDNSu32)sizeof(gDNS64Prefix));
870 switch (IPv6PrefixBitLen)
871 {
872 case 32:
873 gDNS64Prefix.length = nw_nat64_prefix_length_32;
874 copyLen = 4;
875 break;
876
877 case 40:
878 gDNS64Prefix.length = nw_nat64_prefix_length_40;
879 copyLen = 5;
880 break;
881
882 case 48:
883 gDNS64Prefix.length = nw_nat64_prefix_length_48;
884 copyLen = 6;
885 break;
886
887 case 56:
888 gDNS64Prefix.length = nw_nat64_prefix_length_56;
889 copyLen = 7;
890 break;
891
892 case 64:
893 gDNS64Prefix.length = nw_nat64_prefix_length_64;
894 copyLen = 8;
895 break;
896
897 case 96:
898 gDNS64Prefix.length = nw_nat64_prefix_length_96;
899 copyLen = 12;
900 break;
901
902 default:
903 copyLen = 0;
904 break;
905 }
906 if (copyLen > 0)
907 {
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)");
914 }
915 else
916 {
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);
921 }
922 }
923 #endif
924 }
925
926 mDNSexport void DNSProxyTerminate(void)
927 {
928 mDNS *const m = &mDNSStorage;
929 int i;
930
931 // Clear DNSProxy Interface fields from mDNS struct
932 for (i = 0; i < MaxIp; i++)
933 m->dp_ipintf[i] = 0;
934 m->dp_opintf = 0;
935
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;
940 #endif
941 }
942 #else // UNICAST_DISABLED
943
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)
945 {
946 (void) socket;
947 (void) msg;
948 (void) end;
949 (void) srcaddr;
950 (void) srcport;
951 (void) dstaddr;
952 (void) dstport;
953 (void) InterfaceID;
954 (void) context;
955 }
956
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)
958 {
959 (void) socket;
960 (void) msg;
961 (void) end;
962 (void) srcaddr;
963 (void) srcport;
964 (void) dstaddr;
965 (void) dstport;
966 (void) InterfaceID;
967 (void) context;
968 }
969
970 mDNSexport void DNSProxyInit(mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf)
971 {
972 (void) IpIfArr;
973 (void) OpIf;
974 }
975 extern void DNSProxyTerminate(void)
976 {
977 }
978
979
980 #endif // UNICAST_DISABLED