2 Copyright (c) 2016-2020 Apple Inc. All rights reserved.
5 #include "DNSMessage.h"
6 #include <CoreUtils/CoreUtils.h>
9 //===========================================================================================================================
10 // MARK: - Local Prototypes
12 static Boolean
_NameIsPrivate( const char * const inDomainNameStr
);
14 _AppendDomainNameString( DataBuffer
*inDB
, Boolean inPrivacy
, const char *inDomainNameStr
);
16 _AppendDomainNameStringEx( DataBuffer
*inDB
, const char *inSeparator
, Boolean inPrivacy
, const char *inDomainNameStr
);
18 _AppendOPTRDataString(
20 const uint8_t * inRDataPtr
,
21 const uint8_t * inRDataEnd
,
24 _AppendSVCBRDataString(
26 const uint8_t * inRDataPtr
,
27 const uint8_t * inRDataEnd
,
32 const char * inSeparator
,
33 const uint8_t inAddrBytes
[ STATIC_PARAM
4 ],
38 const char * inSeparator
,
39 const uint8_t inAddrBytes
[ STATIC_PARAM
16 ],
44 const char * inSeparator
,
45 const uint8_t * inAddrPtr
,
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 ] );
53 _DNSMessagePrintObfuscatedIPAddress(
56 const uint8_t * inAddrBytes
,
59 //===========================================================================================================================
60 // MARK: - CoreUtils Framework Soft Linking
62 #define _DEFINE_GET_CU_SYM_ADDR( SYMBOL ) \
63 static __typeof__( SYMBOL ) * _GetCUSymAddr_ ## SYMBOL( void ) \
65 static dispatch_once_t sOnce = 0; \
66 static __typeof__( SYMBOL ) * sAddr = NULL; \
68 dispatch_once( &sOnce, \
70 void * const handle = _GetCULibHandle(); \
71 require_return( handle ); \
72 sAddr = (__typeof__( SYMBOL ) *) dlsym( handle, #SYMBOL ); \
76 extern int _DNSMessageDummyVariable
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
);
87 #define _CallCUVoidFunction( NAME, UNAVAILABLE_RETURN_VALUE, ... ) \
88 ( likely( _GetCUSymAddr_ ## NAME() ) ? \
89 ( ( ( _GetCUSymAddr_ ## NAME() )( __VA_ARGS__ ) ), kNoErr ) : ( UNAVAILABLE_RETURN_VALUE ) )
91 #define _CallCUFunction( NAME, UNAVAILABLE_RETURN_VALUE, ... ) \
92 ( likely( _GetCUSymAddr_ ## NAME() ) ? \
93 ( ( _GetCUSymAddr_ ## NAME() )( __VA_ARGS__ ) ) : ( UNAVAILABLE_RETURN_VALUE ) )
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__ )
104 //===========================================================================================================================
105 // MARK: - Public Functions
107 #define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 )
110 DNSMessageExtractDomainName(
111 const uint8_t * inMsgPtr
,
113 const uint8_t * inPtr
,
114 uint8_t outName
[ kDomainNameLengthMax
],
115 const uint8_t ** outPtr
)
118 const uint8_t * label
;
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
;
126 require_action_quiet( ( inPtr
>= inMsgPtr
) && ( inPtr
< msgEnd
), exit
, err
= kRangeErr
);
128 for( label
= inPtr
; ( labelLen
= label
[ 0 ] ) != 0; label
= nextLabel
)
130 if( labelLen
<= kDomainLabelLengthMax
)
132 nextLabel
= label
+ 1 + labelLen
;
133 require_action_quiet( nextLabel
< msgEnd
, exit
, err
= kUnderrunErr
);
136 require_action_quiet( ( dstLim
- dst
) > ( 1 + labelLen
), exit
, err
= kOverrunErr
);
137 memcpy( dst
, label
, 1 + labelLen
);
138 dst
+= ( 1 + labelLen
);
141 else if( IsCompressionByte( labelLen
) )
145 require_action_quiet( ( msgEnd
- label
) >= 2, exit
, err
= kUnderrunErr
);
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
);
164 if( !nameEnd
) nameEnd
= label
+ 1;
166 if( outPtr
) *outPtr
= nameEnd
;
173 //===========================================================================================================================
176 DNSMessageExtractDomainNameString(
177 const void * inMsgPtr
,
180 char inBuf
[ kDNSServiceMaxDomainName
],
181 const uint8_t ** outPtr
)
184 const uint8_t * nextPtr
;
185 uint8_t domainName
[ kDomainNameLengthMax
];
187 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inPtr
, domainName
, &nextPtr
);
188 require_noerr_quiet( err
, exit
);
190 err
= DomainNameToString( domainName
, NULL
, inBuf
, NULL
);
191 require_noerr_quiet( err
, exit
);
193 if( outPtr
) *outPtr
= nextPtr
;
199 //===========================================================================================================================
202 DNSMessageExtractQuestion(
203 const uint8_t * inMsgPtr
,
205 const uint8_t * inPtr
,
206 uint8_t outName
[ kDomainNameLengthMax
],
209 const uint8_t ** outPtr
)
212 const uint8_t * const msgEnd
= &inMsgPtr
[ inMsgLen
];
214 const dns_fixed_fields_question
* fields
;
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
);
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 ];
229 //===========================================================================================================================
232 _DNSMessageExtractRecordEx(
233 const uint8_t * inMsgPtr
,
235 const uint8_t * inPtr
,
236 uint8_t outName
[ kDomainNameLengthMax
],
240 const uint8_t ** outRDataPtr
,
241 size_t * outRDataLen
,
244 size_t * outCopiedRDataLen
,
245 size_t * outExpandedRDataLen
,
246 const uint8_t ** outPtr
);
249 DNSMessageExtractRecord(
250 const uint8_t * inMsgPtr
,
252 const uint8_t * inPtr
,
253 uint8_t outName
[ kDomainNameLengthMax
],
257 const uint8_t ** outRDataPtr
,
258 size_t * outRDataLen
,
259 const uint8_t ** outPtr
)
261 return( _DNSMessageExtractRecordEx( inMsgPtr
, inMsgLen
, inPtr
, outName
, outType
, outClass
, outTTL
,
262 outRDataPtr
, outRDataLen
, NULL
, 0, NULL
, NULL
, outPtr
) );
265 //===========================================================================================================================
267 OSStatus
DNSMessageGetAnswerSection( const uint8_t *inMsgPtr
, size_t inMsgLen
, const uint8_t **outPtr
)
270 unsigned int questionCount
, i
;
271 const DNSHeader
* hdr
;
274 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
276 hdr
= (DNSHeader
*) inMsgPtr
;
277 questionCount
= DNSHeaderGetQuestionCount( hdr
);
279 ptr
= (const uint8_t *) &hdr
[ 1 ];
280 for( i
= 0; i
< questionCount
; ++i
)
282 err
= DNSMessageExtractQuestion( inMsgPtr
, inMsgLen
, ptr
, NULL
, NULL
, NULL
, &ptr
);
283 require_noerr_quiet( err
, exit
);
285 if( outPtr
) *outPtr
= ptr
;
292 //===========================================================================================================================
295 DNSMessageGetOptRecord(
296 const uint8_t * inMsgPtr
,
298 const uint8_t ** outOptPtr
,
302 const DNSHeader
* hdr
;
304 const uint8_t * optPtr
;
306 uint32_t skipCount
, additionalCount
, i
;
308 err
= DNSMessageGetAnswerSection( inMsgPtr
, inMsgLen
, &ptr
);
309 require_noerr_quiet( err
, exit
);
311 hdr
= (DNSHeader
*) inMsgPtr
;
312 skipCount
= DNSHeaderGetAnswerCount( hdr
) + DNSHeaderGetAuthorityCount( hdr
);
313 for( i
= 0; i
< skipCount
; ++i
)
317 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, NULL
, &type
, NULL
, NULL
, NULL
, NULL
, &ptr
);
318 require_noerr_quiet( err
, exit
);
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>.
323 require_action_quiet( type
!= kDNSRecordType_OPT
, exit
, err
= kMalformedErr
);
327 additionalCount
= DNSHeaderGetAdditionalCount( hdr
);
328 for( i
= 0; i
< additionalCount
; ++i
)
330 const uint8_t * namePtr
;
334 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, NULL
, &type
, NULL
, NULL
, NULL
, NULL
, &ptr
);
335 require_noerr_quiet( err
, exit
);
337 if( type
!= kDNSRecordType_OPT
) continue;
339 // Make sure that there's only one OPT record.
340 // See <https://tools.ietf.org/html/rfc6891#section-6.1.1>.
342 require_action_quiet( !optPtr
, exit
, err
= kMalformedErr
);
344 // The OPT record's name must be 0 (root domain).
345 // See <https://tools.ietf.org/html/rfc6891#section-6.1.2>.
347 require_action_quiet( *namePtr
== 0, exit
, err
= kMalformedErr
);
350 optLen
= (size_t)( ptr
- optPtr
);
351 check( optLen
>= sizeof( dns_fixed_fields_opt
) );
353 if( outOptPtr
) *outOptPtr
= optPtr
;
354 if( outOptLen
) *outOptLen
= optLen
;
360 //===========================================================================================================================
363 DNSMessageWriteQuery(
366 const uint8_t * inQName
,
369 uint8_t outMsg
[ STATIC_PARAM kDNSQueryMessageMaxLen
],
373 DNSHeader
* const hdr
= (DNSHeader
*) outMsg
;
378 memset( hdr
, 0, sizeof( *hdr
) );
379 DNSHeaderSetID( hdr
, inMsgID
);
380 DNSHeaderSetFlags( hdr
, inFlags
);
381 DNSHeaderSetQuestionCount( hdr
, 1 );
383 qnameLen
= DomainNameLength( inQName
);
384 require_action_quiet( qnameLen
<= kDomainNameLengthMax
, exit
, err
= kSizeErr
);
386 ptr
= (uint8_t *) &hdr
[ 1 ];
387 memcpy( ptr
, inQName
, qnameLen
);
390 dns_fixed_fields_question_init( (dns_fixed_fields_question
*) ptr
, inQType
, inQClass
);
391 ptr
+= sizeof( dns_fixed_fields_question
);
393 msgLen
= (size_t)( ptr
- outMsg
);
395 if( outLen
) *outLen
= msgLen
;
402 //===========================================================================================================================
406 const uint8_t * const inMsgPtr
,
407 const size_t inMsgLen
,
408 size_t * const outMsgLen
,
409 OSStatus
* const outError
)
412 uint8_t * newMsg
= NULL
;
413 const DNSHeader
* hdr
;
415 const uint8_t * answerSection
;
416 uint8_t * bufPtr
= NULL
;
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
];
427 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
429 hdr
= (DNSHeader
*) inMsgPtr
;
430 questionCount
= DNSHeaderGetQuestionCount( hdr
);
431 require_action_quiet( questionCount
== 1, exit
, err
= kCountErr
);
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
);
438 qnameLen
= DomainNameLength( qname
);
441 // The initial target name is the QNAME.
443 memcpy( target
, qname
, qnameLen
);
445 // Starting with QNAME, follow the CNAME chain, if any, to the end.
447 minCNameTTL
= UINT32_MAX
;
448 answerCount
= DNSHeaderGetAnswerCount( hdr
);
449 for( i
= 0; i
< answerCount
; ++i
)
451 Boolean followedCNAME
;
454 followedCNAME
= false;
455 for( j
= 0; j
< answerCount
; ++j
)
457 const uint8_t * rdataPtr
;
459 uint16_t type
, class;
460 uint8_t name
[ kDomainNameLengthMax
];
462 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, name
, &type
, &class, &ttl
, &rdataPtr
, NULL
, &ptr
);
463 require_noerr_quiet( err
, exit
);
465 if( type
!= kDNSRecordType_CNAME
) continue;
466 if( class != qclass
) continue;
467 if( !DomainNameEqual( name
, target
) ) continue;
469 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, rdataPtr
, target
, NULL
);
470 require_noerr_quiet( err
, exit
);
472 minCNameTTL
= Min( minCNameTTL
, ttl
);
473 followedCNAME
= true;
475 if( !followedCNAME
) break;
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.
484 bufLen
= kDNSHeaderLength
+ qnameLen
+ sizeof( dns_fixed_fields_question
);
487 for( i
= 0; i
< 2; ++i
)
490 unsigned int newAnswerCount
;
494 for( j
= 0; j
< answerCount
; ++j
)
496 const uint8_t * recordPtr
;
497 dns_fixed_fields_record
* fields
;
498 size_t copiedRDataLen
, expandedRDataLen
;
500 uint16_t type
, class;
501 uint8_t name
[ kDomainNameLengthMax
];
504 err
= _DNSMessageExtractRecordEx( inMsgPtr
, inMsgLen
, ptr
, name
, &type
, &class, &ttl
, NULL
, NULL
,
505 NULL
, 0, NULL
, &expandedRDataLen
, &ptr
);
506 require_noerr_quiet( err
, exit
);
508 if( type
!= qtype
) continue;
509 if( class != qclass
) continue;
510 if( !DomainNameEqual( name
, target
) ) continue;
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.
516 bufLen
+= ( kDNSCompressionPointerLength
+ sizeof( *fields
) + expandedRDataLen
);
520 // Write the record's name as a compression pointer to QNAME, which is located right after the header.
522 require_action_quiet( ( lim
- dst
) >= kDNSCompressionPointerLength
, exit
, err
= kInternalErr
);
523 DNSMessageWriteLabelPointer( dst
, kDNSHeaderLength
);
524 dst
+= kDNSCompressionPointerLength
;
526 // Write the record's fixed field values.
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
);
534 // Write the record's expanded RDATA.
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
);
541 dst
+= copiedRDataLen
;
547 dns_fixed_fields_question
* fields
;
549 // Allocate memory for the collapsed message.
551 bufPtr
= (uint8_t *) calloc( 1, bufLen
);
552 require_action_quiet( bufPtr
, exit
, err
= kNoMemoryErr
);
555 lim
= &bufPtr
[ bufLen
];
557 // Write a tentative header based on the original header.
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
);
564 DNSHeaderSetAnswerCount( newHdr
, 0 );
565 DNSHeaderSetAuthorityCount( newHdr
, 0 );
566 DNSHeaderSetAdditionalCount( newHdr
, 0 );
568 // Write the question section.
570 require_action_quiet( ( (size_t)( lim
- dst
) ) >= qnameLen
, exit
, err
= kInternalErr
);
571 memcpy( dst
, qname
, qnameLen
);
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
);
579 DNSHeaderSetQuestionCount( newHdr
, 1 );
583 // Finally, set the answer count.
585 require_action_quiet( bufLen
>= sizeof( *newHdr
), exit
, err
= kInternalErr
);
586 newHdr
= (DNSHeader
*) bufPtr
;
587 DNSHeaderSetAnswerCount( newHdr
, newAnswerCount
);
591 if( outMsgLen
) *outMsgLen
= (size_t)( dst
- bufPtr
);
596 if( outError
) *outError
= err
;
597 ForgetMem( &bufPtr
);
601 //===========================================================================================================================
604 _DNSMessageExtractRData(
605 const uint8_t * inMsgPtr
,
607 const uint8_t * inRDataPtr
,
612 size_t * outCopiedRDataLen
,
613 size_t * outExpandedRDataLen
);
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
)
633 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
635 const dns_fixed_fields_record
* fields
;
636 const uint8_t * rdata
;
637 size_t rdLength
, copiedLen
, expandedLen
;
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
);
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
);
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
);
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
];
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
)
680 const uint8_t * const rdataEnd
= &inRDataPtr
[ inRDataLen
];
681 size_t copyLen
, expandedLen
;
682 uint8_t name1
[ kDomainNameLengthMax
];
683 uint8_t name2
[ kDomainNameLengthMax
];
685 // According to <https://tools.ietf.org/html/rfc1123#section-6.1.3.5>:
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.
694 // Therefore, compressed domain names in RDATA is only handled for record types from
695 // <https://tools.ietf.org/html/rfc1035#section-3.3>.
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>
708 // The RDATA consists of one domain name.
710 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inRDataPtr
, name1
, &ptr
);
711 require_noerr_quiet( err
, exit
);
712 require_action_quiet( ptr
== rdataEnd
, exit
, err
= kMalformedErr
);
714 expandedLen
= DomainNameLength( name1
);
715 copyLen
= Min( inBufLen
, expandedLen
);
716 memcpy( inBufPtr
, name1
, copyLen
);
719 case kDNSRecordType_MINFO
: // <https://tools.ietf.org/html/rfc1035#section-3.3.7>
721 uint8_t * dst
= inBufPtr
;
722 const uint8_t * const lim
= &inBufPtr
[ inBufLen
];
723 size_t nameLen1
, nameLen2
;
725 // The RDATA consists of two domain names.
727 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inRDataPtr
, name1
, &ptr
);
728 require_noerr_quiet( err
, exit
);
730 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, ptr
, name2
, &ptr
);
731 require_noerr_quiet( err
, exit
);
732 require_action_quiet( ptr
== rdataEnd
, exit
, err
= kMalformedErr
);
734 nameLen1
= DomainNameLength( name1
);
735 nameLen2
= DomainNameLength( name2
);
736 expandedLen
= nameLen1
+ nameLen2
;
738 copyLen
= (size_t)( lim
- dst
);
739 copyLen
= Min( copyLen
, nameLen1
);
740 memcpy( dst
, name1
, copyLen
);
743 copyLen
= (size_t)( lim
- dst
);
744 copyLen
= Min( copyLen
, nameLen2
);
745 memcpy( dst
, name2
, copyLen
);
748 copyLen
= (size_t)( dst
- inBufPtr
);
751 case kDNSRecordType_MX
: // <https://tools.ietf.org/html/rfc1035#section-3.3.9>
753 uint8_t * dst
= inBufPtr
;
754 const uint8_t * const lim
= &inBufPtr
[ inBufLen
];
755 const uint8_t * exchange
;
758 // The RDATA format is a 2-octet preference value followed by exchange, which is a domain name.
760 require_action_quiet( inRDataLen
> 2, exit
, err
= kMalformedErr
);
761 exchange
= &inRDataPtr
[ 2 ];
763 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, exchange
, name1
, &ptr
);
764 require_noerr_quiet( err
, exit
);
765 require_action_quiet( ptr
== rdataEnd
, exit
, err
= kMalformedErr
);
767 nameLen1
= DomainNameLength( name1
);
768 expandedLen
= 2 + nameLen1
;
770 copyLen
= (size_t)( lim
- dst
);
771 copyLen
= Min( copyLen
, 2 );
772 memcpy( dst
, inRDataPtr
, copyLen
);
775 copyLen
= (size_t)( lim
- dst
);
776 copyLen
= Min( copyLen
, nameLen1
);
777 memcpy( dst
, name1
, copyLen
);
780 copyLen
= (size_t)( dst
- inBufPtr
);
783 case kDNSRecordType_SOA
: // <https://tools.ietf.org/html/rfc1035#section-3.3.13>
785 uint8_t * dst
= inBufPtr
;
786 const uint8_t * const lim
= &inBufPtr
[ inBufLen
];
787 size_t nameLen1
, nameLen2
;
789 // MNAME is a domain name.
791 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inRDataPtr
, name1
, &ptr
);
792 require_noerr_quiet( err
, exit
);
794 // RNAME is a domain name.
796 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, ptr
, name2
, &ptr
);
797 require_noerr_quiet( err
, exit
);
799 // MNAME and RNAME are followed by fixed-sized fields.
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
);
806 copyLen
= (size_t)( lim
- dst
);
807 copyLen
= Min( copyLen
, nameLen1
);
808 memcpy( dst
, name1
, copyLen
);
811 copyLen
= (size_t)( lim
- dst
);
812 copyLen
= Min( copyLen
, nameLen2
);
813 memcpy( dst
, name2
, copyLen
);
816 copyLen
= (size_t)( lim
- dst
);
817 copyLen
= Min( copyLen
, sizeof( dns_fixed_fields_soa
) );
818 memcpy( dst
, ptr
, copyLen
);
821 copyLen
= (size_t)( dst
- inBufPtr
);
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>
829 // The RDATA contains no compressed domain names.
831 expandedLen
= inRDataLen
;
832 copyLen
= Min( inBufLen
, expandedLen
);
833 memcpy( inBufPtr
, inRDataPtr
, copyLen
);
839 if( outCopiedRDataLen
) *outCopiedRDataLen
= copyLen
;
840 if( outExpandedRDataLen
) *outExpandedRDataLen
= expandedLen
;
846 //===========================================================================================================================
849 DomainNameAppendDomainName(
850 uint8_t inName
[ STATIC_PARAM kDomainNameLengthMax
],
851 const uint8_t * inOtherName
,
856 const size_t adjustedLen
= DomainNameLength( inName
) - 1; // Don't count the root label.
857 const size_t otherLen
= DomainNameLength( inOtherName
);
859 require_action_quiet( adjustedLen
<= kDomainNameLengthMax
, exit
, err
= kSizeErr
);
860 require_action_quiet( otherLen
<= kDomainNameLengthMax
, exit
, err
= kSizeErr
);
862 newLen
= adjustedLen
+ otherLen
;
863 require_action_quiet( newLen
<= kDomainNameLengthMax
, exit
, err
= kSizeErr
);
865 memcpy( &inName
[ adjustedLen
], inOtherName
, otherLen
);
866 if( outEnd
) *outEnd
= &inName
[ newLen
];
873 //===========================================================================================================================
876 DomainNameAppendString(
877 uint8_t inDomainName
[ STATIC_PARAM kDomainNameLengthMax
],
878 const char * inString
,
884 const uint8_t * const nameLim
= inDomainName
+ kDomainNameLengthMax
;
886 for( root
= inDomainName
; ( root
< nameLim
) && *root
; root
+= ( 1 + *root
) ) {}
887 require_action_quiet( root
< nameLim
, exit
, err
= kMalformedErr
);
889 // If the string is a single dot, denoting the root domain, then there are no non-empty labels.
892 if( ( src
[ 0 ] == '.' ) && ( src
[ 1 ] == '\0' ) ) ++src
;
895 uint8_t * const label
= root
;
896 const uint8_t * const labelLim
= Min( &label
[ 1 + kDomainLabelLengthMax
], nameLim
- 1 );
902 while( *src
&& ( ( c
= *src
++ ) != '.' ) )
906 require_action_quiet( *src
!= '\0', exit
, err
= kUnderrunErr
);
908 if( isdigit_safe( c
) && isdigit_safe( src
[ 0 ] ) && isdigit_safe( src
[ 1 ] ) )
910 const int decimal
= ( ( c
- '0' ) * 100 ) + ( ( src
[ 0 ] - '0' ) * 10 ) + ( src
[ 1 ] - '0' );
919 require_action_quiet( dst
< labelLim
, exit
, err
= kOverrunErr
);
920 *dst
++ = (uint8_t) c
;
923 labelLen
= (size_t)( dst
- &label
[ 1 ] );
924 require_action_quiet( labelLen
> 0, exit
, err
= kMalformedErr
);
926 label
[ 0 ] = (uint8_t) labelLen
;
930 if( outEnd
) *outEnd
= root
+ 1;
937 //===========================================================================================================================
939 #define _isupper_ascii( X ) ( ( (X) >= 'A' ) && ( (X) <= 'Z' ) )
941 static void _DomainNameLower( uint8_t *inName
);
943 OSStatus
DomainNameDupEx( const uint8_t *inName
, Boolean inLower
, uint8_t **outNamePtr
, size_t *outNameLen
)
947 const size_t nameLen
= DomainNameLength( inName
);
949 namePtr
= (uint8_t *) malloc( nameLen
);
950 require_action_quiet( namePtr
, exit
, err
= kNoMemoryErr
);
952 memcpy( namePtr
, inName
, nameLen
);
953 if( inLower
) _DomainNameLower( namePtr
);
955 *outNamePtr
= namePtr
;
956 if( outNameLen
) *outNameLen
= nameLen
;
963 static void _DomainNameLower( uint8_t *inName
)
969 while( ( len
= *ptr
++ ) != 0 )
973 if( _isupper_ascii( *ptr
) ) *ptr
+= 32;
979 //===========================================================================================================================
981 #define _tolower_ascii( X ) ( _isupper_ascii( X ) ? ( (X) + 32 ) : (X) )
983 Boolean
DomainNameEqual( const uint8_t *inName1
, const uint8_t *inName2
)
985 const uint8_t * p1
= inName1
;
986 const uint8_t * p2
= inName2
;
987 if( p1
== p2
) return( true );
991 const int len2
= *p2
++;
992 if( len1
!= len2
) return( false );
993 if( len1
== 0 ) return( true );
996 const int c1
= *p1
++;
997 const int c2
= *p2
++;
998 if( _tolower_ascii( c1
) != _tolower_ascii( c2
) ) return( false );
1003 //===========================================================================================================================
1006 DomainNameFromString(
1007 uint8_t outName
[ STATIC_PARAM kDomainNameLengthMax
],
1008 const char * inString
,
1012 return( DomainNameAppendString( outName
, inString
, outEnd
) );
1015 //===========================================================================================================================
1017 size_t DomainNameLength( const uint8_t *inName
)
1019 const uint8_t * label
;
1022 for( label
= inName
; ( len
= *label
) != 0; label
= &label
[ 1 + len
] ) {}
1023 return( (size_t)( label
- inName
) + 1 );
1026 //===========================================================================================================================
1028 int DomainNameLabelCount( const uint8_t * const inName
)
1030 const uint8_t * label
;
1031 const uint8_t * nextLabel
;
1032 int labelCount
, result
;
1033 unsigned int labelLen
;
1036 for( label
= inName
; ( labelLen
= *label
) != 0; label
= nextLabel
)
1038 require_action_quiet( labelLen
<= kDomainLabelLengthMax
, exit
, result
= -1 );
1040 nextLabel
= &label
[ 1 + labelLen
];
1041 require_action_quiet( ( nextLabel
- inName
) < kDomainNameLengthMax
, exit
, result
= -1 );
1045 result
= labelCount
;
1051 //===========================================================================================================================
1053 #define _isprint_ascii( X ) ( ( (X) >= 32 ) && ( (X) <= 126 ) )
1057 const uint8_t * inName
,
1058 const uint8_t * inLimit
,
1059 char outString
[ STATIC_PARAM kDNSServiceMaxDomainName
],
1060 const uint8_t ** outPtr
)
1063 const uint8_t * label
;
1065 const uint8_t * nextLabel
;
1067 const uint8_t * src
;
1069 require_action_quiet( !inLimit
|| ( ( inLimit
- inName
) > 0 ), exit
, err
= kUnderrunErr
);
1071 // Convert each label up until the root label, i.e., the zero-length label.
1074 for( label
= inName
; ( labelLen
= label
[ 0 ] ) != 0; label
= nextLabel
)
1076 require_action_quiet( labelLen
<= kDomainLabelLengthMax
, exit
, err
= kMalformedErr
);
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
);
1082 for( src
= &label
[ 1 ]; src
< nextLabel
; ++src
)
1084 if( _isprint_ascii( *src
) )
1086 if( ( *src
== '.' ) || ( *src
== '\\' ) || ( *src
== ' ' ) ) *dst
++ = '\\';
1087 *dst
++ = (char) *src
;
1092 *dst
++ = '0' + ( *src
/ 100 );
1093 *dst
++ = '0' + ( ( *src
/ 10 ) % 10 );
1094 *dst
++ = '0' + ( *src
% 10 );
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.
1103 if( label
== inName
) *dst
++ = '.';
1105 if( outPtr
) *outPtr
= label
+ 1;
1112 //===========================================================================================================================
1114 Boolean
DomainLabelEqual( const uint8_t *inLabel1
, const uint8_t *inLabel2
)
1116 const uint8_t * p1
= inLabel1
;
1117 const uint8_t * p2
= inLabel2
;
1118 if( p1
== p2
) return( true );
1120 const int len2
= *p2
++;
1121 if( len1
!= len2
) return( false );
1124 const int c1
= *p1
++;
1125 const int c2
= *p2
++;
1126 if( _tolower_ascii( c1
) != _tolower_ascii( c2
) ) return( false );
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
1136 const char * DNSRecordTypeValueToString( int inValue
)
1138 const char * string
;
1141 else if( ( inValue
>= 1 ) && ( inValue
<= 53 ) )
1143 static const char * const sNames_1_53
[] =
1199 string
= sNames_1_53
[ inValue
- 1 ];
1201 else if( ( inValue
>= 55 ) && ( inValue
<= 65 ) )
1203 static const char * const sNames_55_65
[] =
1217 string
= sNames_55_65
[ inValue
- 55 ];
1219 else if( ( inValue
>= 99 ) && ( inValue
<= 109 ) )
1221 static const char * const sNames_99_109
[] =
1235 string
= sNames_99_109
[ inValue
- 99 ];
1237 else if( ( inValue
>= 249 ) && ( inValue
<= 260 ) )
1239 static const char * const sNames_249_260
[] =
1254 string
= sNames_249_260
[ inValue
- 249 ];
1256 else if( ( inValue
>= 32768 ) && ( inValue
<= 32769 ) )
1258 static const char * const sNames_32768_32769
[] =
1263 string
= sNames_32768_32769
[ inValue
- 32768 ];
1265 else if( inValue
== 65535 )
1267 static const char * const sNames_65535
[] =
1269 "Reserved", // 65535
1271 string
= sNames_65535
[ inValue
- 65535 ];
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
1291 } _DNSRecordTypeItem
;
1293 static int _DNSRecordTypeStringToValueCmp( const void *inKey
, const void *inElement
);
1295 uint16_t DNSRecordTypeStringToValue( const char *inString
)
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.
1300 static const _DNSRecordTypeItem sTable
[] =
1306 { "AMTRELAY", 260 },
1359 { "NSEC3PARAM", 51 },
1362 { "OPENPGPKEY", 61 },
1366 { "Reserved", 65535 },
1393 const _DNSRecordTypeItem
* item
;
1395 item
= (_DNSRecordTypeItem
*) bsearch( inString
, sTable
, sizeof( sTable
) / sizeof( sTable
[ 0 ] ),
1396 sizeof( sTable
[ 0 ] ), _DNSRecordTypeStringToValueCmp
);
1397 return( item
? item
->value
: 0 );
1400 static int _DNSRecordTypeStringToValueCmp( const void *inKey
, const void *inElement
)
1402 const _DNSRecordTypeItem
* const item
= (const _DNSRecordTypeItem
*) inElement
;
1403 return( strcasecmp( (const char *) inKey
, item
->name
) );
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
1410 const char * DNSRCodeToString( const int inValue
)
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
);
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
1439 } _DNSRCodeTableEntry
;
1441 static int _DNSRCodeFromStringCmp( const void *inKey
, const void *inElement
);
1443 int DNSRCodeFromString( const char * const inString
)
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.
1448 static const _DNSRCodeTableEntry sTable
[] =
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
}
1463 const _DNSRCodeTableEntry
* entry
;
1465 entry
= (_DNSRCodeTableEntry
*) bsearch( inString
, sTable
, sizeof( sTable
) / sizeof( sTable
[ 0 ] ),
1466 sizeof( sTable
[ 0 ] ), _DNSRCodeFromStringCmp
);
1467 return( entry
? entry
->value
: -1 );
1470 static int _DNSRCodeFromStringCmp( const void * const inKey
, const void * const inElement
)
1472 const _DNSRCodeTableEntry
* const entry
= (const _DNSRCodeTableEntry
*) inElement
;
1473 return( strcasecmp( (const char *) inKey
, entry
->name
) );
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>.
1480 static const char * _DNSOpCodeToString( int inOpCode
);
1487 } _DNSHeaderFlagDesc
;
1489 static const _DNSHeaderFlagDesc kDNSHeaderFlagsDescs
[] =
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" },
1502 const uint8_t * inMsgPtr
,
1504 const DNSMessageToStringFlags inFlags
,
1509 const DNSHeader
* hdr
;
1510 const uint8_t * ptr
;
1511 const char * separator
;
1512 char * rdataStr
= NULL
;
1515 uint32_t qCount
, aCount
, authCount
, addCount
, i
, totalCount
;
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;
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 )
1534 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
1536 hdr
= (DNSHeader
*) inMsgPtr
;
1537 qCount
= DNSHeaderGetQuestionCount( hdr
);
1538 aCount
= DNSHeaderGetAnswerCount( hdr
);
1539 authCount
= DNSHeaderGetAuthorityCount( hdr
);
1540 addCount
= DNSHeaderGetAdditionalCount( hdr
);
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
);
1551 _AppendF( "id: 0x%04X (%u), flags: 0x%04X (%c/",
1552 id
, id
, flags
, ( flags
& kDNSHeaderFlag_Response
) ? 'R' : 'Q' );
1556 _AppendF( "ID: 0x%04X (%u)\n", id
, id
);
1557 _AppendF( "Flags: 0x%04X %c/", flags
, ( flags
& kDNSHeaderFlag_Response
) ? 'R' : 'Q' );
1559 str
= _DNSOpCodeToString( opcode
);
1560 if( str
) _AppendF( "%s", str
);
1561 else _AppendF( "OPCODE%d", opcode
);
1562 for( i
= 0; i
< countof( kDNSHeaderFlagsDescs
); ++i
)
1564 const _DNSHeaderFlagDesc
* const flagDesc
= &kDNSHeaderFlagsDescs
[ i
];
1566 if( flags
& flagDesc
->flag
) _AppendF( ", %s", flagDesc
->desc
);
1568 str
= DNSRCodeToString( rcode
);
1569 if( str
) _AppendF( ", %s", str
);
1570 else _AppendF( ", RCODE%d", rcode
);
1573 _AppendF( "), counts: %u/%u/%u/%u", qCount
, aCount
, authCount
, addCount
);
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
);
1585 if( headerOnly
) goto done
;
1587 ptr
= (const uint8_t *) &hdr
[ 1 ];
1590 for( i
= 0; i
< qCount
; ++i
)
1592 uint16_t qtype
, qclass
;
1595 err
= DNSMessageExtractQuestion( inMsgPtr
, inMsgLen
, ptr
, name
, &qtype
, &qclass
, &ptr
);
1596 require_noerr_quiet( err
, exit
);
1598 isQU
= ( isMDNS
&& ( qclass
& kMDNSClassUnicastResponseBit
) ) ? true : false;
1599 if( isMDNS
) qclass
&= ~kMDNSClassUnicastResponseBit
;
1602 _AppendF( "%s", separator
);
1603 if( !nameCur
|| !DomainNameEqual( name
, nameCur
) )
1605 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
1606 require_noerr_quiet( err
, exit
);
1608 if( privacy
&& _NameIsPrivate( nameStr
) ) _AppendF( "%~s ", nameStr
);
1609 else _AppendF( "%s ", nameStr
);
1611 name
= ( name
== nameBuf1
) ? nameBuf2
: nameBuf1
;
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
);
1624 if( i
== 0 ) _AppendF( "\nQUESTION SECTION\n" );
1625 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
1626 require_noerr_quiet( err
, exit
);
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
);
1638 totalCount
= aCount
+ authCount
+ addCount
;
1639 for( i
= 0; i
< totalCount
; ++i
)
1641 const uint8_t * rdataPtr
;
1643 const char * typeStr
;
1645 uint16_t type
, class;
1648 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, name
, &type
, &class, &ttl
, &rdataPtr
, &rdataLen
, &ptr
);
1649 require_noerr_quiet( err
, exit
);
1651 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
1652 require_noerr_quiet( err
, exit
);
1654 cacheFlush
= ( isMDNS
&& ( class & kMDNSClassCacheFlushBit
) ) ? true : false;
1655 if( isMDNS
) class &= ~kMDNSClassCacheFlushBit
;
1659 _AppendF( "%s", separator
);
1660 if( !nameCur
|| !DomainNameEqual( name
, nameCur
) )
1662 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
1663 require_noerr_quiet( err
, exit
);
1665 if( privacy
&& _NameIsPrivate( nameStr
) ) _AppendF( "%~s ", nameStr
);
1666 else _AppendF( "%s ", nameStr
);
1668 name
= ( name
== nameBuf1
) ? nameBuf2
: nameBuf1
;
1671 if( type
== kDNSRecordType_OPT
)
1673 if( cacheFlush
) _AppendF( "CF " );
1674 _AppendF( "OPT %u", class );
1675 if( ttl
== 0 ) _AppendF( " 0" );
1676 else _AppendF( " 0x%08X", ttl
);
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
);
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" );
1696 if( type
== kDNSRecordType_OPT
)
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
);
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
);
1716 if( !rawRData
) DNSRecordDataToStringEx( rdataPtr
, rdataLen
, type
, inMsgPtr
, inMsgLen
, privacy
, &rdataStr
);
1719 _AppendF( " %s", rdataStr
);
1720 ForgetMem( &rdataStr
);
1724 if( privacy
) _AppendF( " [%zu B]", rdataLen
);
1725 else _AppendF( " %#H", rdataPtr
, (int) rdataLen
, (int) rdataLen
);
1730 if( type
== kDNSRecordType_CNAME
)
1732 if( DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, rdataPtr
, name
, NULL
) == kNoErr
)
1735 name
= ( name
== nameBuf1
) ? nameBuf2
: nameBuf1
;
1747 err
= _DataBuffer_Append( &db
, "", 1 ); // NUL terminator.
1748 require_noerr_quiet( err
, exit
);
1750 err
= _DataBuffer_Detach( &db
, (uint8_t **) outString
, &len
);
1751 require_noerr_quiet( err
, exit
);
1754 FreeNullSafe( rdataStr
);
1755 _DataBuffer_Free( &db
);
1759 // See <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5>.
1761 static const char * _DNSOpCodeToString( int inOpCode
)
1763 const char * string
;
1765 if( ( inOpCode
>= 0 ) && ( inOpCode
<= 6 ) )
1767 static const char * const sNames
[] =
1772 NULL
, // 3 (Unassigned)
1777 string
= sNames
[ inOpCode
];
1786 //===========================================================================================================================
1789 _DNSRecordDataAppendTypeBitMap(
1791 const uint8_t * inPtr
,
1792 const uint8_t * inEnd
,
1793 const uint8_t ** outPtr
);
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
)
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
;
1812 uint8_t dbBuf
[ 256 ];
1813 char domainNameStr
[ kDNSServiceMaxDomainName
];
1815 err
= _DataBuffer_Init( &db
, dbBuf
, sizeof( dbBuf
), SIZE_MAX
);
1816 require_noerr_quiet( err
, exit
);
1820 if( inRecordType
== kDNSRecordType_A
)
1822 require_action_quiet( inRDataLen
== 4, exit
, err
= kMalformedErr
);
1824 err
= _AppendIPv4Address( &db
, NULL
, rdataPtr
, inPrivacy
);
1825 require_noerr_quiet( err
, exit
);
1830 else if( inRecordType
== kDNSRecordType_AAAA
)
1832 require_action_quiet( inRDataLen
== 16, exit
, err
= kMalformedErr
);
1834 err
= _AppendIPv6Address( &db
, NULL
, rdataPtr
, inPrivacy
);
1835 require_noerr_quiet( err
, exit
);
1838 // PTR, CNAME, or NS Record
1840 else if( ( inRecordType
== kDNSRecordType_PTR
) ||
1841 ( inRecordType
== kDNSRecordType_CNAME
) ||
1842 ( inRecordType
== kDNSRecordType_NS
) )
1846 err
= DNSMessageExtractDomainNameString( msgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, NULL
);
1847 require_noerr_quiet( err
, exit
);
1851 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, NULL
);
1852 require_noerr_quiet( err
, exit
);
1854 err
= _AppendDomainNameString( &db
, inPrivacy
, domainNameStr
);
1855 require_noerr_quiet( err
, exit
);
1860 else if( inRecordType
== kDNSRecordType_SRV
)
1862 const dns_fixed_fields_srv
* fields
;
1863 const uint8_t * target
;
1865 require_action_quiet( inRDataLen
> sizeof( dns_fixed_fields_srv
), exit
, err
= kMalformedErr
);
1867 fields
= (const dns_fixed_fields_srv
*) rdataPtr
;
1868 target
= (const uint8_t *) &fields
[ 1 ];
1871 err
= DNSMessageExtractDomainNameString( msgPtr
, inMsgLen
, target
, domainNameStr
, NULL
);
1872 require_noerr_quiet( err
, exit
);
1876 err
= DomainNameToString( target
, rdataEnd
, domainNameStr
, NULL
);
1877 require_noerr_quiet( err
, exit
);
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
);
1885 err
= _AppendDomainNameString( &db
, inPrivacy
, domainNameStr
);
1886 require_noerr_quiet( err
, exit
);
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>.
1892 else if( ( inRecordType
== kDNSRecordType_TXT
) || ( inRecordType
== kDNSRecordType_HINFO
) )
1894 require_action_quiet( inRDataLen
> 0, exit
, err
= kMalformedErr
);
1898 err
= _DataBuffer_AppendF( &db
, "[%zu B]", inRDataLen
);
1899 require_noerr_quiet( err
, exit
);
1903 if( inRDataLen
== 1 )
1905 err
= _DataBuffer_AppendF( &db
, "%#H", rdataPtr
, (int) inRDataLen
, (int) inRDataLen
);
1906 require_noerr_quiet( err
, exit
);
1910 err
= _DataBuffer_AppendF( &db
, "%#{txt}", rdataPtr
, (size_t) inRDataLen
);
1911 require_noerr_quiet( err
, exit
);
1918 else if( inRecordType
== kDNSRecordType_SOA
)
1920 const dns_fixed_fields_soa
* fields
;
1926 err
= DNSMessageExtractDomainNameString( msgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
1927 require_noerr_quiet( err
, exit
);
1928 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
1932 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
1933 require_noerr_quiet( err
, exit
);
1935 err
= _AppendDomainNameString( &db
, inPrivacy
, domainNameStr
);
1936 require_noerr_quiet( err
, exit
);
1942 err
= DNSMessageExtractDomainNameString( msgPtr
, inMsgLen
, ptr
, domainNameStr
, &ptr
);
1943 require_noerr_quiet( err
, exit
);
1947 err
= DomainNameToString( ptr
, rdataEnd
, domainNameStr
, &ptr
);
1948 require_noerr_quiet( err
, exit
);
1950 err
= _AppendDomainNameStringEx( &db
, " ", inPrivacy
, domainNameStr
);
1951 require_noerr_quiet( err
, exit
);
1953 require_action_quiet( ( rdataEnd
- ptr
) == sizeof( dns_fixed_fields_soa
), exit
, err
= kMalformedErr
);
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
);
1967 else if( inRecordType
== kDNSRecordType_NSEC
)
1971 err
= DNSMessageExtractDomainNameString( msgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
1972 require_noerr_quiet( err
, exit
);
1976 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
1977 require_noerr_quiet( err
, exit
);
1979 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
1981 err
= _AppendDomainNameString( &db
, inPrivacy
, domainNameStr
);
1982 require_noerr_quiet( err
, exit
);
1984 err
= _DNSRecordDataAppendTypeBitMap( &db
, ptr
, rdataEnd
, NULL
);
1985 require_noerr_quiet( err
, exit
);
1990 else if( inRecordType
== kDNSRecordType_MX
)
1992 uint16_t preference
;
1993 const uint8_t * exchange
;
1995 require_action_quiet( ( rdataEnd
- rdataPtr
) > 2, exit
, err
= kMalformedErr
);
1997 preference
= ReadBig16( rdataPtr
);
1998 exchange
= &rdataPtr
[ 2 ];
2002 err
= DNSMessageExtractDomainNameString( msgPtr
, inMsgLen
, exchange
, domainNameStr
, NULL
);
2003 require_noerr_quiet( err
, exit
);
2007 err
= DomainNameToString( exchange
, rdataEnd
, domainNameStr
, NULL
);
2008 require_noerr_quiet( err
, exit
);
2010 err
= _DataBuffer_AppendF( &db
, "%u", preference
);
2011 require_noerr_quiet( err
, exit
);
2013 err
= _AppendDomainNameStringEx( &db
, " ", inPrivacy
, domainNameStr
);
2014 require_noerr_quiet( err
, exit
);
2017 // DNSKEY Record (see <https://tools.ietf.org/html/rfc4034#section-2.2>)
2019 else if( inRecordType
== kDNSRecordType_DNSKEY
)
2021 const dns_fixed_fields_dnskey
* fields
;
2022 char * publicKeyBase64
;
2024 require_action_quiet( ( (size_t)( rdataEnd
- rdataPtr
) ) > sizeof( *fields
), exit
, err
= kMalformedErr
);
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
);
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
);
2040 err
= _DataBuffer_AppendF( &db
, " %s", publicKeyBase64
);
2041 ForgetMem( &publicKeyBase64
);
2042 require_noerr_quiet( err
, exit
);
2045 // RRSIG Record (see <https://tools.ietf.org/html/rfc4034#section-3.2>)
2047 else if( inRecordType
== kDNSRecordType_RRSIG
)
2049 const dns_fixed_fields_rrsig
* fields
;
2050 const char * typeStr
;
2051 int year
, month
, day
, hour
, minute
, second
;
2052 char * signatureBase64
;
2054 require_action_quiet( ( (size_t)( rdataEnd
- rdataPtr
) ) > sizeof( *fields
), exit
, err
= kMalformedErr
);
2056 fields
= (const dns_fixed_fields_rrsig
*) rdataPtr
;
2057 typeStr
= DNSRecordTypeValueToString( dns_fixed_fields_rrsig_get_type_covered( fields
) );
2060 err
= _DataBuffer_AppendF( &db
, "%s", typeStr
);
2061 require_noerr_quiet( err
, exit
);
2065 err
= _DataBuffer_AppendF( &db
, "TYPE%u", dns_fixed_fields_rrsig_get_type_covered( fields
) );
2066 require_noerr_quiet( err
, exit
);
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
);
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
);
2084 err
= _DataBuffer_AppendF( &db
, " %u%02u%02u%02u%02u%02u", year
, month
, day
, hour
, minute
, second
);
2085 require_noerr_quiet( err
, exit
);
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
);
2091 err
= _DataBuffer_AppendF( &db
, " %u%02u%02u%02u%02u%02u", year
, month
, day
, hour
, minute
, second
);
2092 require_noerr_quiet( err
, exit
);
2094 err
= _DataBuffer_AppendF( &db
, " %u", dns_fixed_fields_rrsig_get_key_tag( fields
) );
2095 require_noerr_quiet( err
, exit
);
2099 ptr
= (const uint8_t *) &fields
[ 1 ];
2100 err
= DomainNameToString( ptr
, rdataEnd
, domainNameStr
, &ptr
);
2101 require_noerr_quiet( err
, exit
);
2103 err
= _AppendDomainNameStringEx( &db
, " ", inPrivacy
, domainNameStr
);
2104 require_noerr_quiet( err
, exit
);
2108 signatureBase64
= NULL
;
2109 err
= _Base64EncodeCopyEx( ptr
, (size_t)( rdataEnd
- ptr
), kBase64Flags_None
, &signatureBase64
, NULL
);
2110 require_noerr_quiet( err
, exit
);
2112 err
= _DataBuffer_AppendF( &db
, " %s", signatureBase64
);
2113 FreeNullSafe( signatureBase64
);
2114 require_noerr_quiet( err
, exit
);
2117 // NSEC3 Record (see <https://tools.ietf.org/html/rfc5155#section-3.3>)
2119 else if( inRecordType
== kDNSRecordType_NSEC3
)
2121 const dns_fixed_fields_nsec3
* fields
;
2122 size_t saltLen
, hashLen
, rem
;
2123 const uint8_t * hashEnd
;
2125 require_action_quiet( ( (size_t)( rdataEnd
- rdataPtr
) ) > sizeof( *fields
), exit
, err
= kMalformedErr
);
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
);
2134 ptr
= (const uint8_t *) &fields
[ 1 ];
2135 require_action_quiet( ( rdataEnd
- ptr
) >= 1, exit
, err
= kMalformedErr
);
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
);
2143 require_action_quiet( ( rdataEnd
- ptr
) >= 1, exit
, err
= kMalformedErr
);
2146 require_action_quiet( ( (size_t)( rdataEnd
- ptr
) ) >= hashLen
, exit
, err
= kMalformedErr
);
2150 err
= _DataBuffer_AppendF( &db
, " " );
2151 require_noerr_quiet( err
, exit
);
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
2158 hashEnd
= ptr
+ hashLen
;
2159 while( ( rem
= (size_t)( hashEnd
- ptr
) ) > 0 )
2163 char encodedBuf
[ 8 ];
2164 static const char kBase32ExtendedHex
[] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
2166 check_compile_time_code( sizeof_string( kBase32ExtendedHex
) == 32 );
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
2178 ptr
+= ( ( rem
> 5 ) ? 5 : rem
);
2185 encodedBuf
[ 7 ] = kBase32ExtendedHex
[ quantum
& 0x1F ]; // Bits 35 - 39
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;
2194 encodedBuf
[ 4 ] = kBase32ExtendedHex
[ ( quantum
>> 15 ) & 0x1F ]; // Bits 20 - 24
2195 if( encodedLen
== 0 ) encodedLen
= 5;
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;
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;
2207 err
= _DataBuffer_Append( &db
, encodedBuf
, encodedLen
);
2208 require_noerr_quiet( err
, exit
);
2210 err
= _DNSRecordDataAppendTypeBitMap( &db
, ptr
, rdataEnd
, NULL
);
2211 require_noerr_quiet( err
, exit
);
2214 // DS Record (see <https://tools.ietf.org/html/rfc4034#section-5.3>)
2216 else if( inRecordType
== kDNSRecordType_DS
)
2218 const dns_fixed_fields_ds
* fields
;
2219 const uint8_t * digestPtr
;
2222 require_action_quiet( ( (size_t)( rdataEnd
- rdataPtr
) ) >= sizeof( *fields
), exit
, err
= kMalformedErr
);
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
);
2231 digestPtr
= (const uint8_t *) &fields
[ 1 ];
2232 digestLen
= (size_t)( rdataEnd
- digestPtr
);
2235 err
= _DataBuffer_AppendF( &db
, " %.4H", digestPtr
, (int) digestLen
, (int) digestLen
);
2236 require_noerr_quiet( err
, exit
);
2240 // HTTPS or SVCB Record
2242 else if( ( inRecordType
== kDNSRecordType_HTTPS
) || ( inRecordType
== kDNSRecordType_SVCB
) )
2244 err
= _AppendSVCBRDataString( &db
, rdataPtr
, rdataEnd
, inPrivacy
);
2245 require_noerr_quiet( err
, exit
);
2248 // OPT Record (see <https://tools.ietf.org/html/rfc6891#section-6.1.2>)
2250 else if( inRecordType
== kDNSRecordType_OPT
)
2252 err
= _AppendOPTRDataString( &db
, rdataPtr
, rdataEnd
, inPrivacy
);
2253 require_noerr_quiet( err
, exit
);
2256 // Unhandled record type
2260 err
= kNotHandledErr
;
2263 err
= _DataBuffer_Append( &db
, "", 1 ); // NUL terminator.
2264 require_noerr_quiet( err
, exit
);
2266 err
= _DataBuffer_Detach( &db
, (uint8_t **) outString
, &len
);
2267 require_noerr_quiet( err
, exit
);
2270 _DataBuffer_Free( &db
);
2275 _DNSRecordDataAppendTypeBitMap(
2277 const uint8_t * inPtr
,
2278 const uint8_t * inEnd
,
2279 const uint8_t ** outPtr
)
2282 const uint8_t * ptr
= inPtr
;
2285 while( ( inEnd
- ptr
) > 0 )
2289 require_action_quiet( ( inEnd
- ptr
) > 2, exit
, err
= kMalformedErr
);
2291 windowBlock
= *ptr
++;
2293 require_action_quiet( ( bitmapLen
>= 1 ) && ( bitmapLen
<= 32 ) , exit
, err
= kMalformedErr
);
2294 require_action_quiet( ( inEnd
- ptr
) >= bitmapLen
, exit
, err
= kMalformedErr
);
2296 for( i
= 0; i
< BitArray_MaxBits( bitmapLen
); ++i
)
2298 const int windowBase
= windowBlock
* 256;
2300 if( BitArray_GetBit( ptr
, bitmapLen
, i
) )
2303 const char * typeStr
;
2306 recordType
= windowBase
+ i
;
2307 typeStr
= DNSRecordTypeValueToString( recordType
);
2310 snprintf( typeBuf
, sizeof( typeBuf
), "TYPE%u", recordType
);
2313 err
= _DataBuffer_AppendF( inDB
, " %s", typeStr
);
2314 require_noerr_quiet( err
, exit
);
2319 if( outPtr
) *outPtr
= ptr
;
2326 //===========================================================================================================================
2327 // Based on reference implementation from <https://tools.ietf.org/html/rfc4034#appendix-B>.
2329 uint16_t DNSComputeDNSKeyTag( const void *inRDataPtr
, size_t inRDataLen
)
2331 const uint8_t * const rdataPtr
= (const uint8_t *) inRDataPtr
;
2332 uint32_t accumulator
;
2336 for( i
= 0; i
< inRDataLen
; ++i
)
2338 accumulator
+= ( i
& 1 ) ? rdataPtr
[ i
] : (uint32_t)( rdataPtr
[ i
] << 8 );
2340 accumulator
+= ( accumulator
>> 16 ) & UINT32_C( 0xFFFF );
2341 return( accumulator
& UINT32_C( 0xFFFF ) );
2344 //===========================================================================================================================
2346 int DNSMessagePrintObfuscatedString( char *inBufPtr
, size_t inBufLen
, const char *inString
)
2348 if( _NameIsPrivate( inString
) )
2350 return( _SNPrintF( inBufPtr
, inBufLen
, "%~s", inString
) );
2354 return( _SNPrintF( inBufPtr
, inBufLen
, "%s", inString
) );
2358 //===========================================================================================================================
2360 int DNSMessagePrintObfuscatedIPv4Address( char *inBufPtr
, size_t inBufLen
, const uint32_t inAddr
)
2362 uint8_t addrBytes
[ 4 ];
2364 WriteBig32( addrBytes
, inAddr
);
2365 if( !_IPv4AddressIsWhitelisted( addrBytes
) )
2367 return( _DNSMessagePrintObfuscatedIPAddress( inBufPtr
, inBufLen
, addrBytes
, sizeof( addrBytes
) ) );
2371 return( _SNPrintF( inBufPtr
, inBufLen
, "%#.4a", &inAddr
) );
2375 //===========================================================================================================================
2377 int DNSMessagePrintObfuscatedIPv6Address( char *inBufPtr
, size_t inBufLen
, const uint8_t inAddr
[ STATIC_PARAM
16 ] )
2379 if( !_IPv6AddressIsWhitelisted( inAddr
) )
2381 return( _DNSMessagePrintObfuscatedIPAddress( inBufPtr
, inBufLen
, inAddr
, 16 ) );
2385 return( _SNPrintF( inBufPtr
, inBufLen
, "%.16a", inAddr
) );
2389 //===========================================================================================================================
2390 // MARK: - Helper Functions
2392 static Boolean
_NameIsPrivate( const char * const inDomainNameStr
)
2394 if( strcasecmp( inDomainNameStr
, "." ) == 0 ) return( false );
2395 if( strcasecmp( inDomainNameStr
, "ipv4only.arpa." ) == 0 ) return( false );
2399 //===========================================================================================================================
2402 _AppendDomainNameString(
2403 DataBuffer
* const inDB
,
2404 const Boolean inPrivacy
,
2405 const char * const inDomainNameStr
)
2407 return( _AppendDomainNameStringEx( inDB
, NULL
, inPrivacy
, inDomainNameStr
) );
2410 //===========================================================================================================================
2413 _AppendDomainNameStringEx(
2414 DataBuffer
* const inDB
,
2415 const char * const inSeparator
,
2416 const Boolean inPrivacy
,
2417 const char * const inDomainNameStr
)
2420 const char * const sep
= inSeparator
? inSeparator
: "";
2422 if( inPrivacy
&& _NameIsPrivate( inDomainNameStr
) )
2424 err
= _DataBuffer_AppendF( inDB
, "%s%~s", sep
, inDomainNameStr
);
2425 require_noerr_quiet( err
, exit
);
2429 err
= _DataBuffer_AppendF( inDB
, "%s%s", sep
, inDomainNameStr
);
2430 require_noerr_quiet( err
, exit
);
2437 //===========================================================================================================================
2440 _AppendOPTRDataString(
2441 DataBuffer
* const inDB
,
2442 const uint8_t * const inRDataPtr
,
2443 const uint8_t * const inRDataEnd
,
2444 const Boolean inPrivacy
)
2447 const uint8_t * ptr
;
2451 require_action_quiet( ptr
<= inRDataEnd
, exit
, err
= kRangeErr
);
2454 while( ptr
< inRDataEnd
)
2456 const dns_fixed_fields_option
* fields
;
2457 const uint8_t * data
;
2458 unsigned int code
, length
;
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 ];
2466 require_action_quiet( ( (size_t)( inRDataEnd
- ptr
) ) >= length
, exit
, err
= kUnderrunErr
);
2470 err
= _DataBuffer_AppendF( inDB
, "%s{", sep
);
2471 require_noerr_quiet( err
, exit
);
2473 if( code
== kDNSEDNS0OptionCode_Padding
)
2475 err
= _DataBuffer_AppendF( inDB
, "Padding" );
2476 require_noerr_quiet( err
, exit
);
2480 err
= _DataBuffer_AppendF( inDB
, "CODE%u", code
);
2481 require_noerr_quiet( err
, exit
);
2483 err
= _DataBuffer_AppendF( inDB
, ", " );
2484 require_noerr_quiet( err
, exit
);
2488 err
= _DataBuffer_AppendF( inDB
, "[%u B]", length
);
2489 require_noerr_quiet( err
, exit
);
2493 if( ( code
== kDNSEDNS0OptionCode_Padding
) && _MemIsAllZeros( data
, length
) )
2495 err
= _DataBuffer_AppendF( inDB
, "<%u zero bytes>", length
);
2496 require_noerr_quiet( err
, exit
);
2500 err
= _DataBuffer_AppendF( inDB
, "'%H'", data
, (int) length
, (int) length
);
2501 require_noerr_quiet( err
, exit
);
2504 err
= _DataBuffer_AppendF( inDB
, "}" );
2505 require_noerr_quiet( err
, exit
);
2515 //===========================================================================================================================
2517 static const char * _DNSSVCBKeyToString( int inValue
);
2520 _AppendSVCBRDataString(
2521 DataBuffer
* const inDB
,
2522 const uint8_t * const inRDataPtr
,
2523 const uint8_t * const inRDataEnd
,
2524 const Boolean inPrivacy
)
2527 const uint8_t * ptr
;
2529 const dns_fixed_fields_svcb
* fields
;
2530 char domainNameStr
[ kDNSServiceMaxDomainName
];
2533 require_action_quiet( ptr
<= inRDataEnd
, exit
, err
= kRangeErr
);
2534 require_action_quiet( ( (size_t)( inRDataEnd
- ptr
) ) >= sizeof( *fields
), exit
, err
= kMalformedErr
);
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
);
2544 ptr
= (const uint8_t *) &fields
[ 1 ];
2545 err
= DomainNameToString( ptr
, inRDataEnd
, domainNameStr
, &ptr
);
2546 require_noerr_quiet( err
, exit
);
2548 err
= _AppendDomainNameStringEx( inDB
, " ", inPrivacy
, domainNameStr
);
2549 require_noerr_quiet( err
, exit
);
2552 // Follows types for <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-00>
2554 while( ptr
< inRDataEnd
)
2556 const dns_fixed_fields_svcb_param
* paramFields
;
2557 const uint8_t * limit
;
2558 const char * keyStr
;
2560 unsigned int valLen
;
2562 require_action_quiet( ( (size_t)( inRDataEnd
- ptr
) ) >= sizeof( *paramFields
), exit
, err
= kUnderrunErr
);
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
);
2568 keyStr
= _DNSSVCBKeyToString( key
);
2571 err
= _DataBuffer_AppendF( inDB
, " %s=\"", keyStr
);
2572 require_noerr_quiet( err
, exit
);
2576 err
= _DataBuffer_AppendF( inDB
, " key%u=\"", key
);
2577 require_noerr_quiet( err
, exit
);
2579 ptr
= (const uint8_t *) ¶mFields
[ 1 ];
2580 require_action_quiet( ( (size_t)( inRDataEnd
- ptr
) ) >= valLen
, exit
, err
= kUnderrunErr
);
2584 case kDNSSVCParamKey_Mandatory
:
2586 // List of 16-bit keys
2587 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-6.5>.
2589 require_action_quiet( ( valLen
% 2 ) == 0, exit
, err
= kMalformedErr
);
2592 limit
= &ptr
[ valLen
];
2593 while( ptr
< limit
)
2596 const char * mandatoryKeyStr
;
2598 mandatoryKey
= ReadBig16( ptr
);
2601 mandatoryKeyStr
= _DNSSVCBKeyToString( mandatoryKey
);
2604 err
= _DataBuffer_AppendF( inDB
, "%s", sep
);
2605 require_noerr_quiet( err
, exit
);
2607 if( mandatoryKeyStr
)
2609 err
= _DataBuffer_AppendF( inDB
, "%s", mandatoryKeyStr
);
2610 require_noerr_quiet( err
, exit
);
2614 err
= _DataBuffer_AppendF( inDB
, "key%u", mandatoryKey
);
2615 require_noerr_quiet( err
, exit
);
2622 case kDNSSVCParamKey_ALPN
:
2624 // Length-prefixed ALPN protocol ID octet sequences
2625 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-6.1>.
2628 limit
= &ptr
[ valLen
];
2629 while( ptr
< limit
)
2631 const uint8_t * alpnLimit
;
2632 const unsigned int alpnLen
= *ptr
++;
2634 require_action_quiet( ( (size_t)( limit
- ptr
) ) >= alpnLen
, exit
, err
= kMalformedErr
);
2638 err
= _DataBuffer_AppendF( inDB
, "%s", sep
);
2639 require_noerr_quiet( err
, exit
);
2641 for( alpnLimit
= &ptr
[ alpnLen
]; ptr
< alpnLimit
; ++ptr
)
2645 if( _isprint_ascii( c
) )
2647 if( ( c
== ',' ) || ( c
== '\\' ) )
2649 // Escape commas and backslashes.
2651 err
= _DataBuffer_AppendF( inDB
, "\\%c", c
);
2652 require_noerr_quiet( err
, exit
);
2656 err
= _DataBuffer_AppendF( inDB
, "%c", c
);
2657 require_noerr_quiet( err
, exit
);
2662 // Use a three digit decimal escape code (\DDD) for non-printable octets.
2664 err
= _DataBuffer_AppendF( inDB
, "\\%03d", c
);
2665 require_noerr_quiet( err
, exit
);
2672 case kDNSSVCParamKey_Port
:
2677 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-6.2>.
2679 require_action_quiet( valLen
== 2, exit
, err
= kMalformedErr
);
2681 port
= ReadBig16( ptr
);
2684 err
= _DataBuffer_AppendF( inDB
, "%u", port
);
2685 require_noerr_quiet( err
, exit
);
2688 case kDNSSVCParamKey_IPv4Hint
:
2690 // IPv4 address list
2691 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-6.4>.
2693 require_action_quiet( ( valLen
% 4 ) == 0, exit
, err
= kMalformedErr
);
2696 for( limit
= &ptr
[ valLen
]; ptr
< limit
; ptr
+= 4 )
2698 err
= _AppendIPv4Address( inDB
, sep
, ptr
, inPrivacy
);
2699 require_noerr_quiet( err
, exit
);
2704 case kDNSSVCParamKey_IPv6Hint
:
2706 // IPv6 address list
2707 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-6.4>.
2709 require_action_quiet( ( valLen
% 16 ) == 0, exit
, err
= kMalformedErr
);
2712 for( limit
= &ptr
[ valLen
]; ptr
< limit
; ptr
+= 16 )
2714 err
= _AppendIPv6Address( inDB
, sep
, ptr
, inPrivacy
);
2715 require_noerr_quiet( err
, exit
);
2723 // See <https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01#section-2.1.1>.
2727 err
= _DataBuffer_AppendF( inDB
, "<%u redacted bytes>", valLen
);
2728 require_noerr_quiet( err
, exit
);
2733 for( limit
= &ptr
[ valLen
]; ptr
< limit
; ++ptr
)
2737 if( ( c
>= 0x21 ) && ( c
<= 0x7E ) )
2739 // Visible characters are printed.
2743 // Escape reserved characters.
2750 err
= _DataBuffer_AppendF( inDB
, "\\%c", c
);
2751 require_noerr_quiet( err
, exit
);
2755 err
= _DataBuffer_AppendF( inDB
, "%c", c
);
2756 require_noerr_quiet( err
, exit
);
2762 // Invisible characters use a three digit decimal escape code (\DDD).
2764 err
= _DataBuffer_AppendF( inDB
, "\\%03d", c
);
2765 require_noerr_quiet( err
, exit
);
2772 err
= _DataBuffer_AppendF( inDB
, "\"" );
2773 require_noerr_quiet( err
, exit
);
2780 static const char * _DNSSVCBKeyToString( const int inValue
)
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
);
2796 //===========================================================================================================================
2800 DataBuffer
* const inDB
,
2801 const char * const inSeparator
,
2802 const uint8_t inAddrBytes
[ STATIC_PARAM
4 ],
2803 const Boolean inPrivacy
)
2805 return( _AppendIPAddress( inDB
, inSeparator
, inAddrBytes
, 4, inPrivacy
&& !_IPv4AddressIsWhitelisted( inAddrBytes
) ) );
2808 //===========================================================================================================================
2812 DataBuffer
* const inDB
,
2813 const char * const inSeparator
,
2814 const uint8_t inAddrBytes
[ STATIC_PARAM
16 ],
2817 return( _AppendIPAddress( inDB
, inSeparator
, inAddrBytes
, 16, inPrivacy
&& !_IPv6AddressIsWhitelisted( inAddrBytes
) ) );
2820 //===========================================================================================================================
2825 const char * inSeparator
,
2826 const uint8_t * inAddrPtr
,
2831 const char * const sep
= inSeparator
? inSeparator
: "";
2833 require_action_quiet( ( inAddrLen
== 4 ) || ( inAddrLen
== 16 ), exit
, err
= kSizeErr
);
2838 char tmpBuf
[ ( 16 * 2 ) + 1 ];
2840 n
= _SNPrintF( tmpBuf
, sizeof( tmpBuf
), "%.4H", inAddrPtr
, (int) inAddrLen
, (int) inAddrLen
);
2841 require_action_quiet( n
>= 0, exit
, err
= n
);
2843 err
= _DataBuffer_AppendF( inDB
, "%s%~s", sep
, tmpBuf
);
2844 require_noerr_quiet( err
, exit
);
2848 err
= _DataBuffer_AppendF( inDB
, "%s%.*a", sep
, inAddrLen
, inAddrPtr
);
2849 require_noerr_quiet( err
, exit
);
2856 //===========================================================================================================================
2858 static void * _GetCULibHandle( void )
2860 static dispatch_once_t sOnce
= 0;
2861 static void * sHandle
= NULL
;
2863 dispatch_once( &sOnce
,
2865 sHandle
= dlopen( "/System/Library/PrivateFrameworks/CoreUtils.framework/CoreUtils", RTLD_NOW
);
2870 //===========================================================================================================================
2872 static Boolean
_MemIsAllZeros( const uint8_t * const inMemPtr
, const size_t inMemLen
)
2874 require_return_value( inMemLen
> 0, false );
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.
2881 return( ( inMemPtr
[ 0 ] == 0 ) && ( memcmp( inMemPtr
, inMemPtr
+ 1, inMemLen
- 1 ) == 0 ) );
2884 //===========================================================================================================================
2886 static Boolean
_IPv4AddressIsWhitelisted( const uint8_t inAddrBytes
[ STATIC_PARAM
4 ] )
2888 // Whitelist the all-zeros and localhost addresses.
2890 switch( ReadBig32( inAddrBytes
) )
2893 case UINT32_C( 0x7F000001 ): // 127.0.0.1
2901 //===========================================================================================================================
2903 static Boolean
_IPv6AddressIsWhitelisted( const uint8_t inAddrBytes
[ STATIC_PARAM
16 ] )
2905 // Whitelist the all-zeros and localhost addresses, i.e., :: and ::1.
2907 if( ( memcmp( inAddrBytes
, AllZeros16Bytes
, 15 ) == 0 ) && ( ( inAddrBytes
[ 15 ] == 0 ) || ( inAddrBytes
[ 15 ] == 1 ) ) )
2917 //===========================================================================================================================
2920 _DNSMessagePrintObfuscatedIPAddress(
2923 const uint8_t * inAddrBytes
,
2927 char tmpBuf
[ ( 16 * 2 ) + 1 ];
2929 require_return_value( ( inAddrLen
== 4 ) || ( inAddrLen
== 16 ), kSizeErr
);
2931 n
= _SNPrintF( tmpBuf
, sizeof( tmpBuf
), "%.4H", inAddrBytes
, (int) inAddrLen
, (int) inAddrLen
);
2932 require_return_value( n
>= 0, n
);
2934 return( _SNPrintF( inBufPtr
, inBufLen
, "%~s", tmpBuf
) );