2 Copyright (c) 2016-2019 Apple Inc. All rights reserved.
8 #include <CoreUtils/CoreUtils.h>
14 //---------------------------------------------------------------------------------------------------------------------------
15 /*! @group DNS domain name size limits
17 @discussion See <https://tools.ietf.org/html/rfc1035#section-2.3.4>.
19 #define kDomainLabelLengthMax 63
20 #define kDomainNameLengthMax 256 // For compatibility with mDNS. See <https://tools.ietf.org/html/rfc6762#appendix-C>.
22 //---------------------------------------------------------------------------------------------------------------------------
23 /*! @group DNS message header
29 uint8_t questionCount
[ 2 ];
30 uint8_t answerCount
[ 2 ];
31 uint8_t authorityCount
[ 2 ];
32 uint8_t additionalCount
[ 2 ];
36 #define kDNSHeaderLength 12
37 check_compile_time( sizeof( DNSHeader
) == kDNSHeaderLength
);
39 #define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id )
40 #define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags )
41 #define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount )
42 #define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount )
43 #define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount )
44 #define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount )
46 #define DNSHeaderSetID( HDR, X ) WriteBig16( ( HDR )->id, (X) )
47 #define DNSHeaderSetFlags( HDR, X ) WriteBig16( ( HDR )->flags, (X) )
48 #define DNSHeaderSetQuestionCount( HDR, X ) WriteBig16( ( HDR )->questionCount, (X) )
49 #define DNSHeaderSetAnswerCount( HDR, X ) WriteBig16( ( HDR )->answerCount, (X) )
50 #define DNSHeaderSetAuthorityCount( HDR, X ) WriteBig16( ( HDR )->authorityCount, (X) )
51 #define DNSHeaderSetAdditionalCount( HDR, X ) WriteBig16( ( HDR )->additionalCount, (X) )
53 // Single-bit DNS header fields
55 #define kDNSHeaderFlag_Response ( 1U << 15 ) // QR (bit 15), Query (0)/Response (1)
56 #define kDNSHeaderFlag_AuthAnswer ( 1U << 10 ) // AA (bit 10), Authoritative Answer
57 #define kDNSHeaderFlag_Truncation ( 1U << 9 ) // TC (bit 9), TrunCation
58 #define kDNSHeaderFlag_RecursionDesired ( 1U << 8 ) // RD (bit 8), Recursion Desired
59 #define kDNSHeaderFlag_RecursionAvailable ( 1U << 7 ) // RA (bit 7), Recursion Available
60 #define kDNSHeaderFlag_Z ( 1U << 6 ) // Z (bit 6), Reserved (must be zero)
61 #define kDNSHeaderFlag_AuthenticData ( 1U << 5 ) // AD (bit 5), Authentic Data (RFC 2535, Section 6)
62 #define kDNSHeaderFlag_CheckingDisabled ( 1U << 4 ) // CD (bit 4), Checking Disabled (RFC 2535, Section 6)
64 // OPCODE (bits 14-11), Operation Code
66 #define DNSFlagsGetOpCode( FLAGS ) ( ( (FLAGS) >> 11 ) & 0x0FU )
67 #define DNSFlagsSetOpCode( FLAGS, OPCODE ) \
68 do { (FLAGS) = ( (FLAGS) & ~0x7800U ) | ( ( (OPCODE) & 0x0FU ) << 11 ); } while( 0 )
70 #define kDNSOpCode_Query 0 // QUERY (standard query)
71 #define kDNSOpCode_InverseQuery 1 // IQUERY (inverse query)
72 #define kDNSOpCode_Status 2 // STATUS
73 #define kDNSOpCode_Notify 4 // NOTIFY
74 #define kDNSOpCode_Update 5 // UPDATE
76 // RCODE (bits 3-0), Response Code
78 #define DNSFlagsGetRCode( FLAGS ) ( (FLAGS) & 0x0FU )
79 #define DNSFlagsSetRCode( FLAGS, RCODE ) \
80 do { (FLAGS) = ( (FLAGS) & ~0x000FU ) | ( (RCODE) & 0x0FU ); } while( 0 )
82 #define kDNSRCode_NoError 0
83 #define kDNSRCode_FormatError 1
84 #define kDNSRCode_ServerFailure 2
85 #define kDNSRCode_NXDomain 3
86 #define kDNSRCode_NotImplemented 4
87 #define kDNSRCode_Refused 5
89 //---------------------------------------------------------------------------------------------------------------------------
90 /*! @group Misc. DNS message data structures
93 #define dns_fixed_fields_define_accessors( TYPE, FIELD, BIT_SIZE ) \
94 STATIC_INLINE uint ## BIT_SIZE ##_t \
95 dns_fixed_fields_ ## TYPE ## _get_ ## FIELD ( \
96 const dns_fixed_fields_ ## TYPE * inFields ) \
98 return ReadBig ## BIT_SIZE ( inFields->FIELD ); \
102 dns_fixed_fields_ ## TYPE ## _set_ ## FIELD ( \
103 dns_fixed_fields_ ## TYPE * inFields, \
104 uint ## BIT_SIZE ## _t inValue ) \
106 WriteBig ## BIT_SIZE ( inFields->FIELD, inValue ); \
108 check_compile_time( ( sizeof_field( dns_fixed_fields_ ## TYPE, FIELD ) * 8 ) == (BIT_SIZE) )
110 // DNS question fixed-length fields (see <https://tools.ietf.org/html/rfc1035#section-4.1.2>)
117 } dns_fixed_fields_question
;
119 check_compile_time( sizeof( dns_fixed_fields_question
) == 4 );
121 dns_fixed_fields_define_accessors( question
, type
, 16 );
122 dns_fixed_fields_define_accessors( question
, class, 16 );
125 dns_fixed_fields_question_init(
126 dns_fixed_fields_question
* inFields
,
130 dns_fixed_fields_question_set_type( inFields
, inQType
);
131 dns_fixed_fields_question_set_class( inFields
, inQClass
);
134 // DNS resource record fixed-length fields (see <https://tools.ietf.org/html/rfc1035#section-4.1.3>)
141 uint8_t rdlength
[ 2 ];
143 } dns_fixed_fields_record
;
145 check_compile_time( sizeof( dns_fixed_fields_record
) == 10 );
147 dns_fixed_fields_define_accessors( record
, type
, 16 );
148 dns_fixed_fields_define_accessors( record
, class, 16 );
149 dns_fixed_fields_define_accessors( record
, ttl
, 32 );
150 dns_fixed_fields_define_accessors( record
, rdlength
, 16 );
153 dns_fixed_fields_record_init(
154 dns_fixed_fields_record
* inFields
,
158 uint16_t inRDLength
)
160 dns_fixed_fields_record_set_type( inFields
, inType
);
161 dns_fixed_fields_record_set_class( inFields
, inClass
);
162 dns_fixed_fields_record_set_ttl( inFields
, inTTL
);
163 dns_fixed_fields_record_set_rdlength( inFields
, inRDLength
);
166 // DNS SRV record data fixed-length fields (see <https://tools.ietf.org/html/rfc2782>)
170 uint8_t priority
[ 2 ];
174 } dns_fixed_fields_srv
;
176 check_compile_time( sizeof( dns_fixed_fields_srv
) == 6 );
178 dns_fixed_fields_define_accessors( srv
, priority
, 16 );
179 dns_fixed_fields_define_accessors( srv
, weight
, 16 );
180 dns_fixed_fields_define_accessors( srv
, port
, 16 );
183 dns_fixed_fields_srv_init(
184 dns_fixed_fields_srv
* inFields
,
189 dns_fixed_fields_srv_set_priority( inFields
, inPriority
);
190 dns_fixed_fields_srv_set_weight( inFields
, inWeight
);
191 dns_fixed_fields_srv_set_port( inFields
, inPort
);
194 // DNS SOA record data fixed-length fields (see <https://tools.ietf.org/html/rfc1035#section-3.3.13>)
199 uint8_t refresh
[ 4 ];
202 uint8_t minimum
[ 4 ];
204 } dns_fixed_fields_soa
;
206 check_compile_time( sizeof( dns_fixed_fields_soa
) == 20 );
208 dns_fixed_fields_define_accessors( soa
, serial
, 32 );
209 dns_fixed_fields_define_accessors( soa
, refresh
, 32 );
210 dns_fixed_fields_define_accessors( soa
, retry
, 32 );
211 dns_fixed_fields_define_accessors( soa
, expire
, 32 );
212 dns_fixed_fields_define_accessors( soa
, minimum
, 32 );
215 dns_fixed_fields_soa_init(
216 dns_fixed_fields_soa
* inFields
,
223 dns_fixed_fields_soa_set_serial( inFields
, inSerial
);
224 dns_fixed_fields_soa_set_refresh( inFields
, inRefresh
);
225 dns_fixed_fields_soa_set_retry( inFields
, inRetry
);
226 dns_fixed_fields_soa_set_expire( inFields
, inExpire
);
227 dns_fixed_fields_soa_set_minimum( inFields
, inMinimum
);
230 //---------------------------------------------------------------------------------------------------------------------------
231 /*! @brief Extracts a domain name from a DNS message.
233 @param inMsgPtr Pointer to the beginning of the DNS message containing the domain name.
234 @param inMsgLen Length of the DNS message containing the domain name.
235 @param inPtr Pointer to the domain name field.
236 @param outName Buffer to write extracted domain name. (Optional)
237 @param outPtr Gets set to point to the end of the domain name field. (Optional)
240 DNSMessageExtractDomainName(
241 const uint8_t * inMsgPtr
,
243 const uint8_t * inPtr
,
244 uint8_t outName
[ kDomainNameLengthMax
],
245 const uint8_t ** outPtr
);
247 //---------------------------------------------------------------------------------------------------------------------------
248 /*! @brief Extracts a domain name from a DNS message as a C string.
250 @param inMsgPtr Pointer to the beginning of the DNS message containing the domain name.
251 @param inMsgLen Length of the DNS message containing the domain name.
252 @param inPtr Pointer to the domain name field.
253 @param outName Buffer to write extracted domain name. (Optional)
254 @param outPtr Gets set to point to the end of the domain name field. (Optional)
257 DNSMessageExtractDomainNameString(
258 const void * inMsgPtr
,
261 char outName
[ kDNSServiceMaxDomainName
],
262 const uint8_t ** outPtr
);
264 //---------------------------------------------------------------------------------------------------------------------------
265 /*! @brief Extracts a question from a DNS message.
267 @param inMsgPtr Pointer to the beginning of the DNS message containing a question.
268 @param inMsgLen Length of the DNS message containing the question.
269 @param inPtr Pointer to the question.
270 @param outName Buffer to write the question's QNAME. (Optional)
271 @param outType Gets set to question's QTYPE value. (Optional)
272 @param outClass Gets set to question's QCLASS value. (Optional)
273 @param outPtr Gets set to point to the end of the question. (Optional)
276 DNSMessageExtractQuestion(
277 const uint8_t * inMsgPtr
,
279 const uint8_t * inPtr
,
280 uint8_t outName
[ kDomainNameLengthMax
],
283 const uint8_t ** outPtr
);
285 //---------------------------------------------------------------------------------------------------------------------------
286 /*! @brief Extracts a resource record from a DNS message.
288 @param inMsgPtr Pointer to the beginning of the DNS message containing the resource record.
289 @param inMsgLen Length of the DNS message containing the resource record.
290 @param inPtr Pointer to the resource record.
291 @param outName Buffer to write the resource record's NAME. (Optional)
292 @param outType Gets set to resource record's TYPE value. (Optional)
293 @param outClass Gets set to resource record's CLASS value. (Optional)
294 @param outTTL Gets set to resource record's TTL value. (Optional)
295 @param outRDataPtr Gets set to point to the resource record's RDATA. (Optional)
296 @param outRDataLen Gets set to the resource record's RDLENGTH. (Optional)
297 @param outPtr Gets set to point to the end of the resource record. (Optional)
300 DNSMessageExtractRecord(
301 const uint8_t * inMsgPtr
,
303 const uint8_t * inPtr
,
304 uint8_t outName
[ kDomainNameLengthMax
],
308 const uint8_t ** outRDataPtr
,
309 size_t * outRDataLen
,
310 const uint8_t ** outPtr
);
312 //---------------------------------------------------------------------------------------------------------------------------
313 /*! @brief Returns pointer to the start of the answer section, i.e., the end of the question section, of a DNS message.
315 @param inMsgPtr Pointer to the beginning of the DNS message.
316 @param inMsgLen Length of the DNS message.
317 @param outPtr Gets set to point to the start of the answer section. (Optional)
319 OSStatus
DNSMessageGetAnswerSection( const uint8_t *inMsgPtr
, size_t inMsgLen
, const uint8_t **outPtr
);
321 //---------------------------------------------------------------------------------------------------------------------------
322 /*! @brief Writes a DNS message compression label pointer.
324 @param inLabelPtr Pointer to the two bytes to which to write the label pointer.
325 @param inOffset The label pointer's offset value. This offset is relative to the start of the DNS message.
327 @discussion See <https://tools.ietf.org/html/rfc1035#section-4.1.4>.
329 STATIC_INLINE
void DNSMessageWriteLabelPointer( uint8_t inLabelPtr
[ STATIC_PARAM
2 ], size_t inOffset
)
331 inLabelPtr
[ 0 ] = (uint8_t)( ( ( inOffset
>> 8 ) & 0x3F ) | 0xC0 );
332 inLabelPtr
[ 1 ] = (uint8_t)( inOffset
& 0xFF );
335 #define kDNSCompressionOffsetMax 0x3FFF
337 //---------------------------------------------------------------------------------------------------------------------------
338 /*! @brief Writes a single-question DNS query message.
340 @param inMsgID The query message's ID.
341 @param inFlags The query message's flags.
342 @param inQName The question's QNAME in label format.
343 @param inQType The question's QTYPE.
344 @param inQClass The question's QCLASS.
345 @param outMsg Buffer to write DNS query message.
346 @param outLen Gets set to the length of the DNS query message.
348 @discussion The duplicate domain name must be freed with free() when no longer needed.
350 #define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + sizeof( dns_fixed_fields_question ) )
353 DNSMessageWriteQuery(
356 const uint8_t * inQName
,
359 uint8_t outMsg
[ STATIC_PARAM kDNSQueryMessageMaxLen
],
362 //---------------------------------------------------------------------------------------------------------------------------
363 /*! @brief Appends a C string representing a textual sequence of labels to a domain name.
365 @param inName Pointer to the domain name.
366 @param inString Pointer to textual sequence of labels as a C string. (Optional)
367 @param outEnd Gets set to point to the new end of the domain name if the append succeeded. (Optional)
370 DomainNameAppendString(
371 uint8_t inName
[ STATIC_PARAM kDomainNameLengthMax
],
372 const char * inString
,
375 //---------------------------------------------------------------------------------------------------------------------------
376 /*! @brief Creates a duplicate domain name.
378 @param inName The domain name to duplicate.
379 @param inLower If true, uppercase letters in the duplicate are converted to lowercase.
380 @param outNamePtr Gets set to point to a dynamically allocated duplicate.
381 @param outNameLen Gets set to the length of the duplicate.
383 @discussion The duplicate domain name must be freed with free() when no longer needed.
385 OSStatus
DomainNameDupEx( const uint8_t *inName
, Boolean inLower
, uint8_t **outNamePtr
, size_t *outNameLen
);
387 #define DomainNameDup( IN_NAME, OUT_NAME, OUT_LEN ) DomainNameDupEx( IN_NAME, false, OUT_NAME, OUT_LEN )
388 #define DomainNameDupLower( IN_NAME, OUT_NAME, OUT_LEN ) DomainNameDupEx( IN_NAME, true, OUT_NAME, OUT_LEN )
390 //---------------------------------------------------------------------------------------------------------------------------
391 /*! @brief Compares two domain names in label format for case-insensitive equality.
393 @param inName1 Pointer to the first domain name.
394 @param inName2 Pointer to the second domain name.
396 @result If the domain names are equal, returns true, otherwise, returns false.
398 Boolean
DomainNameEqual( const uint8_t *inName1
, const uint8_t *inName2
);
400 //---------------------------------------------------------------------------------------------------------------------------
401 /*! @brief Converts a domain name's textual representation to a domain name in label format.
403 @param outName Buffer to write the domain name in label format.
404 @param inString Textual representation of a domain name as a C string.
405 @param outEnd Gets set to point to the new end of the domain name if the append succeeded. (Optional)
407 @discussion The duplicate domain name must be freed with free() when no longer needed.
410 DomainNameFromString(
411 uint8_t outName
[ STATIC_PARAM kDomainNameLengthMax
],
412 const char * inString
,
415 //---------------------------------------------------------------------------------------------------------------------------
416 /*! @brief Gets the next label in a domain name label sequence.
418 @param inLabel Pointer to the current label.
420 @result If the current label is a root label, returns NULL. Otherwise, returns the next label.
422 STATIC_INLINE
const uint8_t * DomainNameGetNextLabel( const uint8_t *inLabel
)
424 const int len
= *inLabel
;
425 return ( ( len
== 0 ) ? NULL
: &inLabel
[ 1 + len
] );
428 //---------------------------------------------------------------------------------------------------------------------------
429 /*! @brief Computes the length of a domain name in label format.
431 @param inName The domain name.
433 STATIC_INLINE
size_t DomainNameLength( const uint8_t *inName
)
435 const uint8_t * label
;
438 for( label
= inName
; ( len
= *label
) != 0; label
= &label
[ 1 + len
] ) {}
439 return( (size_t)( label
- inName
) + 1 );
442 //---------------------------------------------------------------------------------------------------------------------------
443 /*! @brief Converts a domain name in label format to its textual representation as a C string.
445 @param inName Pointer to the domain name.
446 @param inLimit Pointer to not exceed while parsing a potentially truncated domain name. (Optional)
447 @param outString Buffer to write the C string.
448 @param outPtr Gets set to point to the end of the domain name. (Optional)
452 const uint8_t * inName
,
453 const uint8_t * inLimit
,
454 char outString
[ STATIC_PARAM kDNSServiceMaxDomainName
],
455 const uint8_t ** outPtr
);
461 #endif // __DNSMessage_h