]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil/DNSMessage.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / Clients / dnssdutil / DNSMessage.c
1 /*
2 Copyright (c) 2016-2020 Apple Inc. All rights reserved.
3 */
4
5 #include "DNSMessage.h"
6 #include <CoreUtils/CoreUtils.h>
7 #include <stdlib.h>
8
9 //===========================================================================================================================
10 // MARK: - Local Prototypes
11
12 static Boolean _NameIsPrivate( const char * const inDomainNameStr );
13 static OSStatus
14 _AppendDomainNameString( DataBuffer *inDB, Boolean inPrivacy, const char *inDomainNameStr );
15 static OSStatus
16 _AppendDomainNameStringEx( DataBuffer *inDB, const char *inSeparator, Boolean inPrivacy, const char *inDomainNameStr );
17 static OSStatus
18 _AppendOPTRDataString(
19 DataBuffer * inDB,
20 const uint8_t * inRDataPtr,
21 const uint8_t * inRDataEnd,
22 Boolean inPrivacy );
23 static OSStatus
24 _AppendSVCBRDataString(
25 DataBuffer * inDB,
26 const uint8_t * inRDataPtr,
27 const uint8_t * inRDataEnd,
28 Boolean inPrivacy );
29 static OSStatus
30 _AppendIPv4Address(
31 DataBuffer * inDB,
32 const char * inSeparator,
33 const uint8_t inAddrBytes[ STATIC_PARAM 4 ],
34 Boolean inPrivacy );
35 static OSStatus
36 _AppendIPv6Address(
37 DataBuffer * inDB,
38 const char * inSeparator,
39 const uint8_t inAddrBytes[ STATIC_PARAM 16 ],
40 Boolean inPrivacy );
41 static OSStatus
42 _AppendIPAddress(
43 DataBuffer * inDB,
44 const char * inSeparator,
45 const uint8_t * inAddrPtr,
46 int inAddrLen,
47 Boolean inPrivacy );
48 static void * _GetCULibHandle( void );
49 static Boolean _MemIsAllZeros( const uint8_t *inMemPtr, size_t inMemLen );
50 static Boolean _IPv4AddressIsWhitelisted( const uint8_t inAddrBytes[ STATIC_PARAM 4 ] );
51 static Boolean _IPv6AddressIsWhitelisted( const uint8_t inAddrBytes[ STATIC_PARAM 16 ] );
52 static int
53 _DNSMessagePrintObfuscatedIPAddress(
54 char * inBufPtr,
55 size_t inBufLen,
56 const uint8_t * inAddrBytes,
57 size_t inAddrLen );
58
59 //===========================================================================================================================
60 // MARK: - CoreUtils Framework Soft Linking
61
62 #define _DEFINE_GET_CU_SYM_ADDR( SYMBOL ) \
63 static __typeof__( SYMBOL ) * _GetCUSymAddr_ ## SYMBOL( void ) \
64 { \
65 static dispatch_once_t sOnce = 0; \
66 static __typeof__( SYMBOL ) * sAddr = NULL; \
67 \
68 dispatch_once( &sOnce, \
69 ^{ \
70 void * const handle = _GetCULibHandle(); \
71 require_return( handle ); \
72 sAddr = (__typeof__( SYMBOL ) *) dlsym( handle, #SYMBOL ); \
73 } ); \
74 return sAddr; \
75 } \
76 extern int _DNSMessageDummyVariable
77
78 _DEFINE_GET_CU_SYM_ADDR( Base64EncodeCopyEx );
79 _DEFINE_GET_CU_SYM_ADDR( DataBuffer_Append );
80 _DEFINE_GET_CU_SYM_ADDR( DataBuffer_AppendF );
81 _DEFINE_GET_CU_SYM_ADDR( DataBuffer_Detach );
82 _DEFINE_GET_CU_SYM_ADDR( DataBuffer_Free );
83 _DEFINE_GET_CU_SYM_ADDR( DataBuffer_Init );
84 _DEFINE_GET_CU_SYM_ADDR( SecondsToYMD_HMS );
85 _DEFINE_GET_CU_SYM_ADDR( SNPrintF );
86
87 #define _CallCUVoidFunction( NAME, UNAVAILABLE_RETURN_VALUE, ... ) \
88 ( likely( _GetCUSymAddr_ ## NAME() ) ? \
89 ( ( ( _GetCUSymAddr_ ## NAME() )( __VA_ARGS__ ) ), kNoErr ) : ( UNAVAILABLE_RETURN_VALUE ) )
90
91 #define _CallCUFunction( NAME, UNAVAILABLE_RETURN_VALUE, ... ) \
92 ( likely( _GetCUSymAddr_ ## NAME() ) ? \
93 ( ( _GetCUSymAddr_ ## NAME() )( __VA_ARGS__ ) ) : ( UNAVAILABLE_RETURN_VALUE ) )
94
95 #define _Base64EncodeCopyEx( ... ) _CallCUFunction( Base64EncodeCopyEx, kUnsupportedErr, __VA_ARGS__ )
96 #define _DataBuffer_Init( ... ) _CallCUVoidFunction( DataBuffer_Init, kUnsupportedErr, __VA_ARGS__ )
97 #define _DataBuffer_Free( ... ) _CallCUVoidFunction( DataBuffer_Free, kUnsupportedErr, __VA_ARGS__ )
98 #define _DataBuffer_Append( ... ) _CallCUFunction( DataBuffer_Append, kUnsupportedErr, __VA_ARGS__ )
99 #define _DataBuffer_AppendF( ... ) _CallCUFunction( DataBuffer_AppendF, kUnsupportedErr, __VA_ARGS__ )
100 #define _DataBuffer_Detach( ... ) _CallCUFunction( DataBuffer_Detach, kUnsupportedErr, __VA_ARGS__ )
101 #define _SecondsToYMD_HMS( ... ) _CallCUVoidFunction( SecondsToYMD_HMS, kUnsupportedErr, __VA_ARGS__ )
102 #define _SNPrintF( ... ) _CallCUFunction( SNPrintF, kUnsupportedErr, __VA_ARGS__ )
103
104 //===========================================================================================================================
105 // MARK: - Public Functions
106
107 #define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 )
108
109 OSStatus
110 DNSMessageExtractDomainName(
111 const uint8_t * inMsgPtr,
112 size_t inMsgLen,
113 const uint8_t * inPtr,
114 uint8_t outName[ kDomainNameLengthMax ],
115 const uint8_t ** outPtr )
116 {
117 OSStatus err;
118 const uint8_t * label;
119 uint8_t labelLen;
120 const uint8_t * nextLabel;
121 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
122 uint8_t * dst = outName;
123 const uint8_t * const dstLim = outName ? ( outName + kDomainNameLengthMax ) : NULL;
124 const uint8_t * nameEnd = NULL;
125
126 require_action_quiet( ( inPtr >= inMsgPtr ) && ( inPtr < msgEnd ), exit, err = kRangeErr );
127
128 for( label = inPtr; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
129 {
130 if( labelLen <= kDomainLabelLengthMax )
131 {
132 nextLabel = label + 1 + labelLen;
133 require_action_quiet( nextLabel < msgEnd, exit, err = kUnderrunErr );
134 if( dst )
135 {
136 require_action_quiet( ( dstLim - dst ) > ( 1 + labelLen ), exit, err = kOverrunErr );
137 memcpy( dst, label, 1 + labelLen );
138 dst += ( 1 + labelLen );
139 }
140 }
141 else if( IsCompressionByte( labelLen ) )
142 {
143 uint16_t offset;
144
145 require_action_quiet( ( msgEnd - label ) >= 2, exit, err = kUnderrunErr );
146 if( !nameEnd )
147 {
148 nameEnd = label + 2;
149 if( !dst ) break;
150 }
151 offset = (uint16_t)( ( ( label[ 0 ] & 0x3F ) << 8 ) | label[ 1 ] );
152 nextLabel = inMsgPtr + offset;
153 require_action_quiet( nextLabel < msgEnd, exit, err = kUnderrunErr );
154 require_action_quiet( !IsCompressionByte( nextLabel[ 0 ] ), exit, err = kMalformedErr );
155 }
156 else
157 {
158 err = kMalformedErr;
159 goto exit;
160 }
161 }
162
163 if( dst ) *dst = 0;
164 if( !nameEnd ) nameEnd = label + 1;
165
166 if( outPtr ) *outPtr = nameEnd;
167 err = kNoErr;
168
169 exit:
170 return( err );
171 }
172
173 //===========================================================================================================================
174
175 OSStatus
176 DNSMessageExtractDomainNameString(
177 const void * inMsgPtr,
178 size_t inMsgLen,
179 const void * inPtr,
180 char inBuf[ kDNSServiceMaxDomainName ],
181 const uint8_t ** outPtr )
182 {
183 OSStatus err;
184 const uint8_t * nextPtr;
185 uint8_t domainName[ kDomainNameLengthMax ];
186
187 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, domainName, &nextPtr );
188 require_noerr_quiet( err, exit );
189
190 err = DomainNameToString( domainName, NULL, inBuf, NULL );
191 require_noerr_quiet( err, exit );
192
193 if( outPtr ) *outPtr = nextPtr;
194
195 exit:
196 return( err );
197 }
198
199 //===========================================================================================================================
200
201 OSStatus
202 DNSMessageExtractQuestion(
203 const uint8_t * inMsgPtr,
204 size_t inMsgLen,
205 const uint8_t * inPtr,
206 uint8_t outName[ kDomainNameLengthMax ],
207 uint16_t * outType,
208 uint16_t * outClass,
209 const uint8_t ** outPtr )
210 {
211 OSStatus err;
212 const uint8_t * const msgEnd = &inMsgPtr[ inMsgLen ];
213 const uint8_t * ptr;
214 const dns_fixed_fields_question * fields;
215
216 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, outName, &ptr );
217 require_noerr_quiet( err, exit );
218 require_action_quiet( (size_t)( msgEnd - ptr ) >= sizeof( dns_fixed_fields_question ), exit, err = kUnderrunErr );
219
220 fields = (const dns_fixed_fields_question *) ptr;
221 if( outType ) *outType = dns_fixed_fields_question_get_type( fields );
222 if( outClass ) *outClass = dns_fixed_fields_question_get_class( fields );
223 if( outPtr ) *outPtr = (const uint8_t *) &fields[ 1 ];
224
225 exit:
226 return( err );
227 }
228
229 //===========================================================================================================================
230
231 static OSStatus
232 _DNSMessageExtractRecordEx(
233 const uint8_t * inMsgPtr,
234 size_t inMsgLen,
235 const uint8_t * inPtr,
236 uint8_t outName[ kDomainNameLengthMax ],
237 uint16_t * outType,
238 uint16_t * outClass,
239 uint32_t * outTTL,
240 const uint8_t ** outRDataPtr,
241 size_t * outRDataLen,
242 uint8_t * inBufPtr,
243 size_t inBufLen,
244 size_t * outCopiedRDataLen,
245 size_t * outExpandedRDataLen,
246 const uint8_t ** outPtr );
247
248 OSStatus
249 DNSMessageExtractRecord(
250 const uint8_t * inMsgPtr,
251 size_t inMsgLen,
252 const uint8_t * inPtr,
253 uint8_t outName[ kDomainNameLengthMax ],
254 uint16_t * outType,
255 uint16_t * outClass,
256 uint32_t * outTTL,
257 const uint8_t ** outRDataPtr,
258 size_t * outRDataLen,
259 const uint8_t ** outPtr )
260 {
261 return( _DNSMessageExtractRecordEx( inMsgPtr, inMsgLen, inPtr, outName, outType, outClass, outTTL,
262 outRDataPtr, outRDataLen, NULL, 0, NULL, NULL, outPtr ) );
263 }
264
265 //===========================================================================================================================
266
267 OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr )
268 {
269 OSStatus err;
270 unsigned int questionCount, i;
271 const DNSHeader * hdr;
272 const uint8_t * ptr;
273
274 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
275
276 hdr = (DNSHeader *) inMsgPtr;
277 questionCount = DNSHeaderGetQuestionCount( hdr );
278
279 ptr = (const uint8_t *) &hdr[ 1 ];
280 for( i = 0; i < questionCount; ++i )
281 {
282 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, NULL, NULL, NULL, &ptr );
283 require_noerr_quiet( err, exit );
284 }
285 if( outPtr ) *outPtr = ptr;
286 err = kNoErr;
287
288 exit:
289 return( err );
290 }
291
292 //===========================================================================================================================
293
294 OSStatus
295 DNSMessageGetOptRecord(
296 const uint8_t * inMsgPtr,
297 size_t inMsgLen,
298 const uint8_t ** outOptPtr,
299 size_t * outOptLen )
300 {
301 OSStatus err;
302 const DNSHeader * hdr;
303 const uint8_t * ptr;
304 const uint8_t * optPtr;
305 size_t optLen;
306 uint32_t skipCount, additionalCount, i;
307
308 err = DNSMessageGetAnswerSection( inMsgPtr, inMsgLen, &ptr );
309 require_noerr_quiet( err, exit );
310
311 hdr = (DNSHeader *) inMsgPtr;
312 skipCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr );
313 for( i = 0; i < skipCount; ++i )
314 {
315 uint16_t type;
316
317 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, NULL, &type, NULL, NULL, NULL, NULL, &ptr );
318 require_noerr_quiet( err, exit );
319
320 // Make sure that there are no OPT records in the answer and authority sections.
321 // See <https://tools.ietf.org/html/rfc6891#section-6.1.1>.
322
323 require_action_quiet( type != kDNSRecordType_OPT, exit, err = kMalformedErr );
324 }
325 optPtr = NULL;
326 optLen = 0;
327 additionalCount = DNSHeaderGetAdditionalCount( hdr );
328 for( i = 0; i < additionalCount; ++i )
329 {
330 const uint8_t * namePtr;
331 uint16_t type;
332
333 namePtr = ptr;
334 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, NULL, &type, NULL, NULL, NULL, NULL, &ptr );
335 require_noerr_quiet( err, exit );
336
337 if( type != kDNSRecordType_OPT ) continue;
338
339 // Make sure that there's only one OPT record.
340 // See <https://tools.ietf.org/html/rfc6891#section-6.1.1>.
341
342 require_action_quiet( !optPtr, exit, err = kMalformedErr );
343
344 // The OPT record's name must be 0 (root domain).
345 // See <https://tools.ietf.org/html/rfc6891#section-6.1.2>.
346
347 require_action_quiet( *namePtr == 0, exit, err = kMalformedErr );
348
349 optPtr = namePtr;
350 optLen = (size_t)( ptr - optPtr );
351 check( optLen >= sizeof( dns_fixed_fields_opt ) );
352 }
353 if( outOptPtr ) *outOptPtr = optPtr;
354 if( outOptLen ) *outOptLen = optLen;
355
356 exit:
357 return( err );
358 }
359
360 //===========================================================================================================================
361
362 OSStatus
363 DNSMessageWriteQuery(
364 uint16_t inMsgID,
365 uint16_t inFlags,
366 const uint8_t * inQName,
367 uint16_t inQType,
368 uint16_t inQClass,
369 uint8_t outMsg[ STATIC_PARAM kDNSQueryMessageMaxLen ],
370 size_t * outLen )
371 {
372 OSStatus err;
373 DNSHeader * const hdr = (DNSHeader *) outMsg;
374 uint8_t * ptr;
375 size_t qnameLen;
376 size_t msgLen;
377
378 memset( hdr, 0, sizeof( *hdr ) );
379 DNSHeaderSetID( hdr, inMsgID );
380 DNSHeaderSetFlags( hdr, inFlags );
381 DNSHeaderSetQuestionCount( hdr, 1 );
382
383 qnameLen = DomainNameLength( inQName );
384 require_action_quiet( qnameLen <= kDomainNameLengthMax, exit, err = kSizeErr );
385
386 ptr = (uint8_t *) &hdr[ 1 ];
387 memcpy( ptr, inQName, qnameLen );
388 ptr += qnameLen;
389
390 dns_fixed_fields_question_init( (dns_fixed_fields_question *) ptr, inQType, inQClass );
391 ptr += sizeof( dns_fixed_fields_question );
392
393 msgLen = (size_t)( ptr - outMsg );
394
395 if( outLen ) *outLen = msgLen;
396 err = kNoErr;
397
398 exit:
399 return( err );
400 }
401
402 //===========================================================================================================================
403
404 uint8_t *
405 DNSMessageCollapse(
406 const uint8_t * const inMsgPtr,
407 const size_t inMsgLen,
408 size_t * const outMsgLen,
409 OSStatus * const outError )
410 {
411 OSStatus err;
412 uint8_t * newMsg = NULL;
413 const DNSHeader * hdr;
414 const uint8_t * ptr;
415 const uint8_t * answerSection;
416 uint8_t * bufPtr = NULL;
417 size_t bufLen;
418 uint8_t * dst;
419 const uint8_t * lim;
420 size_t qnameLen;
421 unsigned int questionCount, answerCount, i, j;
422 uint32_t minCNameTTL;
423 uint16_t qtype, qclass;
424 uint8_t qname[ kDomainNameLengthMax ];
425 uint8_t target[ kDomainNameLengthMax ];
426
427 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
428
429 hdr = (DNSHeader *) inMsgPtr;
430 questionCount = DNSHeaderGetQuestionCount( hdr );
431 require_action_quiet( questionCount == 1, exit, err = kCountErr );
432
433 ptr = (const uint8_t *) &hdr[ 1 ];
434 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, qname, &qtype, &qclass, &ptr );
435 require_noerr_quiet( err, exit );
436 require_action_quiet( qclass == kDNSClassType_IN, exit, err = kTypeErr );
437
438 qnameLen = DomainNameLength( qname );
439 answerSection = ptr;
440
441 // The initial target name is the QNAME.
442
443 memcpy( target, qname, qnameLen );
444
445 // Starting with QNAME, follow the CNAME chain, if any, to the end.
446
447 minCNameTTL = UINT32_MAX;
448 answerCount = DNSHeaderGetAnswerCount( hdr );
449 for( i = 0; i < answerCount; ++i )
450 {
451 Boolean followedCNAME;
452
453 ptr = answerSection;
454 followedCNAME = false;
455 for( j = 0; j < answerCount; ++j )
456 {
457 const uint8_t * rdataPtr;
458 uint32_t ttl;
459 uint16_t type, class;
460 uint8_t name[ kDomainNameLengthMax ];
461
462 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, &rdataPtr, NULL, &ptr );
463 require_noerr_quiet( err, exit );
464
465 if( type != kDNSRecordType_CNAME ) continue;
466 if( class != qclass ) continue;
467 if( !DomainNameEqual( name, target ) ) continue;
468
469 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, rdataPtr, target, NULL );
470 require_noerr_quiet( err, exit );
471
472 minCNameTTL = Min( minCNameTTL, ttl );
473 followedCNAME = true;
474 }
475 if( !followedCNAME ) break;
476 }
477
478 // The target is now either QNAME or the end of the CNAME chain if there was one.
479 // Iterate over the answer section twice.
480 // The first iteration determines how much space is needed in the collapsed message for answer records.
481 // The second iteration writes the collapsed message.
482
483 bufPtr = NULL;
484 bufLen = kDNSHeaderLength + qnameLen + sizeof( dns_fixed_fields_question );
485 dst = NULL;
486 lim = NULL;
487 for( i = 0; i < 2; ++i )
488 {
489 DNSHeader * newHdr;
490 unsigned int newAnswerCount;
491
492 ptr = answerSection;
493 newAnswerCount = 0;
494 for( j = 0; j < answerCount; ++j )
495 {
496 const uint8_t * recordPtr;
497 dns_fixed_fields_record * fields;
498 size_t copiedRDataLen, expandedRDataLen;
499 uint32_t ttl;
500 uint16_t type, class;
501 uint8_t name[ kDomainNameLengthMax ];
502
503 recordPtr = ptr;
504 err = _DNSMessageExtractRecordEx( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, NULL, NULL,
505 NULL, 0, NULL, &expandedRDataLen, &ptr );
506 require_noerr_quiet( err, exit );
507
508 if( type != qtype ) continue;
509 if( class != qclass ) continue;
510 if( !DomainNameEqual( name, target ) ) continue;
511 if( !bufPtr )
512 {
513 // Add the amount of space needed for this record.
514 // Note that the record's name will be a compression pointer to the QNAME.
515
516 bufLen += ( kDNSCompressionPointerLength + sizeof( *fields ) + expandedRDataLen );
517 }
518 else
519 {
520 // Write the record's name as a compression pointer to QNAME, which is located right after the header.
521
522 require_action_quiet( ( lim - dst ) >= kDNSCompressionPointerLength, exit, err = kInternalErr );
523 DNSMessageWriteLabelPointer( dst, kDNSHeaderLength );
524 dst += kDNSCompressionPointerLength;
525
526 // Write the record's fixed field values.
527
528 require_action_quiet( ( (size_t)( lim - dst ) ) >= sizeof( *fields ), exit, err = kInternalErr );
529 fields = (dns_fixed_fields_record *) dst;
530 ttl = Min( ttl, minCNameTTL );
531 dns_fixed_fields_record_init( fields, type, class, ttl, (uint16_t) expandedRDataLen );
532 dst += sizeof( *fields );
533
534 // Write the record's expanded RDATA.
535
536 require_action_quiet( ( (size_t)( lim - dst ) ) >= expandedRDataLen, exit, err = kInternalErr );
537 err = _DNSMessageExtractRecordEx( inMsgPtr, inMsgLen, recordPtr, NULL, NULL, NULL, NULL, NULL, NULL,
538 dst, expandedRDataLen, &copiedRDataLen, NULL, NULL );
539 require_noerr_quiet( err, exit );
540
541 dst += copiedRDataLen;
542 ++newAnswerCount;
543 }
544 }
545 if( !bufPtr )
546 {
547 dns_fixed_fields_question * fields;
548
549 // Allocate memory for the collapsed message.
550
551 bufPtr = (uint8_t *) calloc( 1, bufLen );
552 require_action_quiet( bufPtr, exit, err = kNoMemoryErr );
553
554 dst = bufPtr;
555 lim = &bufPtr[ bufLen ];
556
557 // Write a tentative header based on the original header.
558
559 require_action_quiet( ( (size_t)( lim - dst ) ) >= sizeof( *newHdr ), exit, err = kInternalErr );
560 newHdr = (DNSHeader *) dst;
561 memcpy( newHdr, hdr, sizeof( *newHdr ) );
562 dst += sizeof( *newHdr );
563
564 DNSHeaderSetAnswerCount( newHdr, 0 );
565 DNSHeaderSetAuthorityCount( newHdr, 0 );
566 DNSHeaderSetAdditionalCount( newHdr, 0 );
567
568 // Write the question section.
569
570 require_action_quiet( ( (size_t)( lim - dst ) ) >= qnameLen, exit, err = kInternalErr );
571 memcpy( dst, qname, qnameLen );
572 dst += qnameLen;
573
574 require_action_quiet( ( (size_t)( lim - dst ) ) >= sizeof( *fields ), exit, err = kInternalErr );
575 fields = (dns_fixed_fields_question *) dst;
576 dns_fixed_fields_question_init( fields, qtype, qclass );
577 dst += sizeof( *fields );
578
579 DNSHeaderSetQuestionCount( newHdr, 1 );
580 }
581 else
582 {
583 // Finally, set the answer count.
584
585 require_action_quiet( bufLen >= sizeof( *newHdr ), exit, err = kInternalErr );
586 newHdr = (DNSHeader *) bufPtr;
587 DNSHeaderSetAnswerCount( newHdr, newAnswerCount );
588 break;
589 }
590 }
591 if( outMsgLen ) *outMsgLen = (size_t)( dst - bufPtr );
592 newMsg = bufPtr;
593 bufPtr = NULL;
594
595 exit:
596 if( outError ) *outError = err;
597 ForgetMem( &bufPtr );
598 return( newMsg );
599 }
600
601 //===========================================================================================================================
602
603 static OSStatus
604 _DNSMessageExtractRData(
605 const uint8_t * inMsgPtr,
606 size_t inMsgLen,
607 const uint8_t * inRDataPtr,
608 size_t inRDataLen,
609 unsigned int inType,
610 uint8_t * inBufPtr,
611 size_t inBufLen,
612 size_t * outCopiedRDataLen,
613 size_t * outExpandedRDataLen );
614
615 static OSStatus
616 _DNSMessageExtractRecordEx(
617 const uint8_t * const inMsgPtr,
618 const size_t inMsgLen,
619 const uint8_t * const inPtr,
620 uint8_t outName[ kDomainNameLengthMax ],
621 uint16_t * const outType,
622 uint16_t * const outClass,
623 uint32_t * const outTTL,
624 const uint8_t ** const outRDataPtr,
625 size_t * const outRDataLen,
626 uint8_t * const inBufPtr,
627 const size_t inBufLen,
628 size_t * const outCopiedRDataLen,
629 size_t * const outExpandedRDataLen,
630 const uint8_t ** outPtr )
631 {
632 OSStatus err;
633 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
634 const uint8_t * ptr;
635 const dns_fixed_fields_record * fields;
636 const uint8_t * rdata;
637 size_t rdLength, copiedLen, expandedLen;
638 uint16_t type;
639
640 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, outName, &ptr );
641 require_noerr_quiet( err, exit );
642 require_action_quiet( (size_t)( msgEnd - ptr ) >= sizeof( *fields ), exit, err = kUnderrunErr );
643
644 fields = (const dns_fixed_fields_record *) ptr;
645 rdata = ptr + sizeof( *fields );
646 rdLength = dns_fixed_fields_record_get_rdlength( fields );
647 require_action_quiet( (size_t)( msgEnd - rdata ) >= rdLength , exit, err = kUnderrunErr );
648
649 type = dns_fixed_fields_record_get_type( fields );
650 err = _DNSMessageExtractRData( inMsgPtr, inMsgLen, rdata, rdLength, type, inBufPtr, inBufLen, &copiedLen, &expandedLen );
651 require_noerr_quiet( err, exit );
652
653 if( outType ) *outType = type;
654 if( outClass ) *outClass = dns_fixed_fields_record_get_class( fields );
655 if( outTTL ) *outTTL = dns_fixed_fields_record_get_ttl( fields );
656 if( outRDataPtr ) *outRDataPtr = rdata;
657 if( outRDataLen ) *outRDataLen = rdLength;
658 if( outCopiedRDataLen ) *outCopiedRDataLen = copiedLen;
659 if( outExpandedRDataLen ) *outExpandedRDataLen = expandedLen;
660 if( outPtr ) *outPtr = &rdata[ rdLength ];
661
662 exit:
663 return( err );
664 }
665
666 static OSStatus
667 _DNSMessageExtractRData(
668 const uint8_t * const inMsgPtr,
669 const size_t inMsgLen,
670 const uint8_t * const inRDataPtr,
671 const size_t inRDataLen,
672 const unsigned int inType,
673 uint8_t * const inBufPtr,
674 const size_t inBufLen,
675 size_t * const outCopiedRDataLen,
676 size_t * const outExpandedRDataLen )
677 {
678 OSStatus err;
679 const uint8_t * ptr;
680 const uint8_t * const rdataEnd = &inRDataPtr[ inRDataLen ];
681 size_t copyLen, expandedLen;
682 uint8_t name1[ kDomainNameLengthMax ];
683 uint8_t name2[ kDomainNameLengthMax ];
684
685 // According to <https://tools.ietf.org/html/rfc1123#section-6.1.3.5>:
686 //
687 // Compression relies on knowledge of the format of data
688 // inside a particular RR. Hence compression must only be
689 // used for the contents of well-known, class-independent
690 // RRs, and must never be used for class-specific RRs or
691 // RR types that are not well-known. The owner name of an
692 // RR is always eligible for compression.
693 //
694 // Therefore, compressed domain names in RDATA is only handled for record types from
695 // <https://tools.ietf.org/html/rfc1035#section-3.3>.
696
697 switch( inType )
698 {
699 case kDNSRecordType_CNAME: // <https://tools.ietf.org/html/rfc1035#section-3.3.1>
700 case kDNSRecordType_MB: // <https://tools.ietf.org/html/rfc1035#section-3.3.3>
701 case kDNSRecordType_MD: // <https://tools.ietf.org/html/rfc1035#section-3.3.4>
702 case kDNSRecordType_MF: // <https://tools.ietf.org/html/rfc1035#section-3.3.5>
703 case kDNSRecordType_MG: // <https://tools.ietf.org/html/rfc1035#section-3.3.6>
704 case kDNSRecordType_MR: // <https://tools.ietf.org/html/rfc1035#section-3.3.8>
705 case kDNSRecordType_NS: // <https://tools.ietf.org/html/rfc1035#section-3.3.11>
706 case kDNSRecordType_PTR: // <https://tools.ietf.org/html/rfc1035#section-3.3.12>
707 {
708 // The RDATA consists of one domain name.
709
710 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inRDataPtr, name1, &ptr );
711 require_noerr_quiet( err, exit );
712 require_action_quiet( ptr == rdataEnd, exit, err = kMalformedErr );
713
714 expandedLen = DomainNameLength( name1 );
715 copyLen = Min( inBufLen, expandedLen );
716 memcpy( inBufPtr, name1, copyLen );
717 break;
718 }
719 case kDNSRecordType_MINFO: // <https://tools.ietf.org/html/rfc1035#section-3.3.7>
720 {
721 uint8_t * dst = inBufPtr;
722 const uint8_t * const lim = &inBufPtr[ inBufLen ];
723 size_t nameLen1, nameLen2;
724
725 // The RDATA consists of two domain names.
726
727 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inRDataPtr, name1, &ptr );
728 require_noerr_quiet( err, exit );
729
730 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, ptr, name2, &ptr );
731 require_noerr_quiet( err, exit );
732 require_action_quiet( ptr == rdataEnd, exit, err = kMalformedErr );
733
734 nameLen1 = DomainNameLength( name1 );
735 nameLen2 = DomainNameLength( name2 );
736 expandedLen = nameLen1 + nameLen2;
737
738 copyLen = (size_t)( lim - dst );
739 copyLen = Min( copyLen, nameLen1 );
740 memcpy( dst, name1, copyLen );
741 dst += copyLen;
742
743 copyLen = (size_t)( lim - dst );
744 copyLen = Min( copyLen, nameLen2 );
745 memcpy( dst, name2, copyLen );
746 dst += copyLen;
747
748 copyLen = (size_t)( dst - inBufPtr );
749 break;
750 }
751 case kDNSRecordType_MX: // <https://tools.ietf.org/html/rfc1035#section-3.3.9>
752 {
753 uint8_t * dst = inBufPtr;
754 const uint8_t * const lim = &inBufPtr[ inBufLen ];
755 const uint8_t * exchange;
756 size_t nameLen1;
757
758 // The RDATA format is a 2-octet preference value followed by exchange, which is a domain name.
759
760 require_action_quiet( inRDataLen > 2, exit, err = kMalformedErr );
761 exchange = &inRDataPtr[ 2 ];
762
763 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, exchange, name1, &ptr );
764 require_noerr_quiet( err, exit );
765 require_action_quiet( ptr == rdataEnd, exit, err = kMalformedErr );
766
767 nameLen1 = DomainNameLength( name1 );
768 expandedLen = 2 + nameLen1;
769
770 copyLen = (size_t)( lim - dst );
771 copyLen = Min( copyLen, 2 );
772 memcpy( dst, inRDataPtr, copyLen );
773 dst += copyLen;
774
775 copyLen = (size_t)( lim - dst );
776 copyLen = Min( copyLen, nameLen1 );
777 memcpy( dst, name1, copyLen );
778 dst += copyLen;
779
780 copyLen = (size_t)( dst - inBufPtr );
781 break;
782 }
783 case kDNSRecordType_SOA: // <https://tools.ietf.org/html/rfc1035#section-3.3.13>
784 {
785 uint8_t * dst = inBufPtr;
786 const uint8_t * const lim = &inBufPtr[ inBufLen ];
787 size_t nameLen1, nameLen2;
788
789 // MNAME is a domain name.
790
791 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inRDataPtr, name1, &ptr );
792 require_noerr_quiet( err, exit );
793
794 // RNAME is a domain name.
795
796 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, ptr, name2, &ptr );
797 require_noerr_quiet( err, exit );
798
799 // MNAME and RNAME are followed by fixed-sized fields.
800
801 require_action_quiet( ( rdataEnd - ptr ) == sizeof( dns_fixed_fields_soa ), exit, err = kMalformedErr );
802 nameLen1 = DomainNameLength( name1 );
803 nameLen2 = DomainNameLength( name2 );
804 expandedLen = nameLen1 + nameLen2 + sizeof( dns_fixed_fields_soa );
805
806 copyLen = (size_t)( lim - dst );
807 copyLen = Min( copyLen, nameLen1 );
808 memcpy( dst, name1, copyLen );
809 dst += copyLen;
810
811 copyLen = (size_t)( lim - dst );
812 copyLen = Min( copyLen, nameLen2 );
813 memcpy( dst, name2, copyLen );
814 dst += copyLen;
815
816 copyLen = (size_t)( lim - dst );
817 copyLen = Min( copyLen, sizeof( dns_fixed_fields_soa ) );
818 memcpy( dst, ptr, copyLen );
819 dst += copyLen;
820
821 copyLen = (size_t)( dst - inBufPtr );
822 break;
823 }
824 default:
825 case kDNSRecordType_HINFO: // <https://tools.ietf.org/html/rfc1035#section-3.3.2>
826 case kDNSRecordType_NULL: // <https://tools.ietf.org/html/rfc1035#section-3.3.10>
827 case kDNSRecordType_TXT: // <https://tools.ietf.org/html/rfc1035#section-3.3.14>
828 {
829 // The RDATA contains no compressed domain names.
830
831 expandedLen = inRDataLen;
832 copyLen = Min( inBufLen, expandedLen );
833 memcpy( inBufPtr, inRDataPtr, copyLen );
834 break;
835 }
836 }
837 err = kNoErr;
838
839 if( outCopiedRDataLen ) *outCopiedRDataLen = copyLen;
840 if( outExpandedRDataLen ) *outExpandedRDataLen = expandedLen;
841
842 exit:
843 return( err );
844 }
845
846 //===========================================================================================================================
847
848 OSStatus
849 DomainNameAppendDomainName(
850 uint8_t inName[ STATIC_PARAM kDomainNameLengthMax ],
851 const uint8_t * inOtherName,
852 uint8_t ** outEnd )
853 {
854 OSStatus err;
855 size_t newLen;
856 const size_t adjustedLen = DomainNameLength( inName ) - 1; // Don't count the root label.
857 const size_t otherLen = DomainNameLength( inOtherName );
858
859 require_action_quiet( adjustedLen <= kDomainNameLengthMax, exit, err = kSizeErr );
860 require_action_quiet( otherLen <= kDomainNameLengthMax, exit, err = kSizeErr );
861
862 newLen = adjustedLen + otherLen;
863 require_action_quiet( newLen <= kDomainNameLengthMax, exit, err = kSizeErr );
864
865 memcpy( &inName[ adjustedLen ], inOtherName, otherLen );
866 if( outEnd ) *outEnd = &inName[ newLen ];
867 err = kNoErr;
868
869 exit:
870 return( err );
871 }
872
873 //===========================================================================================================================
874
875 OSStatus
876 DomainNameAppendString(
877 uint8_t inDomainName[ STATIC_PARAM kDomainNameLengthMax ],
878 const char * inString,
879 uint8_t ** outEnd )
880 {
881 OSStatus err;
882 const char * src;
883 uint8_t * root;
884 const uint8_t * const nameLim = inDomainName + kDomainNameLengthMax;
885
886 for( root = inDomainName; ( root < nameLim ) && *root; root += ( 1 + *root ) ) {}
887 require_action_quiet( root < nameLim, exit, err = kMalformedErr );
888
889 // If the string is a single dot, denoting the root domain, then there are no non-empty labels.
890
891 src = inString;
892 if( ( src[ 0 ] == '.' ) && ( src[ 1 ] == '\0' ) ) ++src;
893 while( *src )
894 {
895 uint8_t * const label = root;
896 const uint8_t * const labelLim = Min( &label[ 1 + kDomainLabelLengthMax ], nameLim - 1 );
897 uint8_t * dst;
898 int c;
899 size_t labelLen;
900
901 dst = &label[ 1 ];
902 while( *src && ( ( c = *src++ ) != '.' ) )
903 {
904 if( c == '\\' )
905 {
906 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
907 c = *src++;
908 if( isdigit_safe( c ) && isdigit_safe( src[ 0 ] ) && isdigit_safe( src[ 1 ] ) )
909 {
910 const int decimal = ( ( c - '0' ) * 100 ) + ( ( src[ 0 ] - '0' ) * 10 ) + ( src[ 1 ] - '0' );
911
912 if( decimal <= 255 )
913 {
914 c = decimal;
915 src += 2;
916 }
917 }
918 }
919 require_action_quiet( dst < labelLim, exit, err = kOverrunErr );
920 *dst++ = (uint8_t) c;
921 }
922
923 labelLen = (size_t)( dst - &label[ 1 ] );
924 require_action_quiet( labelLen > 0, exit, err = kMalformedErr );
925
926 label[ 0 ] = (uint8_t) labelLen;
927 root = dst;
928 *root = 0;
929 }
930 if( outEnd ) *outEnd = root + 1;
931 err = kNoErr;
932
933 exit:
934 return( err );
935 }
936
937 //===========================================================================================================================
938
939 #define _isupper_ascii( X ) ( ( (X) >= 'A' ) && ( (X) <= 'Z' ) )
940
941 static void _DomainNameLower( uint8_t *inName );
942
943 OSStatus DomainNameDupEx( const uint8_t *inName, Boolean inLower, uint8_t **outNamePtr, size_t *outNameLen )
944 {
945 OSStatus err;
946 uint8_t * namePtr;
947 const size_t nameLen = DomainNameLength( inName );
948
949 namePtr = (uint8_t *) malloc( nameLen );
950 require_action_quiet( namePtr, exit, err = kNoMemoryErr );
951
952 memcpy( namePtr, inName, nameLen );
953 if( inLower ) _DomainNameLower( namePtr );
954
955 *outNamePtr = namePtr;
956 if( outNameLen ) *outNameLen = nameLen;
957 err = kNoErr;
958
959 exit:
960 return( err );
961 }
962
963 static void _DomainNameLower( uint8_t *inName )
964 {
965 uint8_t * ptr;
966 int len;
967
968 ptr = inName;
969 while( ( len = *ptr++ ) != 0 )
970 {
971 while( len-- > 0 )
972 {
973 if( _isupper_ascii( *ptr ) ) *ptr += 32;
974 ++ptr;
975 }
976 }
977 }
978
979 //===========================================================================================================================
980
981 #define _tolower_ascii( X ) ( _isupper_ascii( X ) ? ( (X) + 32 ) : (X) )
982
983 Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 )
984 {
985 const uint8_t * p1 = inName1;
986 const uint8_t * p2 = inName2;
987 if( p1 == p2 ) return( true );
988 for( ;; )
989 {
990 int len1 = *p1++;
991 const int len2 = *p2++;
992 if( len1 != len2 ) return( false );
993 if( len1 == 0 ) return( true );
994 while( len1-- > 0 )
995 {
996 const int c1 = *p1++;
997 const int c2 = *p2++;
998 if( _tolower_ascii( c1 ) != _tolower_ascii( c2 ) ) return( false );
999 }
1000 }
1001 }
1002
1003 //===========================================================================================================================
1004
1005 OSStatus
1006 DomainNameFromString(
1007 uint8_t outName[ STATIC_PARAM kDomainNameLengthMax ],
1008 const char * inString,
1009 uint8_t ** outEnd )
1010 {
1011 outName[ 0 ] = 0;
1012 return( DomainNameAppendString( outName, inString, outEnd ) );
1013 }
1014
1015 //===========================================================================================================================
1016
1017 size_t DomainNameLength( const uint8_t *inName )
1018 {
1019 const uint8_t * label;
1020 int len;
1021
1022 for( label = inName; ( len = *label ) != 0; label = &label[ 1 + len ] ) {}
1023 return( (size_t)( label - inName ) + 1 );
1024 }
1025
1026 //===========================================================================================================================
1027
1028 int DomainNameLabelCount( const uint8_t * const inName )
1029 {
1030 const uint8_t * label;
1031 const uint8_t * nextLabel;
1032 int labelCount, result;
1033 unsigned int labelLen;
1034
1035 labelCount = 0;
1036 for( label = inName; ( labelLen = *label ) != 0; label = nextLabel )
1037 {
1038 require_action_quiet( labelLen <= kDomainLabelLengthMax, exit, result = -1 );
1039
1040 nextLabel = &label[ 1 + labelLen ];
1041 require_action_quiet( ( nextLabel - inName ) < kDomainNameLengthMax, exit, result = -1 );
1042
1043 ++labelCount;
1044 }
1045 result = labelCount;
1046
1047 exit:
1048 return( result );
1049 }
1050
1051 //===========================================================================================================================
1052
1053 #define _isprint_ascii( X ) ( ( (X) >= 32 ) && ( (X) <= 126 ) )
1054
1055 OSStatus
1056 DomainNameToString(
1057 const uint8_t * inName,
1058 const uint8_t * inLimit,
1059 char outString[ STATIC_PARAM kDNSServiceMaxDomainName ],
1060 const uint8_t ** outPtr )
1061 {
1062 OSStatus err;
1063 const uint8_t * label;
1064 uint8_t labelLen;
1065 const uint8_t * nextLabel;
1066 char * dst;
1067 const uint8_t * src;
1068
1069 require_action_quiet( !inLimit || ( ( inLimit - inName ) > 0 ), exit, err = kUnderrunErr );
1070
1071 // Convert each label up until the root label, i.e., the zero-length label.
1072
1073 dst = outString;
1074 for( label = inName; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
1075 {
1076 require_action_quiet( labelLen <= kDomainLabelLengthMax, exit, err = kMalformedErr );
1077
1078 nextLabel = &label[ 1 + labelLen ];
1079 require_action_quiet( ( nextLabel - inName ) < kDomainNameLengthMax, exit, err = kMalformedErr );
1080 require_action_quiet( !inLimit || ( nextLabel < inLimit ), exit, err = kUnderrunErr );
1081
1082 for( src = &label[ 1 ]; src < nextLabel; ++src )
1083 {
1084 if( _isprint_ascii( *src ) )
1085 {
1086 if( ( *src == '.' ) || ( *src == '\\' ) || ( *src == ' ' ) ) *dst++ = '\\';
1087 *dst++ = (char) *src;
1088 }
1089 else
1090 {
1091 *dst++ = '\\';
1092 *dst++ = '0' + ( *src / 100 );
1093 *dst++ = '0' + ( ( *src / 10 ) % 10 );
1094 *dst++ = '0' + ( *src % 10 );
1095 }
1096 }
1097 *dst++ = '.';
1098 }
1099
1100 // At this point, label points to the root label.
1101 // If the root label was the only label, then write a dot for it.
1102
1103 if( label == inName ) *dst++ = '.';
1104 *dst = '\0';
1105 if( outPtr ) *outPtr = label + 1;
1106 err = kNoErr;
1107
1108 exit:
1109 return( err );
1110 }
1111
1112 //===========================================================================================================================
1113
1114 Boolean DomainLabelEqual( const uint8_t *inLabel1, const uint8_t *inLabel2 )
1115 {
1116 const uint8_t * p1 = inLabel1;
1117 const uint8_t * p2 = inLabel2;
1118 if( p1 == p2 ) return( true );
1119 int len1 = *p1++;
1120 const int len2 = *p2++;
1121 if( len1 != len2 ) return( false );
1122 while( len1-- > 0 )
1123 {
1124 const int c1 = *p1++;
1125 const int c2 = *p2++;
1126 if( _tolower_ascii( c1 ) != _tolower_ascii( c2 ) ) return( false );
1127 }
1128 return( true );
1129 }
1130
1131 //===========================================================================================================================
1132 // This code was autogenerated on 2020-06-30 by dns-rr-func-autogen version 1.3
1133 // Data source URL: https://www.iana.org/assignments/dns-parameters/dns-parameters-4.csv
1134 // Overrides: none
1135
1136 const char * DNSRecordTypeValueToString( int inValue )
1137 {
1138 const char * string;
1139
1140 if( 0 ) {}
1141 else if( ( inValue >= 1 ) && ( inValue <= 53 ) )
1142 {
1143 static const char * const sNames_1_53[] =
1144 {
1145 "A", // 1
1146 "NS", // 2
1147 "MD", // 3
1148 "MF", // 4
1149 "CNAME", // 5
1150 "SOA", // 6
1151 "MB", // 7
1152 "MG", // 8
1153 "MR", // 9
1154 "NULL", // 10
1155 "WKS", // 11
1156 "PTR", // 12
1157 "HINFO", // 13
1158 "MINFO", // 14
1159 "MX", // 15
1160 "TXT", // 16
1161 "RP", // 17
1162 "AFSDB", // 18
1163 "X25", // 19
1164 "ISDN", // 20
1165 "RT", // 21
1166 "NSAP", // 22
1167 "NSAP-PTR", // 23
1168 "SIG", // 24
1169 "KEY", // 25
1170 "PX", // 26
1171 "GPOS", // 27
1172 "AAAA", // 28
1173 "LOC", // 29
1174 "NXT", // 30
1175 "EID", // 31
1176 "NIMLOC", // 32
1177 "SRV", // 33
1178 "ATMA", // 34
1179 "NAPTR", // 35
1180 "KX", // 36
1181 "CERT", // 37
1182 "A6", // 38
1183 "DNAME", // 39
1184 "SINK", // 40
1185 "OPT", // 41
1186 "APL", // 42
1187 "DS", // 43
1188 "SSHFP", // 44
1189 "IPSECKEY", // 45
1190 "RRSIG", // 46
1191 "NSEC", // 47
1192 "DNSKEY", // 48
1193 "DHCID", // 49
1194 "NSEC3", // 50
1195 "NSEC3PARAM", // 51
1196 "TLSA", // 52
1197 "SMIMEA", // 53
1198 };
1199 string = sNames_1_53[ inValue - 1 ];
1200 }
1201 else if( ( inValue >= 55 ) && ( inValue <= 65 ) )
1202 {
1203 static const char * const sNames_55_65[] =
1204 {
1205 "HIP", // 55
1206 "NINFO", // 56
1207 "RKEY", // 57
1208 "TALINK", // 58
1209 "CDS", // 59
1210 "CDNSKEY", // 60
1211 "OPENPGPKEY", // 61
1212 "CSYNC", // 62
1213 "ZONEMD", // 63
1214 "SVCB", // 64
1215 "HTTPS", // 65
1216 };
1217 string = sNames_55_65[ inValue - 55 ];
1218 }
1219 else if( ( inValue >= 99 ) && ( inValue <= 109 ) )
1220 {
1221 static const char * const sNames_99_109[] =
1222 {
1223 "SPF", // 99
1224 "UINFO", // 100
1225 "UID", // 101
1226 "GID", // 102
1227 "UNSPEC", // 103
1228 "NID", // 104
1229 "L32", // 105
1230 "L64", // 106
1231 "LP", // 107
1232 "EUI48", // 108
1233 "EUI64", // 109
1234 };
1235 string = sNames_99_109[ inValue - 99 ];
1236 }
1237 else if( ( inValue >= 249 ) && ( inValue <= 260 ) )
1238 {
1239 static const char * const sNames_249_260[] =
1240 {
1241 "TKEY", // 249
1242 "TSIG", // 250
1243 "IXFR", // 251
1244 "AXFR", // 252
1245 "MAILB", // 253
1246 "MAILA", // 254
1247 "ANY", // 255
1248 "URI", // 256
1249 "CAA", // 257
1250 "AVC", // 258
1251 "DOA", // 259
1252 "AMTRELAY", // 260
1253 };
1254 string = sNames_249_260[ inValue - 249 ];
1255 }
1256 else if( ( inValue >= 32768 ) && ( inValue <= 32769 ) )
1257 {
1258 static const char * const sNames_32768_32769[] =
1259 {
1260 "TA", // 32768
1261 "DLV", // 32769
1262 };
1263 string = sNames_32768_32769[ inValue - 32768 ];
1264 }
1265 else if( inValue == 65535 )
1266 {
1267 static const char * const sNames_65535[] =
1268 {
1269 "Reserved", // 65535
1270 };
1271 string = sNames_65535[ inValue - 65535 ];
1272 }
1273 else
1274 {
1275 string = NULL;
1276 }
1277 return( string );
1278 }
1279
1280
1281 //===========================================================================================================================
1282 // This code was autogenerated on 2020-06-30 by dns-rr-func-autogen version 1.3
1283 // Data source URL: https://www.iana.org/assignments/dns-parameters/dns-parameters-4.csv
1284 // Overrides: none
1285
1286 typedef struct
1287 {
1288 const char * name;
1289 uint16_t value;
1290
1291 } _DNSRecordTypeItem;
1292
1293 static int _DNSRecordTypeStringToValueCmp( const void *inKey, const void *inElement );
1294
1295 uint16_t DNSRecordTypeStringToValue( const char *inString )
1296 {
1297 // The name-value table is sorted by name in ascending lexicographical order to allow going from name to
1298 // value in logarithmic time via a binary search.
1299
1300 static const _DNSRecordTypeItem sTable[] =
1301 {
1302 { "A", 1 },
1303 { "A6", 38 },
1304 { "AAAA", 28 },
1305 { "AFSDB", 18 },
1306 { "AMTRELAY", 260 },
1307 { "ANY", 255 },
1308 { "APL", 42 },
1309 { "ATMA", 34 },
1310 { "AVC", 258 },
1311 { "AXFR", 252 },
1312 { "CAA", 257 },
1313 { "CDNSKEY", 60 },
1314 { "CDS", 59 },
1315 { "CERT", 37 },
1316 { "CNAME", 5 },
1317 { "CSYNC", 62 },
1318 { "DHCID", 49 },
1319 { "DLV", 32769 },
1320 { "DNAME", 39 },
1321 { "DNSKEY", 48 },
1322 { "DOA", 259 },
1323 { "DS", 43 },
1324 { "EID", 31 },
1325 { "EUI48", 108 },
1326 { "EUI64", 109 },
1327 { "GID", 102 },
1328 { "GPOS", 27 },
1329 { "HINFO", 13 },
1330 { "HIP", 55 },
1331 { "HTTPS", 65 },
1332 { "IPSECKEY", 45 },
1333 { "ISDN", 20 },
1334 { "IXFR", 251 },
1335 { "KEY", 25 },
1336 { "KX", 36 },
1337 { "L32", 105 },
1338 { "L64", 106 },
1339 { "LOC", 29 },
1340 { "LP", 107 },
1341 { "MAILA", 254 },
1342 { "MAILB", 253 },
1343 { "MB", 7 },
1344 { "MD", 3 },
1345 { "MF", 4 },
1346 { "MG", 8 },
1347 { "MINFO", 14 },
1348 { "MR", 9 },
1349 { "MX", 15 },
1350 { "NAPTR", 35 },
1351 { "NID", 104 },
1352 { "NIMLOC", 32 },
1353 { "NINFO", 56 },
1354 { "NS", 2 },
1355 { "NSAP", 22 },
1356 { "NSAP-PTR", 23 },
1357 { "NSEC", 47 },
1358 { "NSEC3", 50 },
1359 { "NSEC3PARAM", 51 },
1360 { "NULL", 10 },
1361 { "NXT", 30 },
1362 { "OPENPGPKEY", 61 },
1363 { "OPT", 41 },
1364 { "PTR", 12 },
1365 { "PX", 26 },
1366 { "Reserved", 65535 },
1367 { "RKEY", 57 },
1368 { "RP", 17 },
1369 { "RRSIG", 46 },
1370 { "RT", 21 },
1371 { "SIG", 24 },
1372 { "SINK", 40 },
1373 { "SMIMEA", 53 },
1374 { "SOA", 6 },
1375 { "SPF", 99 },
1376 { "SRV", 33 },
1377 { "SSHFP", 44 },
1378 { "SVCB", 64 },
1379 { "TA", 32768 },
1380 { "TALINK", 58 },
1381 { "TKEY", 249 },
1382 { "TLSA", 52 },
1383 { "TSIG", 250 },
1384 { "TXT", 16 },
1385 { "UID", 101 },
1386 { "UINFO", 100 },
1387 { "UNSPEC", 103 },
1388 { "URI", 256 },
1389 { "WKS", 11 },
1390 { "X25", 19 },
1391 { "ZONEMD", 63 },
1392 };
1393 const _DNSRecordTypeItem * item;
1394
1395 item = (_DNSRecordTypeItem *) bsearch( inString, sTable, sizeof( sTable ) / sizeof( sTable[ 0 ] ),
1396 sizeof( sTable[ 0 ] ), _DNSRecordTypeStringToValueCmp );
1397 return( item ? item->value : 0 );
1398 }
1399
1400 static int _DNSRecordTypeStringToValueCmp( const void *inKey, const void *inElement )
1401 {
1402 const _DNSRecordTypeItem * const item = (const _DNSRecordTypeItem *) inElement;
1403 return( strcasecmp( (const char *) inKey, item->name ) );
1404 }
1405
1406 //===========================================================================================================================
1407 // This code was autogenerated on 2020-06-15 by dns-rcode-func-autogen version 1.0
1408 // Data source URL: https://www.iana.org/assignments/dns-parameters/dns-parameters-6.csv
1409
1410 const char * DNSRCodeToString( const int inValue )
1411 {
1412 switch( inValue )
1413 {
1414 case kDNSRCode_NoError: return( "NoError" );
1415 case kDNSRCode_FormErr: return( "FormErr" );
1416 case kDNSRCode_ServFail: return( "ServFail" );
1417 case kDNSRCode_NXDomain: return( "NXDomain" );
1418 case kDNSRCode_NotImp: return( "NotImp" );
1419 case kDNSRCode_Refused: return( "Refused" );
1420 case kDNSRCode_YXDomain: return( "YXDomain" );
1421 case kDNSRCode_YXRRSet: return( "YXRRSet" );
1422 case kDNSRCode_NXRRSet: return( "NXRRSet" );
1423 case kDNSRCode_NotAuth: return( "NotAuth" );
1424 case kDNSRCode_NotZone: return( "NotZone" );
1425 case kDNSRCode_DSOTYPENI: return( "DSOTYPENI" );
1426 default: return( NULL );
1427 }
1428 }
1429
1430 //===========================================================================================================================
1431 // This code was autogenerated on 2020-06-15 by dns-rcode-func-autogen version 1.0
1432 // Data source URL: https://www.iana.org/assignments/dns-parameters/dns-parameters-6.csv
1433
1434 typedef struct
1435 {
1436 const char * name;
1437 int value;
1438
1439 } _DNSRCodeTableEntry;
1440
1441 static int _DNSRCodeFromStringCmp( const void *inKey, const void *inElement );
1442
1443 int DNSRCodeFromString( const char * const inString )
1444 {
1445 // The name-value table is sorted by name in ascending lexicographical order to allow going from name to
1446 // value in logarithmic time via a binary search.
1447
1448 static const _DNSRCodeTableEntry sTable[] =
1449 {
1450 { "DSOTYPENI", kDNSRCode_DSOTYPENI },
1451 { "FormErr", kDNSRCode_FormErr },
1452 { "NoError", kDNSRCode_NoError },
1453 { "NotAuth", kDNSRCode_NotAuth },
1454 { "NotImp", kDNSRCode_NotImp },
1455 { "NotZone", kDNSRCode_NotZone },
1456 { "NXDomain", kDNSRCode_NXDomain },
1457 { "NXRRSet", kDNSRCode_NXRRSet },
1458 { "Refused", kDNSRCode_Refused },
1459 { "ServFail", kDNSRCode_ServFail },
1460 { "YXDomain", kDNSRCode_YXDomain },
1461 { "YXRRSet", kDNSRCode_YXRRSet }
1462 };
1463 const _DNSRCodeTableEntry * entry;
1464
1465 entry = (_DNSRCodeTableEntry *) bsearch( inString, sTable, sizeof( sTable ) / sizeof( sTable[ 0 ] ),
1466 sizeof( sTable[ 0 ] ), _DNSRCodeFromStringCmp );
1467 return( entry ? entry->value : -1 );
1468 }
1469
1470 static int _DNSRCodeFromStringCmp( const void * const inKey, const void * const inElement )
1471 {
1472 const _DNSRCodeTableEntry * const entry = (const _DNSRCodeTableEntry *) inElement;
1473 return( strcasecmp( (const char *) inKey, entry->name ) );
1474 }
1475
1476 //===========================================================================================================================
1477 // Note: Unknown resource record types and classes are represented as text using the convention described in
1478 // <https://tools.ietf.org/html/rfc3597#section-5>.
1479
1480 static const char * _DNSOpCodeToString( int inOpCode );
1481
1482 typedef struct
1483 {
1484 unsigned int flag;
1485 const char * desc;
1486
1487 } _DNSHeaderFlagDesc;
1488
1489 static const _DNSHeaderFlagDesc kDNSHeaderFlagsDescs[] =
1490 {
1491 { kDNSHeaderFlag_AuthAnswer, "AA" },
1492 { kDNSHeaderFlag_Truncation, "TC" },
1493 { kDNSHeaderFlag_RecursionDesired, "RD" },
1494 { kDNSHeaderFlag_RecursionAvailable, "RA" },
1495 { kDNSHeaderFlag_Z, "Z" },
1496 { kDNSHeaderFlag_AuthenticData, "AD" },
1497 { kDNSHeaderFlag_CheckingDisabled, "CD" },
1498 };
1499
1500 OSStatus
1501 DNSMessageToString(
1502 const uint8_t * inMsgPtr,
1503 size_t inMsgLen,
1504 const DNSMessageToStringFlags inFlags,
1505 char ** outString )
1506 {
1507 OSStatus err;
1508 DataBuffer db;
1509 const DNSHeader * hdr;
1510 const uint8_t * ptr;
1511 const char * separator;
1512 char * rdataStr = NULL;
1513 const char * str;
1514 size_t len;
1515 uint32_t qCount, aCount, authCount, addCount, i, totalCount;
1516 uint8_t * name;
1517 const uint8_t * nameCur;
1518 uint8_t dbBuf[ 256 ];
1519 char nameStr[ kDNSServiceMaxDomainName ];
1520 uint8_t nameBuf1[ kDomainNameLengthMax ];
1521 uint8_t nameBuf2[ kDomainNameLengthMax ];
1522 const Boolean oneLine = ( inFlags & kDNSMessageToStringFlag_OneLine ) ? true : false;
1523 const Boolean isMDNS = ( inFlags & kDNSMessageToStringFlag_MDNS ) ? true : false;
1524 const Boolean rawRData = ( inFlags & kDNSMessageToStringFlag_RawRData ) ? true : false;
1525 const Boolean privacy = ( inFlags & kDNSMessageToStringFlag_Privacy ) ? true : false;
1526 const Boolean headerOnly = ( inFlags & kDNSMessageToStringFlag_HeaderOnly ) ? true : false;
1527 const Boolean bodyOnly = ( inFlags & kDNSMessageToStringFlag_BodyOnly ) ? true : false;
1528
1529 err = _DataBuffer_Init( &db, dbBuf, sizeof( dbBuf ), SIZE_MAX );
1530 require_noerr_quiet( err, exit );
1531 #define _AppendF( ... ) \
1532 do { err = _DataBuffer_AppendF( &db, __VA_ARGS__ ); require_noerr_quiet( err, exit ); } while( 0 )
1533
1534 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
1535
1536 hdr = (DNSHeader *) inMsgPtr;
1537 qCount = DNSHeaderGetQuestionCount( hdr );
1538 aCount = DNSHeaderGetAnswerCount( hdr );
1539 authCount = DNSHeaderGetAuthorityCount( hdr );
1540 addCount = DNSHeaderGetAdditionalCount( hdr );
1541 separator = "";
1542 if( !bodyOnly )
1543 {
1544 const unsigned int id = DNSHeaderGetID( hdr );
1545 const unsigned int flags = DNSHeaderGetFlags( hdr );
1546 const int opcode = DNSFlagsGetOpCode( flags );
1547 const int rcode = DNSFlagsGetRCode( flags );
1548
1549 if( oneLine )
1550 {
1551 _AppendF( "id: 0x%04X (%u), flags: 0x%04X (%c/",
1552 id, id, flags, ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q' );
1553 }
1554 else
1555 {
1556 _AppendF( "ID: 0x%04X (%u)\n", id, id );
1557 _AppendF( "Flags: 0x%04X %c/", flags, ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q' );
1558 }
1559 str = _DNSOpCodeToString( opcode );
1560 if( str ) _AppendF( "%s", str );
1561 else _AppendF( "OPCODE%d", opcode );
1562 for( i = 0; i < countof( kDNSHeaderFlagsDescs ); ++i )
1563 {
1564 const _DNSHeaderFlagDesc * const flagDesc = &kDNSHeaderFlagsDescs[ i ];
1565
1566 if( flags & flagDesc->flag ) _AppendF( ", %s", flagDesc->desc );
1567 }
1568 str = DNSRCodeToString( rcode );
1569 if( str ) _AppendF( ", %s", str );
1570 else _AppendF( ", RCODE%d", rcode );
1571 if( oneLine )
1572 {
1573 _AppendF( "), counts: %u/%u/%u/%u", qCount, aCount, authCount, addCount );
1574 separator = ", ";
1575 }
1576 else
1577 {
1578 _AppendF( "\n" );
1579 _AppendF( "Question count: %u\n", qCount );
1580 _AppendF( "Answer count: %u\n", aCount );
1581 _AppendF( "Authority count: %u\n", authCount );
1582 _AppendF( "Additional count: %u\n", addCount );
1583 }
1584 }
1585 if( headerOnly ) goto done;
1586
1587 ptr = (const uint8_t *) &hdr[ 1 ];
1588 name = nameBuf1;
1589 nameCur = NULL;
1590 for( i = 0; i < qCount; ++i )
1591 {
1592 uint16_t qtype, qclass;
1593 Boolean isQU;
1594
1595 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, name, &qtype, &qclass, &ptr );
1596 require_noerr_quiet( err, exit );
1597
1598 isQU = ( isMDNS && ( qclass & kMDNSClassUnicastResponseBit ) ) ? true : false;
1599 if( isMDNS ) qclass &= ~kMDNSClassUnicastResponseBit;
1600 if( oneLine )
1601 {
1602 _AppendF( "%s", separator );
1603 if( !nameCur || !DomainNameEqual( name, nameCur ) )
1604 {
1605 err = DomainNameToString( name, NULL, nameStr, NULL );
1606 require_noerr_quiet( err, exit );
1607
1608 if( privacy && _NameIsPrivate( nameStr ) ) _AppendF( "%~s ", nameStr );
1609 else _AppendF( "%s ", nameStr );
1610 nameCur = name;
1611 name = ( name == nameBuf1 ) ? nameBuf2 : nameBuf1;
1612 *name = 0;
1613 }
1614 if( qclass == kDNSClassType_IN ) _AppendF( "IN" );
1615 else _AppendF( "CLASS%u", qclass );
1616 if( isMDNS ) _AppendF( " %s", isQU ? "QU" : "QM" );
1617 str = DNSRecordTypeValueToString( qtype );
1618 if( str ) _AppendF( " %s?", str );
1619 else _AppendF( " TYPE%u?", qtype );
1620 separator = ", ";
1621 }
1622 else
1623 {
1624 if( i == 0 ) _AppendF( "\nQUESTION SECTION\n" );
1625 err = DomainNameToString( name, NULL, nameStr, NULL );
1626 require_noerr_quiet( err, exit );
1627
1628 if( privacy && _NameIsPrivate( nameStr ) ) _AppendF( "%~-30s", nameStr );
1629 else _AppendF( "%-30s", nameStr );
1630 _AppendF( " %2s", isMDNS ? ( isQU ? "QU" : "QM" ) : "" );
1631 if( qclass == kDNSClassType_IN ) _AppendF( " IN" );
1632 else _AppendF( " CLASS%u", qclass );
1633 str = DNSRecordTypeValueToString( qtype );
1634 if( str ) _AppendF( " %-5s\n", str );
1635 else _AppendF( " TYPE%u\n", qtype );
1636 }
1637 }
1638 totalCount = aCount + authCount + addCount;
1639 for( i = 0; i < totalCount; ++i )
1640 {
1641 const uint8_t * rdataPtr;
1642 size_t rdataLen;
1643 const char * typeStr;
1644 uint32_t ttl;
1645 uint16_t type, class;
1646 Boolean cacheFlush;
1647
1648 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, &rdataPtr, &rdataLen, &ptr );
1649 require_noerr_quiet( err, exit );
1650
1651 err = DomainNameToString( name, NULL, nameStr, NULL );
1652 require_noerr_quiet( err, exit );
1653
1654 cacheFlush = ( isMDNS && ( class & kMDNSClassCacheFlushBit ) ) ? true : false;
1655 if( isMDNS ) class &= ~kMDNSClassCacheFlushBit;
1656
1657 if( oneLine )
1658 {
1659 _AppendF( "%s", separator );
1660 if( !nameCur || !DomainNameEqual( name, nameCur ) )
1661 {
1662 err = DomainNameToString( name, NULL, nameStr, NULL );
1663 require_noerr_quiet( err, exit );
1664
1665 if( privacy && _NameIsPrivate( nameStr ) ) _AppendF( "%~s ", nameStr );
1666 else _AppendF( "%s ", nameStr );
1667 nameCur = name;
1668 name = ( name == nameBuf1 ) ? nameBuf2 : nameBuf1;
1669 *name = 0;
1670 }
1671 if( type == kDNSRecordType_OPT )
1672 {
1673 if( cacheFlush ) _AppendF( "CF " );
1674 _AppendF( "OPT %u", class );
1675 if( ttl == 0 ) _AppendF( " 0" );
1676 else _AppendF( " 0x%08X", ttl );
1677 }
1678 else
1679 {
1680 _AppendF( "%u", ttl );
1681 if( cacheFlush ) _AppendF( " CF" );
1682 if( class == kDNSClassType_IN ) _AppendF( " IN" );
1683 else _AppendF( " CLASS%u", class );
1684 typeStr = DNSRecordTypeValueToString( type );
1685 if( typeStr ) _AppendF( " %s", typeStr );
1686 else _AppendF( " TYPE%u", type );
1687 }
1688 separator = ", ";
1689 }
1690 else
1691 {
1692 if( ( aCount != 0 ) && ( i == 0 ) ) _AppendF( "\nANSWER SECTION\n" );
1693 else if( ( authCount != 0 ) && ( i == aCount ) ) _AppendF( "\nAUTHORITY SECTION\n" );
1694 else if( ( addCount != 0 ) && ( i == ( aCount + authCount ) ) ) _AppendF( "\nADDITIONAL SECTION\n" );
1695
1696 if( type == kDNSRecordType_OPT )
1697 {
1698 if( privacy && _NameIsPrivate( nameStr ) ) _AppendF( "%~s", nameStr );
1699 else _AppendF( "%s", nameStr );
1700 _AppendF( "%s OPT %u", cacheFlush ? " CF" : "", class );
1701 if( ttl == 0 ) _AppendF( " 0" );
1702 else _AppendF( " 0x%08X", ttl );
1703 }
1704 else
1705 {
1706 if( privacy ) _AppendF( "%~-42s", nameStr );
1707 else _AppendF( "%-42s", nameStr );
1708 _AppendF( " %6u %2s", ttl, cacheFlush ? "CF" : "" );
1709 if( class == kDNSClassType_IN ) _AppendF( " IN" );
1710 else _AppendF( " CLASS%u", class );
1711 typeStr = DNSRecordTypeValueToString( type );
1712 if( typeStr ) _AppendF( " %-5s", typeStr );
1713 else _AppendF( " TYPE%u", type );
1714 }
1715 }
1716 if( !rawRData ) DNSRecordDataToStringEx( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, privacy, &rdataStr );
1717 if( rdataStr )
1718 {
1719 _AppendF( " %s", rdataStr );
1720 ForgetMem( &rdataStr );
1721 }
1722 else
1723 {
1724 if( privacy ) _AppendF( " [%zu B]", rdataLen );
1725 else _AppendF( " %#H", rdataPtr, (int) rdataLen, (int) rdataLen );
1726 }
1727
1728 if( oneLine )
1729 {
1730 if( type == kDNSRecordType_CNAME )
1731 {
1732 if( DNSMessageExtractDomainName( inMsgPtr, inMsgLen, rdataPtr, name, NULL ) == kNoErr )
1733 {
1734 nameCur = name;
1735 name = ( name == nameBuf1 ) ? nameBuf2 : nameBuf1;
1736 }
1737 *name = 0;
1738 }
1739 }
1740 else
1741 {
1742 _AppendF( "\n" );
1743 }
1744 }
1745 #undef _AppendF
1746 done:
1747 err = _DataBuffer_Append( &db, "", 1 ); // NUL terminator.
1748 require_noerr_quiet( err, exit );
1749
1750 err = _DataBuffer_Detach( &db, (uint8_t **) outString, &len );
1751 require_noerr_quiet( err, exit );
1752
1753 exit:
1754 FreeNullSafe( rdataStr );
1755 _DataBuffer_Free( &db );
1756 return( err );
1757 }
1758
1759 // See <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5>.
1760
1761 static const char * _DNSOpCodeToString( int inOpCode )
1762 {
1763 const char * string;
1764
1765 if( ( inOpCode >= 0 ) && ( inOpCode <= 6 ) )
1766 {
1767 static const char * const sNames[] =
1768 {
1769 "Query", // 0
1770 "IQuery", // 1
1771 "Status", // 2
1772 NULL, // 3 (Unassigned)
1773 "Notify", // 4
1774 "Update", // 5
1775 "DSO", // 6
1776 };
1777 string = sNames[ inOpCode ];
1778 }
1779 else
1780 {
1781 string = NULL;
1782 }
1783 return( string );
1784 }
1785
1786 //===========================================================================================================================
1787
1788 static OSStatus
1789 _DNSRecordDataAppendTypeBitMap(
1790 DataBuffer * inDB,
1791 const uint8_t * inPtr,
1792 const uint8_t * inEnd,
1793 const uint8_t ** outPtr );
1794
1795 OSStatus
1796 DNSRecordDataToStringEx(
1797 const void * const inRDataPtr,
1798 const size_t inRDataLen,
1799 const int inRecordType,
1800 const void * const inMsgPtr,
1801 const size_t inMsgLen,
1802 const Boolean inPrivacy,
1803 char ** const outString )
1804 {
1805 OSStatus err;
1806 const uint8_t * const rdataPtr = (uint8_t *) inRDataPtr;
1807 const uint8_t * const rdataEnd = rdataPtr + inRDataLen;
1808 const void * const msgPtr = inMsgPtr;
1809 const uint8_t * ptr;
1810 DataBuffer db;
1811 size_t len;
1812 uint8_t dbBuf[ 256 ];
1813 char domainNameStr[ kDNSServiceMaxDomainName ];
1814
1815 err = _DataBuffer_Init( &db, dbBuf, sizeof( dbBuf ), SIZE_MAX );
1816 require_noerr_quiet( err, exit );
1817
1818 // A Record
1819
1820 if( inRecordType == kDNSRecordType_A )
1821 {
1822 require_action_quiet( inRDataLen == 4, exit, err = kMalformedErr );
1823
1824 err = _AppendIPv4Address( &db, NULL, rdataPtr, inPrivacy );
1825 require_noerr_quiet( err, exit );
1826 }
1827
1828 // AAAA Record
1829
1830 else if( inRecordType == kDNSRecordType_AAAA )
1831 {
1832 require_action_quiet( inRDataLen == 16, exit, err = kMalformedErr );
1833
1834 err = _AppendIPv6Address( &db, NULL, rdataPtr, inPrivacy );
1835 require_noerr_quiet( err, exit );
1836 }
1837
1838 // PTR, CNAME, or NS Record
1839
1840 else if( ( inRecordType == kDNSRecordType_PTR ) ||
1841 ( inRecordType == kDNSRecordType_CNAME ) ||
1842 ( inRecordType == kDNSRecordType_NS ) )
1843 {
1844 if( msgPtr )
1845 {
1846 err = DNSMessageExtractDomainNameString( msgPtr, inMsgLen, rdataPtr, domainNameStr, NULL );
1847 require_noerr_quiet( err, exit );
1848 }
1849 else
1850 {
1851 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, NULL );
1852 require_noerr_quiet( err, exit );
1853 }
1854 err = _AppendDomainNameString( &db, inPrivacy, domainNameStr );
1855 require_noerr_quiet( err, exit );
1856 }
1857
1858 // SRV Record
1859
1860 else if( inRecordType == kDNSRecordType_SRV )
1861 {
1862 const dns_fixed_fields_srv * fields;
1863 const uint8_t * target;
1864
1865 require_action_quiet( inRDataLen > sizeof( dns_fixed_fields_srv ), exit, err = kMalformedErr );
1866
1867 fields = (const dns_fixed_fields_srv *) rdataPtr;
1868 target = (const uint8_t *) &fields[ 1 ];
1869 if( msgPtr )
1870 {
1871 err = DNSMessageExtractDomainNameString( msgPtr, inMsgLen, target, domainNameStr, NULL );
1872 require_noerr_quiet( err, exit );
1873 }
1874 else
1875 {
1876 err = DomainNameToString( target, rdataEnd, domainNameStr, NULL );
1877 require_noerr_quiet( err, exit );
1878 }
1879 err = _DataBuffer_AppendF( &db, "%u %u %u ",
1880 dns_fixed_fields_srv_get_priority( fields ),
1881 dns_fixed_fields_srv_get_weight( fields ),
1882 dns_fixed_fields_srv_get_port( fields ) );
1883 require_noerr_quiet( err, exit );
1884
1885 err = _AppendDomainNameString( &db, inPrivacy, domainNameStr );
1886 require_noerr_quiet( err, exit );
1887 }
1888
1889 // TXT or HINFO Record
1890 // See <https://tools.ietf.org/html/rfc1035#section-3.3.14> and <https://tools.ietf.org/html/rfc1035#section-3.3.2>.
1891
1892 else if( ( inRecordType == kDNSRecordType_TXT ) || ( inRecordType == kDNSRecordType_HINFO ) )
1893 {
1894 require_action_quiet( inRDataLen > 0, exit, err = kMalformedErr );
1895
1896 if( inPrivacy )
1897 {
1898 err = _DataBuffer_AppendF( &db, "[%zu B]", inRDataLen );
1899 require_noerr_quiet( err, exit );
1900 }
1901 else
1902 {
1903 if( inRDataLen == 1 )
1904 {
1905 err = _DataBuffer_AppendF( &db, "%#H", rdataPtr, (int) inRDataLen, (int) inRDataLen );
1906 require_noerr_quiet( err, exit );
1907 }
1908 else
1909 {
1910 err = _DataBuffer_AppendF( &db, "%#{txt}", rdataPtr, (size_t) inRDataLen );
1911 require_noerr_quiet( err, exit );
1912 }
1913 }
1914 }
1915
1916 // SOA Record
1917
1918 else if( inRecordType == kDNSRecordType_SOA )
1919 {
1920 const dns_fixed_fields_soa * fields;
1921
1922 // Get MNAME.
1923
1924 if( msgPtr )
1925 {
1926 err = DNSMessageExtractDomainNameString( msgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
1927 require_noerr_quiet( err, exit );
1928 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
1929 }
1930 else
1931 {
1932 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
1933 require_noerr_quiet( err, exit );
1934 }
1935 err = _AppendDomainNameString( &db, inPrivacy, domainNameStr );
1936 require_noerr_quiet( err, exit );
1937
1938 // Get RNAME.
1939
1940 if( msgPtr )
1941 {
1942 err = DNSMessageExtractDomainNameString( msgPtr, inMsgLen, ptr, domainNameStr, &ptr );
1943 require_noerr_quiet( err, exit );
1944 }
1945 else
1946 {
1947 err = DomainNameToString( ptr, rdataEnd, domainNameStr, &ptr );
1948 require_noerr_quiet( err, exit );
1949 }
1950 err = _AppendDomainNameStringEx( &db, " ", inPrivacy, domainNameStr );
1951 require_noerr_quiet( err, exit );
1952
1953 require_action_quiet( ( rdataEnd - ptr ) == sizeof( dns_fixed_fields_soa ), exit, err = kMalformedErr );
1954
1955 fields = (const dns_fixed_fields_soa *) ptr;
1956 err = _DataBuffer_AppendF( &db, " %u %u %u %u %u",
1957 dns_fixed_fields_soa_get_serial( fields ),
1958 dns_fixed_fields_soa_get_refresh( fields ),
1959 dns_fixed_fields_soa_get_retry( fields ),
1960 dns_fixed_fields_soa_get_expire( fields ),
1961 dns_fixed_fields_soa_get_minimum( fields ) );
1962 require_noerr_quiet( err, exit );
1963 }
1964
1965 // NSEC Record
1966
1967 else if( inRecordType == kDNSRecordType_NSEC )
1968 {
1969 if( msgPtr )
1970 {
1971 err = DNSMessageExtractDomainNameString( msgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
1972 require_noerr_quiet( err, exit );
1973 }
1974 else
1975 {
1976 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
1977 require_noerr_quiet( err, exit );
1978 }
1979 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
1980
1981 err = _AppendDomainNameString( &db, inPrivacy, domainNameStr );
1982 require_noerr_quiet( err, exit );
1983
1984 err = _DNSRecordDataAppendTypeBitMap( &db, ptr, rdataEnd, NULL );
1985 require_noerr_quiet( err, exit );
1986 }
1987
1988 // MX Record
1989
1990 else if( inRecordType == kDNSRecordType_MX )
1991 {
1992 uint16_t preference;
1993 const uint8_t * exchange;
1994
1995 require_action_quiet( ( rdataEnd - rdataPtr ) > 2, exit, err = kMalformedErr );
1996
1997 preference = ReadBig16( rdataPtr );
1998 exchange = &rdataPtr[ 2 ];
1999
2000 if( msgPtr )
2001 {
2002 err = DNSMessageExtractDomainNameString( msgPtr, inMsgLen, exchange, domainNameStr, NULL );
2003 require_noerr_quiet( err, exit );
2004 }
2005 else
2006 {
2007 err = DomainNameToString( exchange, rdataEnd, domainNameStr, NULL );
2008 require_noerr_quiet( err, exit );
2009 }
2010 err = _DataBuffer_AppendF( &db, "%u", preference );
2011 require_noerr_quiet( err, exit );
2012
2013 err = _AppendDomainNameStringEx( &db, " ", inPrivacy, domainNameStr );
2014 require_noerr_quiet( err, exit );
2015 }
2016
2017 // DNSKEY Record (see <https://tools.ietf.org/html/rfc4034#section-2.2>)
2018
2019 else if( inRecordType == kDNSRecordType_DNSKEY )
2020 {
2021 const dns_fixed_fields_dnskey * fields;
2022 char * publicKeyBase64;
2023
2024 require_action_quiet( ( (size_t)( rdataEnd - rdataPtr ) ) > sizeof( *fields ), exit, err = kMalformedErr );
2025
2026 fields = (const dns_fixed_fields_dnskey *) rdataPtr;
2027 err = _DataBuffer_AppendF( &db, "%u %u %u",
2028 dns_fixed_fields_dnskey_get_flags( fields ),
2029 dns_fixed_fields_dnskey_get_protocol( fields ),
2030 dns_fixed_fields_dnskey_get_algorithm( fields ) );
2031 require_noerr_quiet( err, exit );
2032
2033 // Public Key
2034
2035 ptr = (const uint8_t *) &fields[ 1 ];
2036 publicKeyBase64 = NULL;
2037 err = _Base64EncodeCopyEx( ptr, (size_t)( rdataEnd - ptr ), kBase64Flags_None, &publicKeyBase64, NULL );
2038 require_noerr_quiet( err, exit );
2039
2040 err = _DataBuffer_AppendF( &db, " %s", publicKeyBase64 );
2041 ForgetMem( &publicKeyBase64 );
2042 require_noerr_quiet( err, exit );
2043 }
2044
2045 // RRSIG Record (see <https://tools.ietf.org/html/rfc4034#section-3.2>)
2046
2047 else if( inRecordType == kDNSRecordType_RRSIG )
2048 {
2049 const dns_fixed_fields_rrsig * fields;
2050 const char * typeStr;
2051 int year, month, day, hour, minute, second;
2052 char * signatureBase64;
2053
2054 require_action_quiet( ( (size_t)( rdataEnd - rdataPtr ) ) > sizeof( *fields ), exit, err = kMalformedErr );
2055
2056 fields = (const dns_fixed_fields_rrsig *) rdataPtr;
2057 typeStr = DNSRecordTypeValueToString( dns_fixed_fields_rrsig_get_type_covered( fields ) );
2058 if( typeStr )
2059 {
2060 err = _DataBuffer_AppendF( &db, "%s", typeStr );
2061 require_noerr_quiet( err, exit );
2062 }
2063 else
2064 {
2065 err = _DataBuffer_AppendF( &db, "TYPE%u", dns_fixed_fields_rrsig_get_type_covered( fields ) );
2066 require_noerr_quiet( err, exit );
2067 }
2068 err = _DataBuffer_AppendF( &db, " %u %u %u",
2069 dns_fixed_fields_rrsig_get_algorithm( fields ),
2070 dns_fixed_fields_rrsig_get_labels( fields ),
2071 dns_fixed_fields_rrsig_get_original_ttl( fields ) );
2072 require_noerr_quiet( err, exit );
2073
2074 year = 0;
2075 month = 0;
2076 day = 0;
2077 hour = 0;
2078 minute = 0;
2079 second = 0;
2080 err = _SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch ) * kSecondsPerDay ) +
2081 dns_fixed_fields_rrsig_get_signature_expiration( fields ), &year, &month, &day, &hour, &minute, &second );
2082 require_noerr_quiet( err, exit );
2083
2084 err = _DataBuffer_AppendF( &db, " %u%02u%02u%02u%02u%02u", year, month, day, hour, minute, second );
2085 require_noerr_quiet( err, exit );
2086
2087 err = _SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch ) * kSecondsPerDay ) +
2088 dns_fixed_fields_rrsig_get_signature_inception( fields ), &year, &month, &day, &hour, &minute, &second );
2089 require_noerr_quiet( err, exit );
2090
2091 err = _DataBuffer_AppendF( &db, " %u%02u%02u%02u%02u%02u", year, month, day, hour, minute, second );
2092 require_noerr_quiet( err, exit );
2093
2094 err = _DataBuffer_AppendF( &db, " %u", dns_fixed_fields_rrsig_get_key_tag( fields ) );
2095 require_noerr_quiet( err, exit );
2096
2097 // Signer's name
2098
2099 ptr = (const uint8_t *) &fields[ 1 ];
2100 err = DomainNameToString( ptr, rdataEnd, domainNameStr, &ptr );
2101 require_noerr_quiet( err, exit );
2102
2103 err = _AppendDomainNameStringEx( &db, " ", inPrivacy, domainNameStr );
2104 require_noerr_quiet( err, exit );
2105
2106 // Signature
2107
2108 signatureBase64 = NULL;
2109 err = _Base64EncodeCopyEx( ptr, (size_t)( rdataEnd - ptr ), kBase64Flags_None, &signatureBase64, NULL );
2110 require_noerr_quiet( err, exit );
2111
2112 err = _DataBuffer_AppendF( &db, " %s", signatureBase64 );
2113 FreeNullSafe( signatureBase64 );
2114 require_noerr_quiet( err, exit );
2115 }
2116
2117 // NSEC3 Record (see <https://tools.ietf.org/html/rfc5155#section-3.3>)
2118
2119 else if( inRecordType == kDNSRecordType_NSEC3 )
2120 {
2121 const dns_fixed_fields_nsec3 * fields;
2122 size_t saltLen, hashLen, rem;
2123 const uint8_t * hashEnd;
2124
2125 require_action_quiet( ( (size_t)( rdataEnd - rdataPtr ) ) > sizeof( *fields ), exit, err = kMalformedErr );
2126
2127 fields = (const dns_fixed_fields_nsec3 *) rdataPtr;
2128 err = _DataBuffer_AppendF( &db, "%u %u %u",
2129 dns_fixed_fields_nsec3_get_hash_alg( fields ),
2130 dns_fixed_fields_nsec3_get_flags( fields ),
2131 dns_fixed_fields_nsec3_get_iterations( fields ) );
2132 require_noerr_quiet( err, exit );
2133
2134 ptr = (const uint8_t *) &fields[ 1 ];
2135 require_action_quiet( ( rdataEnd - ptr ) >= 1, exit, err = kMalformedErr );
2136
2137 saltLen = *ptr++;
2138 require_action_quiet( ( (size_t)( rdataEnd - ptr ) ) >= saltLen, exit, err = kMalformedErr );
2139 err = _DataBuffer_AppendF( &db, " %.4H", ptr, (int) saltLen, (int) saltLen );
2140 require_noerr_quiet( err, exit );
2141
2142 ptr += saltLen;
2143 require_action_quiet( ( rdataEnd - ptr ) >= 1, exit, err = kMalformedErr );
2144
2145 hashLen = *ptr++;
2146 require_action_quiet( ( (size_t)( rdataEnd - ptr ) ) >= hashLen, exit, err = kMalformedErr );
2147
2148 if( hashLen > 0 )
2149 {
2150 err = _DataBuffer_AppendF( &db, " " );
2151 require_noerr_quiet( err, exit );
2152 }
2153
2154 // Unpadded Base 32 Encoding with Extended Hex Alphabet (see <https://tools.ietf.org/html/rfc4648#section-7>)
2155 // A full quantum is 40 bits, i.e., five concatenated 8-bit input groups are treated as eight concatenated 5-bit
2156 // groups.
2157
2158 hashEnd = ptr + hashLen;
2159 while( ( rem = (size_t)( hashEnd - ptr ) ) > 0 )
2160 {
2161 uint64_t quantum;
2162 size_t encodedLen;
2163 char encodedBuf[ 8 ];
2164 static const char kBase32ExtendedHex[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
2165
2166 check_compile_time_code( sizeof_string( kBase32ExtendedHex ) == 32 );
2167
2168 quantum = 0;
2169 switch( rem )
2170 {
2171 default:
2172 case 5: quantum |= (uint64_t)ptr[ 4 ]; // Bits 32 - 39
2173 case 4: quantum |= ( (uint64_t)ptr[ 3 ] ) << 8; // Bits 24 - 31
2174 case 3: quantum |= ( (uint64_t)ptr[ 2 ] ) << 16; // Bits 16 - 23
2175 case 2: quantum |= ( (uint64_t)ptr[ 1 ] ) << 24; // Bits 8 - 15
2176 case 1: quantum |= ( (uint64_t)ptr[ 0 ] ) << 32; // Bits 0 - 7
2177 }
2178 ptr += ( ( rem > 5 ) ? 5 : rem );
2179
2180 encodedLen = 0;
2181 switch( rem )
2182 {
2183 default:
2184 case 5:
2185 encodedBuf[ 7 ] = kBase32ExtendedHex[ quantum & 0x1F ]; // Bits 35 - 39
2186 encodedLen = 8;
2187
2188 case 4:
2189 encodedBuf[ 6 ] = kBase32ExtendedHex[ ( quantum >> 5 ) & 0x1F ]; // Bits 30 - 34
2190 encodedBuf[ 5 ] = kBase32ExtendedHex[ ( quantum >> 10 ) & 0x1F ]; // Bits 25 - 29
2191 if( encodedLen == 0 ) encodedLen = 7;
2192
2193 case 3:
2194 encodedBuf[ 4 ] = kBase32ExtendedHex[ ( quantum >> 15 ) & 0x1F ]; // Bits 20 - 24
2195 if( encodedLen == 0 ) encodedLen = 5;
2196
2197 case 2:
2198 encodedBuf[ 3 ] = kBase32ExtendedHex[ ( quantum >> 20 ) & 0x1F ]; // Bits 15 - 19
2199 encodedBuf[ 2 ] = kBase32ExtendedHex[ ( quantum >> 25 ) & 0x1F ]; // Bits 10 - 14
2200 if( encodedLen == 0 ) encodedLen = 4;
2201
2202 case 1:
2203 encodedBuf[ 1 ] = kBase32ExtendedHex[ ( quantum >> 30 ) & 0x1F ]; // Bits 5 - 9
2204 encodedBuf[ 0 ] = kBase32ExtendedHex[ ( quantum >> 35 ) & 0x1F ]; // Bits 0 - 4
2205 if( encodedLen == 0 ) encodedLen = 2;
2206 }
2207 err = _DataBuffer_Append( &db, encodedBuf, encodedLen );
2208 require_noerr_quiet( err, exit );
2209 }
2210 err = _DNSRecordDataAppendTypeBitMap( &db, ptr, rdataEnd, NULL );
2211 require_noerr_quiet( err, exit );
2212 }
2213
2214 // DS Record (see <https://tools.ietf.org/html/rfc4034#section-5.3>)
2215
2216 else if( inRecordType == kDNSRecordType_DS )
2217 {
2218 const dns_fixed_fields_ds * fields;
2219 const uint8_t * digestPtr;
2220 size_t digestLen;
2221
2222 require_action_quiet( ( (size_t)( rdataEnd - rdataPtr ) ) >= sizeof( *fields ), exit, err = kMalformedErr );
2223
2224 fields = (const dns_fixed_fields_ds *) rdataPtr;
2225 err = _DataBuffer_AppendF( &db, "%u %u %u",
2226 dns_fixed_fields_ds_get_key_tag( fields ),
2227 dns_fixed_fields_ds_get_algorithm( fields ),
2228 dns_fixed_fields_ds_get_digest_type( fields ) );
2229 require_noerr_quiet( err, exit );
2230
2231 digestPtr = (const uint8_t *) &fields[ 1 ];
2232 digestLen = (size_t)( rdataEnd - digestPtr );
2233 if( digestLen > 0 )
2234 {
2235 err = _DataBuffer_AppendF( &db, " %.4H", digestPtr, (int) digestLen, (int) digestLen );
2236 require_noerr_quiet( err, exit );
2237 }
2238 }
2239
2240 // HTTPS or SVCB Record
2241
2242 else if( ( inRecordType == kDNSRecordType_HTTPS ) || ( inRecordType == kDNSRecordType_SVCB ) )
2243 {
2244 err = _AppendSVCBRDataString( &db, rdataPtr, rdataEnd, inPrivacy );
2245 require_noerr_quiet( err, exit );
2246 }
2247
2248 // OPT Record (see <https://tools.ietf.org/html/rfc6891#section-6.1.2>)
2249
2250 else if( inRecordType == kDNSRecordType_OPT )
2251 {
2252 err = _AppendOPTRDataString( &db, rdataPtr, rdataEnd, inPrivacy );
2253 require_noerr_quiet( err, exit );
2254 }
2255
2256 // Unhandled record type
2257
2258 else
2259 {
2260 err = kNotHandledErr;
2261 goto exit;
2262 }
2263 err = _DataBuffer_Append( &db, "", 1 ); // NUL terminator.
2264 require_noerr_quiet( err, exit );
2265
2266 err = _DataBuffer_Detach( &db, (uint8_t **) outString, &len );
2267 require_noerr_quiet( err, exit );
2268
2269 exit:
2270 _DataBuffer_Free( &db );
2271 return( err );
2272 }
2273
2274 static OSStatus
2275 _DNSRecordDataAppendTypeBitMap(
2276 DataBuffer * inDB,
2277 const uint8_t * inPtr,
2278 const uint8_t * inEnd,
2279 const uint8_t ** outPtr )
2280 {
2281 OSStatus err;
2282 const uint8_t * ptr = inPtr;
2283 int bitmapLen;
2284
2285 while( ( inEnd - ptr ) > 0 )
2286 {
2287 int windowBlock, i;
2288
2289 require_action_quiet( ( inEnd - ptr ) > 2, exit, err = kMalformedErr );
2290
2291 windowBlock = *ptr++;
2292 bitmapLen = *ptr++;
2293 require_action_quiet( ( bitmapLen >= 1 ) && ( bitmapLen <= 32 ) , exit, err = kMalformedErr );
2294 require_action_quiet( ( inEnd - ptr ) >= bitmapLen, exit, err = kMalformedErr );
2295
2296 for( i = 0; i < BitArray_MaxBits( bitmapLen ); ++i )
2297 {
2298 const int windowBase = windowBlock * 256;
2299
2300 if( BitArray_GetBit( ptr, bitmapLen, i ) )
2301 {
2302 int recordType;
2303 const char * typeStr;
2304 char typeBuf[ 32 ];
2305
2306 recordType = windowBase + i;
2307 typeStr = DNSRecordTypeValueToString( recordType );
2308 if( !typeStr )
2309 {
2310 snprintf( typeBuf, sizeof( typeBuf ), "TYPE%u", recordType );
2311 typeStr = typeBuf;
2312 }
2313 err = _DataBuffer_AppendF( inDB, " %s", typeStr );
2314 require_noerr_quiet( err, exit );
2315 }
2316 }
2317 ptr += bitmapLen;
2318 }
2319 if( outPtr ) *outPtr = ptr;
2320 err = kNoErr;
2321
2322 exit:
2323 return( err );
2324 }
2325
2326 //===========================================================================================================================
2327 // Based on reference implementation from <https://tools.ietf.org/html/rfc4034#appendix-B>.
2328
2329 uint16_t DNSComputeDNSKeyTag( const void *inRDataPtr, size_t inRDataLen )
2330 {
2331 const uint8_t * const rdataPtr = (const uint8_t *) inRDataPtr;
2332 uint32_t accumulator;
2333 size_t i;
2334
2335 accumulator = 0;
2336 for( i = 0; i < inRDataLen; ++i )
2337 {
2338 accumulator += ( i & 1 ) ? rdataPtr[ i ] : (uint32_t)( rdataPtr[ i ] << 8 );
2339 }
2340 accumulator += ( accumulator >> 16 ) & UINT32_C( 0xFFFF );
2341 return( accumulator & UINT32_C( 0xFFFF ) );
2342 }
2343
2344 //===========================================================================================================================
2345
2346 int DNSMessagePrintObfuscatedString( char *inBufPtr, size_t inBufLen, const char *inString )
2347 {
2348 if( _NameIsPrivate( inString ) )
2349 {
2350 return( _SNPrintF( inBufPtr, inBufLen, "%~s", inString ) );
2351 }
2352 else
2353 {
2354 return( _SNPrintF( inBufPtr, inBufLen, "%s", inString ) );
2355 }
2356 }
2357
2358 //===========================================================================================================================
2359
2360 int DNSMessagePrintObfuscatedIPv4Address( char *inBufPtr, size_t inBufLen, const uint32_t inAddr )
2361 {
2362 uint8_t addrBytes[ 4 ];
2363
2364 WriteBig32( addrBytes, inAddr );
2365 if( !_IPv4AddressIsWhitelisted( addrBytes ) )
2366 {
2367 return( _DNSMessagePrintObfuscatedIPAddress( inBufPtr, inBufLen, addrBytes, sizeof( addrBytes ) ) );
2368 }
2369 else
2370 {
2371 return( _SNPrintF( inBufPtr, inBufLen, "%#.4a", &inAddr ) );
2372 }
2373 }
2374
2375 //===========================================================================================================================
2376
2377 int DNSMessagePrintObfuscatedIPv6Address( char *inBufPtr, size_t inBufLen, const uint8_t inAddr[ STATIC_PARAM 16 ] )
2378 {
2379 if( !_IPv6AddressIsWhitelisted( inAddr ) )
2380 {
2381 return( _DNSMessagePrintObfuscatedIPAddress( inBufPtr, inBufLen, inAddr, 16 ) );
2382 }
2383 else
2384 {
2385 return( _SNPrintF( inBufPtr, inBufLen, "%.16a", inAddr ) );
2386 }
2387 }
2388
2389 //===========================================================================================================================
2390 // MARK: - Helper Functions
2391
2392 static Boolean _NameIsPrivate( const char * const inDomainNameStr )
2393 {
2394 if( strcasecmp( inDomainNameStr, "." ) == 0 ) return( false );
2395 if( strcasecmp( inDomainNameStr, "ipv4only.arpa." ) == 0 ) return( false );
2396 return( true );
2397 }
2398
2399 //===========================================================================================================================
2400
2401 static OSStatus
2402 _AppendDomainNameString(
2403 DataBuffer * const inDB,
2404 const Boolean inPrivacy,
2405 const char * const inDomainNameStr )
2406 {
2407 return( _AppendDomainNameStringEx( inDB, NULL, inPrivacy, inDomainNameStr ) );
2408 }
2409
2410 //===========================================================================================================================
2411
2412 static OSStatus
2413 _AppendDomainNameStringEx(
2414 DataBuffer * const inDB,
2415 const char * const inSeparator,
2416 const Boolean inPrivacy,
2417 const char * const inDomainNameStr )
2418 {
2419 OSStatus err;
2420 const char * const sep = inSeparator ? inSeparator : "";
2421
2422 if( inPrivacy && _NameIsPrivate( inDomainNameStr ) )
2423 {
2424 err = _DataBuffer_AppendF( inDB, "%s%~s", sep, inDomainNameStr );
2425 require_noerr_quiet( err, exit );
2426 }
2427 else
2428 {
2429 err = _DataBuffer_AppendF( inDB, "%s%s", sep, inDomainNameStr );
2430 require_noerr_quiet( err, exit );
2431 }
2432
2433 exit:
2434 return( err );
2435 }
2436
2437 //===========================================================================================================================
2438
2439 static OSStatus
2440 _AppendOPTRDataString(
2441 DataBuffer * const inDB,
2442 const uint8_t * const inRDataPtr,
2443 const uint8_t * const inRDataEnd,
2444 const Boolean inPrivacy )
2445 {
2446 OSStatus err;
2447 const uint8_t * ptr;
2448 const char * sep;
2449
2450 ptr = inRDataPtr;
2451 require_action_quiet( ptr <= inRDataEnd, exit, err = kRangeErr );
2452
2453 sep = "";
2454 while( ptr < inRDataEnd )
2455 {
2456 const dns_fixed_fields_option * fields;
2457 const uint8_t * data;
2458 unsigned int code, length;
2459
2460 require_action_quiet( ( (size_t)( inRDataEnd - ptr ) ) >= sizeof( *fields ), exit, err = kUnderrunErr );
2461 fields = (const dns_fixed_fields_option *) ptr;
2462 code = dns_fixed_fields_option_get_code( fields );
2463 length = dns_fixed_fields_option_get_length( fields );
2464 ptr = (const uint8_t *) &fields[ 1 ];
2465
2466 require_action_quiet( ( (size_t)( inRDataEnd - ptr ) ) >= length, exit, err = kUnderrunErr );
2467 data = ptr;
2468 ptr += length;
2469
2470 err = _DataBuffer_AppendF( inDB, "%s{", sep );
2471 require_noerr_quiet( err, exit );
2472
2473 if( code == kDNSEDNS0OptionCode_Padding )
2474 {
2475 err = _DataBuffer_AppendF( inDB, "Padding" );
2476 require_noerr_quiet( err, exit );
2477 }
2478 else
2479 {
2480 err = _DataBuffer_AppendF( inDB, "CODE%u", code );
2481 require_noerr_quiet( err, exit );
2482 }
2483 err = _DataBuffer_AppendF( inDB, ", " );
2484 require_noerr_quiet( err, exit );
2485
2486 if( inPrivacy )
2487 {
2488 err = _DataBuffer_AppendF( inDB, "[%u B]", length );
2489 require_noerr_quiet( err, exit );
2490 }
2491 else
2492 {
2493 if( ( code == kDNSEDNS0OptionCode_Padding ) && _MemIsAllZeros( data, length ) )
2494 {
2495 err = _DataBuffer_AppendF( inDB, "<%u zero bytes>", length );
2496 require_noerr_quiet( err, exit );
2497 }
2498 else
2499 {
2500 err = _DataBuffer_AppendF( inDB, "'%H'", data, (int) length, (int) length );
2501 require_noerr_quiet( err, exit );
2502 }
2503 }
2504 err = _DataBuffer_AppendF( inDB, "}" );
2505 require_noerr_quiet( err, exit );
2506
2507 sep = ", ";
2508 }
2509 err = kNoErr;
2510
2511 exit:
2512 return( err );
2513 }
2514
2515 //===========================================================================================================================
2516
2517 static const char * _DNSSVCBKeyToString( int inValue );
2518
2519 static OSStatus
2520 _AppendSVCBRDataString(
2521 DataBuffer * const inDB,
2522 const uint8_t * const inRDataPtr,
2523 const uint8_t * const inRDataEnd,
2524 const Boolean inPrivacy )
2525 {
2526 OSStatus err;
2527 const uint8_t * ptr;
2528 const char * sep;
2529 const dns_fixed_fields_svcb * fields;
2530 char domainNameStr[ kDNSServiceMaxDomainName ];
2531
2532 ptr = inRDataPtr;
2533 require_action_quiet( ptr <= inRDataEnd, exit, err = kRangeErr );
2534 require_action_quiet( ( (size_t)( inRDataEnd - ptr ) ) >= sizeof( *fields ), exit, err = kMalformedErr );
2535
2536 // SvcFieldPriority
2537
2538 fields = (const dns_fixed_fields_svcb *) ptr;
2539 err = _DataBuffer_AppendF( inDB, "%u", dns_fixed_fields_svcb_get_priority( fields ) );
2540 require_noerr_quiet( err, exit );
2541
2542 // SvcDomainName
2543
2544 ptr = (const uint8_t *) &fields[ 1 ];
2545 err = DomainNameToString( ptr, inRDataEnd, domainNameStr, &ptr );
2546 require_noerr_quiet( err, exit );
2547
2548 err = _AppendDomainNameStringEx( inDB, " ", inPrivacy, domainNameStr );
2549 require_noerr_quiet( err, exit );
2550
2551 // SvcFieldValue
2552 // Follows types for <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-00>
2553
2554 while( ptr < inRDataEnd )
2555 {
2556 const dns_fixed_fields_svcb_param * paramFields;
2557 const uint8_t * limit;
2558 const char * keyStr;
2559 int key;
2560 unsigned int valLen;
2561
2562 require_action_quiet( ( (size_t)( inRDataEnd - ptr ) ) >= sizeof( *paramFields ), exit, err = kUnderrunErr );
2563
2564 paramFields = (const dns_fixed_fields_svcb_param *) ptr;
2565 key = dns_fixed_fields_svcb_param_get_key( paramFields );
2566 valLen = dns_fixed_fields_svcb_param_get_value_length( paramFields );
2567
2568 keyStr = _DNSSVCBKeyToString( key );
2569 if( keyStr )
2570 {
2571 err = _DataBuffer_AppendF( inDB, " %s=\"", keyStr );
2572 require_noerr_quiet( err, exit );
2573 }
2574 else
2575 {
2576 err = _DataBuffer_AppendF( inDB, " key%u=\"", key );
2577 require_noerr_quiet( err, exit );
2578 }
2579 ptr = (const uint8_t *) &paramFields[ 1 ];
2580 require_action_quiet( ( (size_t)( inRDataEnd - ptr ) ) >= valLen, exit, err = kUnderrunErr );
2581
2582 switch( key )
2583 {
2584 case kDNSSVCParamKey_Mandatory:
2585 {
2586 // List of 16-bit keys
2587 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-6.5>.
2588
2589 require_action_quiet( ( valLen % 2 ) == 0, exit, err = kMalformedErr );
2590
2591 sep = NULL;
2592 limit = &ptr[ valLen ];
2593 while( ptr < limit )
2594 {
2595 int mandatoryKey;
2596 const char * mandatoryKeyStr;
2597
2598 mandatoryKey = ReadBig16( ptr );
2599 ptr += 2;
2600
2601 mandatoryKeyStr = _DNSSVCBKeyToString( mandatoryKey );
2602 if( sep )
2603 {
2604 err = _DataBuffer_AppendF( inDB, "%s", sep );
2605 require_noerr_quiet( err, exit );
2606 }
2607 if( mandatoryKeyStr )
2608 {
2609 err = _DataBuffer_AppendF( inDB, "%s", mandatoryKeyStr );
2610 require_noerr_quiet( err, exit );
2611 }
2612 else
2613 {
2614 err = _DataBuffer_AppendF( inDB, "key%u", mandatoryKey );
2615 require_noerr_quiet( err, exit );
2616 }
2617 sep = ",";
2618 }
2619 break;
2620 }
2621
2622 case kDNSSVCParamKey_ALPN:
2623 {
2624 // Length-prefixed ALPN protocol ID octet sequences
2625 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-6.1>.
2626
2627 sep = NULL;
2628 limit = &ptr[ valLen ];
2629 while( ptr < limit )
2630 {
2631 const uint8_t * alpnLimit;
2632 const unsigned int alpnLen = *ptr++;
2633
2634 require_action_quiet( ( (size_t)( limit - ptr ) ) >= alpnLen, exit, err = kMalformedErr );
2635
2636 if( sep )
2637 {
2638 err = _DataBuffer_AppendF( inDB, "%s", sep );
2639 require_noerr_quiet( err, exit );
2640 }
2641 for( alpnLimit = &ptr[ alpnLen ]; ptr < alpnLimit; ++ptr )
2642 {
2643 const int c = *ptr;
2644
2645 if( _isprint_ascii( c ) )
2646 {
2647 if( ( c == ',' ) || ( c == '\\' ) )
2648 {
2649 // Escape commas and backslashes.
2650
2651 err = _DataBuffer_AppendF( inDB, "\\%c", c );
2652 require_noerr_quiet( err, exit );
2653 }
2654 else
2655 {
2656 err = _DataBuffer_AppendF( inDB, "%c", c );
2657 require_noerr_quiet( err, exit );
2658 }
2659 }
2660 else
2661 {
2662 // Use a three digit decimal escape code (\DDD) for non-printable octets.
2663
2664 err = _DataBuffer_AppendF( inDB, "\\%03d", c );
2665 require_noerr_quiet( err, exit );
2666 }
2667 }
2668 sep = ",";
2669 }
2670 break;
2671 }
2672 case kDNSSVCParamKey_Port:
2673 {
2674 unsigned int port;
2675
2676 // 16-bit Integer
2677 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-6.2>.
2678
2679 require_action_quiet( valLen == 2, exit, err = kMalformedErr );
2680
2681 port = ReadBig16( ptr );
2682 ptr += valLen;
2683
2684 err = _DataBuffer_AppendF( inDB, "%u", port );
2685 require_noerr_quiet( err, exit );
2686 break;
2687 }
2688 case kDNSSVCParamKey_IPv4Hint:
2689 {
2690 // IPv4 address list
2691 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-6.4>.
2692
2693 require_action_quiet( ( valLen % 4 ) == 0, exit, err = kMalformedErr );
2694
2695 sep = "";
2696 for( limit = &ptr[ valLen ]; ptr < limit; ptr += 4 )
2697 {
2698 err = _AppendIPv4Address( inDB, sep, ptr, inPrivacy );
2699 require_noerr_quiet( err, exit );
2700 sep = ",";
2701 }
2702 break;
2703 }
2704 case kDNSSVCParamKey_IPv6Hint:
2705 {
2706 // IPv6 address list
2707 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-6.4>.
2708
2709 require_action_quiet( ( valLen % 16 ) == 0, exit, err = kMalformedErr );
2710
2711 sep = "";
2712 for( limit = &ptr[ valLen ]; ptr < limit; ptr += 16 )
2713 {
2714 err = _AppendIPv6Address( inDB, sep, ptr, inPrivacy );
2715 require_noerr_quiet( err, exit );
2716 sep = ",";
2717 }
2718 break;
2719 }
2720 default:
2721 {
2722 // Other keys.
2723 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-2.1.1>.
2724
2725 if( inPrivacy )
2726 {
2727 err = _DataBuffer_AppendF( inDB, "<%u redacted bytes>", valLen );
2728 require_noerr_quiet( err, exit );
2729 ptr += valLen;
2730 }
2731 else
2732 {
2733 for( limit = &ptr[ valLen ]; ptr < limit; ++ptr )
2734 {
2735 const int c = *ptr;
2736
2737 if( ( c >= 0x21 ) && ( c <= 0x7E ) )
2738 {
2739 // Visible characters are printed.
2740
2741 switch( c )
2742 {
2743 // Escape reserved characters.
2744
2745 case '"':
2746 case ';':
2747 case '(':
2748 case ')':
2749 case '\\':
2750 err = _DataBuffer_AppendF( inDB, "\\%c", c );
2751 require_noerr_quiet( err, exit );
2752 break;
2753
2754 default:
2755 err = _DataBuffer_AppendF( inDB, "%c", c );
2756 require_noerr_quiet( err, exit );
2757 break;
2758 }
2759 }
2760 else
2761 {
2762 // Invisible characters use a three digit decimal escape code (\DDD).
2763
2764 err = _DataBuffer_AppendF( inDB, "\\%03d", c );
2765 require_noerr_quiet( err, exit );
2766 }
2767 }
2768 }
2769 break;
2770 }
2771 }
2772 err = _DataBuffer_AppendF( inDB, "\"" );
2773 require_noerr_quiet( err, exit );
2774 }
2775
2776 exit:
2777 return( err );
2778 }
2779
2780 static const char * _DNSSVCBKeyToString( const int inValue )
2781 {
2782 switch( inValue )
2783 {
2784 case kDNSSVCParamKey_Mandatory: return( "mandatory" );
2785 case kDNSSVCParamKey_ALPN: return( "alpn" );
2786 case kDNSSVCParamKey_NoDefaultALPN: return( "no-default-alpn" );
2787 case kDNSSVCParamKey_Port: return( "port" );
2788 case kDNSSVCParamKey_IPv4Hint: return( "ipv4hint" );
2789 case kDNSSVCParamKey_ECHConfig: return( "echconfig" );
2790 case kDNSSVCParamKey_IPv6Hint: return( "ipv6hint" );
2791 case kDNSSVCParamKey_DOHURI: return( "dohuri" );
2792 default: return( NULL );
2793 }
2794 }
2795
2796 //===========================================================================================================================
2797
2798 static OSStatus
2799 _AppendIPv4Address(
2800 DataBuffer * const inDB,
2801 const char * const inSeparator,
2802 const uint8_t inAddrBytes[ STATIC_PARAM 4 ],
2803 const Boolean inPrivacy )
2804 {
2805 return( _AppendIPAddress( inDB, inSeparator, inAddrBytes, 4, inPrivacy && !_IPv4AddressIsWhitelisted( inAddrBytes ) ) );
2806 }
2807
2808 //===========================================================================================================================
2809
2810 static OSStatus
2811 _AppendIPv6Address(
2812 DataBuffer * const inDB,
2813 const char * const inSeparator,
2814 const uint8_t inAddrBytes[ STATIC_PARAM 16 ],
2815 Boolean inPrivacy )
2816 {
2817 return( _AppendIPAddress( inDB, inSeparator, inAddrBytes, 16, inPrivacy && !_IPv6AddressIsWhitelisted( inAddrBytes ) ) );
2818 }
2819
2820 //===========================================================================================================================
2821
2822 static OSStatus
2823 _AppendIPAddress(
2824 DataBuffer * inDB,
2825 const char * inSeparator,
2826 const uint8_t * inAddrPtr,
2827 int inAddrLen,
2828 Boolean inPrivacy )
2829 {
2830 OSStatus err;
2831 const char * const sep = inSeparator ? inSeparator : "";
2832
2833 require_action_quiet( ( inAddrLen == 4 ) || ( inAddrLen == 16 ), exit, err = kSizeErr );
2834
2835 if( inPrivacy )
2836 {
2837 int n;
2838 char tmpBuf[ ( 16 * 2 ) + 1 ];
2839
2840 n = _SNPrintF( tmpBuf, sizeof( tmpBuf ), "%.4H", inAddrPtr, (int) inAddrLen, (int) inAddrLen );
2841 require_action_quiet( n >= 0, exit, err = n );
2842
2843 err = _DataBuffer_AppendF( inDB, "%s%~s", sep, tmpBuf );
2844 require_noerr_quiet( err, exit );
2845 }
2846 else
2847 {
2848 err = _DataBuffer_AppendF( inDB, "%s%.*a", sep, inAddrLen, inAddrPtr );
2849 require_noerr_quiet( err, exit );
2850 }
2851
2852 exit:
2853 return( err );
2854 }
2855
2856 //===========================================================================================================================
2857
2858 static void * _GetCULibHandle( void )
2859 {
2860 static dispatch_once_t sOnce = 0;
2861 static void * sHandle = NULL;
2862
2863 dispatch_once( &sOnce,
2864 ^{
2865 sHandle = dlopen( "/System/Library/PrivateFrameworks/CoreUtils.framework/CoreUtils", RTLD_NOW );
2866 } );
2867 return( sHandle );
2868 }
2869
2870 //===========================================================================================================================
2871
2872 static Boolean _MemIsAllZeros( const uint8_t * const inMemPtr, const size_t inMemLen )
2873 {
2874 require_return_value( inMemLen > 0, false );
2875
2876 // The memcmp() call below compares two subregions of length inMemLen - 1. The first subregion starts at
2877 // inMemPtr, and the second subregion starts at inMemPtr + 1. The memcmp() call will return zero if for all
2878 // i in {0, 1, ..., inMemLen - 2}, inMemPtr[i] == inMemPtr[i + 1]. That is, memcmp() will return zero if all
2879 // bytes are equal. So if inMemPtr[0] == 0, and the memcmp() call returns zero, then all bytes are equal to zero.
2880
2881 return( ( inMemPtr[ 0 ] == 0 ) && ( memcmp( inMemPtr, inMemPtr + 1, inMemLen - 1 ) == 0 ) );
2882 }
2883
2884 //===========================================================================================================================
2885
2886 static Boolean _IPv4AddressIsWhitelisted( const uint8_t inAddrBytes[ STATIC_PARAM 4 ] )
2887 {
2888 // Whitelist the all-zeros and localhost addresses.
2889
2890 switch( ReadBig32( inAddrBytes ) )
2891 {
2892 case 0: // 0.0.0.0
2893 case UINT32_C( 0x7F000001 ): // 127.0.0.1
2894 return( true );
2895
2896 default:
2897 return( false );
2898 }
2899 }
2900
2901 //===========================================================================================================================
2902
2903 static Boolean _IPv6AddressIsWhitelisted( const uint8_t inAddrBytes[ STATIC_PARAM 16 ] )
2904 {
2905 // Whitelist the all-zeros and localhost addresses, i.e., :: and ::1.
2906
2907 if( ( memcmp( inAddrBytes, AllZeros16Bytes, 15 ) == 0 ) && ( ( inAddrBytes[ 15 ] == 0 ) || ( inAddrBytes[ 15 ] == 1 ) ) )
2908 {
2909 return( true );
2910 }
2911 else
2912 {
2913 return( false );
2914 }
2915 }
2916
2917 //===========================================================================================================================
2918
2919 static int
2920 _DNSMessagePrintObfuscatedIPAddress(
2921 char * inBufPtr,
2922 size_t inBufLen,
2923 const uint8_t * inAddrBytes,
2924 size_t inAddrLen )
2925 {
2926 int n;
2927 char tmpBuf[ ( 16 * 2 ) + 1 ];
2928
2929 require_return_value( ( inAddrLen == 4 ) || ( inAddrLen == 16 ), kSizeErr );
2930
2931 n = _SNPrintF( tmpBuf, sizeof( tmpBuf ), "%.4H", inAddrBytes, (int) inAddrLen, (int) inAddrLen );
2932 require_return_value( n >= 0, n );
2933
2934 return( _SNPrintF( inBufPtr, inBufLen, "%~s", tmpBuf ) );
2935 }