]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil/DNSMessage.c
mDNSResponder-1096.0.2.tar.gz
[apple/mdnsresponder.git] / Clients / dnssdutil / DNSMessage.c
1 /*
2 Copyright (c) 2016-2019 Apple Inc. All rights reserved.
3 */
4
5 #include "DNSMessage.h"
6
7 //===========================================================================================================================
8
9 #define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 )
10
11 OSStatus
12 DNSMessageExtractDomainName(
13 const uint8_t * inMsgPtr,
14 size_t inMsgLen,
15 const uint8_t * inPtr,
16 uint8_t outName[ kDomainNameLengthMax ],
17 const uint8_t ** outPtr )
18 {
19 OSStatus err;
20 const uint8_t * label;
21 uint8_t labelLen;
22 const uint8_t * nextLabel;
23 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
24 uint8_t * dst = outName;
25 const uint8_t * const dstLim = outName ? ( outName + kDomainNameLengthMax ) : NULL;
26 const uint8_t * nameEnd = NULL;
27
28 require_action_quiet( ( inPtr >= inMsgPtr ) && ( inPtr < msgEnd ), exit, err = kRangeErr );
29
30 for( label = inPtr; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
31 {
32 if( labelLen <= kDomainLabelLengthMax )
33 {
34 nextLabel = label + 1 + labelLen;
35 require_action_quiet( nextLabel < msgEnd, exit, err = kUnderrunErr );
36 if( dst )
37 {
38 require_action_quiet( ( dstLim - dst ) > ( 1 + labelLen ), exit, err = kOverrunErr );
39 memcpy( dst, label, 1 + labelLen );
40 dst += ( 1 + labelLen );
41 }
42 }
43 else if( IsCompressionByte( labelLen ) )
44 {
45 uint16_t offset;
46
47 require_action_quiet( ( msgEnd - label ) >= 2, exit, err = kUnderrunErr );
48 if( !nameEnd )
49 {
50 nameEnd = label + 2;
51 if( !dst ) break;
52 }
53 offset = (uint16_t)( ( ( label[ 0 ] & 0x3F ) << 8 ) | label[ 1 ] );
54 nextLabel = inMsgPtr + offset;
55 require_action_quiet( nextLabel < msgEnd, exit, err = kUnderrunErr );
56 require_action_quiet( !IsCompressionByte( nextLabel[ 0 ] ), exit, err = kMalformedErr );
57 }
58 else
59 {
60 err = kMalformedErr;
61 goto exit;
62 }
63 }
64
65 if( dst ) *dst = 0;
66 if( !nameEnd ) nameEnd = label + 1;
67
68 if( outPtr ) *outPtr = nameEnd;
69 err = kNoErr;
70
71 exit:
72 return( err );
73 }
74
75 //===========================================================================================================================
76
77 OSStatus
78 DNSMessageExtractDomainNameString(
79 const void * inMsgPtr,
80 size_t inMsgLen,
81 const void * inPtr,
82 char inBuf[ kDNSServiceMaxDomainName ],
83 const uint8_t ** outPtr )
84 {
85 OSStatus err;
86 const uint8_t * nextPtr;
87 uint8_t domainName[ kDomainNameLengthMax ];
88
89 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, domainName, &nextPtr );
90 require_noerr_quiet( err, exit );
91
92 err = DomainNameToString( domainName, NULL, inBuf, NULL );
93 require_noerr_quiet( err, exit );
94
95 if( outPtr ) *outPtr = nextPtr;
96
97 exit:
98 return( err );
99 }
100
101 //===========================================================================================================================
102
103 OSStatus
104 DNSMessageExtractQuestion(
105 const uint8_t * inMsgPtr,
106 size_t inMsgLen,
107 const uint8_t * inPtr,
108 uint8_t outName[ kDomainNameLengthMax ],
109 uint16_t * outType,
110 uint16_t * outClass,
111 const uint8_t ** outPtr )
112 {
113 OSStatus err;
114 const uint8_t * const msgEnd = &inMsgPtr[ inMsgLen ];
115 const uint8_t * ptr;
116 const dns_fixed_fields_question * fields;
117
118 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, outName, &ptr );
119 require_noerr_quiet( err, exit );
120 require_action_quiet( (size_t)( msgEnd - ptr ) >= sizeof( dns_fixed_fields_question ), exit, err = kUnderrunErr );
121
122 fields = (const dns_fixed_fields_question *) ptr;
123 if( outType ) *outType = dns_fixed_fields_question_get_type( fields );
124 if( outClass ) *outClass = dns_fixed_fields_question_get_class( fields );
125 if( outPtr ) *outPtr = (const uint8_t *) &fields[ 1 ];
126
127 exit:
128 return( err );
129 }
130
131 //===========================================================================================================================
132
133 OSStatus
134 DNSMessageExtractRecord(
135 const uint8_t * inMsgPtr,
136 size_t inMsgLen,
137 const uint8_t * inPtr,
138 uint8_t outName[ kDomainNameLengthMax ],
139 uint16_t * outType,
140 uint16_t * outClass,
141 uint32_t * outTTL,
142 const uint8_t ** outRDataPtr,
143 size_t * outRDataLen,
144 const uint8_t ** outPtr )
145 {
146 OSStatus err;
147 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
148 const uint8_t * ptr;
149 const dns_fixed_fields_record * fields;
150 const uint8_t * rdata;
151 size_t rdLength;
152
153 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, outName, &ptr );
154 require_noerr_quiet( err, exit );
155 require_action_quiet( (size_t)( msgEnd - ptr ) >= sizeof( *fields ), exit, err = kUnderrunErr );
156
157 fields = (const dns_fixed_fields_record *) ptr;
158 rdata = ptr + sizeof( *fields );
159
160 rdLength = dns_fixed_fields_record_get_rdlength( fields );
161 require_action_quiet( (size_t)( msgEnd - rdata ) >= rdLength , exit, err = kUnderrunErr );
162
163 if( outType ) *outType = dns_fixed_fields_record_get_type( fields );
164 if( outClass ) *outClass = dns_fixed_fields_record_get_class( fields );
165 if( outTTL ) *outTTL = dns_fixed_fields_record_get_ttl( fields );
166 if( outRDataPtr ) *outRDataPtr = rdata;
167 if( outRDataLen ) *outRDataLen = rdLength;
168 if( outPtr ) *outPtr = &rdata[ rdLength ];
169
170 exit:
171 return( err );
172 }
173
174 //===========================================================================================================================
175
176 OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr )
177 {
178 OSStatus err;
179 unsigned int questionCount, i;
180 const DNSHeader * hdr;
181 const uint8_t * ptr;
182
183 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
184
185 hdr = (DNSHeader *) inMsgPtr;
186 questionCount = DNSHeaderGetQuestionCount( hdr );
187
188 ptr = (const uint8_t *) &hdr[ 1 ];
189 for( i = 0; i < questionCount; ++i )
190 {
191 err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, NULL, NULL, NULL, &ptr );
192 require_noerr_quiet( err, exit );
193 }
194
195 if( outPtr ) *outPtr = ptr;
196 err = kNoErr;
197
198 exit:
199 return( err );
200 }
201
202 //===========================================================================================================================
203
204 OSStatus
205 DNSMessageWriteQuery(
206 uint16_t inMsgID,
207 uint16_t inFlags,
208 const uint8_t * inQName,
209 uint16_t inQType,
210 uint16_t inQClass,
211 uint8_t outMsg[ STATIC_PARAM kDNSQueryMessageMaxLen ],
212 size_t * outLen )
213 {
214 OSStatus err;
215 DNSHeader * const hdr = (DNSHeader *) outMsg;
216 uint8_t * ptr;
217 size_t qnameLen;
218 size_t msgLen;
219
220 memset( hdr, 0, sizeof( *hdr ) );
221 DNSHeaderSetID( hdr, inMsgID );
222 DNSHeaderSetFlags( hdr, inFlags );
223 DNSHeaderSetQuestionCount( hdr, 1 );
224
225 qnameLen = DomainNameLength( inQName );
226 require_action_quiet( qnameLen <= kDomainNameLengthMax, exit, err = kSizeErr );
227
228 ptr = (uint8_t *) &hdr[ 1 ];
229 memcpy( ptr, inQName, qnameLen );
230 ptr += qnameLen;
231
232 dns_fixed_fields_question_init( (dns_fixed_fields_question *) ptr, inQType, inQClass );
233 ptr += sizeof( dns_fixed_fields_question );
234
235 msgLen = (size_t)( ptr - outMsg );
236
237 if( outLen ) *outLen = msgLen;
238 err = kNoErr;
239
240 exit:
241 return( err );
242 }
243
244 //===========================================================================================================================
245
246 OSStatus
247 DomainNameAppendString(
248 uint8_t inDomainName[ STATIC_PARAM kDomainNameLengthMax ],
249 const char * inString,
250 uint8_t ** outEnd )
251 {
252 OSStatus err;
253 const char * src;
254 uint8_t * root;
255 const uint8_t * const nameLim = inDomainName + kDomainNameLengthMax;
256
257 for( root = inDomainName; ( root < nameLim ) && *root; root += ( 1 + *root ) ) {}
258 require_action_quiet( root < nameLim, exit, err = kMalformedErr );
259
260 // If the string is a single dot, denoting the root domain, then there are no non-empty labels.
261
262 src = inString;
263 if( ( src[ 0 ] == '.' ) && ( src[ 1 ] == '\0' ) ) ++src;
264 while( *src )
265 {
266 uint8_t * const label = root;
267 const uint8_t * const labelLim = Min( &label[ 1 + kDomainLabelLengthMax ], nameLim - 1 );
268 uint8_t * dst;
269 int c;
270 size_t labelLen;
271
272 dst = &label[ 1 ];
273 while( *src && ( ( c = *src++ ) != '.' ) )
274 {
275 if( c == '\\' )
276 {
277 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
278 c = *src++;
279 if( isdigit_safe( c ) && isdigit_safe( src[ 0 ] ) && isdigit_safe( src[ 1 ] ) )
280 {
281 const int decimal = ( ( c - '0' ) * 100 ) + ( ( src[ 0 ] - '0' ) * 10 ) + ( src[ 1 ] - '0' );
282
283 if( decimal <= 255 )
284 {
285 c = decimal;
286 src += 2;
287 }
288 }
289 }
290 require_action_quiet( dst < labelLim, exit, err = kOverrunErr );
291 *dst++ = (uint8_t) c;
292 }
293
294 labelLen = (size_t)( dst - &label[ 1 ] );
295 require_action_quiet( labelLen > 0, exit, err = kMalformedErr );
296
297 label[ 0 ] = (uint8_t) labelLen;
298 root = dst;
299 *root = 0;
300 }
301
302 if( outEnd ) *outEnd = root + 1;
303 err = kNoErr;
304
305 exit:
306 return( err );
307 }
308
309 //===========================================================================================================================
310
311 OSStatus DomainNameDupEx( const uint8_t *inName, Boolean inLower, uint8_t **outNamePtr, size_t *outNameLen )
312 {
313 OSStatus err;
314 uint8_t * namePtr;
315 const size_t nameLen = DomainNameLength( inName );
316
317 namePtr = (uint8_t *) malloc( nameLen );
318 require_action_quiet( namePtr, exit, err = kNoMemoryErr );
319
320 if( inLower )
321 {
322 const uint8_t * src;
323 uint8_t * dst;
324 unsigned int len;
325
326 src = inName;
327 dst = namePtr;
328 while( ( len = *src ) != 0 )
329 {
330 *dst++ = *src++;
331 while( len-- > 0 )
332 {
333 const int c = *src++;
334 *dst++ = (uint8_t) tolower_safe( c );
335 }
336 }
337 *dst = 0;
338 }
339 else
340 {
341 memcpy( namePtr, inName, nameLen );
342 }
343
344 *outNamePtr = namePtr;
345 if( outNameLen ) *outNameLen = nameLen;
346 err = kNoErr;
347
348 exit:
349 return( err );
350 }
351
352 //===========================================================================================================================
353
354 Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 )
355 {
356 const uint8_t * p1 = inName1;
357 const uint8_t * p2 = inName2;
358 if( p1 == p2 ) return( true );
359 for( ;; )
360 {
361 int len1 = *p1++;
362 const int len2 = *p2++;
363 if( len1 != len2 ) return( false );
364 if( len1 == 0 ) return( true );
365 while( len1-- > 0 )
366 {
367 const int c1 = *p1++;
368 const int c2 = *p2++;
369 if( tolower_safe( c1 ) != tolower_safe( c2 ) ) return( false );
370 }
371 }
372 }
373
374 //===========================================================================================================================
375
376 OSStatus
377 DomainNameFromString(
378 uint8_t outName[ STATIC_PARAM kDomainNameLengthMax ],
379 const char * inString,
380 uint8_t ** outEnd )
381 {
382 outName[ 0 ] = 0;
383 return( DomainNameAppendString( outName, inString, outEnd ) );
384 }
385
386 //===========================================================================================================================
387
388 OSStatus
389 DomainNameToString(
390 const uint8_t * inName,
391 const uint8_t * inLimit,
392 char outString[ STATIC_PARAM kDNSServiceMaxDomainName ],
393 const uint8_t ** outPtr )
394 {
395 OSStatus err;
396 const uint8_t * label;
397 uint8_t labelLen;
398 const uint8_t * nextLabel;
399 char * dst;
400 const uint8_t * src;
401
402 require_action_quiet( !inLimit || ( inName < inLimit ), exit, err = kUnderrunErr );
403
404 // Convert each label up until the root label, i.e., the zero-length label.
405
406 dst = outString;
407 for( label = inName; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
408 {
409 require_action_quiet( labelLen <= kDomainLabelLengthMax, exit, err = kMalformedErr );
410
411 nextLabel = &label[ 1 + labelLen ];
412 require_action_quiet( ( nextLabel - inName ) < kDomainNameLengthMax, exit, err = kMalformedErr );
413 require_action_quiet( !inLimit || ( nextLabel < inLimit ), exit, err = kUnderrunErr );
414
415 for( src = &label[ 1 ]; src < nextLabel; ++src )
416 {
417 // Only allow 7-bit ASCII characters.
418 if( ( *src >= 32 ) && ( *src <= 126 ) )
419 {
420 if( ( *src == '.' ) || ( *src == '\\' ) || ( *src == ' ' ) ) *dst++ = '\\';
421 *dst++ = (char) *src;
422 }
423 else
424 {
425 *dst++ = '\\';
426 *dst++ = '0' + ( *src / 100 );
427 *dst++ = '0' + ( ( *src / 10 ) % 10 );
428 *dst++ = '0' + ( *src % 10 );
429 }
430 }
431 *dst++ = '.';
432 }
433
434 // At this point, label points to the root label.
435 // If the root label was the only label, then write a dot for it.
436
437 if( label == inName ) *dst++ = '.';
438 *dst = '\0';
439 if( outPtr ) *outPtr = label + 1;
440 err = kNoErr;
441
442 exit:
443 return( err );
444 }