2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
25 Change History (most recent first):
28 Revision 1.35 2004/06/05 00:14:44 cheshire
29 Fix signed/unsigned and other compiler warnings
31 Revision 1.34 2004/06/04 00:25:25 cheshire
32 Fix misaligned write exception that occurs on some platforms
34 Revision 1.33 2004/06/04 00:16:18 cheshire
35 Remove non-portable use of 'inline'
37 Revision 1.32 2004/06/03 03:09:58 ksekar
38 <rdar://problem/3668626>: Garbage Collection for Dynamic Updates
40 Revision 1.31 2004/05/28 23:42:36 ksekar
41 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
43 Revision 1.30 2004/05/26 09:08:04 bradley
44 Added cast to correct structure pointer when allocating domain name list element to fix C++ builds.
46 Revision 1.29 2004/05/18 23:51:25 cheshire
47 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
49 Revision 1.28 2004/05/13 04:54:20 ksekar
50 Unified list copy/free code. Added symetric list for
52 Revision 1.27 2004/04/22 20:29:07 cheshire
53 Log error message if no count field passed to PutResourceRecordTTL()
55 Revision 1.26 2004/04/22 04:07:01 cheshire
56 Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it
58 Revision 1.25 2004/04/22 03:05:28 cheshire
59 kDNSClass_ANY should be kDNSQClass_ANY
61 Revision 1.24 2004/04/22 02:51:20 cheshire
62 Use common code for HINFO/TXT and TSIG cases in putRData
64 Revision 1.23 2004/04/15 00:51:28 bradley
65 Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
66 Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
68 Revision 1.22 2004/04/14 23:09:28 ksekar
69 Support for TSIG signed dynamic updates.
71 Revision 1.21 2004/04/09 16:47:28 cheshire
72 <rdar://problem/3617655>: mDNSResponder escape handling inconsistent with BIND
74 Revision 1.20 2004/04/09 16:37:15 cheshire
75 Suggestion from Bob Bradley:
76 Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers
78 Revision 1.19 2004/04/02 19:34:38 cheshire
81 Revision 1.18 2004/03/30 06:45:00 cheshire
82 Compiler warning fixes from Don Woodward at Roku Labs
84 Revision 1.17 2004/03/19 22:25:20 cheshire
85 <rdar://problem/3579561>: Need to limit service types to fourteen characters
86 Won't actually do this for now, but keep the code around just in case
88 Revision 1.16 2004/03/08 02:45:35 cheshire
89 Minor change to make a couple of the log messages a bit shorter
91 Revision 1.15 2004/03/08 02:44:09 cheshire
92 <rdar://problem/3579561>: Need to limit service types to fourteen characters
94 Revision 1.14 2004/02/21 02:06:24 cheshire
95 Can't use anonymous unions -- they're non-standard and don't work on all compilers
97 Revision 1.13 2004/02/06 23:04:18 ksekar
98 Basic Dynamic Update support via mDNS_Register (dissabled via
99 UNICAST_REGISTRATION #define)
101 Revision 1.12 2004/02/03 22:37:10 cheshire
102 Delete unused (commented-out) code
104 Revision 1.11 2004/02/03 22:35:34 cheshire
105 <rdar://problem/3548256>: Should not allow empty string for resolve domain
107 Revision 1.10 2004/02/03 19:47:36 ksekar
108 Added an asyncronous state machine mechanism to uDNS.c, including
109 calls to find the parent zone for a domain name. Changes include code
110 in repository previously dissabled via "#if 0 //incomplete". Codepath
111 is currently unused, and will be called to create update records, etc.
113 Revision 1.9 2004/01/27 20:15:22 cheshire
114 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
116 Revision 1.8 2004/01/24 23:24:36 cheshire
117 Expanded out the list of local domains to reduce risk of mistakes in future
119 Revision 1.7 2004/01/24 08:32:30 bradley
120 Mask values with 0xFF before casting to avoid runtime truncation errors on Windows debug builds.
121 Separated octal-escaped sequences preceding decimal digits to avoid errors with some compilers wanting
122 to signal potentially hidden errors about the subsequent digit not being part of the octal sequence.
124 Revision 1.6 2004/01/24 04:59:15 cheshire
125 Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
127 Revision 1.5 2004/01/23 23:23:14 ksekar
128 Added TCP support for truncated unicast messages.
130 Revision 1.4 2004/01/22 02:15:33 cheshire
131 <rdar://problem/3536597>: Link-local reverse-mapping domains need to be resolved using link-local multicast
133 Revision 1.3 2004/01/21 21:16:29 cheshire
134 Minor tidy-up: Deleted a bunch of blank lines, trailing spaces, tabs, etc.
136 Revision 1.2 2003/12/13 05:47:48 bradley
137 Made local ptr const to fix error when assigning from const structure. Disable benign conditional
138 expression is constant warning when building with Microsoft compilers.
140 Revision 1.1 2003/12/13 03:05:27 ksekar
141 <rdar://problem/3192548>: DynDNS: Unicast query of service records
145 // Set mDNS_InstantiateInlines to tell mDNSClientAPI.h to instantiate inline functions, if necessary
146 #define mDNS_InstantiateInlines 1
147 #include "DNSCommon.h"
149 // Disable certain benign warnings with Microsoft compilers
150 #if (defined(_MSC_VER))
151 // Disable "conditional expression is constant" warning for debug macros.
152 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
153 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
154 #pragma warning(disable:4127)
157 // ***************************************************************************
158 #if COMPILER_LIKES_PRAGMA_MARK
160 #pragma mark - DNameList copy/deallocation routines
163 mDNSexport DNameListElem
*mDNS_CopyDNameList(const DNameListElem
*orig
)
165 DNameListElem
*copy
= mDNSNULL
, *newelem
;
166 const DNameListElem
*ptr
;
168 for (ptr
= orig
; ptr
; ptr
= ptr
->next
)
170 newelem
= (DNameListElem
*)mDNSPlatformMemAllocate(sizeof(DNameListElem
));
171 if (!newelem
) { LogMsg("ERROR: malloc"); return mDNSNULL
; }
172 mDNSPlatformStrCopy(ptr
->name
.c
, newelem
->name
.c
);
173 newelem
->next
= copy
;
179 mDNSexport
void mDNS_FreeDNameList(DNameListElem
*list
)
187 mDNSPlatformMemFree(fptr
);
191 // ***************************************************************************
192 #if COMPILER_LIKES_PRAGMA_MARK
194 #pragma mark - General Utility Functions
197 mDNSexport
const NetworkInterfaceInfo
*GetFirstActiveInterface(const NetworkInterfaceInfo
*intf
)
199 while (intf
&& !intf
->InterfaceActive
) intf
= intf
->next
;
203 mDNSexport mDNSInterfaceID
GetNextActiveInterfaceID(const NetworkInterfaceInfo
*intf
)
205 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
206 if (next
) return(next
->InterfaceID
); else return(mDNSNULL
);
209 mDNSexport mDNSu32
NumCacheRecordsForInterfaceID(const mDNS
*const m
, mDNSInterfaceID id
)
211 mDNSu32 slot
, used
= 0;
213 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
214 for (rr
= m
->rrcache_hash
[slot
]; rr
; rr
=rr
->next
)
215 if (rr
->resrec
.InterfaceID
== id
) used
++;
219 mDNSexport
char *DNSTypeName(mDNSu16 rrtype
)
223 case kDNSType_A
: return("Addr");
224 case kDNSType_CNAME
:return("CNAME");
225 case kDNSType_NULL
: return("NULL");
226 case kDNSType_PTR
: return("PTR");
227 case kDNSType_HINFO
:return("HINFO");
228 case kDNSType_TXT
: return("TXT");
229 case kDNSType_AAAA
: return("AAAA");
230 case kDNSType_SRV
: return("SRV");
231 case kDNSQType_ANY
: return("ANY");
233 static char buffer
[16];
234 mDNS_snprintf(buffer
, sizeof(buffer
), "(%d)", rrtype
);
240 mDNSexport
char *GetRRDisplayString_rdb(mDNS
*const m
, const ResourceRecord
*rr
, RDataBody
*rd
)
242 char *ptr
= m
->MsgBuffer
;
243 mDNSu32 length
= mDNS_snprintf(m
->MsgBuffer
, 79, "%4d %##s %s ", rr
->rdlength
, rr
->name
.c
, DNSTypeName(rr
->rrtype
));
246 case kDNSType_A
: mDNS_snprintf(m
->MsgBuffer
+length
, 79-length
, "%.4a", &rd
->ip
); break;
247 case kDNSType_CNAME
:// Same as PTR
248 case kDNSType_PTR
: mDNS_snprintf(m
->MsgBuffer
+length
, 79-length
, "%##s", &rd
->name
); break;
249 case kDNSType_HINFO
:// Display this the same as TXT (just show first string)
250 case kDNSType_TXT
: mDNS_snprintf(m
->MsgBuffer
+length
, 79-length
, "%#s", rd
->txt
.c
); break;
251 case kDNSType_AAAA
: mDNS_snprintf(m
->MsgBuffer
+length
, 79-length
, "%.16a", &rd
->ipv6
); break;
252 case kDNSType_SRV
: mDNS_snprintf(m
->MsgBuffer
+length
, 79-length
, "%##s", &rd
->srv
.target
); break;
253 default: mDNS_snprintf(m
->MsgBuffer
+length
, 79-length
, "RDLen %d: %s",
254 rr
->rdlength
, rd
->data
); break;
256 for (ptr
= m
->MsgBuffer
; *ptr
; ptr
++) if (*ptr
< ' ') *ptr
='.';
257 return(m
->MsgBuffer
);
260 mDNSexport mDNSu32
mDNSRandom(mDNSu32 max
)
262 static mDNSu32 seed
= 0;
265 if (!seed
) seed
= (mDNSu32
)mDNSPlatformTimeNow();
266 while (mask
< max
) mask
= (mask
<< 1) | 1;
267 do seed
= seed
* 21 + 1; while ((seed
& mask
) > max
);
268 return (seed
& mask
);
271 mDNSexport mDNSBool
mDNSSameAddress(const mDNSAddr
*ip1
, const mDNSAddr
*ip2
)
273 if (ip1
->type
== ip2
->type
)
277 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip1
->ip
.v4
, ip2
->ip
.v4
));
278 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip1
->ip
.v6
, ip2
->ip
.v6
));
284 mDNSexport mDNSBool
mDNSAddrIsDNSMulticast(const mDNSAddr
*ip
)
288 case mDNSAddrType_IPv4
: return(mDNSBool
)(ip
->ip
.v4
.NotAnInteger
== AllDNSLinkGroup
.NotAnInteger
);
289 case mDNSAddrType_IPv6
: return(mDNSBool
)(ip
->ip
.v6
.l
[0] == AllDNSLinkGroupv6
.l
[0] &&
290 ip
->ip
.v6
.l
[1] == AllDNSLinkGroupv6
.l
[1] &&
291 ip
->ip
.v6
.l
[2] == AllDNSLinkGroupv6
.l
[2] &&
292 ip
->ip
.v6
.l
[3] == AllDNSLinkGroupv6
.l
[3] );
293 default: return(mDNSfalse
);
297 // ***************************************************************************
298 #if COMPILER_LIKES_PRAGMA_MARK
300 #pragma mark - Domain Name Utility Functions
303 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
306 const int len
= *a
++;
308 if (len
> MAX_DOMAIN_LABEL
)
309 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
311 if (len
!= *b
++) return(mDNSfalse
);
312 for (i
=0; i
<len
; i
++)
316 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
317 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
318 if (ac
!= bc
) return(mDNSfalse
);
323 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
325 const mDNSu8
* a
= d1
->c
;
326 const mDNSu8
* b
= d2
->c
;
327 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
331 if (a
+ 1 + *a
>= max
)
332 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse
); }
333 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
341 mDNSexport mDNSBool
IsLocalDomain(const domainname
*d
)
343 // Domains that are defined to be resolved via link-local multicast are:
344 // local., 254.169.in-addr.arpa., and 0.8.E.F.ip6.arpa.
345 static const domainname
*n0
= (domainname
*)"\x5" "local";
346 static const domainname
*n1
= (domainname
*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
347 static const domainname
*n2
= (domainname
*)"\x1" "0" "\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
349 const domainname
*d1
, *d2
, *d3
, *d4
, *d5
, *d6
; // Top-level domain, second-level domain, etc.
350 d1
= d2
= d3
= d4
= d5
= d6
= mDNSNULL
;
353 d6
= d5
; d5
= d4
; d4
= d3
; d3
= d2
; d2
= d1
; d1
= d
;
354 d
= (domainname
*)(d
->c
+ 1 + d
->c
[0]);
357 if (d1
&& SameDomainName(d1
, n0
)) return(mDNStrue
);
358 if (d4
&& SameDomainName(d4
, n1
)) return(mDNStrue
);
359 if (d6
&& SameDomainName(d6
, n2
)) return(mDNStrue
);
363 // Returns length of a domain name INCLUDING the byte for the final null label
364 // i.e. for the root label "." it returns one
365 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
366 // Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
367 // If the given domainname is invalid, result is 256
368 mDNSexport mDNSu16
DomainNameLength(const domainname
*const name
)
370 const mDNSu8
*src
= name
->c
;
373 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
375 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
377 return((mDNSu16
)(src
- name
->c
+ 1));
380 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
381 // for the final null label i.e. for the root label "." it returns one.
382 // E.g. for the FQDN "foo.com." it returns 9
383 // (length, three data bytes, length, three more data bytes, final zero).
384 // In the case where a parent domain name is provided, and the given name is a child
385 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
386 // of the child name, plus TWO bytes for the compression pointer.
387 // E.g. for the name "foo.com." with parent "com.", it returns 6
388 // (length, three data bytes, two-byte compression pointer).
389 mDNSexport mDNSu16
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
391 const mDNSu8
*src
= name
->c
;
392 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
395 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
396 if (parent
&& SameDomainName((domainname
*)src
, parent
)) return((mDNSu16
)(src
- name
->c
+ 2));
398 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
400 return((mDNSu16
)(src
- name
->c
+ 1));
403 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
404 // The C string contains the label as-is, with no escaping, etc.
405 // Any dots in the name are literal dots, not label separators
406 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
407 // in the domainname bufer (i.e., the next byte after the terminating zero).
408 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
409 // AppendLiteralLabelString returns mDNSNULL.
410 mDNSexport mDNSu8
*AppendLiteralLabelString(domainname
*const name
, const char *cstr
)
412 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
413 const mDNSu8
*const lim1
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
414 const mDNSu8
*const lim2
= ptr
+ 1 + MAX_DOMAIN_LABEL
;
415 const mDNSu8
*const lim
= (lim1
< lim2
) ? lim1
: lim2
;
416 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
418 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the data
419 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
420 *ptr
++ = 0; // Put the null root label on the end
421 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
422 else return(ptr
); // Success: return new value of ptr
425 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
426 // The C string is in conventional DNS syntax:
427 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
428 // If successful, AppendDNSNameString returns a pointer to the next unused byte
429 // in the domainname bufer (i.e., the next byte after the terminating zero).
430 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
431 // AppendDNSNameString returns mDNSNULL.
432 mDNSexport mDNSu8
*AppendDNSNameString(domainname
*const name
, const char *cstr
)
434 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
435 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
436 while (*cstr
&& ptr
< lim
) // While more characters, and space to put them...
438 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
439 while (*cstr
&& *cstr
!= '.' && ptr
< lim
) // While we have characters in the label...
441 mDNSu8 c
= (mDNSu8
)*cstr
++; // Read the character
442 if (c
== '\\') // If escape character, check next character
444 c
= (mDNSu8
)*cstr
++; // Assume we'll just take the next character
445 if (mdnsIsDigit(cstr
[-1]) && mdnsIsDigit(cstr
[0]) && mdnsIsDigit(cstr
[1]))
446 { // If three decimal digits,
447 int v0
= cstr
[-1] - '0'; // then interpret as three-digit decimal
448 int v1
= cstr
[ 0] - '0';
449 int v2
= cstr
[ 1] - '0';
450 int val
= v0
* 100 + v1
* 10 + v2
;
451 if (val
<= 255) { c
= (mDNSu8
)val
; cstr
+= 2; } // If valid three-digit decimal value, use it
454 *ptr
++ = c
; // Write the character
456 if (*cstr
) cstr
++; // Skip over the trailing dot (if present)
457 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) // If illegal label, abort
459 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
462 *ptr
++ = 0; // Put the null root label on the end
463 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
464 else return(ptr
); // Success: return new value of ptr
467 // AppendDomainLabel appends a single label to a name.
468 // If successful, AppendDomainLabel returns a pointer to the next unused byte
469 // in the domainname bufer (i.e., the next byte after the terminating zero).
470 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
471 // AppendDomainLabel returns mDNSNULL.
472 mDNSexport mDNSu8
*AppendDomainLabel(domainname
*const name
, const domainlabel
*const label
)
475 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
477 // Check label is legal
478 if (label
->c
[0] > MAX_DOMAIN_LABEL
) return(mDNSNULL
);
480 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
481 if (ptr
+ 1 + label
->c
[0] + 1 > name
->c
+ MAX_DOMAIN_NAME
) return(mDNSNULL
);
483 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
]; // Copy the label data
484 *ptr
++ = 0; // Put the null root label on the end
488 mDNSexport mDNSu8
*AppendDomainName(domainname
*const name
, const domainname
*const append
)
490 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
491 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
492 const mDNSu8
* src
= append
->c
;
496 if (ptr
+ 1 + src
[0] > lim
) return(mDNSNULL
);
497 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
498 *ptr
= 0; // Put the null root label on the end
504 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
505 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
506 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
507 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
508 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
509 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
510 mDNSexport mDNSBool
MakeDomainLabelFromLiteralString(domainlabel
*const label
, const char *cstr
)
512 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
513 const mDNSu8
*const limit
= label
->c
+ 1 + MAX_DOMAIN_LABEL
; // The maximum we can put
514 while (*cstr
&& ptr
< limit
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the label
515 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1); // Set the length byte
516 return(*cstr
== 0); // Return mDNStrue if we successfully consumed all input
519 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
520 // The C string is in conventional DNS syntax:
521 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
522 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
523 // in the domainname bufer (i.e., the next byte after the terminating zero).
524 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
525 // MakeDomainNameFromDNSNameString returns mDNSNULL.
526 mDNSexport mDNSu8
*MakeDomainNameFromDNSNameString(domainname
*const name
, const char *cstr
)
528 name
->c
[0] = 0; // Make an empty domain name
529 return(AppendDNSNameString(name
, cstr
)); // And then add this string to it
532 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
534 const mDNSu8
* src
= label
->c
; // Domain label we're reading
535 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
536 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
537 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
538 while (src
< end
) // While we have characters in the label
543 if (c
== '.' || c
== esc
) // If character is a dot or the escape character
544 *ptr
++ = esc
; // Output escape character
545 else if (c
<= ' ') // If non-printing ascii,
546 { // Output decimal escape sequence
548 *ptr
++ = (char) ('0' + (c
/ 100) );
549 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
550 c
= (mDNSu8
)('0' + (c
) % 10);
553 *ptr
++ = (char)c
; // Copy the character
555 *ptr
= 0; // Null-terminate the string
556 return(ptr
); // and return
559 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes)
560 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
562 const mDNSu8
*src
= name
->c
; // Domain name we're reading
563 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
565 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
567 while (*src
) // While more characters in the domain name
569 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
570 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
571 if (!ptr
) return(mDNSNULL
);
573 *ptr
++ = '.'; // Write the dot after the label
576 *ptr
++ = 0; // Null-terminate the string
577 return(ptr
); // and return
581 // Host names must start with a letter, end with a letter or digit,
582 // and have as interior characters only letters, digits, and hyphen.
583 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
585 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
587 const mDNSu8
* src
= &UTF8Name
[1];
588 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
589 mDNSu8
* ptr
= &hostlabel
->c
[1];
590 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
593 // Delete apostrophes from source name
594 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
595 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
596 { src
+= 3; continue; } // Unicode curly apostrophe
599 if (mdnsValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
600 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
604 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
605 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
608 #if MDNS_ENFORCE_SERVICE_TYPE_LENGTH
609 mDNSlocal mDNSBool
AllowedServiceNameException(const mDNSu8
*const src
)
611 if (SameDomainLabel(src
, (mDNSu8
*)"\x12_MacOSXDupSuppress")) return(mDNStrue
);
612 LogMsg("Application protocol name %#s too long; see <http://www.dns-sd.org/ServiceTypes.html>", src
);
617 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
618 const domainlabel
*name
, const domainname
*type
, const domainname
*const domain
)
621 mDNSu8
*dst
= fqdn
->c
;
623 const char *errormsg
;
625 // In the case where there is no name (and ONLY in that case),
626 // a single-label subtype is allowed as the first label of a three-part "type"
629 const mDNSu8
*s2
= type
->c
+ 1 + type
->c
[0];
630 if (type
->c
[0] > 0 && type
->c
[0] < 0x40 &&
631 s2
[0] > 0 && s2
[0] < 0x40 &&
632 s2
[1+s2
[0]] > 0 && s2
[1+s2
[0]] < 0x40)
634 name
= (domainlabel
*)type
;
635 type
= (domainname
*)s2
;
639 if (name
&& name
->c
[0])
641 src
= name
->c
; // Put the service name into the domain name
643 if (len
>= 0x40) { errormsg
="Service instance name too long"; goto fail
; }
644 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
647 name
= (domainlabel
*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
649 src
= type
->c
; // Put the service type into the domain name
651 #if MDNS_ENFORCE_SERVICE_TYPE_LENGTH
652 if (len
< 2 || len
> 15)
653 if (!AllowedServiceNameException(src
)) // If length not legal, check our grandfather-exceptions list
654 { errormsg
="Application protocol name must be underscore plus 1-14 characters"; goto fail
; }
656 if (len
< 2 || len
>= 0x40) { errormsg
="Application protocol name should be underscore plus 1-14 characters"; goto fail
; }
658 if (src
[1] != '_') { errormsg
="Application protocol name must begin with underscore"; goto fail
; }
659 for (i
=2; i
<=len
; i
++)
660 if (!mdnsIsLetter(src
[i
]) && !mdnsIsDigit(src
[i
]) && src
[i
] != '-' && src
[i
] != '_')
661 { errormsg
="Application protocol name must contain only letters, digits, and hyphens"; goto fail
; }
662 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
665 if (!(len
== 4 && src
[1] == '_' &&
666 (((src
[2] | 0x20) == 'u' && (src
[3] | 0x20) == 'd') || ((src
[2] | 0x20) == 't' && (src
[3] | 0x20) == 'c')) &&
667 (src
[4] | 0x20) == 'p'))
668 { errormsg
="Service transport protocol name must be _udp or _tcp"; goto fail
; }
669 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
671 if (*src
) { errormsg
="Service type must have only two labels"; goto fail
; }
674 if (!domain
->c
[0]) { errormsg
="Service domain must be non-empty"; goto fail
; }
675 dst
= AppendDomainName(fqdn
, domain
);
676 if (!dst
) { errormsg
="Service domain too long"; goto fail
; }
680 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg
, name
->c
, type
->c
, domain
->c
);
684 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
685 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
688 const mDNSu8
*src
= fqdn
->c
;
689 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
692 dst
= name
->c
; // Extract the service name from the domain name
694 if (len
>= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse
); }
695 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
697 dst
= type
->c
; // Extract the service type from the domain name
699 if (len
>= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse
); }
700 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
703 if (len
>= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse
); }
704 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
705 *dst
++ = 0; // Put the null root label on the end of the service type
707 dst
= domain
->c
; // Extract the service domain from the domain name
712 { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse
); }
713 if (src
+ 1 + len
+ 1 >= max
)
714 { debugf("DeconstructServiceName: service domain too long"); return(mDNSfalse
); }
715 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
717 *dst
++ = 0; // Put the null root label on the end
722 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
723 // name ends in "-nnn", where n is some decimal number.
724 mDNSexport mDNSBool
LabelContainsSuffix(const domainlabel
*const name
, const mDNSBool RichText
)
726 mDNSu16 l
= name
->c
[0];
730 if (l
< 4) return mDNSfalse
; // Need at least " (2)"
731 if (name
->c
[l
--] != ')') return mDNSfalse
; // Last char must be ')'
732 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Preceeded by a digit
734 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
735 return (name
->c
[l
] == '(' && name
->c
[l
- 1] == ' ');
739 if (l
< 2) return mDNSfalse
; // Need at least "-2"
740 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Last char must be a digit
742 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
743 return (name
->c
[l
] == '-');
747 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
748 // responsible for ensuring that the label does indeed contain a suffix. returns the number
749 // from the suffix that was removed.
750 mDNSexport mDNSu32
RemoveLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
752 mDNSu32 val
= 0, multiplier
= 1;
754 // Chop closing parentheses from RichText suffix
755 if (RichText
&& name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == ')') name
->c
[0]--;
757 // Get any existing numerical suffix off the name
758 while (mdnsIsDigit(name
->c
[name
->c
[0]]))
759 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
761 // Chop opening parentheses or dash from suffix
764 if (name
->c
[0] >= 2 && name
->c
[name
->c
[0]] == '(' && name
->c
[name
->c
[0]-1] == ' ') name
->c
[0] -= 2;
768 if (name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == '-') name
->c
[0] -= 1;
774 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
775 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
776 mDNSexport
void AppendLabelSuffix(domainlabel
*name
, mDNSu32 val
, mDNSBool RichText
)
778 mDNSu32 divisor
= 1, chars
= 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
779 if (RichText
) chars
= 4; // Shortest possible RichText suffix is 4 characters (" (2)")
781 // Truncate trailing spaces from RichText names
782 if (RichText
) while (name
->c
[name
->c
[0]] == ' ') name
->c
[0]--;
784 while (val
>= divisor
* 10) { divisor
*= 10; chars
++; }
786 if (name
->c
[0] > (mDNSu8
)(MAX_DOMAIN_LABEL
- chars
))
788 name
->c
[0] = (mDNSu8
)(MAX_DOMAIN_LABEL
- chars
);
789 // If the following character is a UTF-8 continuation character,
790 // we just chopped a multi-byte UTF-8 character in the middle, so strip back to a safe truncation point
791 while (name
->c
[0] > 0 && (name
->c
[name
->c
[0]+1] & 0xC0) == 0x80) name
->c
[0]--;
794 if (RichText
) { name
->c
[++name
->c
[0]] = ' '; name
->c
[++name
->c
[0]] = '('; }
795 else { name
->c
[++name
->c
[0]] = '-'; }
799 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
804 if (RichText
) name
->c
[++name
->c
[0]] = ')';
807 mDNSexport
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
811 if (LabelContainsSuffix(name
, RichText
))
812 val
= RemoveLabelSuffix(name
, RichText
);
814 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
815 // If existing suffix in the range 2-9, increment it.
816 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
817 // so add a random increment to improve the chances of finding an available name next time.
818 if (val
== 0) val
= 2;
819 else if (val
< 10) val
++;
820 else val
+= 1 + mDNSRandom(99);
822 AppendLabelSuffix(name
, val
, RichText
);
825 // ***************************************************************************
826 #if COMPILER_LIKES_PRAGMA_MARK
828 #pragma mark - Resource Record Utility Functions
831 mDNSexport mDNSu32
RDataHashValue(mDNSu16
const rdlength
, const RDataBody
*const rdb
)
835 for (i
=0; i
+1 < rdlength
; i
+=2)
837 sum
+= (((mDNSu32
)(rdb
->data
[i
])) << 8) | rdb
->data
[i
+1];
838 sum
= (sum
<<3) | (sum
>>29);
842 sum
+= ((mDNSu32
)(rdb
->data
[i
])) << 8;
847 mDNSexport mDNSBool
SameRData(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
849 if (r1
->rrtype
!= r2
->rrtype
) return(mDNSfalse
);
850 if (r1
->rdlength
!= r2
->rdlength
) return(mDNSfalse
);
851 if (r1
->rdatahash
!= r2
->rdatahash
) return(mDNSfalse
);
852 if (r1
->rdnamehash
!= r2
->rdnamehash
) return(mDNSfalse
);
855 case kDNSType_CNAME
:// Same as PTR
856 case kDNSType_PTR
: return(SameDomainName(&r1
->rdata
->u
.name
, &r2
->rdata
->u
.name
));
858 case kDNSType_SRV
: return(mDNSBool
)( r1
->rdata
->u
.srv
.priority
== r2
->rdata
->u
.srv
.priority
&&
859 r1
->rdata
->u
.srv
.weight
== r2
->rdata
->u
.srv
.weight
&&
860 r1
->rdata
->u
.srv
.port
.NotAnInteger
== r2
->rdata
->u
.srv
.port
.NotAnInteger
&&
861 SameDomainName(&r1
->rdata
->u
.srv
.target
, &r2
->rdata
->u
.srv
.target
) );
863 default: return(mDNSPlatformMemSame(r1
->rdata
->u
.data
, r2
->rdata
->u
.data
, r1
->rdlength
));
867 mDNSexport mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
869 if (rr
->InterfaceID
&&
871 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
873 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
874 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->qtype
&& q
->qtype
!= kDNSQType_ANY
) return(mDNSfalse
);
875 if ( rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
876 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(&rr
->name
, &q
->qname
));
879 mDNSexport mDNSu16
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
881 RDataBody
*rd
= &rr
->rdata
->u
;
882 const domainname
*const name
= estimate
? &rr
->name
: mDNSNULL
;
885 case kDNSType_A
: return(sizeof(rd
->ip
));
886 case kDNSType_CNAME
:// Same as PTR
887 case kDNSType_NS
: // Same as PTR
888 case kDNSType_PTR
: return(CompressedDomainNameLength(&rd
->name
, name
));
889 case kDNSType_HINFO
:return(mDNSu16
)(2 + (int)rd
->data
[0] + (int)rd
->data
[1 + (int)rd
->data
[0]]);
890 case kDNSType_NULL
: // Same as TXT -- not self-describing, so have to just trust rdlength
891 case kDNSType_TXT
: return(rr
->rdlength
); // TXT is not self-describing, so have to just trust rdlength
892 case kDNSType_AAAA
: return(sizeof(rd
->ipv6
));
893 case kDNSType_SRV
: return(mDNSu16
)(6 + CompressedDomainNameLength(&rd
->srv
.target
, name
));
894 case kDNSType_SOA
: return (mDNSu16
)(CompressedDomainNameLength(&rd
->soa
.mname
, name
) +
895 CompressedDomainNameLength(&rd
->soa
.rname
, name
) +
896 5 * sizeof(mDNSOpaque32
));
897 case kDNSType_OPT
: return(rr
->rdlength
);
898 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
899 return(rr
->rdlength
);
903 mDNSexport mDNSBool
ValidateRData(const mDNSu16 rrtype
, const mDNSu16 rdlength
, const RData
*const rd
)
908 case kDNSType_A
: return(rdlength
== sizeof(mDNSv4Addr
));
910 case kDNSType_NS
: // Same as PTR
911 case kDNSType_MD
: // Same as PTR
912 case kDNSType_MF
: // Same as PTR
913 case kDNSType_CNAME
:// Same as PTR
914 //case kDNSType_SOA not checked
915 case kDNSType_MB
: // Same as PTR
916 case kDNSType_MG
: // Same as PTR
917 case kDNSType_MR
: // Same as PTR
918 //case kDNSType_NULL not checked (no specified format, so always valid)
919 //case kDNSType_WKS not checked
920 case kDNSType_PTR
: len
= DomainNameLength(&rd
->u
.name
);
921 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== len
);
923 case kDNSType_HINFO
:// Same as TXT (roughly)
924 case kDNSType_MINFO
:// Same as TXT (roughly)
926 const mDNSu8
*ptr
= rd
->u
.txt
.c
;
927 const mDNSu8
*end
= rd
->u
.txt
.c
+ rdlength
;
928 while (ptr
< end
) ptr
+= 1 + ptr
[0];
932 case kDNSType_AAAA
: return(rdlength
== sizeof(mDNSv6Addr
));
934 case kDNSType_MX
: len
= DomainNameLength(&rd
->u
.mx
.exchange
);
935 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 2+len
);
937 case kDNSType_SRV
: len
= DomainNameLength(&rd
->u
.srv
.target
);
938 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 6+len
);
940 default: return(mDNStrue
); // Allow all other types without checking
944 // ***************************************************************************
945 #if COMPILER_LIKES_PRAGMA_MARK
948 #pragma mark - DNS Message Creation Functions
951 mDNSexport
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
957 h
->numAuthorities
= 0;
958 h
->numAdditionals
= 0;
961 mDNSexport
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
963 const mDNSu8
*result
= end
- *domname
- 1;
965 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
967 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
968 while (result
>= base
)
970 // If the length byte and first character of the label match, then check further to see
971 // if this location in the packet will yield a useful name compression pointer.
972 if (result
[0] == domname
[0] && result
[1] == domname
[1])
974 const mDNSu8
*name
= domname
;
975 const mDNSu8
*targ
= result
;
976 while (targ
+ *name
< end
)
978 // First see if this label matches
980 const mDNSu8
*pointertarget
;
981 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
982 if (i
<= *name
) break; // If label did not match, bail out
983 targ
+= 1 + *name
; // Else, did match, so advance target pointer
984 name
+= 1 + *name
; // and proceed to check next label
985 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
986 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
988 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
989 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
990 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
991 if (targ
+1 >= end
) break; // Second byte not present!
992 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
993 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
994 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
995 targ
= pointertarget
;
998 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
1003 // Put a string of dot-separated labels as length-prefixed labels
1004 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1005 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1006 // end points to the end of the message so far
1007 // ptr points to where we want to put the name
1008 // limit points to one byte past the end of the buffer that we must not overrun
1009 // domainname is the name to put
1010 mDNSexport mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
1011 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
1013 const mDNSu8
*const base
= (const mDNSu8
*)msg
;
1014 const mDNSu8
* np
= name
->c
;
1015 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1016 const mDNSu8
* pointer
= mDNSNULL
;
1017 const mDNSu8
*const searchlimit
= ptr
;
1019 while (*np
&& ptr
< limit
-1) // While we've got characters in the name, and space to write them in the message...
1021 if (*np
> MAX_DOMAIN_LABEL
)
1022 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name
->c
); return(mDNSNULL
); }
1024 // This check correctly allows for the final trailing root label:
1026 // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
1027 // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
1028 // We know that max will be at name->c[255]
1029 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1030 // six bytes, then exit the loop, write the final terminating root label, and the domain
1031 // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
1032 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1033 if (np
+ 1 + *np
>= max
)
1034 { LogMsg("Malformed domain name %##s (more than 255 bytes)", name
->c
); return(mDNSNULL
); }
1036 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
1037 if (pointer
) // Use a compression pointer if we can
1039 mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
1040 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
1041 *ptr
++ = (mDNSu8
)( offset
& 0xFF);
1044 else // Else copy one label and try again
1048 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
1050 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
1054 if (ptr
< limit
) // If we didn't run out of space
1056 *ptr
++ = 0; // Put the final root label
1057 return(ptr
); // and return
1063 mDNSlocal mDNSu8
*putVal16(mDNSu8
*ptr
, mDNSu16 val
)
1065 ptr
[0] = (mDNSu8
)((val
>> 8 ) & 0xFF);
1066 ptr
[1] = (mDNSu8
)((val
) & 0xFF);
1067 return ptr
+ sizeof(mDNSOpaque16
);
1070 mDNSlocal mDNSu8
*putOptRData(mDNSu8
*ptr
, const mDNSu8
*limit
, ResourceRecord
*rr
)
1075 while (nput
< rr
->rdlength
)
1077 // check if space for opt/optlen
1078 if (ptr
+ (2 * sizeof(mDNSu16
)) > limit
) goto space_err
;
1079 (mDNSu8
*)opt
= rr
->rdata
->u
.data
+ nput
;
1080 ptr
= putVal16(ptr
, opt
->opt
);
1081 ptr
= putVal16(ptr
, opt
->optlen
);
1082 nput
+= 2 * sizeof(mDNSu16
);
1083 if (opt
->opt
== kDNSOpt_LLQ
)
1085 if (ptr
+ sizeof(LLQOptData
) > limit
) goto space_err
;
1086 ptr
= putVal16(ptr
, opt
->OptData
.llq
.vers
);
1087 ptr
= putVal16(ptr
, opt
->OptData
.llq
.llqOp
);
1088 ptr
= putVal16(ptr
, opt
->OptData
.llq
.err
);
1089 mDNSPlatformMemCopy(opt
->OptData
.llq
.id
, ptr
, 8); // 8-byte id
1091 *(mDNSOpaque32
*)ptr
= mDNSOpaque32fromIntVal(opt
->OptData
.llq
.lease
);
1092 ptr
+= sizeof(mDNSOpaque32
);
1093 nput
+= sizeof(LLQOptData
);
1095 else if (opt
->opt
== kDNSOpt_Lease
)
1097 if (ptr
+ sizeof(mDNSs32
) > limit
) goto space_err
;
1098 *(mDNSOpaque32
*)ptr
= mDNSOpaque32fromIntVal(opt
->OptData
.lease
);
1099 ptr
+= sizeof(mDNSs32
);
1100 nput
+= sizeof(mDNSs32
);
1102 else { LogMsg("putOptRData - unknown option %d", opt
->opt
); return mDNSNULL
; }
1108 LogMsg("ERROR: putOptRData - out of space");
1112 mDNSlocal mDNSu16
getVal16(const mDNSu8
**ptr
)
1114 mDNSu16 val
= (mDNSu16
)(((mDNSu16
)(*ptr
)[0]) << 8 | (*ptr
)[1]);
1115 *ptr
+= sizeof(mDNSOpaque16
);
1119 mDNSlocal
const mDNSu8
*getOptRdata(const mDNSu8
*ptr
, const mDNSu8
*limit
, ResourceRecord
*rr
, mDNSu16 pktRDLen
)
1124 while (nread
< pktRDLen
)
1126 opt
= (rdataOpt
*)(rr
->rdata
->u
.data
+ nread
);
1127 // space for opt + optlen
1128 if (nread
+ (2 * sizeof(mDNSu16
)) > rr
->rdata
->MaxRDLength
) goto space_err
;
1129 opt
->opt
= getVal16(&ptr
);
1130 opt
->optlen
= getVal16(&ptr
);
1131 nread
+= 2 * sizeof(mDNSu16
);
1132 if (opt
->opt
== kDNSOpt_LLQ
)
1134 if ((unsigned)(limit
- ptr
) < sizeof(LLQOptData
)) goto space_err
;
1135 opt
->OptData
.llq
.vers
= getVal16(&ptr
);
1136 opt
->OptData
.llq
.llqOp
= getVal16(&ptr
);
1137 opt
->OptData
.llq
.err
= getVal16(&ptr
);
1138 mDNSPlatformMemCopy(ptr
, opt
->OptData
.llq
.id
, 8);
1140 opt
->OptData
.llq
.lease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1141 if (opt
->OptData
.llq
.lease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1142 opt
->OptData
.llq
.lease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1143 ptr
+= sizeof(mDNSOpaque32
);
1144 nread
+= sizeof(LLQOptData
);
1146 else if (opt
->opt
== kDNSOpt_Lease
)
1148 if ((unsigned)(limit
- ptr
) < sizeof(mDNSs32
)) goto space_err
;
1150 opt
->OptData
.lease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1151 if (opt
->OptData
.lease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1152 opt
->OptData
.lease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1153 ptr
+= sizeof(mDNSs32
);
1154 nread
+= sizeof(mDNSs32
);
1156 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt
->opt
); return mDNSNULL
; }
1159 rr
->rdlength
= pktRDLen
;
1163 LogMsg("ERROR: getLLQRdata - out of space");
1167 mDNSexport mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, ResourceRecord
*rr
)
1171 case kDNSType_A
: if (rr
->rdlength
!= 4)
1173 debugf("putRData: Illegal length %d for kDNSType_A", rr
->rdlength
);
1176 if (ptr
+ 4 > limit
) return(mDNSNULL
);
1177 *ptr
++ = rr
->rdata
->u
.ip
.b
[0];
1178 *ptr
++ = rr
->rdata
->u
.ip
.b
[1];
1179 *ptr
++ = rr
->rdata
->u
.ip
.b
[2];
1180 *ptr
++ = rr
->rdata
->u
.ip
.b
[3];
1183 case kDNSType_CNAME
:// Same as PTR
1184 case kDNSType_PTR
: return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.name
));
1186 case kDNSType_AAAA
: if (rr
->rdlength
!= sizeof(rr
->rdata
->u
.ipv6
))
1188 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr
->rdlength
);
1191 if (ptr
+ sizeof(rr
->rdata
->u
.ipv6
) > limit
) return(mDNSNULL
);
1192 mDNSPlatformMemCopy(&rr
->rdata
->u
.ipv6
, ptr
, sizeof(rr
->rdata
->u
.ipv6
));
1193 return(ptr
+ sizeof(rr
->rdata
->u
.ipv6
));
1195 case kDNSType_SRV
: if (ptr
+ 6 > limit
) return(mDNSNULL
);
1196 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
>> 8);
1197 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
& 0xFF);
1198 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
>> 8);
1199 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
& 0xFF);
1200 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[0];
1201 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[1];
1202 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.srv
.target
));
1203 case kDNSType_OPT
: return putOptRData(ptr
, limit
, rr
);
1205 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr
->rrtype
);
1206 // Fall through to common code below
1207 case kDNSType_HINFO
:
1209 case kDNSType_TSIG
: if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
1210 mDNSPlatformMemCopy(rr
->rdata
->u
.data
, ptr
, rr
->rdlength
);
1211 return(ptr
+ rr
->rdlength
);
1215 mDNSexport mDNSu8
*PutResourceRecordTTL(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32 ttl
)
1218 mDNSu16 actualLength
;
1219 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1221 // If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes,
1222 // but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet
1223 if (msg
->h
.numAnswers
|| msg
->h
.numAuthorities
|| msg
->h
.numAdditionals
)
1224 limit
= msg
->data
+ NormalMaxDNSMessageData
;
1226 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
1228 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
1232 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->name
);
1233 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1234 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
1235 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
1236 ptr
[2] = (mDNSu8
)(rr
->rrclass
>> 8);
1237 ptr
[3] = (mDNSu8
)(rr
->rrclass
& 0xFF);
1238 ptr
[4] = (mDNSu8
)((ttl
>> 24) & 0xFF);
1239 ptr
[5] = (mDNSu8
)((ttl
>> 16) & 0xFF);
1240 ptr
[6] = (mDNSu8
)((ttl
>> 8) & 0xFF);
1241 ptr
[7] = (mDNSu8
)( ttl
& 0xFF);
1242 endofrdata
= putRData(msg
, ptr
+10, limit
, rr
);
1243 if (!endofrdata
) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
)); return(mDNSNULL
); }
1245 // Go back and fill in the actual number of data bytes we wrote
1246 // (actualLength can be less than rdlength when domain name compression is used)
1247 actualLength
= (mDNSu16
)(endofrdata
- ptr
- 10);
1248 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
1249 ptr
[9] = (mDNSu8
)(actualLength
& 0xFF);
1251 if (count
) (*count
)++;
1252 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr
->name
.c
, DNSTypeName(rr
->rrtype
));
1256 mDNSexport mDNSu8
*PutResourceRecordCappedTTL(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32
1259 if (maxttl
> rr
->rroriginalttl
) maxttl
= rr
->rroriginalttl
;
1260 return(PutResourceRecordTTL(msg
, ptr
, count
, rr
, maxttl
));
1263 mDNSexport mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1264 mDNSu16
*count
, const AuthRecord
*rr
)
1266 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->resrec
.name
);
1267 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1268 ptr
[0] = (mDNSu8
)(rr
->resrec
.rrtype
>> 8); // Put type
1269 ptr
[1] = (mDNSu8
)(rr
->resrec
.rrtype
& 0xFF);
1270 ptr
[2] = (mDNSu8
)(rr
->resrec
.rrclass
>> 8); // Put class
1271 ptr
[3] = (mDNSu8
)(rr
->resrec
.rrclass
& 0xFF);
1272 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
1273 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
1278 mDNSexport mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
1280 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1281 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1282 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1283 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1284 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1285 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
1286 msg
->h
.numQuestions
++;
1290 // ***************************************************************************
1291 #if COMPILER_LIKES_PRAGMA_MARK
1293 #pragma mark - DNS Message Parsing Functions
1296 mDNSexport mDNSu32
DomainNameHashValue(const domainname
*const name
)
1301 for (c
= name
->c
; c
[0] != 0 && c
[1] != 0; c
+= 2)
1303 sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8) |
1304 (mDNSIsUpperCase(c
[1]) ? c
[1] + 'a' - 'A' : c
[1]);
1305 sum
= (sum
<<3) | (sum
>>29);
1307 if (c
[0]) sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8);
1311 mDNSexport
void SetNewRData(ResourceRecord
*const rr
, RData
*NewRData
, mDNSu16 rdlength
)
1316 rr
->rdata
= NewRData
;
1317 rr
->rdlength
= rdlength
;
1319 // Must not try to get target pointer until after updating rr->rdata
1320 target
= GetRRDomainNameTarget(rr
);
1321 rr
->rdlength
= GetRDLength(rr
, mDNSfalse
);
1322 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
1323 rr
->rdatahash
= RDataHashValue(rr
->rdlength
, &rr
->rdata
->u
);
1324 rr
->rdnamehash
= target
? DomainNameHashValue(target
) : 0;
1327 mDNSexport
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
1331 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1332 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1334 while (1) // Read sequence of labels
1336 const mDNSu8 len
= *ptr
++; // Read length of this label
1337 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
1340 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1341 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1342 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
1343 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1348 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
1349 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
1350 case 0xC0: return(ptr
+1);
1355 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
1356 mDNSexport
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
1357 domainname
*const name
)
1359 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
1360 mDNSu8
*np
= name
->c
; // Name pointer
1361 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
1363 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1364 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1366 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1368 while (1) // Read sequence of labels
1370 const mDNSu8 len
= *ptr
++; // Read length of this label
1371 if (len
== 0) break; // If length is zero, that means this name is complete
1377 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1378 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1379 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
1380 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1382 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
1383 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1386 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
1389 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
1391 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
1392 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
1393 ptr
= (mDNSu8
*)msg
+ offset
;
1394 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1395 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
1397 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
1402 if (nextbyte
) return(nextbyte
);
1406 mDNSexport
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1408 mDNSu16 pktrdlength
;
1410 ptr
= skipDomainName(msg
, ptr
, end
);
1411 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1413 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1414 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1416 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1418 return(ptr
+ pktrdlength
);
1421 mDNSexport
const mDNSu8
*GetResourceRecord(mDNS
*const m
, const DNSMessage
* const msg
, const mDNSu8
*ptr
,
1422 const mDNSu8
* const end
, const mDNSInterfaceID InterfaceID
, mDNSu8 RecordType
, CacheRecord
*rr
, RData
*RDataStorage
)
1424 mDNSu16 pktrdlength
;
1426 rr
->next
= mDNSNULL
;
1427 rr
->resrec
.RecordType
= RecordType
;
1429 rr
->NextInKAList
= mDNSNULL
;
1430 rr
->TimeRcvd
= m
->timenow
;
1431 rr
->NextRequiredQuery
= m
->timenow
; // Will be updated to the real value when we call SetNextCacheCheckTime()
1432 rr
->LastUsed
= m
->timenow
;
1434 rr
->CRActiveQuestion
= mDNSNULL
;
1435 rr
->UnansweredQueries
= 0;
1436 rr
->LastUnansweredTime
= 0;
1437 rr
->MPUnansweredQ
= 0;
1438 rr
->MPLastUnansweredQT
= 0;
1439 rr
->MPUnansweredKA
= 0;
1440 rr
->MPExpectingKA
= mDNSfalse
;
1441 rr
->NextInCFList
= mDNSNULL
;
1443 rr
->resrec
.InterfaceID
= InterfaceID
;
1444 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.name
);
1445 if (!ptr
) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1447 if (ptr
+ 10 > end
) { debugf("GetResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1449 rr
->resrec
.rrtype
= (mDNSu16
) ((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1450 rr
->resrec
.rrclass
= (mDNSu16
)(((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSClass_Mask
);
1451 rr
->resrec
.rroriginalttl
= (mDNSu32
) ((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
1452 if (rr
->resrec
.rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
&& (mDNSs32
)rr
->resrec
.rroriginalttl
!= -1)
1453 rr
->resrec
.rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1454 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
1455 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
1456 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1457 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8))
1458 rr
->resrec
.RecordType
|= kDNSRecordTypePacketUniqueMask
;
1460 if (ptr
+ pktrdlength
> end
) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1463 rr
->resrec
.rdata
= RDataStorage
;
1466 rr
->resrec
.rdata
= (RData
*)&rr
->rdatastorage
;
1467 rr
->resrec
.rdata
->MaxRDLength
= sizeof(RDataBody
);
1470 switch (rr
->resrec
.rrtype
)
1472 case kDNSType_A
: rr
->resrec
.rdata
->u
.ip
.b
[0] = ptr
[0];
1473 rr
->resrec
.rdata
->u
.ip
.b
[1] = ptr
[1];
1474 rr
->resrec
.rdata
->u
.ip
.b
[2] = ptr
[2];
1475 rr
->resrec
.rdata
->u
.ip
.b
[3] = ptr
[3];
1478 case kDNSType_CNAME
:// Same as PTR
1480 case kDNSType_PTR
: if (!getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.name
))
1481 { debugf("GetResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
1482 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
1485 case kDNSType_NULL
: //Same as TXT
1486 case kDNSType_HINFO
://Same as TXT
1487 case kDNSType_TXT
: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
1489 debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)",
1490 DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
1493 rr
->resrec
.rdlength
= pktrdlength
;
1494 mDNSPlatformMemCopy(ptr
, rr
->resrec
.rdata
->u
.data
, pktrdlength
);
1497 case kDNSType_AAAA
: mDNSPlatformMemCopy(ptr
, &rr
->resrec
.rdata
->u
.ipv6
, sizeof(rr
->resrec
.rdata
->u
.ipv6
));
1500 case kDNSType_SRV
: rr
->resrec
.rdata
->u
.srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1501 rr
->resrec
.rdata
->u
.srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
1502 rr
->resrec
.rdata
->u
.srv
.port
.b
[0] = ptr
[4];
1503 rr
->resrec
.rdata
->u
.srv
.port
.b
[1] = ptr
[5];
1504 if (!getDomainName(msg
, ptr
+6, end
, &rr
->resrec
.rdata
->u
.srv
.target
))
1505 { debugf("GetResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
1506 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
1509 case kDNSType_SOA
: if (!getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.mname
) ||
1510 !getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.rname
))
1511 { debugf("GetResourceRecord: Malformed SOA RDATA mname/rname"); return mDNSNULL
; }
1512 if ((unsigned)(end
- ptr
) < 5 * sizeof(mDNSOpaque32
))
1513 { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL
; }
1514 rr
->resrec
.rdata
->u
.soa
.serial
.NotAnInteger
= ((mDNSOpaque32
*)ptr
)->NotAnInteger
; ptr
+= 4;
1515 rr
->resrec
.rdata
->u
.soa
.refresh
.NotAnInteger
= ((mDNSOpaque32
*)ptr
)->NotAnInteger
; ptr
+= 4;
1516 rr
->resrec
.rdata
->u
.soa
.retry
.NotAnInteger
= ((mDNSOpaque32
*)ptr
)->NotAnInteger
; ptr
+= 4;
1517 rr
->resrec
.rdata
->u
.soa
.expire
.NotAnInteger
= ((mDNSOpaque32
*)ptr
)->NotAnInteger
; ptr
+= 4;
1518 rr
->resrec
.rdata
->u
.soa
.min
.NotAnInteger
= ((mDNSOpaque32
*)ptr
)->NotAnInteger
;
1521 case kDNSType_OPT
: getOptRdata(ptr
, end
, &rr
->resrec
, pktrdlength
); break;
1523 default: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
1525 debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
1526 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
1529 debugf("GetResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
1530 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
));
1531 // Note: Just because we don't understand the record type, that doesn't
1532 // mean we fail. The DNS protocol specifies rdlength, so we can
1533 // safely skip over unknown records and ignore them.
1534 // We also grab a binary copy of the rdata anyway, since the caller
1535 // might know how to interpret it even if we don't.
1536 rr
->resrec
.rdlength
= pktrdlength
;
1537 mDNSPlatformMemCopy(ptr
, rr
->resrec
.rdata
->u
.data
, pktrdlength
);
1541 rr
->resrec
.namehash
= DomainNameHashValue(&rr
->resrec
.name
);
1542 SetNewRData(&rr
->resrec
, mDNSNULL
, 0);
1544 return(ptr
+ pktrdlength
);
1547 mDNSexport
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1549 ptr
= skipDomainName(msg
, ptr
, end
);
1550 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
1551 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1555 mDNSexport
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
,
1556 DNSQuestion
*question
)
1558 question
->InterfaceID
= InterfaceID
;
1559 ptr
= getDomainName(msg
, ptr
, end
, &question
->qname
);
1560 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
1561 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1563 question
->qnamehash
= DomainNameHashValue(&question
->qname
);
1564 question
->qtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
1565 question
->qclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
1569 mDNSexport
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1572 const mDNSu8
*ptr
= msg
->data
;
1573 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
1577 mDNSexport
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1580 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
1581 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
1585 mDNSexport
const mDNSu8
*LocateAdditionals(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1588 const mDNSu8
*ptr
= LocateAuthorities(msg
, end
);
1589 for (i
= 0; i
< msg
->h
.numAuthorities
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
1593 // ***************************************************************************
1594 #if COMPILER_LIKES_PRAGMA_MARK
1597 #pragma mark - Packet Sending Functions
1600 mDNSlocal mStatus
sendDNSMessage(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
,
1601 mDNSInterfaceID InterfaceID
, const mDNSAddr
*dst
, mDNSIPPort dstport
, int sd
, uDNS_AuthInfo
*authInfo
)
1607 mDNSu16 numQuestions
= msg
->h
.numQuestions
;
1608 mDNSu16 numAnswers
= msg
->h
.numAnswers
;
1609 mDNSu16 numAuthorities
= msg
->h
.numAuthorities
;
1610 mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
1611 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
1613 // Put all the integer values in IETF byte-order (MSB first, LSB second)
1614 *ptr
++ = (mDNSu8
)(numQuestions
>> 8);
1615 *ptr
++ = (mDNSu8
)(numQuestions
& 0xFF);
1616 *ptr
++ = (mDNSu8
)(numAnswers
>> 8);
1617 *ptr
++ = (mDNSu8
)(numAnswers
& 0xFF);
1618 *ptr
++ = (mDNSu8
)(numAuthorities
>> 8);
1619 *ptr
++ = (mDNSu8
)(numAuthorities
& 0xFF);
1620 *ptr
++ = (mDNSu8
)(numAdditionals
>> 8);
1621 *ptr
++ = (mDNSu8
)(numAdditionals
& 0xFF);
1625 end
= DNSDigest_SignMessage(msg
, &end
, &numAdditionals
, authInfo
);
1626 if (!end
) return mStatus_UnknownErr
;
1629 // Send the packet on the wire
1633 msglen
= (mDNSu16
)(end
- (mDNSu8
*)msg
);
1634 lenbuf
[0] = (mDNSu8
)(msglen
>> 8); // host->network byte conversion
1635 lenbuf
[1] = (mDNSu8
)(msglen
& 0xFF);
1636 nsent
= mDNSPlatformWriteTCP(sd
, (char*)lenbuf
, 2);
1637 //!!!KRS make sure kernel is sending these as 1 packet!
1638 if (nsent
!= 2) goto tcp_error
;
1639 nsent
= mDNSPlatformWriteTCP(sd
, (char *)msg
, msglen
);
1640 if (nsent
!= msglen
) goto tcp_error
;
1641 status
= mStatus_NoError
;
1645 status
= mDNSPlatformSendUDP(m
, msg
, end
, InterfaceID
, dst
, dstport
);
1648 // Put all the integer values back the way they were before we return
1649 msg
->h
.numQuestions
= numQuestions
;
1650 msg
->h
.numAnswers
= numAnswers
;
1651 msg
->h
.numAuthorities
= numAuthorities
;
1652 msg
->h
.numAdditionals
= (mDNSu16
)(authInfo
? numAdditionals
- 1 : numAdditionals
);
1657 LogMsg("sendDNSMessage: error sending message over tcp");
1658 return mStatus_UnknownErr
;
1662 mDNSexport mStatus
mDNSSendDNSMessage_tcp(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
* end
, int sd
)
1664 if (sd
< 0) { LogMsg("mDNSSendDNSMessage_tcp: invalid desciptor %d", sd
); return mStatus_UnknownErr
; }
1665 return sendDNSMessage(m
, msg
, end
, mDNSInterface_Any
, &zeroAddr
, zeroIPPort
, sd
, mDNSNULL
);
1668 mDNSexport mStatus
mDNSSendDNSMessage(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
* end
,
1669 mDNSInterfaceID InterfaceID
, const mDNSAddr
*dst
, mDNSIPPort dstport
)
1671 return sendDNSMessage(m
, msg
, end
, InterfaceID
, dst
, dstport
, -1, mDNSNULL
);
1674 mDNSexport mStatus
mDNSSendSignedDNSMessage(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
* end
,
1675 mDNSInterfaceID InterfaceID
, const mDNSAddr
*dst
, mDNSIPPort dstport
, uDNS_AuthInfo
*authInfo
)
1677 return sendDNSMessage(m
, msg
, end
, InterfaceID
, dst
, dstport
, -1, authInfo
);
1680 mDNSexport mStatus
mDNSSendSignedDNSMessage_tcp(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
* end
, int sd
, uDNS_AuthInfo
*authInfo
)
1682 if (sd
< 0) { LogMsg("mDNSSendDNSMessage_tcp: invalid desciptor %d", sd
); return mStatus_UnknownErr
; }
1683 return sendDNSMessage(m
, msg
, end
, mDNSInterface_Any
, &zeroAddr
, zeroIPPort
, sd
, authInfo
);