2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 Change History (most recent first):
26 Revision 1.90 2005/03/21 00:33:51 shersche
27 <rdar://problem/4021486> Fix build warnings on Win32 platform
29 Revision 1.89 2005/03/17 18:59:38 ksekar
30 <rdar://problem/4012279> Properly parse multiple LLQ Options per packet on Windows
32 Revision 1.88 2005/03/16 00:42:32 ksekar
33 <rdar://problem/4012279> Long-lived queries not working on Windows
35 Revision 1.87 2005/02/25 04:21:00 cheshire
36 <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
38 Revision 1.86 2005/02/18 00:43:12 cheshire
39 <rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
41 Revision 1.85 2005/02/10 22:35:17 cheshire
42 <rdar://problem/3727944> Update name
44 Revision 1.84 2005/02/03 00:44:38 cheshire
45 <rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
47 Revision 1.83 2005/01/27 22:57:55 cheshire
48 Fix compile errors on gcc4
50 Revision 1.82 2005/01/19 03:27:03 cheshire
51 <rdar://problem/3961051> CPU Spin in mDNSResponder
52 GetNextScheduledEvent() needs to check LocalRecordReady()
54 Revision 1.81 2004/12/18 03:13:45 cheshire
55 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
57 Revision 1.80 2004/12/16 21:46:43 cheshire
58 Add DNSTypeName case for kDNSType_SOA
60 Revision 1.79 2004/12/16 21:38:37 cheshire
61 Add DNSTypeName case for kDNSType_NS
63 Revision 1.78 2004/12/16 21:27:37 ksekar
64 Fixed build failures when compiled with verbose debugging messages
66 Revision 1.77 2004/12/16 20:12:59 cheshire
67 <rdar://problem/3324626> Cache memory management improvements
69 Revision 1.76 2004/12/16 08:05:29 shersche
70 Remove extranenous semicolons that cause compilation errors on Windows
72 Revision 1.75 2004/12/15 02:11:22 ksekar
73 <rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness
75 Revision 1.74 2004/12/09 22:49:15 ksekar
76 <rdar://problem/3913653> Wide-Area Goodbyes broken
78 Revision 1.73 2004/12/07 22:49:06 cheshire
79 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
81 Revision 1.72 2004/12/06 21:15:20 ksekar
82 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
84 Revision 1.71 2004/12/04 02:12:45 cheshire
85 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
87 Revision 1.70 2004/12/03 19:52:44 ksekar
88 Use PutResourceRecordTTLJumbo for putDeletionRecord()
90 Revision 1.69 2004/12/03 07:20:50 ksekar
91 <rdar://problem/3674208> Wide-Area: Registration of large TXT record fails
93 Revision 1.68 2004/11/24 00:10:43 cheshire
94 <rdar://problem/3869241> For unicast operations, verify that service types are legal
96 Revision 1.67 2004/10/26 03:52:02 cheshire
97 Update checkin comments
99 Revision 1.66 2004/10/23 01:16:00 cheshire
100 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
102 Revision 1.65 2004/10/20 02:15:09 cheshire
103 Add case in GetRRDisplayString() to display NS rdata
105 Revision 1.64 2004/10/13 00:24:02 cheshire
106 Disable "array is too small to include a terminating null character" warning on Windows
108 Revision 1.63 2004/10/10 06:57:14 cheshire
109 Change definition of "localdomain" to make code compile a little smaller
111 Revision 1.62 2004/10/06 01:44:19 cheshire
112 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
114 Revision 1.61 2004/09/30 00:24:56 ksekar
115 <rdar://problem/3695802> Dynamically update default registration domains on config change
117 Revision 1.60 2004/09/27 23:25:30 cheshire
118 Fix compiler warning: soa.serial is signed, not unsigned
120 Revision 1.59 2004/09/27 22:53:45 ksekar
121 Fixed getLargeResourceRecord for SOA rdata.
123 Revision 1.58 2004/09/25 02:41:39 cheshire
124 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
126 Revision 1.57 2004/09/25 02:24:27 cheshire
127 Removed unused rr->UseCount
129 Revision 1.56 2004/09/24 20:57:39 cheshire
130 <rdar://problem/3680902> Eliminate inappropriate casts that cause misaligned-address errors
132 Revision 1.55 2004/09/17 01:08:48 cheshire
133 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
134 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
135 declared in that file are ONLY appropriate to single-address-space embedded applications.
136 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
138 Revision 1.54 2004/09/17 00:49:51 cheshire
139 Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use
140 is GetLargeResourceRecord
142 Revision 1.53 2004/09/17 00:31:51 cheshire
143 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
145 Revision 1.52 2004/09/17 00:19:10 cheshire
146 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
148 Revision 1.51 2004/09/16 02:29:39 cheshire
149 Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
150 uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
152 Revision 1.50 2004/09/16 01:58:14 cheshire
153 Fix compiler warnings
155 Revision 1.49 2004/09/14 23:42:35 cheshire
156 <rdar://problem/3801296> Need to seed random number generator from platform-layer data
158 Revision 1.48 2004/09/14 23:27:46 cheshire
161 Revision 1.47 2004/08/25 02:50:04 cheshire
162 <rdar://problem/3561220> Browses are no longer piggybacking on other browses
163 Make mDNSSameAddress() recognise that two mDNSAddrType_None addresses are necessarily equal
165 Revision 1.46 2004/08/18 17:35:40 ksekar
166 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
168 Revision 1.45 2004/08/15 18:26:00 cheshire
169 Don't use strcpy() on "struct domainname" objects; use AssignDomainName() instead
170 (A "struct domainname" is a collection of packed pascal strings, not a C string.)
172 Revision 1.44 2004/08/13 23:46:58 cheshire
173 "asyncronous" -> "asynchronous"
175 Revision 1.43 2004/08/12 02:55:46 ksekar
176 Fix param order error moving putPrereqNameNotInUse from uDNS.c using
177 ustrcpy macro to DNSCommon.c using mDNSPlatformStrCopy().
179 Revision 1.42 2004/08/10 23:19:14 ksekar
180 <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
181 Moved routines/constants to allow extern access for garbage collection daemon
183 Revision 1.41 2004/08/10 01:10:01 cheshire
184 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
185 Minor revision from Roger Pantos
187 Revision 1.40 2004/08/04 22:10:46 cheshire
188 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
189 Change to use "._sub." instead of ".s." to mark subtypes.
191 Revision 1.39 2004/07/13 21:24:24 rpantos
192 Fix for <rdar://problem/3701120>.
194 Revision 1.38 2004/06/18 21:08:58 cheshire
195 <rdar://problem/3540040> Applications are registering invalid records
196 Attempts to create domain names like "www..apple.com." now logged to aid debugging
198 Revision 1.37 2004/06/18 20:25:42 cheshire
199 <rdar://problem/3488547> Add a syslog message if someone tries to use "local.arpa".
201 Revision 1.36 2004/06/18 19:09:59 cheshire
202 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
204 Revision 1.35 2004/06/05 00:14:44 cheshire
205 Fix signed/unsigned and other compiler warnings
207 Revision 1.34 2004/06/04 00:25:25 cheshire
208 Fix misaligned write exception that occurs on some platforms
210 Revision 1.33 2004/06/04 00:16:18 cheshire
211 Remove non-portable use of 'inline'
213 Revision 1.32 2004/06/03 03:09:58 ksekar
214 <rdar://problem/3668626>: Garbage Collection for Dynamic Updates
216 Revision 1.31 2004/05/28 23:42:36 ksekar
217 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
219 Revision 1.30 2004/05/26 09:08:04 bradley
220 Added cast to correct structure pointer when allocating domain name list element to fix C++ builds.
222 Revision 1.29 2004/05/18 23:51:25 cheshire
223 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
225 Revision 1.28 2004/05/13 04:54:20 ksekar
226 Unified list copy/free code. Added symetric list for
228 Revision 1.27 2004/04/22 20:29:07 cheshire
229 Log error message if no count field passed to PutResourceRecordTTL()
231 Revision 1.26 2004/04/22 04:07:01 cheshire
232 Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it
234 Revision 1.25 2004/04/22 03:05:28 cheshire
235 kDNSClass_ANY should be kDNSQClass_ANY
237 Revision 1.24 2004/04/22 02:51:20 cheshire
238 Use common code for HINFO/TXT and TSIG cases in putRData
240 Revision 1.23 2004/04/15 00:51:28 bradley
241 Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
242 Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
244 Revision 1.22 2004/04/14 23:09:28 ksekar
245 Support for TSIG signed dynamic updates.
247 Revision 1.21 2004/04/09 16:47:28 cheshire
248 <rdar://problem/3617655>: mDNSResponder escape handling inconsistent with BIND
250 Revision 1.20 2004/04/09 16:37:15 cheshire
251 Suggestion from Bob Bradley:
252 Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers
254 Revision 1.19 2004/04/02 19:34:38 cheshire
257 Revision 1.18 2004/03/30 06:45:00 cheshire
258 Compiler warning fixes from Don Woodward at Roku Labs
260 Revision 1.17 2004/03/19 22:25:20 cheshire
261 <rdar://problem/3579561>: Need to limit service types to fourteen characters
262 Won't actually do this for now, but keep the code around just in case
264 Revision 1.16 2004/03/08 02:45:35 cheshire
265 Minor change to make a couple of the log messages a bit shorter
267 Revision 1.15 2004/03/08 02:44:09 cheshire
268 <rdar://problem/3579561>: Need to limit service types to fourteen characters
270 Revision 1.14 2004/02/21 02:06:24 cheshire
271 Can't use anonymous unions -- they're non-standard and don't work on all compilers
273 Revision 1.13 2004/02/06 23:04:18 ksekar
274 Basic Dynamic Update support via mDNS_Register (dissabled via
275 UNICAST_REGISTRATION #define)
277 Revision 1.12 2004/02/03 22:37:10 cheshire
278 Delete unused (commented-out) code
280 Revision 1.11 2004/02/03 22:35:34 cheshire
281 <rdar://problem/3548256>: Should not allow empty string for resolve domain
283 Revision 1.10 2004/02/03 19:47:36 ksekar
284 Added an asynchronous state machine mechanism to uDNS.c, including
285 calls to find the parent zone for a domain name. Changes include code
286 in repository previously dissabled via "#if 0 incomplete". Codepath
287 is currently unused, and will be called to create update records, etc.
289 Revision 1.9 2004/01/27 20:15:22 cheshire
290 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
292 Revision 1.8 2004/01/24 23:24:36 cheshire
293 Expanded out the list of local domains to reduce risk of mistakes in future
295 Revision 1.7 2004/01/24 08:32:30 bradley
296 Mask values with 0xFF before casting to avoid runtime truncation errors on Windows debug builds.
297 Separated octal-escaped sequences preceding decimal digits to avoid errors with some compilers wanting
298 to signal potentially hidden errors about the subsequent digit not being part of the octal sequence.
300 Revision 1.6 2004/01/24 04:59:15 cheshire
301 Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
303 Revision 1.5 2004/01/23 23:23:14 ksekar
304 Added TCP support for truncated unicast messages.
306 Revision 1.4 2004/01/22 02:15:33 cheshire
307 <rdar://problem/3536597>: Link-local reverse-mapping domains need to be resolved using link-local multicast
309 Revision 1.3 2004/01/21 21:16:29 cheshire
310 Minor tidy-up: Deleted a bunch of blank lines, trailing spaces, tabs, etc.
312 Revision 1.2 2003/12/13 05:47:48 bradley
313 Made local ptr const to fix error when assigning from const structure. Disable benign conditional
314 expression is constant warning when building with Microsoft compilers.
316 Revision 1.1 2003/12/13 03:05:27 ksekar
317 <rdar://problem/3192548>: DynDNS: Unicast query of service records
321 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
322 #define mDNS_InstantiateInlines 1
323 #include "DNSCommon.h"
325 // Disable certain benign warnings with Microsoft compilers
326 #if (defined(_MSC_VER))
327 // Disable "conditional expression is constant" warning for debug macros.
328 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
329 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
330 #pragma warning(disable:4127)
331 // Disable "array is too small to include a terminating null character" warning
332 // -- domain labels have an initial length byte, not a terminating null character
333 #pragma warning(disable:4295)
336 // ***************************************************************************
337 #if COMPILER_LIKES_PRAGMA_MARK
339 #pragma mark - DNameList copy/deallocation routines
342 mDNSexport DNameListElem
*mDNS_CopyDNameList(const DNameListElem
*orig
)
344 DNameListElem
*copy
= mDNSNULL
, *newelem
;
345 const DNameListElem
*ptr
;
347 for (ptr
= orig
; ptr
; ptr
= ptr
->next
)
349 newelem
= (DNameListElem
*)mDNSPlatformMemAllocate(sizeof(DNameListElem
));
350 if (!newelem
) { LogMsg("ERROR: malloc"); return mDNSNULL
; }
351 AssignDomainName(&newelem
->name
, &ptr
->name
);
352 newelem
->next
= copy
;
358 mDNSexport
void mDNS_FreeDNameList(DNameListElem
*list
)
366 mDNSPlatformMemFree(fptr
);
370 // ***************************************************************************
371 #if COMPILER_LIKES_PRAGMA_MARK
373 #pragma mark - General Utility Functions
376 // return true for RFC1918 private addresses
377 mDNSexport mDNSBool
IsPrivateV4Addr(mDNSAddr
*addr
)
381 if (addr
->type
!= mDNSAddrType_IPv4
) return mDNSfalse
;
384 return ((b
[0] == 10) || // 10/8 prefix
385 (b
[0] == 172 && b
[1] > 15 && b
[1] < 32) || // 172.16/12
386 (b
[0] == 192 && b
[1] == 168)); // 192.168/16
389 mDNSexport
const NetworkInterfaceInfo
*GetFirstActiveInterface(const NetworkInterfaceInfo
*intf
)
391 while (intf
&& !intf
->InterfaceActive
) intf
= intf
->next
;
395 mDNSexport mDNSInterfaceID
GetNextActiveInterfaceID(const NetworkInterfaceInfo
*intf
)
397 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
398 if (next
) return(next
->InterfaceID
); else return(mDNSNULL
);
401 mDNSexport mDNSu32
NumCacheRecordsForInterfaceID(const mDNS
*const m
, mDNSInterfaceID id
)
403 mDNSu32 slot
, used
= 0;
406 FORALL_CACHERECORDS(slot
, cg
, rr
)
407 if (rr
->resrec
.InterfaceID
== id
) used
++;
411 mDNSexport
char *DNSTypeName(mDNSu16 rrtype
)
415 case kDNSType_A
: return("Addr");
416 case kDNSType_NS
: return("NS");
417 case kDNSType_CNAME
:return("CNAME");
418 case kDNSType_SOA
: return("SOA");
419 case kDNSType_NULL
: return("NULL");
420 case kDNSType_PTR
: return("PTR");
421 case kDNSType_HINFO
:return("HINFO");
422 case kDNSType_TXT
: return("TXT");
423 case kDNSType_AAAA
: return("AAAA");
424 case kDNSType_SRV
: return("SRV");
425 case kDNSQType_ANY
: return("ANY");
427 static char buffer
[16];
428 mDNS_snprintf(buffer
, sizeof(buffer
), "(%d)", rrtype
);
434 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
435 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
436 // long as this routine is only used for debugging messages, it probably isn't a big problem.
437 mDNSexport
char *GetRRDisplayString_rdb(const ResourceRecord
*rr
, RDataBody
*rd
, char *buffer
)
440 mDNSu32 length
= mDNS_snprintf(buffer
, 79, "%4d %##s %s ", rr
->rdlength
, rr
->name
->c
, DNSTypeName(rr
->rrtype
));
443 case kDNSType_A
: mDNS_snprintf(buffer
+length
, 79-length
, "%.4a", &rd
->ipv4
); break;
445 case kDNSType_NS
: // Same as PTR
446 case kDNSType_CNAME
:// Same as PTR
447 case kDNSType_PTR
: mDNS_snprintf(buffer
+length
, 79-length
, "%##s", rd
->name
.c
); break;
449 case kDNSType_HINFO
:// Display this the same as TXT (just show first string)
450 case kDNSType_TXT
: mDNS_snprintf(buffer
+length
, 79-length
, "%#s", rd
->txt
.c
); break;
452 case kDNSType_AAAA
: mDNS_snprintf(buffer
+length
, 79-length
, "%.16a", &rd
->ipv6
); break;
453 case kDNSType_SRV
: mDNS_snprintf(buffer
+length
, 79-length
, "%##s", rd
->srv
.target
.c
); break;
454 default: mDNS_snprintf(buffer
+length
, 79-length
, "RDLen %d: %s", rr
->rdlength
, rd
->data
); break;
456 for (ptr
= buffer
; *ptr
; ptr
++) if (*ptr
< ' ') *ptr
='.';
460 mDNSexport mDNSu32
mDNSRandom(mDNSu32 max
)
462 static mDNSu32 seed
= 0;
468 seed
= mDNSPlatformRandomSeed(); // Pick an initial seed
469 for (i
=0; i
<100; i
++) seed
= seed
* 21 + 1; // And mix it up a bit
471 while (mask
< max
) mask
= (mask
<< 1) | 1;
472 do seed
= seed
* 21 + 1; while ((seed
& mask
) > max
);
473 return (seed
& mask
);
476 mDNSexport mDNSBool
mDNSSameAddress(const mDNSAddr
*ip1
, const mDNSAddr
*ip2
)
478 if (ip1
->type
== ip2
->type
)
482 case mDNSAddrType_None
: return(mDNStrue
); // Empty addresses have no data and are therefore always equal
483 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip1
->ip
.v4
, ip2
->ip
.v4
));
484 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip1
->ip
.v6
, ip2
->ip
.v6
));
490 mDNSexport mDNSBool
mDNSAddrIsDNSMulticast(const mDNSAddr
*ip
)
494 case mDNSAddrType_IPv4
: return(mDNSBool
)(ip
->ip
.v4
.NotAnInteger
== AllDNSLinkGroupv4
.NotAnInteger
);
495 case mDNSAddrType_IPv6
: return(mDNSBool
)(ip
->ip
.v6
.l
[0] == AllDNSLinkGroupv6
.l
[0] &&
496 ip
->ip
.v6
.l
[1] == AllDNSLinkGroupv6
.l
[1] &&
497 ip
->ip
.v6
.l
[2] == AllDNSLinkGroupv6
.l
[2] &&
498 ip
->ip
.v6
.l
[3] == AllDNSLinkGroupv6
.l
[3] );
499 default: return(mDNSfalse
);
503 // ***************************************************************************
504 #if COMPILER_LIKES_PRAGMA_MARK
506 #pragma mark - Domain Name Utility Functions
509 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
512 const int len
= *a
++;
514 if (len
> MAX_DOMAIN_LABEL
)
515 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
517 if (len
!= *b
++) return(mDNSfalse
);
518 for (i
=0; i
<len
; i
++)
522 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
523 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
524 if (ac
!= bc
) return(mDNSfalse
);
529 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
531 const mDNSu8
* a
= d1
->c
;
532 const mDNSu8
* b
= d2
->c
;
533 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
537 if (a
+ 1 + *a
>= max
)
538 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse
); }
539 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
547 mDNSexport mDNSBool
IsLocalDomain(const domainname
*d
)
549 // Domains that are defined to be resolved via link-local multicast are:
550 // local., 254.169.in-addr.arpa., and 0.8.E.F.ip6.arpa.
551 static const domainname
*n0
= (domainname
*)"\x5" "local";
552 static const domainname
*n1
= (domainname
*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
553 static const domainname
*n2
= (domainname
*)"\x1" "0" "\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
555 const domainname
*d1
, *d2
, *d3
, *d4
, *d5
, *d6
; // Top-level domain, second-level domain, etc.
556 d1
= d2
= d3
= d4
= d5
= d6
= mDNSNULL
;
559 d6
= d5
; d5
= d4
; d4
= d3
; d3
= d2
; d2
= d1
; d1
= d
;
560 d
= (domainname
*)(d
->c
+ 1 + d
->c
[0]);
563 if (d1
&& SameDomainName(d1
, n0
)) return(mDNStrue
);
564 if (d4
&& SameDomainName(d4
, n1
)) return(mDNStrue
);
565 if (d6
&& SameDomainName(d6
, n2
)) return(mDNStrue
);
569 // Returns length of a domain name INCLUDING the byte for the final null label
570 // i.e. for the root label "." it returns one
571 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
572 // Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
573 // If the given domainname is invalid, result is 256
574 mDNSexport mDNSu16
DomainNameLength(const domainname
*const name
)
576 const mDNSu8
*src
= name
->c
;
579 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
581 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
583 return((mDNSu16
)(src
- name
->c
+ 1));
586 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
587 // for the final null label i.e. for the root label "." it returns one.
588 // E.g. for the FQDN "foo.com." it returns 9
589 // (length, three data bytes, length, three more data bytes, final zero).
590 // In the case where a parent domain name is provided, and the given name is a child
591 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
592 // of the child name, plus TWO bytes for the compression pointer.
593 // E.g. for the name "foo.com." with parent "com.", it returns 6
594 // (length, three data bytes, two-byte compression pointer).
595 mDNSexport mDNSu16
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
597 const mDNSu8
*src
= name
->c
;
598 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
601 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
602 if (parent
&& SameDomainName((domainname
*)src
, parent
)) return((mDNSu16
)(src
- name
->c
+ 2));
604 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
606 return((mDNSu16
)(src
- name
->c
+ 1));
609 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
610 // The C string contains the label as-is, with no escaping, etc.
611 // Any dots in the name are literal dots, not label separators
612 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
613 // in the domainname bufer (i.e., the next byte after the terminating zero).
614 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
615 // AppendLiteralLabelString returns mDNSNULL.
616 mDNSexport mDNSu8
*AppendLiteralLabelString(domainname
*const name
, const char *cstr
)
618 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
619 const mDNSu8
*const lim1
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
620 const mDNSu8
*const lim2
= ptr
+ 1 + MAX_DOMAIN_LABEL
;
621 const mDNSu8
*const lim
= (lim1
< lim2
) ? lim1
: lim2
;
622 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
624 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the data
625 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
626 *ptr
++ = 0; // Put the null root label on the end
627 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
628 else return(ptr
); // Success: return new value of ptr
631 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
632 // The C string is in conventional DNS syntax:
633 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
634 // If successful, AppendDNSNameString returns a pointer to the next unused byte
635 // in the domainname bufer (i.e., the next byte after the terminating zero).
636 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
637 // AppendDNSNameString returns mDNSNULL.
638 mDNSexport mDNSu8
*AppendDNSNameString(domainname
*const name
, const char *cstring
)
640 const char *cstr
= cstring
;
641 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
642 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
643 while (*cstr
&& ptr
< lim
) // While more characters, and space to put them...
645 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
646 if (*cstr
== '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring
); return(mDNSNULL
); }
647 while (*cstr
&& *cstr
!= '.' && ptr
< lim
) // While we have characters in the label...
649 mDNSu8 c
= (mDNSu8
)*cstr
++; // Read the character
650 if (c
== '\\') // If escape character, check next character
652 c
= (mDNSu8
)*cstr
++; // Assume we'll just take the next character
653 if (mdnsIsDigit(cstr
[-1]) && mdnsIsDigit(cstr
[0]) && mdnsIsDigit(cstr
[1]))
654 { // If three decimal digits,
655 int v0
= cstr
[-1] - '0'; // then interpret as three-digit decimal
656 int v1
= cstr
[ 0] - '0';
657 int v2
= cstr
[ 1] - '0';
658 int val
= v0
* 100 + v1
* 10 + v2
;
659 if (val
<= 255) { c
= (mDNSu8
)val
; cstr
+= 2; } // If valid three-digit decimal value, use it
662 *ptr
++ = c
; // Write the character
664 if (*cstr
) cstr
++; // Skip over the trailing dot (if present)
665 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) // If illegal label, abort
667 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
670 *ptr
++ = 0; // Put the null root label on the end
671 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
672 else return(ptr
); // Success: return new value of ptr
675 // AppendDomainLabel appends a single label to a name.
676 // If successful, AppendDomainLabel returns a pointer to the next unused byte
677 // in the domainname bufer (i.e., the next byte after the terminating zero).
678 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
679 // AppendDomainLabel returns mDNSNULL.
680 mDNSexport mDNSu8
*AppendDomainLabel(domainname
*const name
, const domainlabel
*const label
)
683 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
685 // Check label is legal
686 if (label
->c
[0] > MAX_DOMAIN_LABEL
) return(mDNSNULL
);
688 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
689 if (ptr
+ 1 + label
->c
[0] + 1 > name
->c
+ MAX_DOMAIN_NAME
) return(mDNSNULL
);
691 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
]; // Copy the label data
692 *ptr
++ = 0; // Put the null root label on the end
696 mDNSexport mDNSu8
*AppendDomainName(domainname
*const name
, const domainname
*const append
)
698 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
699 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
700 const mDNSu8
* src
= append
->c
;
704 if (ptr
+ 1 + src
[0] > lim
) return(mDNSNULL
);
705 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
706 *ptr
= 0; // Put the null root label on the end
712 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
713 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
714 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
715 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
716 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
717 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
718 mDNSexport mDNSBool
MakeDomainLabelFromLiteralString(domainlabel
*const label
, const char *cstr
)
720 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
721 const mDNSu8
*const limit
= label
->c
+ 1 + MAX_DOMAIN_LABEL
; // The maximum we can put
722 while (*cstr
&& ptr
< limit
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the label
723 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1); // Set the length byte
724 return(*cstr
== 0); // Return mDNStrue if we successfully consumed all input
727 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
728 // The C string is in conventional DNS syntax:
729 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
730 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
731 // in the domainname bufer (i.e., the next byte after the terminating zero).
732 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
733 // MakeDomainNameFromDNSNameString returns mDNSNULL.
734 mDNSexport mDNSu8
*MakeDomainNameFromDNSNameString(domainname
*const name
, const char *cstr
)
736 name
->c
[0] = 0; // Make an empty domain name
737 return(AppendDNSNameString(name
, cstr
)); // And then add this string to it
740 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
742 const mDNSu8
* src
= label
->c
; // Domain label we're reading
743 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
744 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
745 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
746 while (src
< end
) // While we have characters in the label
751 if (c
== '.' || c
== esc
) // If character is a dot or the escape character
752 *ptr
++ = esc
; // Output escape character
753 else if (c
<= ' ') // If non-printing ascii,
754 { // Output decimal escape sequence
756 *ptr
++ = (char) ('0' + (c
/ 100) );
757 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
758 c
= (mDNSu8
)('0' + (c
) % 10);
761 *ptr
++ = (char)c
; // Copy the character
763 *ptr
= 0; // Null-terminate the string
764 return(ptr
); // and return
767 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes)
768 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
770 const mDNSu8
*src
= name
->c
; // Domain name we're reading
771 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
773 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
775 while (*src
) // While more characters in the domain name
777 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
778 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
779 if (!ptr
) return(mDNSNULL
);
781 *ptr
++ = '.'; // Write the dot after the label
784 *ptr
++ = 0; // Null-terminate the string
785 return(ptr
); // and return
789 // Host names must start with a letter, end with a letter or digit,
790 // and have as interior characters only letters, digits, and hyphen.
791 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
793 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
795 const mDNSu8
* src
= &UTF8Name
[1];
796 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
797 mDNSu8
* ptr
= &hostlabel
->c
[1];
798 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
801 // Delete apostrophes from source name
802 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
803 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
804 { src
+= 3; continue; } // Unicode curly apostrophe
807 if (mdnsValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
808 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
812 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
813 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
816 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
817 const domainlabel
*name
, const domainname
*type
, const domainname
*const domain
)
820 mDNSu8
*dst
= fqdn
->c
;
822 const char *errormsg
;
824 // In the case where there is no name (and ONLY in that case),
825 // a single-label subtype is allowed as the first label of a three-part "type"
828 const mDNSu8
*s0
= type
->c
;
829 if (s0
[0] && s0
[0] < 0x40) // If legal first label (at least one character, and no more than 63)
831 const mDNSu8
* s1
= s0
+ 1 + s0
[0];
832 if (s1
[0] && s1
[0] < 0x40) // and legal second label (at least one character, and no more than 63)
834 const mDNSu8
*s2
= s1
+ 1 + s1
[0];
835 if (s2
[0] && s2
[0] < 0x40 && s2
[1+s2
[0]] == 0) // and we have three and only three labels
837 static const mDNSu8 SubTypeLabel
[5] = "\x04_sub";
838 src
= s0
; // Copy the first label
840 for (i
=0; i
<= len
; i
++) *dst
++ = *src
++;
841 for (i
=0; i
< (int)sizeof(SubTypeLabel
); i
++) *dst
++ = SubTypeLabel
[i
];
842 type
= (domainname
*)s1
;
844 // Special support for queries done by some third-party network monitoring software
845 // For these queries, we retract the "._sub" we just added between the subtype and the main type
846 if (SameDomainName((domainname
*)s0
, (domainname
*)"\x09_services\x07_dns-sd\x04_udp") ||
847 SameDomainName((domainname
*)s0
, (domainname
*)"\x09_services\x05_mdns\x04_udp"))
848 dst
-= sizeof(SubTypeLabel
);
854 if (name
&& name
->c
[0])
856 src
= name
->c
; // Put the service name into the domain name
858 if (len
>= 0x40) { errormsg
="Service instance name too long"; goto fail
; }
859 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
862 name
= (domainlabel
*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
864 src
= type
->c
; // Put the service type into the domain name
866 if (len
< 2 || len
>= 0x40 || (len
> 15 && !SameDomainName(domain
, (domainname
*)"\x05" "local")))
868 errormsg
="Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
871 if (src
[1] != '_') { errormsg
="Application protocol name must begin with underscore"; goto fail
; }
872 for (i
=2; i
<=len
; i
++)
873 if (!mdnsIsLetter(src
[i
]) && !mdnsIsDigit(src
[i
]) && src
[i
] != '-' && src
[i
] != '_')
874 { errormsg
="Application protocol name must contain only letters, digits, and hyphens"; goto fail
; }
875 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
878 if (!(len
== 4 && src
[1] == '_' &&
879 (((src
[2] | 0x20) == 'u' && (src
[3] | 0x20) == 'd') || ((src
[2] | 0x20) == 't' && (src
[3] | 0x20) == 'c')) &&
880 (src
[4] | 0x20) == 'p'))
881 { errormsg
="Transport protocol name must be _udp or _tcp"; goto fail
; }
882 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
884 if (*src
) { errormsg
="Service type must have only two labels"; goto fail
; }
887 if (!domain
->c
[0]) { errormsg
="Service domain must be non-empty"; goto fail
; }
888 if (SameDomainName(domain
, (domainname
*)"\x05" "local" "\x04" "arpa"))
889 { errormsg
="Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail
; }
890 dst
= AppendDomainName(fqdn
, domain
);
891 if (!dst
) { errormsg
="Service domain too long"; goto fail
; }
895 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg
, name
->c
, type
->c
, domain
->c
);
899 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
900 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
903 const mDNSu8
*src
= fqdn
->c
;
904 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
907 dst
= name
->c
; // Extract the service name from the domain name
909 if (len
>= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse
); }
910 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
912 dst
= type
->c
; // Extract the service type from the domain name
914 if (len
>= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse
); }
915 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
918 if (len
>= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse
); }
919 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
920 *dst
++ = 0; // Put the null root label on the end of the service type
922 dst
= domain
->c
; // Extract the service domain from the domain name
927 { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse
); }
928 if (src
+ 1 + len
+ 1 >= max
)
929 { debugf("DeconstructServiceName: service domain too long"); return(mDNSfalse
); }
930 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
932 *dst
++ = 0; // Put the null root label on the end
938 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
939 // 10xxxxxx is a continuation byte of a multi-byte character
940 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
941 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
942 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
943 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
944 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
946 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
947 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
948 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
949 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
950 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
952 mDNSexport mDNSu32
TruncateUTF8ToLength(mDNSu8
*string
, mDNSu32 length
, mDNSu32 max
)
956 mDNSu8 c1
= string
[max
]; // First byte after cut point
957 mDNSu8 c2
= (max
+1 < length
) ? string
[max
+1] : 0xB0; // Second byte after cut point
958 length
= max
; // Trim length down
961 // Check if the byte right after the chop point is a UTF-8 continuation byte,
962 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
963 // If so, then we continue to chop more bytes until we get to a legal chop point.
964 mDNSBool continuation
= ((c1
& 0xC0) == 0x80);
965 mDNSBool secondsurrogate
= (c1
== 0xED && (c2
& 0xF0) == 0xB0);
966 if (!continuation
&& !secondsurrogate
) break;
968 c1
= string
[--length
];
970 // Having truncated characters off the end of our string, also cut off any residual white space
971 while (length
> 0 && string
[length
-1] <= ' ') length
--;
976 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
977 // name ends in "-nnn", where n is some decimal number.
978 mDNSexport mDNSBool
LabelContainsSuffix(const domainlabel
*const name
, const mDNSBool RichText
)
980 mDNSu16 l
= name
->c
[0];
984 if (l
< 4) return mDNSfalse
; // Need at least " (2)"
985 if (name
->c
[l
--] != ')') return mDNSfalse
; // Last char must be ')'
986 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Preceeded by a digit
988 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
989 return (name
->c
[l
] == '(' && name
->c
[l
- 1] == ' ');
993 if (l
< 2) return mDNSfalse
; // Need at least "-2"
994 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Last char must be a digit
996 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
997 return (name
->c
[l
] == '-');
1001 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1002 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1003 // from the suffix that was removed.
1004 mDNSexport mDNSu32
RemoveLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1006 mDNSu32 val
= 0, multiplier
= 1;
1008 // Chop closing parentheses from RichText suffix
1009 if (RichText
&& name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == ')') name
->c
[0]--;
1011 // Get any existing numerical suffix off the name
1012 while (mdnsIsDigit(name
->c
[name
->c
[0]]))
1013 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
1015 // Chop opening parentheses or dash from suffix
1018 if (name
->c
[0] >= 2 && name
->c
[name
->c
[0]] == '(' && name
->c
[name
->c
[0]-1] == ' ') name
->c
[0] -= 2;
1022 if (name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == '-') name
->c
[0] -= 1;
1028 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1029 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1030 mDNSexport
void AppendLabelSuffix(domainlabel
*name
, mDNSu32 val
, mDNSBool RichText
)
1032 mDNSu32 divisor
= 1, chars
= 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1033 if (RichText
) chars
= 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1035 // Truncate trailing spaces from RichText names
1036 if (RichText
) while (name
->c
[name
->c
[0]] == ' ') name
->c
[0]--;
1038 while (val
>= divisor
* 10) { divisor
*= 10; chars
++; }
1040 name
->c
[0] = (mDNSu8
) TruncateUTF8ToLength(name
->c
+1, name
->c
[0], MAX_DOMAIN_LABEL
- chars
);
1042 if (RichText
) { name
->c
[++name
->c
[0]] = ' '; name
->c
[++name
->c
[0]] = '('; }
1043 else { name
->c
[++name
->c
[0]] = '-'; }
1047 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
1052 if (RichText
) name
->c
[++name
->c
[0]] = ')';
1055 mDNSexport
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1059 if (LabelContainsSuffix(name
, RichText
))
1060 val
= RemoveLabelSuffix(name
, RichText
);
1062 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1063 // If existing suffix in the range 2-9, increment it.
1064 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1065 // so add a random increment to improve the chances of finding an available name next time.
1066 if (val
== 0) val
= 2;
1067 else if (val
< 10) val
++;
1068 else val
+= 1 + mDNSRandom(99);
1070 AppendLabelSuffix(name
, val
, RichText
);
1073 // ***************************************************************************
1074 #if COMPILER_LIKES_PRAGMA_MARK
1076 #pragma mark - Resource Record Utility Functions
1079 mDNSexport mDNSu32
RDataHashValue(mDNSu16
const rdlength
, const RDataBody
*const rdb
)
1083 for (i
=0; i
+1 < rdlength
; i
+=2)
1085 sum
+= (((mDNSu32
)(rdb
->data
[i
])) << 8) | rdb
->data
[i
+1];
1086 sum
= (sum
<<3) | (sum
>>29);
1090 sum
+= ((mDNSu32
)(rdb
->data
[i
])) << 8;
1095 mDNSexport mDNSBool
SameRData(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
1097 if (r1
->rrtype
!= r2
->rrtype
) return(mDNSfalse
);
1098 if (r1
->rdlength
!= r2
->rdlength
) return(mDNSfalse
);
1099 if (r1
->rdatahash
!= r2
->rdatahash
) return(mDNSfalse
);
1102 case kDNSType_CNAME
:// Same as PTR
1103 case kDNSType_PTR
: return(SameDomainName(&r1
->rdata
->u
.name
, &r2
->rdata
->u
.name
));
1105 case kDNSType_SRV
: return(mDNSBool
)( r1
->rdata
->u
.srv
.priority
== r2
->rdata
->u
.srv
.priority
&&
1106 r1
->rdata
->u
.srv
.weight
== r2
->rdata
->u
.srv
.weight
&&
1107 r1
->rdata
->u
.srv
.port
.NotAnInteger
== r2
->rdata
->u
.srv
.port
.NotAnInteger
&&
1108 SameDomainName(&r1
->rdata
->u
.srv
.target
, &r2
->rdata
->u
.srv
.target
) );
1110 default: return(mDNSPlatformMemSame(r1
->rdata
->u
.data
, r2
->rdata
->u
.data
, r1
->rdlength
));
1114 mDNSexport mDNSBool
SameResourceRecord(ResourceRecord
*r1
, ResourceRecord
*r2
)
1116 return (r1
->namehash
== r2
->namehash
&&
1117 r1
->rrtype
== r2
->rrtype
&&
1118 SameDomainName(r1
->name
, r2
->name
) &&
1122 mDNSexport mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1124 if (rr
->InterfaceID
&&
1125 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1126 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1128 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1129 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->qtype
&& q
->qtype
!= kDNSQType_ANY
) return(mDNSfalse
);
1130 if ( rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1131 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1134 mDNSexport mDNSu16
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
1136 const RDataBody
*rd
= &rr
->rdata
->u
;
1137 const domainname
*const name
= estimate
? rr
->name
: mDNSNULL
;
1140 case kDNSType_A
: return(sizeof(rd
->ipv4
));
1141 case kDNSType_CNAME
:// Same as PTR
1142 case kDNSType_NS
: // Same as PTR
1143 case kDNSType_PTR
: return(CompressedDomainNameLength(&rd
->name
, name
));
1144 case kDNSType_HINFO
:return(mDNSu16
)(2 + (int)rd
->data
[0] + (int)rd
->data
[1 + (int)rd
->data
[0]]);
1145 case kDNSType_NULL
: // Same as TXT -- not self-describing, so have to just trust rdlength
1146 case kDNSType_TXT
: return(rr
->rdlength
); // TXT is not self-describing, so have to just trust rdlength
1147 case kDNSType_AAAA
: return(sizeof(rd
->ipv6
));
1148 case kDNSType_SRV
: return(mDNSu16
)(6 + CompressedDomainNameLength(&rd
->srv
.target
, name
));
1149 case kDNSType_SOA
: return (mDNSu16
)(CompressedDomainNameLength(&rd
->soa
.mname
, name
) +
1150 CompressedDomainNameLength(&rd
->soa
.rname
, name
) +
1151 5 * sizeof(mDNSOpaque32
));
1152 case kDNSType_OPT
: return(rr
->rdlength
);
1153 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
1154 return(rr
->rdlength
);
1158 mDNSexport mDNSBool
ValidateRData(const mDNSu16 rrtype
, const mDNSu16 rdlength
, const RData
*const rd
)
1164 case kDNSType_A
: return(rdlength
== sizeof(mDNSv4Addr
));
1166 case kDNSType_NS
: // Same as PTR
1167 case kDNSType_MD
: // Same as PTR
1168 case kDNSType_MF
: // Same as PTR
1169 case kDNSType_CNAME
:// Same as PTR
1170 //case kDNSType_SOA not checked
1171 case kDNSType_MB
: // Same as PTR
1172 case kDNSType_MG
: // Same as PTR
1173 case kDNSType_MR
: // Same as PTR
1174 //case kDNSType_NULL not checked (no specified format, so always valid)
1175 //case kDNSType_WKS not checked
1176 case kDNSType_PTR
: if (!rdlength
) return(mDNSfalse
);
1177 len
= DomainNameLength(&rd
->u
.name
);
1178 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== len
);
1180 case kDNSType_HINFO
:// Same as TXT (roughly)
1181 case kDNSType_MINFO
:// Same as TXT (roughly)
1182 case kDNSType_TXT
: if (!rdlength
) return(mDNSfalse
); // TXT record has to be at least one byte (RFC 1035)
1184 const mDNSu8
*ptr
= rd
->u
.txt
.c
;
1185 const mDNSu8
*end
= rd
->u
.txt
.c
+ rdlength
;
1186 while (ptr
< end
) ptr
+= 1 + ptr
[0];
1187 return (ptr
== end
);
1190 case kDNSType_AAAA
: return(rdlength
== sizeof(mDNSv6Addr
));
1192 case kDNSType_MX
: if (!rdlength
) return(mDNSfalse
);
1193 len
= DomainNameLength(&rd
->u
.mx
.exchange
);
1194 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 2+len
);
1196 case kDNSType_SRV
: if (!rdlength
) return(mDNSfalse
);
1197 len
= DomainNameLength(&rd
->u
.srv
.target
);
1198 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 6+len
);
1200 default: return(mDNStrue
); // Allow all other types without checking
1204 // ***************************************************************************
1205 #if COMPILER_LIKES_PRAGMA_MARK
1208 #pragma mark - DNS Message Creation Functions
1211 mDNSexport
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
1215 h
->numQuestions
= 0;
1217 h
->numAuthorities
= 0;
1218 h
->numAdditionals
= 0;
1221 mDNSexport
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
1223 const mDNSu8
*result
= end
- *domname
- 1;
1225 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
1227 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1228 while (result
>= base
)
1230 // If the length byte and first character of the label match, then check further to see
1231 // if this location in the packet will yield a useful name compression pointer.
1232 if (result
[0] == domname
[0] && result
[1] == domname
[1])
1234 const mDNSu8
*name
= domname
;
1235 const mDNSu8
*targ
= result
;
1236 while (targ
+ *name
< end
)
1238 // First see if this label matches
1240 const mDNSu8
*pointertarget
;
1241 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
1242 if (i
<= *name
) break; // If label did not match, bail out
1243 targ
+= 1 + *name
; // Else, did match, so advance target pointer
1244 name
+= 1 + *name
; // and proceed to check next label
1245 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
1246 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
1248 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1249 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
1250 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
1251 if (targ
+1 >= end
) break; // Second byte not present!
1252 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
1253 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
1254 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1255 targ
= pointertarget
;
1258 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
1263 // Put a string of dot-separated labels as length-prefixed labels
1264 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1265 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1266 // end points to the end of the message so far
1267 // ptr points to where we want to put the name
1268 // limit points to one byte past the end of the buffer that we must not overrun
1269 // domainname is the name to put
1270 mDNSexport mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
1271 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
1273 const mDNSu8
*const base
= (const mDNSu8
*)msg
;
1274 const mDNSu8
* np
= name
->c
;
1275 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1276 const mDNSu8
* pointer
= mDNSNULL
;
1277 const mDNSu8
*const searchlimit
= ptr
;
1279 while (*np
&& ptr
< limit
-1) // While we've got characters in the name, and space to write them in the message...
1281 if (*np
> MAX_DOMAIN_LABEL
)
1282 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name
->c
); return(mDNSNULL
); }
1284 // This check correctly allows for the final trailing root label:
1286 // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
1287 // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
1288 // We know that max will be at name->c[255]
1289 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1290 // six bytes, then exit the loop, write the final terminating root label, and the domain
1291 // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
1292 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1293 if (np
+ 1 + *np
>= max
)
1294 { LogMsg("Malformed domain name %##s (more than 255 bytes)", name
->c
); return(mDNSNULL
); }
1296 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
1297 if (pointer
) // Use a compression pointer if we can
1299 mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
1300 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
1301 *ptr
++ = (mDNSu8
)( offset
& 0xFF);
1304 else // Else copy one label and try again
1308 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
1310 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
1314 if (ptr
< limit
) // If we didn't run out of space
1316 *ptr
++ = 0; // Put the final root label
1317 return(ptr
); // and return
1323 mDNSlocal mDNSu8
*putVal16(mDNSu8
*ptr
, mDNSu16 val
)
1325 ptr
[0] = (mDNSu8
)((val
>> 8 ) & 0xFF);
1326 ptr
[1] = (mDNSu8
)((val
) & 0xFF);
1327 return ptr
+ sizeof(mDNSOpaque16
);
1330 mDNSlocal mDNSu8
*putVal32(mDNSu8
*ptr
, mDNSu32 val
)
1332 ptr
[0] = (mDNSu8
)((val
>> 24) & 0xFF);
1333 ptr
[1] = (mDNSu8
)((val
>> 16) & 0xFF);
1334 ptr
[2] = (mDNSu8
)((val
>> 8) & 0xFF);
1335 ptr
[3] = (mDNSu8
)((val
) & 0xFF);
1336 return ptr
+ sizeof(mDNSu32
);
1339 mDNSlocal mDNSu8
*putOptRData(mDNSu8
*ptr
, const mDNSu8
*limit
, ResourceRecord
*rr
)
1344 while (nput
< rr
->rdlength
)
1346 // check if space for opt/optlen
1347 if (ptr
+ (2 * sizeof(mDNSu16
)) > limit
) goto space_err
;
1348 opt
= (rdataOpt
*)(rr
->rdata
->u
.data
+ nput
);
1349 ptr
= putVal16(ptr
, opt
->opt
);
1350 ptr
= putVal16(ptr
, opt
->optlen
);
1351 nput
+= 2 * sizeof(mDNSu16
);
1352 if (opt
->opt
== kDNSOpt_LLQ
)
1354 if (ptr
+ LLQ_OPTLEN
> limit
) goto space_err
;
1355 ptr
= putVal16(ptr
, opt
->OptData
.llq
.vers
);
1356 ptr
= putVal16(ptr
, opt
->OptData
.llq
.llqOp
);
1357 ptr
= putVal16(ptr
, opt
->OptData
.llq
.err
);
1358 mDNSPlatformMemCopy(opt
->OptData
.llq
.id
, ptr
, 8); // 8-byte id
1360 ptr
= putVal32(ptr
, opt
->OptData
.llq
.lease
);
1363 else if (opt
->opt
== kDNSOpt_Lease
)
1365 if (ptr
+ sizeof(mDNSs32
) > limit
) goto space_err
;
1366 ptr
= putVal32(ptr
, opt
->OptData
.lease
);
1367 nput
+= sizeof(mDNSs32
);
1369 else { LogMsg("putOptRData - unknown option %d", opt
->opt
); return mDNSNULL
; }
1375 LogMsg("ERROR: putOptRData - out of space");
1379 mDNSlocal mDNSu16
getVal16(const mDNSu8
**ptr
)
1381 mDNSu16 val
= (mDNSu16
)(((mDNSu16
)(*ptr
)[0]) << 8 | (*ptr
)[1]);
1382 *ptr
+= sizeof(mDNSOpaque16
);
1386 mDNSlocal
const mDNSu8
*getOptRdata(const mDNSu8
*ptr
, const mDNSu8
*limit
, ResourceRecord
*rr
, mDNSu16 pktRDLen
)
1389 rdataOpt
*opt
= (rdataOpt
*)rr
->rdata
->u
.data
;
1391 while (nread
< pktRDLen
&& (mDNSu8
*)opt
< rr
->rdata
->u
.data
+ MaximumRDSize
- sizeof(rdataOpt
))
1393 // space for opt + optlen
1394 if (nread
+ (2 * sizeof(mDNSu16
)) > rr
->rdata
->MaxRDLength
) goto space_err
;
1395 opt
->opt
= getVal16(&ptr
);
1396 opt
->optlen
= getVal16(&ptr
);
1397 nread
+= 2 * sizeof(mDNSu16
);
1398 if (opt
->opt
== kDNSOpt_LLQ
)
1400 if ((unsigned)(limit
- ptr
) < LLQ_OPTLEN
) goto space_err
;
1401 opt
->OptData
.llq
.vers
= getVal16(&ptr
);
1402 opt
->OptData
.llq
.llqOp
= getVal16(&ptr
);
1403 opt
->OptData
.llq
.err
= getVal16(&ptr
);
1404 mDNSPlatformMemCopy(ptr
, opt
->OptData
.llq
.id
, 8);
1406 opt
->OptData
.llq
.lease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1407 if (opt
->OptData
.llq
.lease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1408 opt
->OptData
.llq
.lease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1409 ptr
+= sizeof(mDNSOpaque32
);
1410 nread
+= LLQ_OPTLEN
;
1412 else if (opt
->opt
== kDNSOpt_Lease
)
1414 if ((unsigned)(limit
- ptr
) < sizeof(mDNSs32
)) goto space_err
;
1416 opt
->OptData
.lease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1417 if (opt
->OptData
.lease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1418 opt
->OptData
.lease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1419 ptr
+= sizeof(mDNSs32
);
1420 nread
+= sizeof(mDNSs32
);
1422 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt
->opt
); return mDNSNULL
; }
1423 opt
++; // increment pointer into rdatabody
1426 rr
->rdlength
= pktRDLen
;
1430 LogMsg("ERROR: getLLQRdata - out of space");
1434 mDNSexport mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, ResourceRecord
*rr
)
1438 case kDNSType_A
: if (rr
->rdlength
!= 4)
1440 debugf("putRData: Illegal length %d for kDNSType_A", rr
->rdlength
);
1443 if (ptr
+ 4 > limit
) return(mDNSNULL
);
1444 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[0];
1445 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[1];
1446 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[2];
1447 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[3];
1450 case kDNSType_CNAME
:// Same as PTR
1451 case kDNSType_PTR
: return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.name
));
1453 case kDNSType_AAAA
: if (rr
->rdlength
!= sizeof(rr
->rdata
->u
.ipv6
))
1455 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr
->rdlength
);
1458 if (ptr
+ sizeof(rr
->rdata
->u
.ipv6
) > limit
) return(mDNSNULL
);
1459 mDNSPlatformMemCopy(&rr
->rdata
->u
.ipv6
, ptr
, sizeof(rr
->rdata
->u
.ipv6
));
1460 return(ptr
+ sizeof(rr
->rdata
->u
.ipv6
));
1462 case kDNSType_SRV
: if (ptr
+ 6 > limit
) return(mDNSNULL
);
1463 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
>> 8);
1464 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
& 0xFF);
1465 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
>> 8);
1466 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
& 0xFF);
1467 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[0];
1468 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[1];
1469 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.srv
.target
));
1470 case kDNSType_OPT
: return putOptRData(ptr
, limit
, rr
);
1472 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr
->rrtype
);
1473 // Fall through to common code below
1474 case kDNSType_HINFO
:
1476 case kDNSType_TSIG
: if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
1477 mDNSPlatformMemCopy(rr
->rdata
->u
.data
, ptr
, rr
->rdlength
);
1478 return(ptr
+ rr
->rdlength
);
1482 mDNSexport mDNSu8
*PutResourceRecordTTLWithLimit(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32 ttl
, const mDNSu8
*limit
)
1485 mDNSu16 actualLength
;
1487 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
1489 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1493 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
1494 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1495 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
1496 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
1497 ptr
[2] = (mDNSu8
)(rr
->rrclass
>> 8);
1498 ptr
[3] = (mDNSu8
)(rr
->rrclass
& 0xFF);
1499 ptr
[4] = (mDNSu8
)((ttl
>> 24) & 0xFF);
1500 ptr
[5] = (mDNSu8
)((ttl
>> 16) & 0xFF);
1501 ptr
[6] = (mDNSu8
)((ttl
>> 8) & 0xFF);
1502 ptr
[7] = (mDNSu8
)( ttl
& 0xFF);
1503 endofrdata
= putRData(msg
, ptr
+10, limit
, rr
);
1504 if (!endofrdata
) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
)); return(mDNSNULL
); }
1506 // Go back and fill in the actual number of data bytes we wrote
1507 // (actualLength can be less than rdlength when domain name compression is used)
1508 actualLength
= (mDNSu16
)(endofrdata
- ptr
- 10);
1509 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
1510 ptr
[9] = (mDNSu8
)(actualLength
& 0xFF);
1512 if (count
) (*count
)++;
1513 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1517 mDNSexport mDNSu8
*PutResourceRecordCappedTTL(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32
1520 if (maxttl
> rr
->rroriginalttl
) maxttl
= rr
->rroriginalttl
;
1521 return(PutResourceRecordTTL(msg
, ptr
, count
, rr
, maxttl
));
1524 mDNSexport mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1525 mDNSu16
*count
, const AuthRecord
*rr
)
1527 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->resrec
.name
);
1528 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1529 ptr
[0] = (mDNSu8
)(rr
->resrec
.rrtype
>> 8); // Put type
1530 ptr
[1] = (mDNSu8
)(rr
->resrec
.rrtype
& 0xFF);
1531 ptr
[2] = (mDNSu8
)(rr
->resrec
.rrclass
>> 8); // Put class
1532 ptr
[3] = (mDNSu8
)(rr
->resrec
.rrclass
& 0xFF);
1533 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
1534 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
1539 mDNSexport mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
1541 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1542 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1543 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1544 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1545 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1546 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
1547 msg
->h
.numQuestions
++;
1551 // for dynamic updates
1552 mDNSexport mDNSu8
*putZone(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu8
*limit
, const domainname
*zone
, mDNSOpaque16 zoneClass
)
1554 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, zone
);
1555 if (!ptr
|| ptr
+ 4 > limit
) return mDNSNULL
; // If we're out-of-space, return NULL
1556 *ptr
++ = (mDNSu8
)(kDNSType_SOA
>> 8);
1557 *ptr
++ = (mDNSu8
)(kDNSType_SOA
& 0xFF);
1558 *ptr
++ = zoneClass
.b
[0];
1559 *ptr
++ = zoneClass
.b
[1];
1560 msg
->h
.mDNS_numZones
++;
1564 // for dynamic updates
1565 mDNSexport mDNSu8
*putPrereqNameNotInUse(domainname
*name
, DNSMessage
*msg
, mDNSu8
*ptr
, mDNSu8
*end
)
1569 mDNSPlatformMemZero(&prereq
, sizeof(AuthRecord
));
1570 mDNS_SetupResourceRecord(&prereq
, mDNSNULL
, mDNSInterface_Any
, kDNSQType_ANY
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
1571 AssignDomainName(prereq
.resrec
.name
, name
);
1572 prereq
.resrec
.rrtype
= kDNSQType_ANY
;
1573 prereq
.resrec
.rrclass
= kDNSClass_NONE
;
1574 ptr
= putEmptyResourceRecord(msg
, ptr
, end
, &msg
->h
.mDNS_numPrereqs
, &prereq
);
1578 // for dynamic updates
1579 mDNSexport mDNSu8
*putDeletionRecord(DNSMessage
*msg
, mDNSu8
*ptr
, ResourceRecord
*rr
)
1582 // deletion: specify record w/ TTL 0, class NONE
1584 origclass
= rr
->rrclass
;
1585 rr
->rrclass
= kDNSClass_NONE
;
1586 ptr
= PutResourceRecordTTLJumbo(msg
, ptr
, &msg
->h
.mDNS_numUpdates
, rr
, 0);
1587 rr
->rrclass
= origclass
;
1591 mDNSexport mDNSu8
*putDeleteRRSet(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
, mDNSu16 rrtype
)
1593 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1594 mDNSu16
class = kDNSQClass_ANY
;
1596 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1597 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
1598 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1599 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1600 ptr
[2] = (mDNSu8
)(class >> 8);
1601 ptr
[3] = (mDNSu8
)(class & 0xFF);
1602 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
1603 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
1605 msg
->h
.mDNS_numUpdates
++;
1609 // for dynamic updates
1610 mDNSexport mDNSu8
*putDeleteAllRRSets(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
)
1612 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1613 mDNSu16
class = kDNSQClass_ANY
;
1614 mDNSu16 rrtype
= kDNSQType_ANY
;
1616 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1617 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
1618 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1619 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1620 ptr
[2] = (mDNSu8
)(class >> 8);
1621 ptr
[3] = (mDNSu8
)(class & 0xFF);
1622 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
1623 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
1625 msg
->h
.mDNS_numUpdates
++;
1629 // for dynamic updates
1630 mDNSexport mDNSu8
*putUpdateLease(DNSMessage
*msg
, mDNSu8
*end
, mDNSu32 lease
)
1633 ResourceRecord
*opt
= &rr
.resrec
;
1636 mDNSPlatformMemZero(&rr
, sizeof(AuthRecord
));
1637 mDNS_SetupResourceRecord(&rr
, mDNSNULL
, mDNSInterface_Any
, kDNSType_OPT
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
1639 opt
->RecordType
= kDNSRecordTypeKnownUnique
; // to avoid warnings in other layers
1640 opt
->rrtype
= kDNSType_OPT
;
1641 opt
->rdlength
= LEASE_OPT_RDLEN
;
1642 opt
->rdestimate
= LEASE_OPT_RDLEN
;
1644 optRD
= &rr
.resrec
.rdata
->u
.opt
;
1645 optRD
->opt
= kDNSOpt_Lease
;
1646 optRD
->optlen
= sizeof(mDNSs32
);
1647 optRD
->OptData
.lease
= lease
;
1648 end
= PutResourceRecordTTLJumbo(msg
, end
, &msg
->h
.numAdditionals
, opt
, 0);
1649 if (!end
) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL
; }
1654 // ***************************************************************************
1655 #if COMPILER_LIKES_PRAGMA_MARK
1657 #pragma mark - DNS Message Parsing Functions
1660 mDNSexport mDNSu32
DomainNameHashValue(const domainname
*const name
)
1665 for (c
= name
->c
; c
[0] != 0 && c
[1] != 0; c
+= 2)
1667 sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8) |
1668 (mDNSIsUpperCase(c
[1]) ? c
[1] + 'a' - 'A' : c
[1]);
1669 sum
= (sum
<<3) | (sum
>>29);
1671 if (c
[0]) sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8);
1675 mDNSexport
void SetNewRData(ResourceRecord
*const rr
, RData
*NewRData
, mDNSu16 rdlength
)
1680 rr
->rdata
= NewRData
;
1681 rr
->rdlength
= rdlength
;
1683 // Must not try to get target pointer until after updating rr->rdata
1684 target
= GetRRDomainNameTarget(rr
);
1685 rr
->rdlength
= GetRDLength(rr
, mDNSfalse
);
1686 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
1687 rr
->rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
->rdlength
, &rr
->rdata
->u
);
1690 mDNSexport
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
1694 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1695 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1697 while (1) // Read sequence of labels
1699 const mDNSu8 len
= *ptr
++; // Read length of this label
1700 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
1703 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1704 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1705 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
1706 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1711 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
1712 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
1713 case 0xC0: return(ptr
+1);
1718 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
1719 mDNSexport
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
1720 domainname
*const name
)
1722 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
1723 mDNSu8
*np
= name
->c
; // Name pointer
1724 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
1726 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1727 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1729 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1731 while (1) // Read sequence of labels
1733 const mDNSu8 len
= *ptr
++; // Read length of this label
1734 if (len
== 0) break; // If length is zero, that means this name is complete
1740 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1741 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1742 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
1743 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1745 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
1746 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1749 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
1752 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
1754 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
1755 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
1756 ptr
= (mDNSu8
*)msg
+ offset
;
1757 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1758 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
1760 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
1765 if (nextbyte
) return(nextbyte
);
1769 mDNSexport
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1771 mDNSu16 pktrdlength
;
1773 ptr
= skipDomainName(msg
, ptr
, end
);
1774 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1776 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1777 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1779 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1781 return(ptr
+ pktrdlength
);
1784 mDNSexport
const mDNSu8
*GetLargeResourceRecord(mDNS
*const m
, const DNSMessage
* const msg
, const mDNSu8
*ptr
,
1785 const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
, mDNSu8 RecordType
, LargeCacheRecord
*largecr
)
1787 CacheRecord
*rr
= &largecr
->r
;
1788 mDNSu16 pktrdlength
;
1790 if (largecr
== &m
->rec
&& rr
->resrec
.RecordType
)
1791 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use");
1793 rr
->next
= mDNSNULL
;
1794 rr
->resrec
.RecordType
= RecordType
;
1795 rr
->resrec
.name
= &largecr
->namestorage
;
1797 rr
->NextInKAList
= mDNSNULL
;
1798 rr
->TimeRcvd
= m
? m
->timenow
: 0;
1799 rr
->DelayDelivery
= 0;
1800 rr
->NextRequiredQuery
= m
? m
->timenow
: 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
1801 rr
->LastUsed
= m
? m
->timenow
: 0;
1802 rr
->CRActiveQuestion
= mDNSNULL
;
1803 rr
->UnansweredQueries
= 0;
1804 rr
->LastUnansweredTime
= 0;
1805 rr
->MPUnansweredQ
= 0;
1806 rr
->MPLastUnansweredQT
= 0;
1807 rr
->MPUnansweredKA
= 0;
1808 rr
->MPExpectingKA
= mDNSfalse
;
1809 rr
->NextInCFList
= mDNSNULL
;
1811 rr
->resrec
.InterfaceID
= InterfaceID
;
1812 ptr
= getDomainName(msg
, ptr
, end
, rr
->resrec
.name
);
1813 if (!ptr
) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1815 if (ptr
+ 10 > end
) { debugf("GetResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1817 rr
->resrec
.rrtype
= (mDNSu16
) ((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1818 rr
->resrec
.rrclass
= (mDNSu16
)(((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSClass_Mask
);
1819 rr
->resrec
.rroriginalttl
= (mDNSu32
) ((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
1820 if (rr
->resrec
.rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
&& (mDNSs32
)rr
->resrec
.rroriginalttl
!= -1)
1821 rr
->resrec
.rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1822 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
1823 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
1824 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1825 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8))
1826 rr
->resrec
.RecordType
|= kDNSRecordTypePacketUniqueMask
;
1828 if (ptr
+ pktrdlength
> end
) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1829 end
= ptr
+ pktrdlength
; // Adjust end to indicate the end of the rdata for this resource record
1831 rr
->resrec
.rdata
= (RData
*)&rr
->rdatastorage
;
1832 rr
->resrec
.rdata
->MaxRDLength
= MaximumRDSize
;
1834 if (!RecordType
) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr
->resrec
.name
->c
);
1836 switch (rr
->resrec
.rrtype
)
1838 case kDNSType_A
: rr
->resrec
.rdata
->u
.ipv4
.b
[0] = ptr
[0];
1839 rr
->resrec
.rdata
->u
.ipv4
.b
[1] = ptr
[1];
1840 rr
->resrec
.rdata
->u
.ipv4
.b
[2] = ptr
[2];
1841 rr
->resrec
.rdata
->u
.ipv4
.b
[3] = ptr
[3];
1844 case kDNSType_CNAME
:// Same as PTR
1846 case kDNSType_PTR
: if (!getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.name
))
1847 { debugf("GetResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
1848 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
1851 case kDNSType_NULL
: //Same as TXT
1852 case kDNSType_HINFO
://Same as TXT
1853 case kDNSType_TXT
: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
1855 debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)",
1856 DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
1859 rr
->resrec
.rdlength
= pktrdlength
;
1860 mDNSPlatformMemCopy(ptr
, rr
->resrec
.rdata
->u
.data
, pktrdlength
);
1863 case kDNSType_AAAA
: mDNSPlatformMemCopy(ptr
, &rr
->resrec
.rdata
->u
.ipv6
, sizeof(rr
->resrec
.rdata
->u
.ipv6
));
1866 case kDNSType_SRV
: rr
->resrec
.rdata
->u
.srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1867 rr
->resrec
.rdata
->u
.srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
1868 rr
->resrec
.rdata
->u
.srv
.port
.b
[0] = ptr
[4];
1869 rr
->resrec
.rdata
->u
.srv
.port
.b
[1] = ptr
[5];
1870 if (!getDomainName(msg
, ptr
+6, end
, &rr
->resrec
.rdata
->u
.srv
.target
))
1871 { debugf("GetResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
1872 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
1875 case kDNSType_SOA
: ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.mname
);
1876 if (!ptr
) { debugf("GetResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL
; }
1877 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.rname
);
1878 if (!ptr
) { debugf("GetResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL
; }
1879 if (ptr
+ 0x14 != end
) { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL
; }
1880 rr
->resrec
.rdata
->u
.soa
.serial
= (mDNSs32
) ((mDNSs32
)ptr
[0x00] << 24 | (mDNSs32
)ptr
[0x01] << 16 | (mDNSs32
)ptr
[0x02] << 8 | ptr
[0x03]);
1881 rr
->resrec
.rdata
->u
.soa
.refresh
= (mDNSu32
) ((mDNSu32
)ptr
[0x04] << 24 | (mDNSu32
)ptr
[0x05] << 16 | (mDNSu32
)ptr
[0x06] << 8 | ptr
[0x07]);
1882 rr
->resrec
.rdata
->u
.soa
.retry
= (mDNSu32
) ((mDNSu32
)ptr
[0x08] << 24 | (mDNSu32
)ptr
[0x09] << 16 | (mDNSu32
)ptr
[0x0A] << 8 | ptr
[0x0B]);
1883 rr
->resrec
.rdata
->u
.soa
.expire
= (mDNSu32
) ((mDNSu32
)ptr
[0x0C] << 24 | (mDNSu32
)ptr
[0x0D] << 16 | (mDNSu32
)ptr
[0x0E] << 8 | ptr
[0x0F]);
1884 rr
->resrec
.rdata
->u
.soa
.min
= (mDNSu32
) ((mDNSu32
)ptr
[0x10] << 24 | (mDNSu32
)ptr
[0x11] << 16 | (mDNSu32
)ptr
[0x12] << 8 | ptr
[0x13]);
1887 case kDNSType_OPT
: getOptRdata(ptr
, end
, &rr
->resrec
, pktrdlength
); break;
1889 default: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
1891 debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
1892 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
1895 debugf("GetResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
1896 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
));
1897 // Note: Just because we don't understand the record type, that doesn't
1898 // mean we fail. The DNS protocol specifies rdlength, so we can
1899 // safely skip over unknown records and ignore them.
1900 // We also grab a binary copy of the rdata anyway, since the caller
1901 // might know how to interpret it even if we don't.
1902 rr
->resrec
.rdlength
= pktrdlength
;
1903 mDNSPlatformMemCopy(ptr
, rr
->resrec
.rdata
->u
.data
, pktrdlength
);
1907 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
1908 SetNewRData(&rr
->resrec
, mDNSNULL
, 0);
1910 return(ptr
+ pktrdlength
);
1913 mDNSexport
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1915 ptr
= skipDomainName(msg
, ptr
, end
);
1916 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
1917 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1921 mDNSexport
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
,
1922 DNSQuestion
*question
)
1924 question
->InterfaceID
= InterfaceID
;
1925 ptr
= getDomainName(msg
, ptr
, end
, &question
->qname
);
1926 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
1927 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1929 question
->qnamehash
= DomainNameHashValue(&question
->qname
);
1930 question
->qtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
1931 question
->qclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
1935 mDNSexport
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1938 const mDNSu8
*ptr
= msg
->data
;
1939 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
1943 mDNSexport
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1946 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
1947 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
1951 mDNSexport
const mDNSu8
*LocateAdditionals(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1954 const mDNSu8
*ptr
= LocateAuthorities(msg
, end
);
1955 for (i
= 0; i
< msg
->h
.numAuthorities
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
1959 // ***************************************************************************
1960 #if COMPILER_LIKES_PRAGMA_MARK
1963 #pragma mark - Packet Sending Functions
1966 mDNSexport mStatus
mDNSSendDNSMessage(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
,
1967 mDNSInterfaceID InterfaceID
, const mDNSAddr
*dst
, mDNSIPPort dstport
, int sd
, uDNS_AuthInfo
*authInfo
)
1973 mDNSu16 numQuestions
= msg
->h
.numQuestions
;
1974 mDNSu16 numAnswers
= msg
->h
.numAnswers
;
1975 mDNSu16 numAuthorities
= msg
->h
.numAuthorities
;
1976 mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
1977 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
1979 // Put all the integer values in IETF byte-order (MSB first, LSB second)
1980 *ptr
++ = (mDNSu8
)(numQuestions
>> 8);
1981 *ptr
++ = (mDNSu8
)(numQuestions
& 0xFF);
1982 *ptr
++ = (mDNSu8
)(numAnswers
>> 8);
1983 *ptr
++ = (mDNSu8
)(numAnswers
& 0xFF);
1984 *ptr
++ = (mDNSu8
)(numAuthorities
>> 8);
1985 *ptr
++ = (mDNSu8
)(numAuthorities
& 0xFF);
1986 *ptr
++ = (mDNSu8
)(numAdditionals
>> 8);
1987 *ptr
++ = (mDNSu8
)(numAdditionals
& 0xFF);
1991 end
= DNSDigest_SignMessage(msg
, &end
, &numAdditionals
, authInfo
);
1992 if (!end
) return mStatus_UnknownErr
;
1995 // Send the packet on the wire
1999 msglen
= (mDNSu16
)(end
- (mDNSu8
*)msg
);
2000 lenbuf
[0] = (mDNSu8
)(msglen
>> 8); // host->network byte conversion
2001 lenbuf
[1] = (mDNSu8
)(msglen
& 0xFF);
2002 nsent
= mDNSPlatformWriteTCP(sd
, (char*)lenbuf
, 2);
2003 //!!!KRS make sure kernel is sending these as 1 packet!
2004 if (nsent
!= 2) goto tcp_error
;
2005 nsent
= mDNSPlatformWriteTCP(sd
, (char *)msg
, msglen
);
2006 if (nsent
!= msglen
) goto tcp_error
;
2007 status
= mStatus_NoError
;
2011 status
= mDNSPlatformSendUDP(m
, msg
, end
, InterfaceID
, dst
, dstport
);
2014 // Put all the integer values back the way they were before we return
2015 msg
->h
.numQuestions
= numQuestions
;
2016 msg
->h
.numAnswers
= numAnswers
;
2017 msg
->h
.numAuthorities
= numAuthorities
;
2018 msg
->h
.numAdditionals
= (mDNSu16
)(authInfo
? numAdditionals
- 1 : numAdditionals
);
2023 LogMsg("mDNSSendDNSMessage: error sending message over tcp");
2024 return mStatus_UnknownErr
;
2027 // ***************************************************************************
2028 #if COMPILER_LIKES_PRAGMA_MARK
2030 #pragma mark - RR List Management & Task Management
2033 mDNSexport
void mDNS_Lock(mDNS
*const m
)
2035 // MUST grab the platform lock FIRST!
2036 mDNSPlatformLock(m
);
2038 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2039 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2040 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2041 // If mDNS_busy != mDNS_reentrancy that's a bad sign
2042 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2043 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2045 // If this is an initial entry into the mDNSCore code, set m->timenow
2046 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
2047 if (m
->mDNS_busy
== 0)
2050 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m
->timenow
, mDNSPlatformRawTime() + m
->timenow_adjust
);
2051 m
->timenow
= mDNSPlatformRawTime() + m
->timenow_adjust
;
2052 if (m
->timenow
== 0) m
->timenow
= 1;
2054 else if (m
->timenow
== 0)
2056 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
2057 m
->timenow
= mDNSPlatformRawTime() + m
->timenow_adjust
;
2058 if (m
->timenow
== 0) m
->timenow
= 1;
2061 if (m
->timenow_last
- m
->timenow
> 0)
2063 m
->timenow_adjust
+= m
->timenow_last
- m
->timenow
;
2064 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m
->timenow_last
- m
->timenow
, m
->timenow_adjust
);
2065 m
->timenow
= m
->timenow_last
;
2067 m
->timenow_last
= m
->timenow
;
2069 // Increment mDNS_busy so we'll recognise re-entrant calls
2073 mDNSlocal mDNSs32
GetNextScheduledEvent(const mDNS
*const m
)
2075 mDNSs32 e
= m
->timenow
+ 0x78000000;
2076 if (m
->mDNSPlatformStatus
!= mStatus_NoError
|| m
->SleepState
) return(e
);
2077 if (m
->NewQuestions
)
2079 if (m
->NewQuestions
->DelayAnswering
) e
= m
->NewQuestions
->DelayAnswering
;
2080 else return(m
->timenow
);
2082 if (m
->NewLocalOnlyQuestions
) return(m
->timenow
);
2083 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
)) return(m
->timenow
);
2084 if (m
->SuppressSending
) return(m
->SuppressSending
);
2085 #ifndef UNICAST_DISABLED
2086 if (e
- m
->uDNS_info
.nextevent
> 0) e
= m
->uDNS_info
.nextevent
;
2088 if (e
- m
->NextCacheCheck
> 0) e
= m
->NextCacheCheck
;
2089 if (e
- m
->NextScheduledQuery
> 0) e
= m
->NextScheduledQuery
;
2090 if (e
- m
->NextScheduledProbe
> 0) e
= m
->NextScheduledProbe
;
2091 if (e
- m
->NextScheduledResponse
> 0) e
= m
->NextScheduledResponse
;
2095 mDNSexport
void mDNS_Unlock(mDNS
*const m
)
2097 // Decrement mDNS_busy
2100 // Check for locking failures
2101 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2102 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2104 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
2105 if (m
->mDNS_busy
== 0)
2107 m
->NextScheduledEvent
= GetNextScheduledEvent(m
);
2108 if (m
->timenow
== 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
2112 // MUST release the platform lock LAST!
2113 mDNSPlatformUnlock(m
);