2 * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #if MDNSRESPONDER_SUPPORTS(APPLE, DNS64)
21 #include <AssertMacros.h>
22 #include <nw/private.h>
27 #include "dns_sd_internal.h"
28 #include "mDNSMacOSX.h"
31 //===========================================================================================================================
33 //===========================================================================================================================
35 #define kDNS64IPv4OnlyFQDNString "\x8" "ipv4only" "\x4" "arpa"
36 #define kDNS64IPv4OnlyFQDN ((const domainname *) kDNS64IPv4OnlyFQDNString)
37 #define kDNS64IPv4OnlyFQDNLength 15 // 9 bytes for first label, 5 bytes for second label, and 1 byte for the root label.
39 #define sizeof_field(TYPE, FIELD) sizeof(((TYPE *)0)->FIELD) // From CoreUtils.h
41 check_compile_time(sizeof(kDNS64IPv4OnlyFQDNString
) == kDNS64IPv4OnlyFQDNLength
);
42 check_compile_time(sizeof_field(DNSQuestion
, qname
) >= kDNS64IPv4OnlyFQDNLength
);
43 check_compile_time(sizeof_field(DNS64
, qnameStash
) == kDNS64IPv4OnlyFQDNLength
);
45 //===========================================================================================================================
47 //===========================================================================================================================
49 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
50 mDNSlocal mStatus
_DNS64GetIPv6Addrs(mDNS
*m
, mdns_dns_service_t inDNSService
, struct in6_addr
**outAddrs
, uint32_t *outAddrCount
);
52 mDNSlocal mStatus
_DNS64GetIPv6Addrs(mDNS
*m
, mDNSu32 inResGroupID
, struct in6_addr
**outAddrs
, uint32_t *outAddrCount
);
54 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
55 mDNSlocal mStatus
_DNS64GetPrefixes(mDNS
*m
, mdns_dns_service_t inDNSService
, nw_nat64_prefix_t
**outPrefixes
, uint32_t *outPrefixCount
);
57 mDNSlocal mStatus
_DNS64GetPrefixes(mDNS
*m
, mDNSu32 inResGroupID
, nw_nat64_prefix_t
**outPrefixes
, uint32_t *outPrefixCount
);
59 mDNSlocal mDNSu32
_DNS64IPv4OnlyFQDNHash(void);
60 mDNSlocal
void _DNS64RestartQuestion(mDNS
*m
, DNSQuestion
*q
, DNS64State newState
);
61 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
62 mDNSlocal mDNSBool
_DNS64InterfaceSupportsNAT64(mdns_dns_service_t inDNSService
);
64 mDNSlocal mDNSBool
_DNS64InterfaceSupportsNAT64(uint32_t inIfIndex
);
66 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
67 mDNSlocal mDNSBool
_DNS64TestIPv6Synthesis(mDNS
*m
, mdns_dns_service_t inDNSService
, const mDNSv4Addr
*inV4Addr
);
69 mDNSlocal mDNSBool
_DNS64TestIPv6Synthesis(mDNS
*m
, mDNSu32 inResGroupID
, const mDNSv4Addr
*inV4Addr
);
72 //===========================================================================================================================
74 //===========================================================================================================================
76 mDNSexport mDNSBool
DNS64StateMachine(mDNS
*m
, DNSQuestion
*inQ
, const ResourceRecord
*inRR
, QC_result inResult
)
78 // If this is an mDNS question, then exit early. DNS64 is only for unicast DNS questions.
80 if (mDNSOpaque16IsZero(inQ
->TargetQID
)) return (mDNSfalse
);
82 switch (inQ
->dns64
.state
)
84 // If this question is going to be answered with a negative AAAA record and the question is not for "ipv4only.arpa." and
85 // the question's DNS server's interface supports NAT64, then restart the question as an "ipv4only.arpa." AAAA question.
86 // Otherwise, do nothing.
88 case kDNS64State_Initial
:
89 if ((inRR
->RecordType
== kDNSRecordTypePacketNegative
) && (inResult
== QC_add
))
91 if ((inQ
->qtype
== kDNSType_AAAA
) &&
92 (inRR
->rrtype
== kDNSType_AAAA
) &&
93 (inRR
->rrclass
== kDNSClass_IN
) &&
94 ((inQ
->qnamehash
!= _DNS64IPv4OnlyFQDNHash()) || !SameDomainName(&inQ
->qname
, kDNS64IPv4OnlyFQDN
)) &&
95 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
96 inQ
->dnsservice
&& _DNS64InterfaceSupportsNAT64(inQ
->dnsservice
))
99 _DNS64InterfaceSupportsNAT64((uint32_t)((uintptr_t)inQ
->qDNSServer
->interface
)))
102 _DNS64RestartQuestion(m
, inQ
, kDNS64State_PrefixDiscovery
);
105 else if ((inQ
->qtype
== kDNSType_PTR
) &&
106 (inRR
->rrtype
== kDNSType_PTR
) &&
107 (inRR
->rrclass
== kDNSClass_IN
) &&
108 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
109 inQ
->dnsservice
&& _DNS64InterfaceSupportsNAT64(inQ
->dnsservice
) &&
112 _DNS64InterfaceSupportsNAT64((uint32_t)((uintptr_t)inQ
->qDNSServer
->interface
)) &&
114 GetReverseIPv6Addr(&inQ
->qname
, NULL
))
116 _DNS64RestartQuestion(m
, inQ
, kDNS64State_PrefixDiscoveryPTR
);
122 // If the "ipv4only.arpa." question is going to be answered with a positive AAAA record, then restart it as a question
123 // for an A record with the original AAAA qname.
124 // Otherwise, restart the question for the original AAAA record.
126 case kDNS64State_PrefixDiscovery
:
127 if ((inRR
->RecordType
!= kDNSRecordTypePacketNegative
) &&
128 (inResult
== QC_add
) &&
129 (inRR
->rrtype
== kDNSType_AAAA
) &&
130 (inRR
->rrclass
== kDNSClass_IN
))
132 _DNS64RestartQuestion(m
, inQ
, kDNS64State_QueryA
);
136 _DNS64RestartQuestion(m
, inQ
, kDNS64State_QueryAAAA
);
140 // The "ipv4only.arpa." question is going to be answered. Restart the question now. DNS64HandleNewQuestion() will decide
141 // whether or not to change it to a reverse IPv4 question.
143 case kDNS64State_PrefixDiscoveryPTR
:
144 _DNS64RestartQuestion(m
, inQ
, kDNS64State_QueryPTR
);
147 // If this question is going to be answered with a CNAME, then do nothing.
148 // If this question is going to be answered with a positive A record that's synthesizable, then set the state to
150 // Otherwise, restart the question for the original AAAA record.
152 case kDNS64State_QueryA
:
153 if (inRR
->rrtype
!= kDNSType_CNAME
)
155 if ((inRR
->RecordType
!= kDNSRecordTypePacketNegative
) &&
156 (inResult
== QC_add
) &&
157 (inRR
->rrtype
== kDNSType_A
) &&
158 (inRR
->rrclass
== kDNSClass_IN
) &&
159 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
161 _DNS64TestIPv6Synthesis(m
, inQ
->dnsservice
, &inRR
->rdata
->u
.ipv4
))
164 _DNS64TestIPv6Synthesis(m
, inQ
->qDNSServer
->resGroupID
, &inRR
->rdata
->u
.ipv4
))
167 inQ
->dns64
.state
= kDNS64State_QueryA2
;
171 _DNS64RestartQuestion(m
, inQ
, kDNS64State_QueryAAAA
);
177 // For all other states, do nothing.
179 case kDNS64State_QueryA2
:
180 case kDNS64State_QueryAAAA
:
181 case kDNS64State_QueryPTR
:
182 case kDNS64State_ReverseIPv4
:
183 case kDNS64State_ReverseIPv6
:
187 LogMsg("DNS64StateMachine: unrecognized DNS64 state %d", inQ
->dns64
.state
);
194 //===========================================================================================================================
195 // DNS64AnswerCurrentQuestion
196 //===========================================================================================================================
198 mDNSexport mStatus
DNS64AnswerCurrentQuestion(mDNS
*m
, const ResourceRecord
*inRR
, QC_result inResult
)
201 ResourceRecord newRR
;
203 nw_nat64_prefix_t
* prefixes
= NULL
;
204 uint32_t prefixCount
;
206 struct in_addr v4Addr
;
207 struct in6_addr synthV6
;
208 DNSQuestion
* const q
= m
->CurrentQuestion
;
210 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
211 require_action_quiet(q
->dnsservice
, exit
, err
= mStatus_BadParamErr
);
213 require_action_quiet(q
->qDNSServer
, exit
, err
= mStatus_BadParamErr
);
216 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
217 err
= _DNS64GetPrefixes(m
, q
->dnsservice
, &prefixes
, &prefixCount
);
219 err
= _DNS64GetPrefixes(m
, q
->qDNSServer
->resGroupID
, &prefixes
, &prefixCount
);
221 require_noerr_quiet(err
, exit
);
224 newRR
.rrtype
= kDNSType_AAAA
;
226 rdata
.MaxRDLength
= newRR
.rdlength
;
227 newRR
.rdata
= &rdata
;
229 memcpy(&v4Addr
.s_addr
, inRR
->rdata
->u
.ipv4
.b
, 4);
230 for (i
= 0; i
< prefixCount
; i
++)
232 if (nw_nat64_synthesize_v6(&prefixes
[i
], &v4Addr
, &synthV6
))
234 memcpy(rdata
.u
.ipv6
.b
, synthV6
.s6_addr
, 16);
235 q
->QuestionCallback(m
, q
, &newRR
, inResult
);
236 if (m
->CurrentQuestion
!= q
) break;
239 err
= mStatus_NoError
;
242 if (prefixes
) free(prefixes
);
246 //===========================================================================================================================
247 // DNS64HandleNewQuestion
248 //===========================================================================================================================
250 mDNSexport
void DNS64HandleNewQuestion(mDNS
*m
, DNSQuestion
*inQ
)
252 if (inQ
->dns64
.state
== kDNS64State_QueryPTR
)
254 struct in6_addr v6Addr
;
256 inQ
->dns64
.state
= kDNS64State_ReverseIPv6
;
257 memset(&v6Addr
, 0, sizeof(v6Addr
));
258 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
259 if (inQ
->dnsservice
&& GetReverseIPv6Addr(&inQ
->qname
, v6Addr
.s6_addr
))
261 if (inQ
->qDNSServer
&& GetReverseIPv6Addr(&inQ
->qname
, v6Addr
.s6_addr
))
265 nw_nat64_prefix_t
* prefixes
;
266 uint32_t prefixCount
;
268 struct in_addr v4Addr
;
269 char qnameStr
[MAX_REVERSE_MAPPING_NAME_V4
];
271 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
272 err
= _DNS64GetPrefixes(m
, inQ
->dnsservice
, &prefixes
, &prefixCount
);
274 err
= _DNS64GetPrefixes(m
, inQ
->qDNSServer
->resGroupID
, &prefixes
, &prefixCount
);
276 require_noerr_quiet(err
, exit
);
278 for (i
= 0; i
< prefixCount
; i
++)
280 if (nw_nat64_extract_v4(&prefixes
[i
], &v6Addr
, &v4Addr
))
282 const mDNSu8
* const a
= (const mDNSu8
*)&v4Addr
.s_addr
;
284 snprintf(qnameStr
, sizeof(qnameStr
), "%u.%u.%u.%u.in-addr.arpa.", a
[3], a
[2], a
[1], a
[0]);
285 MakeDomainNameFromDNSNameString(&inQ
->qname
, qnameStr
);
286 inQ
->qnamehash
= DomainNameHashValue(&inQ
->qname
);
287 inQ
->dns64
.state
= kDNS64State_ReverseIPv4
;
299 //===========================================================================================================================
301 //===========================================================================================================================
303 // Called from mDNS_StopQuery_internal().
305 mDNSexport
void DNS64ResetState(DNSQuestion
*inQ
)
307 switch (inQ
->dns64
.state
)
309 case kDNS64State_PrefixDiscoveryPTR
:
310 inQ
->qtype
= kDNSType_PTR
; // Restore qtype to PTR and fall through.
312 case kDNS64State_PrefixDiscovery
:
313 memcpy(&inQ
->qname
, inQ
->dns64
.qnameStash
, sizeof(inQ
->dns64
.qnameStash
)); // Restore the previous qname.
314 inQ
->qnamehash
= DomainNameHashValue(&inQ
->qname
);
317 case kDNS64State_QueryA
:
318 case kDNS64State_QueryA2
:
319 inQ
->qtype
= kDNSType_AAAA
; // Restore qtype to AAAA.
322 // Do nothing for the other states.
324 case kDNS64State_Initial
:
325 case kDNS64State_QueryAAAA
:
326 case kDNS64State_QueryPTR
:
327 case kDNS64State_ReverseIPv4
:
328 case kDNS64State_ReverseIPv6
:
332 LogMsg("DNS64ResetState: unrecognized DNS64 state %d", inQ
->dns64
.state
);
335 inQ
->dns64
.state
= kDNS64State_Initial
;
338 //===========================================================================================================================
339 // DNS64RestartQuestions
340 //===========================================================================================================================
342 #if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
343 mDNSexport
void DNS64RestartQuestions(mDNS
*m
)
346 DNSQuestion
* restartList
= NULL
;
347 DNSServer
* newServer
;
349 m
->RestartQuestion
= m
->Questions
;
350 while (m
->RestartQuestion
)
352 q
= m
->RestartQuestion
;
353 m
->RestartQuestion
= q
->next
;
354 if (q
->dns64
.state
!= kDNS64State_Initial
)
356 SetValidDNSServers(m
, q
);
357 q
->triedAllServersOnce
= mDNSfalse
;
358 newServer
= GetServerForQuestion(m
, q
);
359 if (q
->qDNSServer
!= newServer
)
361 if (!CacheRecordRmvEventsForQuestion(m
, q
))
363 LogInfo("DNS64RestartQuestions: Question deleted while delivering RMV events from cache");
367 LogInfo("DNS64RestartQuestions: Stop question %p %##s (%s)", q
, q
->qname
.c
, DNSTypeName(q
->qtype
));
368 mDNS_StopQuery_internal(m
, q
);
369 q
->next
= restartList
;
375 while ((q
= restartList
) != NULL
)
377 restartList
= restartList
->next
;
379 LogInfo("DNS64RestartQuestions: Start question %p %##s (%s)", q
, q
->qname
.c
, DNSTypeName(q
->qtype
));
380 mDNS_StartQuery_internal(m
, q
);
385 //===========================================================================================================================
386 // _DNS64GetIPv6Addrs
387 //===========================================================================================================================
389 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
390 #define IsPositiveAAAAFromDNSService(RR, DNS_SERVICE) \
391 (((RR)->dnsservice == (DNS_SERVICE)) && \
392 ((RR)->rrtype == kDNSType_AAAA) && \
393 ((RR)->RecordType != kDNSRecordTypePacketNegative) && \
396 #define IsPositiveAAAAFromResGroup(RR, RES_GROUP_ID) \
397 ((RR)->rDNSServer && \
398 ((RR)->rDNSServer->resGroupID == RES_GROUP_ID) && \
399 ((RR)->rrtype == kDNSType_AAAA) && \
400 ((RR)->RecordType != kDNSRecordTypePacketNegative) && \
404 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
405 mDNSlocal mStatus
_DNS64GetIPv6Addrs(mDNS
*m
, mdns_dns_service_t inDNSService
, struct in6_addr
**outAddrs
, uint32_t *outAddrCount
)
407 mDNSlocal mStatus
_DNS64GetIPv6Addrs(mDNS
*m
, const mDNSu32 inResGroupID
, struct in6_addr
**outAddrs
, uint32_t *outAddrCount
)
411 const CacheGroup
* cg
;
412 const CacheRecord
* cr
;
413 struct in6_addr
* addrs
= NULL
;
415 uint32_t recordCount
;
417 cg
= CacheGroupForName(m
, _DNS64IPv4OnlyFQDNHash(), kDNS64IPv4OnlyFQDN
);
418 require_action_quiet(cg
, exit
, err
= mStatus_NoSuchRecord
);
421 for (cr
= cg
->members
; cr
; cr
= cr
->next
)
423 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
424 if (IsPositiveAAAAFromDNSService(&cr
->resrec
, inDNSService
))
426 if (IsPositiveAAAAFromResGroup(&cr
->resrec
, inResGroupID
))
432 require_action_quiet(recordCount
> 0, exit
, err
= mStatus_NoSuchRecord
);
434 addrs
= (struct in6_addr
*)calloc(recordCount
, sizeof(*addrs
));
435 require_action_quiet(addrs
, exit
, err
= mStatus_NoMemoryErr
);
438 for (cr
= cg
->members
; cr
&& (addrCount
< recordCount
); cr
= cr
->next
)
440 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
441 if (IsPositiveAAAAFromDNSService(&cr
->resrec
, inDNSService
))
443 if (IsPositiveAAAAFromResGroup(&cr
->resrec
, inResGroupID
))
446 memcpy(addrs
[addrCount
].s6_addr
, cr
->resrec
.rdata
->u
.ipv6
.b
, 16);
453 *outAddrCount
= addrCount
;
454 err
= mStatus_NoError
;
457 if (addrs
) free(addrs
);
461 //===========================================================================================================================
463 //===========================================================================================================================
465 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
466 mDNSlocal mStatus
_DNS64GetPrefixes(mDNS
*m
, mdns_dns_service_t inDNSService
, nw_nat64_prefix_t
**outPrefixes
, uint32_t *outPrefixCount
)
468 mDNSlocal mStatus
_DNS64GetPrefixes(mDNS
*m
, mDNSu32 inResGroupID
, nw_nat64_prefix_t
**outPrefixes
, uint32_t *outPrefixCount
)
472 struct in6_addr
* v6Addrs
;
473 uint32_t v6AddrCount
;
474 nw_nat64_prefix_t
* prefixes
;
477 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
478 err
= _DNS64GetIPv6Addrs(m
, inDNSService
, &v6Addrs
, &v6AddrCount
);
480 err
= _DNS64GetIPv6Addrs(m
, inResGroupID
, &v6Addrs
, &v6AddrCount
);
482 require_noerr_quiet(err
, exit
);
484 prefixCount
= nw_nat64_copy_prefixes_from_ipv4only_records(v6Addrs
, v6AddrCount
, &prefixes
);
486 require_action_quiet(prefixCount
> 0, exit
, err
= mStatus_UnknownErr
);
488 *outPrefixes
= prefixes
;
489 *outPrefixCount
= prefixCount
;
495 //===========================================================================================================================
496 // _DNS64IPv4OnlyFQDNHash
497 //===========================================================================================================================
499 mDNSlocal mDNSu32
_DNS64IPv4OnlyFQDNHash(void)
501 static dispatch_once_t sHashOnce
;
502 static mDNSu32 sHash
;
504 dispatch_once(&sHashOnce
, ^{ sHash
= DomainNameHashValue(kDNS64IPv4OnlyFQDN
); });
509 //===========================================================================================================================
510 // _DNS64RestartQuestion
511 //===========================================================================================================================
513 mDNSlocal
void _DNS64RestartQuestion(mDNS
*const m
, DNSQuestion
*inQ
, DNS64State inNewState
)
515 mDNS_StopQuery_internal(m
, inQ
);
517 inQ
->dns64
.state
= inNewState
;
518 switch (inQ
->dns64
.state
)
520 case kDNS64State_Initial
:
523 case kDNS64State_PrefixDiscovery
:
524 case kDNS64State_PrefixDiscoveryPTR
:
525 // Save the first 15 bytes from the original qname that are displaced by setting qname to "ipv4only.arpa.".
527 memcpy(inQ
->dns64
.qnameStash
, &inQ
->qname
, sizeof(inQ
->dns64
.qnameStash
));
528 AssignDomainName(&inQ
->qname
, kDNS64IPv4OnlyFQDN
);
529 inQ
->qnamehash
= _DNS64IPv4OnlyFQDNHash();
530 inQ
->qtype
= kDNSType_AAAA
;
533 case kDNS64State_QueryA
:
534 case kDNS64State_QueryA2
:
535 inQ
->qtype
= kDNSType_A
;
538 case kDNS64State_QueryPTR
:
539 case kDNS64State_ReverseIPv4
:
540 case kDNS64State_ReverseIPv6
:
541 inQ
->qtype
= kDNSType_PTR
;
544 case kDNS64State_QueryAAAA
:
545 inQ
->qtype
= kDNSType_AAAA
;
549 LogMsg("DNS64RestartQuestion: unrecognized DNS64 state %d", inQ
->dns64
.state
);
553 mDNS_StartQuery_internal(m
, inQ
);
556 //===========================================================================================================================
557 // _DNS64InterfaceSupportsNAT64
558 //===========================================================================================================================
560 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
561 mDNSlocal mDNSBool
_DNS64InterfaceSupportsNAT64(mdns_dns_service_t inDNSService
)
563 if (!mdns_dns_service_interface_has_ipv4_connectivity(inDNSService
) &&
564 mdns_dns_service_interface_has_ipv6_connectivity(inDNSService
))
574 mDNSlocal mDNSBool
_DNS64InterfaceSupportsNAT64(uint32_t inIfIndex
)
576 mdns_interface_monitor_t monitor
= GetInterfaceMonitorForIndex(inIfIndex
);
577 if (monitor
&& !mdns_interface_monitor_has_ipv4_connectivity(monitor
) &&
578 mdns_interface_monitor_has_ipv6_connectivity(monitor
))
586 //===========================================================================================================================
587 // _DNS64TestIPv6Synthesis
588 //===========================================================================================================================
590 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
591 mDNSlocal mDNSBool
_DNS64TestIPv6Synthesis(mDNS
*m
, mdns_dns_service_t inDNSService
, const mDNSv4Addr
*inV4Addr
)
593 mDNSlocal mDNSBool
_DNS64TestIPv6Synthesis(mDNS
*m
, mDNSu32 inResGroupID
, const mDNSv4Addr
*inV4Addr
)
597 nw_nat64_prefix_t
* prefixes
= NULL
;
598 uint32_t prefixCount
;
600 struct in_addr v4Addr
;
601 struct in6_addr synthV6
;
602 mDNSBool result
= mDNSfalse
;
604 #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
605 err
= _DNS64GetPrefixes(m
, inDNSService
, &prefixes
, &prefixCount
);
607 err
= _DNS64GetPrefixes(m
, inResGroupID
, &prefixes
, &prefixCount
);
609 require_noerr_quiet(err
, exit
);
611 memcpy(&v4Addr
.s_addr
, inV4Addr
->b
, 4);
612 for (i
= 0; i
< prefixCount
; i
++)
614 if (nw_nat64_synthesize_v6(&prefixes
[i
], &v4Addr
, &synthV6
))
622 if (prefixes
) free(prefixes
);
625 #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNS64)