]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil/DNSMessage.h
mDNSResponder-1096.0.2.tar.gz
[apple/mdnsresponder.git] / Clients / dnssdutil / DNSMessage.h
1 /*
2 Copyright (c) 2016-2019 Apple Inc. All rights reserved.
3 */
4
5 #ifndef __DNSMessage_h
6 #define __DNSMessage_h
7
8 #include <CoreUtils/CoreUtils.h>
9
10 #ifdef __cplusplus
11 extern "C" {
12 #endif
13
14 //---------------------------------------------------------------------------------------------------------------------------
15 /*! @group DNS domain name size limits
16
17 @discussion See <https://tools.ietf.org/html/rfc1035#section-2.3.4>.
18 */
19 #define kDomainLabelLengthMax 63
20 #define kDomainNameLengthMax 256 // For compatibility with mDNS. See <https://tools.ietf.org/html/rfc6762#appendix-C>.
21
22 //---------------------------------------------------------------------------------------------------------------------------
23 /*! @group DNS message header
24 */
25 typedef struct
26 {
27 uint8_t id[ 2 ];
28 uint8_t flags[ 2 ];
29 uint8_t questionCount[ 2 ];
30 uint8_t answerCount[ 2 ];
31 uint8_t authorityCount[ 2 ];
32 uint8_t additionalCount[ 2 ];
33
34 } DNSHeader;
35
36 #define kDNSHeaderLength 12
37 check_compile_time( sizeof( DNSHeader ) == kDNSHeaderLength );
38
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 )
45
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) )
52
53 // Single-bit DNS header fields
54
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)
63
64 // OPCODE (bits 14-11), Operation Code
65
66 #define DNSFlagsGetOpCode( FLAGS ) ( ( (FLAGS) >> 11 ) & 0x0FU )
67 #define DNSFlagsSetOpCode( FLAGS, OPCODE ) \
68 do { (FLAGS) = ( (FLAGS) & ~0x7800U ) | ( ( (OPCODE) & 0x0FU ) << 11 ); } while( 0 )
69
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
75
76 // RCODE (bits 3-0), Response Code
77
78 #define DNSFlagsGetRCode( FLAGS ) ( (FLAGS) & 0x0FU )
79 #define DNSFlagsSetRCode( FLAGS, RCODE ) \
80 do { (FLAGS) = ( (FLAGS) & ~0x000FU ) | ( (RCODE) & 0x0FU ); } while( 0 )
81
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
88
89 //---------------------------------------------------------------------------------------------------------------------------
90 /*! @group Misc. DNS message data structures
91 */
92
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 ) \
97 { \
98 return ReadBig ## BIT_SIZE ( inFields->FIELD ); \
99 } \
100 \
101 STATIC_INLINE void \
102 dns_fixed_fields_ ## TYPE ## _set_ ## FIELD ( \
103 dns_fixed_fields_ ## TYPE * inFields, \
104 uint ## BIT_SIZE ## _t inValue ) \
105 { \
106 WriteBig ## BIT_SIZE ( inFields->FIELD, inValue ); \
107 } \
108 check_compile_time( ( sizeof_field( dns_fixed_fields_ ## TYPE, FIELD ) * 8 ) == (BIT_SIZE) )
109
110 // DNS question fixed-length fields (see <https://tools.ietf.org/html/rfc1035#section-4.1.2>)
111
112 typedef struct
113 {
114 uint8_t type[ 2 ];
115 uint8_t class[ 2 ];
116
117 } dns_fixed_fields_question;
118
119 check_compile_time( sizeof( dns_fixed_fields_question ) == 4 );
120
121 dns_fixed_fields_define_accessors( question, type, 16 );
122 dns_fixed_fields_define_accessors( question, class, 16 );
123
124 STATIC_INLINE void
125 dns_fixed_fields_question_init(
126 dns_fixed_fields_question * inFields,
127 uint16_t inQType,
128 uint16_t inQClass )
129 {
130 dns_fixed_fields_question_set_type( inFields, inQType );
131 dns_fixed_fields_question_set_class( inFields, inQClass );
132 }
133
134 // DNS resource record fixed-length fields (see <https://tools.ietf.org/html/rfc1035#section-4.1.3>)
135
136 typedef struct
137 {
138 uint8_t type[ 2 ];
139 uint8_t class[ 2 ];
140 uint8_t ttl[ 4 ];
141 uint8_t rdlength[ 2 ];
142
143 } dns_fixed_fields_record;
144
145 check_compile_time( sizeof( dns_fixed_fields_record ) == 10 );
146
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 );
151
152 STATIC_INLINE void
153 dns_fixed_fields_record_init(
154 dns_fixed_fields_record * inFields,
155 uint16_t inType,
156 uint16_t inClass,
157 uint32_t inTTL,
158 uint16_t inRDLength )
159 {
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 );
164 }
165
166 // DNS SRV record data fixed-length fields (see <https://tools.ietf.org/html/rfc2782>)
167
168 typedef struct
169 {
170 uint8_t priority[ 2 ];
171 uint8_t weight[ 2 ];
172 uint8_t port[ 2 ];
173
174 } dns_fixed_fields_srv;
175
176 check_compile_time( sizeof( dns_fixed_fields_srv ) == 6 );
177
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 );
181
182 STATIC_INLINE void
183 dns_fixed_fields_srv_init(
184 dns_fixed_fields_srv * inFields,
185 uint16_t inPriority,
186 uint16_t inWeight,
187 uint16_t inPort )
188 {
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 );
192 }
193
194 // DNS SOA record data fixed-length fields (see <https://tools.ietf.org/html/rfc1035#section-3.3.13>)
195
196 typedef struct
197 {
198 uint8_t serial[ 4 ];
199 uint8_t refresh[ 4 ];
200 uint8_t retry[ 4 ];
201 uint8_t expire[ 4 ];
202 uint8_t minimum[ 4 ];
203
204 } dns_fixed_fields_soa;
205
206 check_compile_time( sizeof( dns_fixed_fields_soa ) == 20 );
207
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 );
213
214 STATIC_INLINE void
215 dns_fixed_fields_soa_init(
216 dns_fixed_fields_soa * inFields,
217 uint32_t inSerial,
218 uint32_t inRefresh,
219 uint32_t inRetry,
220 uint32_t inExpire,
221 uint32_t inMinimum )
222 {
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 );
228 }
229
230 //---------------------------------------------------------------------------------------------------------------------------
231 /*! @brief Extracts a domain name from a DNS message.
232
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)
238 */
239 OSStatus
240 DNSMessageExtractDomainName(
241 const uint8_t * inMsgPtr,
242 size_t inMsgLen,
243 const uint8_t * inPtr,
244 uint8_t outName[ kDomainNameLengthMax ],
245 const uint8_t ** outPtr );
246
247 //---------------------------------------------------------------------------------------------------------------------------
248 /*! @brief Extracts a domain name from a DNS message as a C string.
249
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)
255 */
256 OSStatus
257 DNSMessageExtractDomainNameString(
258 const void * inMsgPtr,
259 size_t inMsgLen,
260 const void * inPtr,
261 char outName[ kDNSServiceMaxDomainName ],
262 const uint8_t ** outPtr );
263
264 //---------------------------------------------------------------------------------------------------------------------------
265 /*! @brief Extracts a question from a DNS message.
266
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)
274 */
275 OSStatus
276 DNSMessageExtractQuestion(
277 const uint8_t * inMsgPtr,
278 size_t inMsgLen,
279 const uint8_t * inPtr,
280 uint8_t outName[ kDomainNameLengthMax ],
281 uint16_t * outType,
282 uint16_t * outClass,
283 const uint8_t ** outPtr );
284
285 //---------------------------------------------------------------------------------------------------------------------------
286 /*! @brief Extracts a resource record from a DNS message.
287
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)
298 */
299 OSStatus
300 DNSMessageExtractRecord(
301 const uint8_t * inMsgPtr,
302 size_t inMsgLen,
303 const uint8_t * inPtr,
304 uint8_t outName[ kDomainNameLengthMax ],
305 uint16_t * outType,
306 uint16_t * outClass,
307 uint32_t * outTTL,
308 const uint8_t ** outRDataPtr,
309 size_t * outRDataLen,
310 const uint8_t ** outPtr );
311
312 //---------------------------------------------------------------------------------------------------------------------------
313 /*! @brief Returns pointer to the start of the answer section, i.e., the end of the question section, of a DNS message.
314
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)
318 */
319 OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr );
320
321 //---------------------------------------------------------------------------------------------------------------------------
322 /*! @brief Writes a DNS message compression label pointer.
323
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.
326
327 @discussion See <https://tools.ietf.org/html/rfc1035#section-4.1.4>.
328 */
329 STATIC_INLINE void DNSMessageWriteLabelPointer( uint8_t inLabelPtr[ STATIC_PARAM 2 ], size_t inOffset )
330 {
331 inLabelPtr[ 0 ] = (uint8_t)( ( ( inOffset >> 8 ) & 0x3F ) | 0xC0 );
332 inLabelPtr[ 1 ] = (uint8_t)( inOffset & 0xFF );
333 }
334
335 #define kDNSCompressionOffsetMax 0x3FFF
336
337 //---------------------------------------------------------------------------------------------------------------------------
338 /*! @brief Writes a single-question DNS query message.
339
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.
347
348 @discussion The duplicate domain name must be freed with free() when no longer needed.
349 */
350 #define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + sizeof( dns_fixed_fields_question ) )
351
352 OSStatus
353 DNSMessageWriteQuery(
354 uint16_t inMsgID,
355 uint16_t inFlags,
356 const uint8_t * inQName,
357 uint16_t inQType,
358 uint16_t inQClass,
359 uint8_t outMsg[ STATIC_PARAM kDNSQueryMessageMaxLen ],
360 size_t * outLen );
361
362 //---------------------------------------------------------------------------------------------------------------------------
363 /*! @brief Appends a C string representing a textual sequence of labels to a domain name.
364
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)
368 */
369 OSStatus
370 DomainNameAppendString(
371 uint8_t inName[ STATIC_PARAM kDomainNameLengthMax ],
372 const char * inString,
373 uint8_t ** outEnd );
374
375 //---------------------------------------------------------------------------------------------------------------------------
376 /*! @brief Creates a duplicate domain name.
377
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.
382
383 @discussion The duplicate domain name must be freed with free() when no longer needed.
384 */
385 OSStatus DomainNameDupEx( const uint8_t *inName, Boolean inLower, uint8_t **outNamePtr, size_t *outNameLen );
386
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 )
389
390 //---------------------------------------------------------------------------------------------------------------------------
391 /*! @brief Compares two domain names in label format for case-insensitive equality.
392
393 @param inName1 Pointer to the first domain name.
394 @param inName2 Pointer to the second domain name.
395
396 @result If the domain names are equal, returns true, otherwise, returns false.
397 */
398 Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 );
399
400 //---------------------------------------------------------------------------------------------------------------------------
401 /*! @brief Converts a domain name's textual representation to a domain name in label format.
402
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)
406
407 @discussion The duplicate domain name must be freed with free() when no longer needed.
408 */
409 OSStatus
410 DomainNameFromString(
411 uint8_t outName[ STATIC_PARAM kDomainNameLengthMax ],
412 const char * inString,
413 uint8_t ** outEnd );
414
415 //---------------------------------------------------------------------------------------------------------------------------
416 /*! @brief Gets the next label in a domain name label sequence.
417
418 @param inLabel Pointer to the current label.
419
420 @result If the current label is a root label, returns NULL. Otherwise, returns the next label.
421 */
422 STATIC_INLINE const uint8_t * DomainNameGetNextLabel( const uint8_t *inLabel )
423 {
424 const int len = *inLabel;
425 return ( ( len == 0 ) ? NULL : &inLabel[ 1 + len ] );
426 }
427
428 //---------------------------------------------------------------------------------------------------------------------------
429 /*! @brief Computes the length of a domain name in label format.
430
431 @param inName The domain name.
432 */
433 STATIC_INLINE size_t DomainNameLength( const uint8_t *inName )
434 {
435 const uint8_t * label;
436 int len;
437
438 for( label = inName; ( len = *label ) != 0; label = &label[ 1 + len ] ) {}
439 return( (size_t)( label - inName ) + 1 );
440 }
441
442 //---------------------------------------------------------------------------------------------------------------------------
443 /*! @brief Converts a domain name in label format to its textual representation as a C string.
444
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)
449 */
450 OSStatus
451 DomainNameToString(
452 const uint8_t * inName,
453 const uint8_t * inLimit,
454 char outString[ STATIC_PARAM kDNSServiceMaxDomainName ],
455 const uint8_t ** outPtr );
456
457 #ifdef __cplusplus
458 }
459 #endif
460
461 #endif // __DNSMessage_h