xref: /netbsd-src/external/apache2/mDNSResponder/dist/mDNSCore/DNSCommon.h (revision 450dee115da5565aafca4d92dc3ba1c9d6b0cd2a)
1 /* -*- Mode: C; tab-width: 4 -*-
2  *
3  * Copyright (c) 2002-2024 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  *     https://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 #ifndef __DNSCOMMON_H_
19 #define __DNSCOMMON_H_
20 
21 #include "mDNSEmbeddedAPI.h"
22 
23 // For gettimeofday
24 #if !defined(_WIN32)
25 #include <sys/time.h>
26 #else
27 #include "PosixCompat.h"
28 #endif
29 
30 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
31 #include "dnssec_mdns_core.h"
32 #endif
33 
34 #ifdef  __cplusplus
35 extern "C" {
36 #endif
37 
38 //*************************************************************************************************************
39 // Macros
40 
41 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
42 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
43 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
44 #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
45 #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
46 
47 #define ReadField16(PTR) ((mDNSu16)((((mDNSu16)((const mDNSu8 *)(PTR))[0]) << 8) | ((mDNSu16)((const mDNSu8 *)(PTR))[1])))
48 #define ReadField32(PTR) \
49     ((mDNSu32)( \
50         (((mDNSu32)((const mDNSu8 *)(PTR))[0]) << 24) | \
51         (((mDNSu32)((const mDNSu8 *)(PTR))[1]) << 16) | \
52         (((mDNSu32)((const mDNSu8 *)(PTR))[2]) <<  8) | \
53          ((mDNSu32)((const mDNSu8 *)(PTR))[3])))
54 
55 #ifdef UINT64_MAX
56 
57 #define ReadField64(PTR) \
58     ((uint64_t)( \
59         (((uint64_t)((const mDNSu8 *)(PTR))[0]) << 56) | \
60         (((uint64_t)((const mDNSu8 *)(PTR))[1]) << 48) | \
61         (((uint64_t)((const mDNSu8 *)(PTR))[2]) << 40) | \
62         (((uint64_t)((const mDNSu8 *)(PTR))[3]) << 32) | \
63         (((uint64_t)((const mDNSu8 *)(PTR))[4]) << 24) | \
64         (((uint64_t)((const mDNSu8 *)(PTR))[5]) << 16) | \
65         (((uint64_t)((const mDNSu8 *)(PTR))[6]) <<  8) | \
66          ((uint64_t)((const mDNSu8 *)(PTR))[7])))
67 
68 #endif
69 
70 // ***************************************************************************
71 // MARK: - DNS Protocol Constants
72 
73 typedef enum
74 {
75     kDNSFlag0_QR_Mask        = 0x80,    // Query or response?
76     kDNSFlag0_QR_Query       = 0x00,
77     kDNSFlag0_QR_Response    = 0x80,
78 
79     kDNSFlag0_OP_Mask        = 0xF << 3, // Operation type
80     kDNSFlag0_OP_StdQuery    = 0x0 << 3,
81     kDNSFlag0_OP_Iquery      = 0x1 << 3,
82     kDNSFlag0_OP_Status      = 0x2 << 3,
83     kDNSFlag0_OP_Unused3     = 0x3 << 3,
84     kDNSFlag0_OP_Notify      = 0x4 << 3,
85     kDNSFlag0_OP_Update      = 0x5 << 3,
86     kDNSFlag0_OP_DSO         = 0x6 << 3,
87 
88     kDNSFlag0_QROP_Mask   = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask,
89 
90     kDNSFlag0_AA          = 0x04,       // Authoritative Answer?
91     kDNSFlag0_TC          = 0x02,       // Truncated?
92     kDNSFlag0_RD          = 0x01,       // Recursion Desired?
93     kDNSFlag1_RA          = 0x80,       // Recursion Available?
94 
95     kDNSFlag1_Zero        = 0x40,       // Reserved; must be zero
96     kDNSFlag1_AD          = 0x20,       // Authentic Data [RFC 2535]
97     kDNSFlag1_CD          = 0x10,       // Checking Disabled [RFC 2535]
98 
99     kDNSFlag1_RC_Mask     = 0x0F,       // Response code
100     kDNSFlag1_RC_NoErr    = 0x00,
101     kDNSFlag1_RC_FormErr  = 0x01,
102     kDNSFlag1_RC_ServFail = 0x02,
103     kDNSFlag1_RC_NXDomain = 0x03,
104     kDNSFlag1_RC_NotImpl  = 0x04,
105     kDNSFlag1_RC_Refused  = 0x05,
106     kDNSFlag1_RC_YXDomain = 0x06,
107     kDNSFlag1_RC_YXRRSet  = 0x07,
108     kDNSFlag1_RC_NXRRSet  = 0x08,
109     kDNSFlag1_RC_NotAuth  = 0x09,
110     kDNSFlag1_RC_NotZone  = 0x0A,
111 	kDNSFlag1_RC_DSOTypeNI = 0x0B
112 } DNS_Flags;
113 
114 typedef enum
115 {
116     TSIG_ErrBadSig  = 16,
117     TSIG_ErrBadKey  = 17,
118     TSIG_ErrBadTime = 18
119 } TSIG_ErrorCode;
120 
121 
122 // ***************************************************************************
123 // MARK: - General Utility Functions
124 
125 extern NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf);
126 extern mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf);
127 
128 extern mDNSu32 mDNSRandom(mDNSu32 max);     // Returns pseudo-random result from zero to max inclusive
129 
130 #if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
131 extern mDNSu32 mDNS_GetNextResolverGroupID(void);
132 #endif
133 
134 MDNS_CLOSED_ENUM(mDNSNonCryptoHash, mDNSu8,
135     mDNSNonCryptoHash_FNV1a   = 0,
136     mDNSNonCryptoHash_SDBM    = 1,
137 );
138 
139 /*!
140  *  @brief
141  *      Calculate hash given previous calculated hash and new bytes, with given hash algorithm.
142  *
143  *  @param algorithm
144  *      The hash algorithm to use.
145  *
146  *  @param previousHash
147  *      The hash of previous bytes that has been calculated.
148  *
149  *  @param bytes
150  *      Bytes to update the hash.
151  *
152  *  @param len
153  *      The length of the bytes.
154  *
155  *  @result
156  *      The hash value of (previous bytes + new bytes).
157  */
158 extern mDNSu32 mDNS_NonCryptoHashUpdateBytes(mDNSNonCryptoHash algorithm, mDNSu32 previousHash, const mDNSu8 *bytes,
159     mDNSu32 len);
160 
161 /*!
162  *  @brief
163  *      Calculate hash of the bytes.
164  *
165  *  @param algorithm
166  *      The hash algorithm to use.
167  *
168  *  @param bytes
169  *      Bytes to calculate the hash.
170  *
171  *  @param len
172  *      The length of the bytes.
173  *
174  *  @result
175  *      The hash value.
176  */
177 extern mDNSu32 mDNS_NonCryptoHash(mDNSNonCryptoHash algorithm, const mDNSu8 *bytes, mDNSu32 len);
178 
179 /*!
180  *    @brief
181  *      Computes the 32-bit FNV-1a (non-cryptographic) hash value for an domain name.
182  *
183  *    @param name
184  *      The domain name.
185  *
186  *    @result
187  *      The hash value.
188  *
189  *    @discussion
190  *      Since domain name is case-insensitive, to make sure that hash values still match when the case changes,
191  *      the hash value is calculated with the normalized domain name by treating uppercase ASCII letters to their
192  *      lowercase counterparts.
193  *
194  *      For more information about FNV Non-Cryptographic Hash ,
195  *      see <https://datatracker.ietf.org/doc/html/draft-eastlake-fnv-21>.
196  */
197 extern mDNSu32 mDNS_DomainNameFNV1aHash(const domainname *name);
198 
199 extern mDNSs32 mDNSGetTimeOfDay(struct timeval *tv, struct timezone *tz);
200 
201 // ***************************************************************************
202 // MARK: - Domain Name Utility Functions
203 
204 #define mDNSSubTypeLabel   "\x04_sub"
205 
206 #define mDNSIsDigit(X)      ((X) >= '0' && (X) <= '9')
207 #define mDNSIsUpperCase(X)  ((X) >= 'A' && (X) <= 'Z')
208 #define mDNSIsLowerCase(X)  ((X) >= 'a' && (X) <= 'z')
209 #define mDNSIsLetter(X)     (mDNSIsUpperCase(X) || mDNSIsLowerCase(X))
210 #define mDNSIsPrintASCII(X) (((X) >= 32) && ((X) <= 126))
211 
212 /*!
213  *  @brief
214  *      Convert ASCII uppercase character to its lowercase counterparts.
215  *
216  *  @param c
217  *      The ASCII character.
218  *
219  *  @result
220  *      The lowercase value of the character, if the original one is uppercase, otherwise, the original value.
221  */
222 static inline int
223 mDNSASCIITolower(const int c)
224 {
225     if (mDNSIsUpperCase(c))
226     {
227         return (c + ('a' - 'A'));
228     }
229     else
230     {
231         return c;
232     }
233 }
234 
235 /*!
236  *  @brief
237  *      Check if the string consists of all valid UTF-8 characters.
238  *
239  *  @param str
240  *      The string ending with NULL.
241  *
242  *  @result
243  *      True if the string consists of valid UTF-8 characters, otherwise, false.
244  */
245 extern mDNSBool mDNSAreUTF8String(const char *str);
246 
247 // We believe we have adequate safeguards to protect against cache poisoning.
248 // In the event that someone does find a workable cache poisoning attack, we want to limit the lifetime of the poisoned entry.
249 // We set the maximum allowable TTL to one hour.
250 // With the 25% correction factor to avoid the DNS Zeno's paradox bug, that gives us an actual maximum lifetime of 75 minutes.
251 
252 #define mDNSMaximumMulticastTTLSeconds  (mDNSu32)4500
253 #define mDNSMaximumUnicastTTLSeconds    (mDNSu32)3600
254 
255 // Adjustment factor to avoid race condition (used for unicast cache entries) :
256 // Suppose real record has TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100.
257 // If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another
258 // 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox.
259 // To avoid this, we extend the record's effective TTL to give it a little extra grace period.
260 // We adjust the 100 second TTL to 127. This means that when we do our 80% query after 102 seconds,
261 // the cached copy at our local caching server will already have expired, so the server will be forced
262 // to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds.
263 
264 #define RRAdjustTTL(ttl) ((ttl) + ((ttl)/4) + 2)
265 #define RRUnadjustedTTL(ttl) ((((ttl) - 2) * 4) / 5)
266 
267 typedef enum
268 {
269     uDNS_LLQ_Not = 0,   // Normal uDNS answer: Flush any stale records from cache, and respect record TTL
270     uDNS_LLQ_Ignore,    // LLQ initial challenge packet: ignore -- has no useful records for us
271     uDNS_LLQ_Entire,    // LLQ initial set of answers: Flush any stale records from cache, but assume TTL is 2 x LLQ refresh interval
272     uDNS_LLQ_Events     // LLQ event packet: don't flush cache; assume TTL is 2 x LLQ refresh interval
273 } uDNS_LLQType;
274 
275 extern mDNSu32 GetEffectiveTTL(uDNS_LLQType LLQType, mDNSu32 ttl);
276 
277 #define mDNSValidHostChar(X, notfirst, notlast) (mDNSIsLetter(X) || mDNSIsDigit(X) || ((notfirst) && (notlast) && (X) == '-') )
278 
279 extern mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent);
280 extern int CountLabels(const domainname *d);
281 extern const domainname *SkipLeadingLabels(const domainname *d, int skip);
282 
283 extern mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max);
284 extern mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText);
285 extern mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText);
286 extern void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText);
287 #define ValidateDomainName(N) (DomainNameLength(N) <= MAX_DOMAIN_NAME)
288 
289 extern mDNSBool IsSubdomain(const domainname *const subdomain, const domainname *const domain);
290 
291 // ***************************************************************************
292 // MARK: - Resource Record Utility Functions
293 
294 // IdenticalResourceRecord returns true if two resources records have
295 // the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
296 
297 // IdenticalSameNameRecord is the same, except it skips the expensive SameDomainName() check,
298 // which is at its most expensive and least useful in cases where we know in advance that the names match
299 
300 // Note: The dominant use of IdenticalResourceRecord is from ProcessQuery(), handling known-answer lists. In this case
301 // it's common to have a whole bunch or records with exactly the same name (e.g. "_http._tcp.local") but different RDATA.
302 // The SameDomainName() check is expensive when the names match, and in this case *all* the names match, so we
303 // used to waste a lot of CPU time verifying that the names match, only then to find that the RDATA is different.
304 // We observed mDNSResponder spending 30% of its total CPU time on this single task alone.
305 // By swapping the checks so that we check the RDATA first, we can quickly detect when it's different
306 // (99% of the time) and then bail out before we waste time on the expensive SameDomainName() check.
307 
308 extern mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename);
309 
310 static inline mDNSBool IdenticalSameNameRecord(const ResourceRecord *const r1, const ResourceRecord *const r2)
311 {
312     return
313     (
314     #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
315         // Other than the ordinary non-DNSSEC records, there are two types of DNSSEC records:
316         // 1. DNSSEC to be validated: Records that come from DNSSEC-enabled response (with DNSSEC OK/Checking Disabled bits set).
317         // 2. DNSSEC validated: Records that come from the "DNSSEC to be validated" records, and has passed the DNSSEC validation.
318         // Only the records that have the same type can be compared.
319          (resource_records_have_same_dnssec_rr_category(r1, r2))     &&
320     #endif
321          r1->rrtype         == r2->rrtype       &&
322          r1->rrclass        == r2->rrclass      &&
323          r1->rdlength       == r2->rdlength     &&
324          r1->rdatahash      == r2->rdatahash    &&
325          SameRDataBody(r1, &r2->rdata->u, SameDomainName)
326     );
327 }
328 
329 static inline mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2)
330 {
331     return
332     (
333         r1->namehash == r2->namehash        &&
334         IdenticalSameNameRecord(r1, r2)     &&
335         SameDomainName(r1->name, r2->name)
336     );
337 }
338 
339 // A given RRType answers a QuestionType if RRType is CNAME, or types match, or QuestionType is ANY,
340 // or the RRType is NSEC and positively asserts the nonexistence of the type being requested from multicast,
341 // or the question requires the corresponding DNSSEC RRs,
342 // or the RRType is RRSIG that covers the the type being requested.
343 
344 typedef mDNSu32 RRTypeAnswersQuestionTypeFlags;
345 #define kRRTypeAnswersQuestionTypeFlagsNone 0
346 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
347 #define kRRTypeAnswersQuestionTypeFlagsRequiresDNSSECRRToValidate   (1U << 0)   // Use this flag to indicate that question needs "DNSSEC to be validated" records to do validation.
348 #define kRRTypeAnswersQuestionTypeFlagsRequiresDNSSECRRValidated    (1U << 1)   // Use this flag to indicate that question needs "DNSSEC validated" records to return to the client.
349 #endif
350 extern mDNSBool RRTypeAnswersQuestionType(const ResourceRecord *rr, mDNSu16 qtype, RRTypeAnswersQuestionTypeFlags flags);
351 
352 // Unicast NSEC records have the NSEC bit set whereas the multicast NSEC ones don't
353 #define UNICAST_NSEC(rr) ((rr)->rrtype == kDNSType_NSEC && RRAssertsExistence((rr), kDNSType_NSEC))
354 #define MULTICAST_NSEC(rr) ((rr)->rrtype == kDNSType_NSEC && RRAssertsNonexistence((rr), kDNSType_NSEC))
355 
356 extern mDNSu32 RDataHashValue(const ResourceRecord *const rr);
357 extern mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename);
358 extern mDNSBool SameNameCacheRecordAnswersQuestion(const CacheRecord *const cr, const DNSQuestion *const q);
359 extern mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
360 extern mDNSBool AuthRecordAnswersQuestion(const AuthRecord *const ar, const DNSQuestion *const q);
361 extern mDNSBool CacheRecordAnswersQuestion(const CacheRecord *const cr, const DNSQuestion *const q);
362 extern mDNSBool AnyTypeRecordAnswersQuestion (const AuthRecord *const ar, const DNSQuestion *const q);
363 extern mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q);
364 extern mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const rr, const DNSQuestion *const q);
365 extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate);
366 extern mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd);
367 extern mStatus DNSNameToLowerCase(domainname *d, domainname *result);
368 
369 /*!
370  *  @brief
371  *      Gets a pointer to a resource record's record data in wire format.
372  *
373  *  @param rr
374  *      The resource record object.
375  *
376  *  @param bytesBuffer
377  *      The buffer to be used as a temporary space to hold a resource record's record data in wire format if no
378  *      existing wire-format rdata is available.
379  *
380  *  @param bufferSize
381  *      The size of the buffer.
382  *
383  *  @param outRDataLen
384  *      If non-NULL, the address of a variable to set to the length of the resource record's record data in
385  *      wire format.
386  *
387  *  @param outError
388  *      If non-NULL, the address of a variable to set to either a non-zero error code if this function fails, or
389  *      `mStatus_NoError` if this function succeeds.
390  *
391  *  @result
392  *      The pointer to the resource record's record data in wire format if no error occurs. Otherwise mDNSNULL
393  *      and `outError` is set to a non-zero error code.
394  */
395 extern const mDNSu8 * ResourceRecordGetRDataBytesPointer(const ResourceRecord *rr, mDNSu8 *bytesBuffer,
396     mDNSu16 bufferSize, mDNSu16 *outRDataLen, mStatus *outError);
397 
398 #define GetRRDomainNameTarget(RR) (                                                                          \
399         ((RR)->rrtype == kDNSType_NS || (RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_DNAME) ? &(RR)->rdata->u.name        : \
400         ((RR)->rrtype == kDNSType_MX || (RR)->rrtype == kDNSType_AFSDB || (RR)->rrtype == kDNSType_RT  || (RR)->rrtype == kDNSType_KX   ) ? &(RR)->rdata->u.mx.exchange : \
401         ((RR)->rrtype == kDNSType_SRV                                  ) ? &(RR)->rdata->u.srv.target : mDNSNULL )
402 
403 #define LocalRecordReady(X) ((X)->resrec.RecordType != kDNSRecordTypeUnique)
404 
405 // ***************************************************************************
406 // MARK: - DNS Message Creation Functions
407 
408 extern void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags);
409 extern const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname);
410 extern mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name);
411 extern mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr);
412 
413 // If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes,
414 // but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet
415 
416 #define AllowedRRSpace(msg) (((msg)->h.numAnswers || (msg)->h.numAuthorities || (msg)->h.numAdditionals) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData)
417 
418 extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, const ResourceRecord *rr,
419     mDNSu32 ttl, const mDNSu8 *limit);
420 
421 #define PutResourceRecordTTL(msg, ptr, count, rr, ttl) \
422     PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AllowedRRSpace(msg))
423 
424 #define PutResourceRecordTTLJumbo(msg, ptr, count, rr, ttl) \
425     PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AbsoluteMaxDNSMessageData)
426 
427 #define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl)
428 
429 // Calculate TSR only OPT space
430 // Assume local variable 'tsrOptsCount'
431 #define TSR_OPT_SPACE           (tsrOptsCount * DNSOpt_TSRData_Space)
432 #define TSR_OPT_HEADER_SPACE    (tsrOptsCount ? DNSOpt_Header_Space : 0)
433 #define TSR_OPT_TOTAL_SPACE     (TSR_OPT_SPACE + TSR_OPT_HEADER_SPACE)
434 
435 #define PutResourceRecordTSR(msg, ptr, count, rr) \
436     PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (rr)->rroriginalttl, (msg)->data + AllowedRRSpace(msg) - TSR_OPT_TOTAL_SPACE)
437 
438 // Calculate OPT space
439 // Assume local variables 'OwnerRecordSpace', 'TraceRecordSpace' & 'tsrOptsCount'
440 #define RR_OPT_SPACE    \
441     (OwnerRecordSpace + TraceRecordSpace + TSR_OPT_SPACE +     \
442     ((OwnerRecordSpace || TraceRecordSpace) ? 0 : TSR_OPT_HEADER_SPACE))
443 
444 // The PutRR_OS variants assume a local variable 'm', put build the packet at m->omsg,
445 #define PutRR_OS_TTL(ptr, count, rr, ttl) \
446     PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - RR_OPT_SPACE)
447 
448 #define PutRR_OS(P, C, RR) PutRR_OS_TTL((P), (C), (RR), (RR)->rroriginalttl)
449 
450 extern mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass);
451 extern mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass);
452 extern mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end);
453 extern mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr);
454 extern mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit);
455 extern mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit);
456 extern mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name);
457 extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease);
458 extern mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit);
459 
460 extern int baseEncode(char *buffer, int blen, const mDNSu8 *data, int len, int encAlg);
461 extern void NSEC3Parse(const ResourceRecord *const rr, mDNSu8 **salt, int *hashLength, mDNSu8 **nxtName, int *bitmaplen, mDNSu8 **bitmap);
462 
463 // ***************************************************************************
464 // MARK: - DNS Message Parsing Functions
465 
466 #define HashSlotFromNameHash(X) ((X) % CACHE_HASH_SLOTS)
467 extern mDNSu32 DomainNameHashValue(const domainname *const name);
468 extern void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength);
469 extern const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end);
470 extern const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
471                                    domainname *const name);
472 extern const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end);
473 extern const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr,
474                                             const mDNSu8 * end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr);
475 extern mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, ResourceRecord *rr,
476     mDNSu16 rdlength);
477 extern const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end);
478 extern const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
479                                  DNSQuestion *question);
480 extern const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end);
481 extern const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end);
482 extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end);
483 extern const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize);
484 extern const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end);
485 extern mDNSBool GetPktLease(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, mDNSu32 *const lease);
486 extern void DumpPacket(mStatus status, mDNSBool sent, const char *transport, const mDNSAddr *srcaddr, mDNSIPPort srcport,
487     const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end,
488     mDNSInterfaceID interfaceID);
489 extern mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type);
490 extern mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type);
491 extern mDNSBool BitmapTypeCheck(const mDNSu8 *bmap, int bitmaplen, mDNSu16 type);
492 
493 extern mDNSu16 swap16(mDNSu16 x);
494 extern mDNSu32 swap32(mDNSu32 x);
495 
496 extern mDNSBool GetReverseIPv6Addr(const domainname *inQName, mDNSu8 outIPv6[16]);
497 
498 // ***************************************************************************
499 // MARK: - Packet Sending Functions
500 extern mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
501                                   mDNSInterfaceID InterfaceID, TCPSocket *tcpSrc, UDPSocket *udpSrc, const mDNSAddr *dst,
502                                   mDNSIPPort dstport, DomainAuthInfo *authInfo, mDNSBool useBackgroundTrafficClass);
503 
504 // ***************************************************************************
505 // MARK: - DNSQuestion Functions
506 
507 #if MDNSRESPONDER_SUPPORTS(APPLE, LOG_PRIVACY_LEVEL)
508 extern mDNSBool DNSQuestionNeedsSensitiveLogging(const DNSQuestion *q);
509 #endif
510 
511 #if MDNSRESPONDER_SUPPORTS(APPLE, RUNTIME_MDNS_METRICS)
512 extern mDNSBool DNSQuestionCollectsMDNSMetric(const DNSQuestion *q);
513 #endif
514 
515 #if MDNSRESPONDER_SUPPORTS(APPLE, TERMINUS_ASSISTED_UNICAST_DISCOVERY)
516 extern mDNSBool DNSQuestionIsEligibleForMDNSAlternativeService(const DNSQuestion *q);
517 extern mDNSBool DNSQuestionRequestsMDNSAlternativeService(const DNSQuestion *q);
518 extern mDNSBool DNSQuestionUsesMDNSAlternativeService(const DNSQuestion *q);
519 #endif
520 
521 // ***************************************************************************
522 // MARK: - RR List Management & Task Management
523 
524 extern void ShowTaskSchedulingError(mDNS *const m);
525 
526 /*!
527  *  @brief
528  *      Check if the locking state is valid or not by comparing the values of <code> mDNS_busy</code> and <code> mDNS_reentrancy</code>, and it also
529  *      remembers the last function (with the source file line number) that succeeds in doing lock operation including "Lock", "Unlock", "Drop", "Reclaim". If any
530  *      invalid lock state is detected, an error message with the function name of the last successful lock operator will be printed to help debug.
531  *
532  *  @param operation
533  *      A text description of the lock operation that would be finished after(or before) this lock state checking, possible values are "Lock", "Unlock",
534  *      "Drop Lock", "Reclaim Lock" and "Check Lock".
535  *
536  *  @param checkIfLockHeld
537  *      A boolean value to indicate if the caller wants to check if it currently holds the lock. If the lock is not held or the lock state is invalid, an error message will
538  *      be printed.
539  *
540  *  @param mDNS_busy
541  *      The mDNS_busy value getting from the mDNS_struct object, its value indicates how many times the lock have been grabbed. Note that the caller can grab
542  *      the lock and drop it before the user callback to allow the callback to grab the lock again. There should be only one who has grabbed the lock while not
543  *      dropping it.
544  *
545  *  @param mDNS_reentrancy
546  *      The mDNS_reentrancy getting from the mDNS_struct object, its value indicates how many times the lock have been dropped before callback after being
547  *      grabbed by others. In other words, it indicates the depth of callback stack.
548  *
549  *  @param functionName
550  *      The name of the function that calls <code>mDNS_VerifyLockState()</code>.
551  *
552  *  @param lineNumber
553  *      The line number in the source code file where <code>mDNS_VerifyLockState()</code> gets called.
554  *
555  *  @discussion
556  *      This function is called whenever mDNSResponder enters/exits the critical section to help avoid the lock-related bug when mDNSResponder is compiled
557  *      with multi-thread support. On all Apple platforms, we have only two threads, one is the main queue for the main event loop, the other one is the K queue for
558  *      the network configuration event, so we can almost treat mDNSResponder on Apple platform as a single-thread daemon. Such locking issues do not
559  *      always happen because the lock cannot be grabbed twice by different process in a single-thread process. However, mDNSResponder core code should
560  *      not assume that single-thread model is always available, and it should be aware of the possible locking race condition and avoid those.
561  *      <code>mDNS_VerifyLockState()</code> is created to check the state of the lock and make sure the lock is operated correctly even on a single-thread
562  *      environment. When it detects any possible lock inconsistency, it will print a log message with the last successful lock operator's name and the line number,
563  *      to help debug the lock-related bugs.
564  *
565  */
566 void mDNS_VerifyLockState(const char *operation, mDNSBool checkIfLockHeld,
567     mDNSu32 mDNS_busy, mDNSu32 mDNS_reentrancy, const char *functionName, mDNSu32 lineNumber);
568 
569 extern void mDNS_Lock_(mDNS *m, const char *functionname, mDNSu32 lineNumber);
570 extern void mDNS_Unlock_(mDNS *m, const char *functionname, mDNSu32 lineNumber);
571 
572 #if defined(_WIN32)
573  #define __func__ __FUNCTION__
574 #endif
575 
576 #define mDNS_Lock(X) mDNS_Lock_((X), __func__, __LINE__)
577 
578 #define mDNS_Unlock(X) mDNS_Unlock_((X), __func__, __LINE__)
579 
580 #define mDNS_CheckLock(X) mDNS_VerifyLockState("Check Lock", mDNStrue, (X)->mDNS_busy, (X)->mDNS_reentrancy,   \
581                                                __func__, __LINE__)
582 
583 #define mDNS_DropLockBeforeCallback()                                                                               \
584     do                                                                                                              \
585     {                                                                                                               \
586         m->mDNS_reentrancy++;                                                                                       \
587         mDNS_VerifyLockState("Drop Lock", mDNSfalse, m->mDNS_busy, m->mDNS_reentrancy, __func__, __LINE__);         \
588     } while (mDNSfalse)
589 
590 #define mDNS_ReclaimLockAfterCallback()                                                                             \
591     do                                                                                                              \
592     {                                                                                                               \
593         mDNS_VerifyLockState("Reclaim Lock", mDNSfalse, m->mDNS_busy, m->mDNS_reentrancy, __func__, __LINE__);      \
594         m->mDNS_reentrancy--;                                                                                       \
595     } while (mDNSfalse)
596 
597 #ifdef  __cplusplus
598 }
599 #endif
600 
601 #endif // __DNSCOMMON_H_
602