1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
24 Change History (most recent first):
27 Revision 1.96.2.1 2006/10/31 02:50:16 cheshire
28 <rdar://problem/4683163> mDNSResponder insufficiently defensive against malformed browsing PTR responses
30 Revision 1.96 2006/03/10 21:51:42 cheshire
31 <rdar://problem/4111464> After record update, old record sometimes remains in cache
32 Split out SameRDataBody() into a separate routine so it can be called from other code
34 Revision 1.95 2006/03/08 22:43:11 cheshire
35 Use "localdomain" symbol instead of literal string
37 Revision 1.94 2006/03/02 21:59:55 cheshire
38 <rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use"
39 Improve sanity checks & debugging support in GetLargeResourceRecord()
41 Revision 1.93 2006/03/02 20:30:47 cheshire
42 Improved GetRRDisplayString to also show priority, weight, and port for SRV records
44 Revision 1.92 2005/09/16 21:06:49 cheshire
45 Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place
47 Revision 1.91 2005/07/10 22:10:37 cheshire
48 The getOptRdata routine implicitly assumes the destination ResourceRecord is large enough to
49 hold MaximumRDSize bytes, but its parameter was a generic ResourceRecord, which need not be that
50 large. Changing the parameter to a LargeCacheRecord makes it clearer what the routine requires.
52 Revision 1.90 2005/03/21 00:33:51 shersche
53 <rdar://problem/4021486> Fix build warnings on Win32 platform
55 Revision 1.89 2005/03/17 18:59:38 ksekar
56 <rdar://problem/4012279> Properly parse multiple LLQ Options per packet on Windows
58 Revision 1.88 2005/03/16 00:42:32 ksekar
59 <rdar://problem/4012279> Long-lived queries not working on Windows
61 Revision 1.87 2005/02/25 04:21:00 cheshire
62 <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
64 Revision 1.86 2005/02/18 00:43:12 cheshire
65 <rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
67 Revision 1.85 2005/02/10 22:35:17 cheshire
68 <rdar://problem/3727944> Update name
70 Revision 1.84 2005/02/03 00:44:38 cheshire
71 <rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
73 Revision 1.83 2005/01/27 22:57:55 cheshire
74 Fix compile errors on gcc4
76 Revision 1.82 2005/01/19 03:27:03 cheshire
77 <rdar://problem/3961051> CPU Spin in mDNSResponder
78 GetNextScheduledEvent() needs to check LocalRecordReady()
80 Revision 1.81 2004/12/18 03:13:45 cheshire
81 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
83 Revision 1.80 2004/12/16 21:46:43 cheshire
84 Add DNSTypeName case for kDNSType_SOA
86 Revision 1.79 2004/12/16 21:38:37 cheshire
87 Add DNSTypeName case for kDNSType_NS
89 Revision 1.78 2004/12/16 21:27:37 ksekar
90 Fixed build failures when compiled with verbose debugging messages
92 Revision 1.77 2004/12/16 20:12:59 cheshire
93 <rdar://problem/3324626> Cache memory management improvements
95 Revision 1.76 2004/12/16 08:05:29 shersche
96 Remove extranenous semicolons that cause compilation errors on Windows
98 Revision 1.75 2004/12/15 02:11:22 ksekar
99 <rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness
101 Revision 1.74 2004/12/09 22:49:15 ksekar
102 <rdar://problem/3913653> Wide-Area Goodbyes broken
104 Revision 1.73 2004/12/07 22:49:06 cheshire
105 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
107 Revision 1.72 2004/12/06 21:15:20 ksekar
108 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
110 Revision 1.71 2004/12/04 02:12:45 cheshire
111 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
113 Revision 1.70 2004/12/03 19:52:44 ksekar
114 Use PutResourceRecordTTLJumbo for putDeletionRecord()
116 Revision 1.69 2004/12/03 07:20:50 ksekar
117 <rdar://problem/3674208> Wide-Area: Registration of large TXT record fails
119 Revision 1.68 2004/11/24 00:10:43 cheshire
120 <rdar://problem/3869241> For unicast operations, verify that service types are legal
122 Revision 1.67 2004/10/26 03:52:02 cheshire
123 Update checkin comments
125 Revision 1.66 2004/10/23 01:16:00 cheshire
126 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
128 Revision 1.65 2004/10/20 02:15:09 cheshire
129 Add case in GetRRDisplayString() to display NS rdata
131 Revision 1.64 2004/10/13 00:24:02 cheshire
132 Disable "array is too small to include a terminating null character" warning on Windows
134 Revision 1.63 2004/10/10 06:57:14 cheshire
135 Change definition of "localdomain" to make code compile a little smaller
137 Revision 1.62 2004/10/06 01:44:19 cheshire
138 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
140 Revision 1.61 2004/09/30 00:24:56 ksekar
141 <rdar://problem/3695802> Dynamically update default registration domains on config change
143 Revision 1.60 2004/09/27 23:25:30 cheshire
144 Fix compiler warning: soa.serial is signed, not unsigned
146 Revision 1.59 2004/09/27 22:53:45 ksekar
147 Fixed getLargeResourceRecord for SOA rdata.
149 Revision 1.58 2004/09/25 02:41:39 cheshire
150 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
152 Revision 1.57 2004/09/25 02:24:27 cheshire
153 Removed unused rr->UseCount
155 Revision 1.56 2004/09/24 20:57:39 cheshire
156 <rdar://problem/3680902> Eliminate inappropriate casts that cause misaligned-address errors
158 Revision 1.55 2004/09/17 01:08:48 cheshire
159 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
160 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
161 declared in that file are ONLY appropriate to single-address-space embedded applications.
162 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
164 Revision 1.54 2004/09/17 00:49:51 cheshire
165 Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use
166 is GetLargeResourceRecord
168 Revision 1.53 2004/09/17 00:31:51 cheshire
169 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
171 Revision 1.52 2004/09/17 00:19:10 cheshire
172 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
174 Revision 1.51 2004/09/16 02:29:39 cheshire
175 Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
176 uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
178 Revision 1.50 2004/09/16 01:58:14 cheshire
179 Fix compiler warnings
181 Revision 1.49 2004/09/14 23:42:35 cheshire
182 <rdar://problem/3801296> Need to seed random number generator from platform-layer data
184 Revision 1.48 2004/09/14 23:27:46 cheshire
187 Revision 1.47 2004/08/25 02:50:04 cheshire
188 <rdar://problem/3561220> Browses are no longer piggybacking on other browses
189 Make mDNSSameAddress() recognise that two mDNSAddrType_None addresses are necessarily equal
191 Revision 1.46 2004/08/18 17:35:40 ksekar
192 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
194 Revision 1.45 2004/08/15 18:26:00 cheshire
195 Don't use strcpy() on "struct domainname" objects; use AssignDomainName() instead
196 (A "struct domainname" is a collection of packed pascal strings, not a C string.)
198 Revision 1.44 2004/08/13 23:46:58 cheshire
199 "asyncronous" -> "asynchronous"
201 Revision 1.43 2004/08/12 02:55:46 ksekar
202 Fix param order error moving putPrereqNameNotInUse from uDNS.c using
203 ustrcpy macro to DNSCommon.c using mDNSPlatformStrCopy().
205 Revision 1.42 2004/08/10 23:19:14 ksekar
206 <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
207 Moved routines/constants to allow extern access for garbage collection daemon
209 Revision 1.41 2004/08/10 01:10:01 cheshire
210 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
211 Minor revision from Roger Pantos
213 Revision 1.40 2004/08/04 22:10:46 cheshire
214 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
215 Change to use "._sub." instead of ".s." to mark subtypes.
217 Revision 1.39 2004/07/13 21:24:24 rpantos
218 Fix for <rdar://problem/3701120>.
220 Revision 1.38 2004/06/18 21:08:58 cheshire
221 <rdar://problem/3540040> Applications are registering invalid records
222 Attempts to create domain names like "www..apple.com." now logged to aid debugging
224 Revision 1.37 2004/06/18 20:25:42 cheshire
225 <rdar://problem/3488547> Add a syslog message if someone tries to use "local.arpa".
227 Revision 1.36 2004/06/18 19:09:59 cheshire
228 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
230 Revision 1.35 2004/06/05 00:14:44 cheshire
231 Fix signed/unsigned and other compiler warnings
233 Revision 1.34 2004/06/04 00:25:25 cheshire
234 Fix misaligned write exception that occurs on some platforms
236 Revision 1.33 2004/06/04 00:16:18 cheshire
237 Remove non-portable use of 'inline'
239 Revision 1.32 2004/06/03 03:09:58 ksekar
240 <rdar://problem/3668626>: Garbage Collection for Dynamic Updates
242 Revision 1.31 2004/05/28 23:42:36 ksekar
243 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
245 Revision 1.30 2004/05/26 09:08:04 bradley
246 Added cast to correct structure pointer when allocating domain name list element to fix C++ builds.
248 Revision 1.29 2004/05/18 23:51:25 cheshire
249 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
251 Revision 1.28 2004/05/13 04:54:20 ksekar
252 Unified list copy/free code. Added symetric list for
254 Revision 1.27 2004/04/22 20:29:07 cheshire
255 Log error message if no count field passed to PutResourceRecordTTL()
257 Revision 1.26 2004/04/22 04:07:01 cheshire
258 Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it
260 Revision 1.25 2004/04/22 03:05:28 cheshire
261 kDNSClass_ANY should be kDNSQClass_ANY
263 Revision 1.24 2004/04/22 02:51:20 cheshire
264 Use common code for HINFO/TXT and TSIG cases in putRData
266 Revision 1.23 2004/04/15 00:51:28 bradley
267 Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
268 Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
270 Revision 1.22 2004/04/14 23:09:28 ksekar
271 Support for TSIG signed dynamic updates.
273 Revision 1.21 2004/04/09 16:47:28 cheshire
274 <rdar://problem/3617655>: mDNSResponder escape handling inconsistent with BIND
276 Revision 1.20 2004/04/09 16:37:15 cheshire
277 Suggestion from Bob Bradley:
278 Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers
280 Revision 1.19 2004/04/02 19:34:38 cheshire
283 Revision 1.18 2004/03/30 06:45:00 cheshire
284 Compiler warning fixes from Don Woodward at Roku Labs
286 Revision 1.17 2004/03/19 22:25:20 cheshire
287 <rdar://problem/3579561>: Need to limit service types to fourteen characters
288 Won't actually do this for now, but keep the code around just in case
290 Revision 1.16 2004/03/08 02:45:35 cheshire
291 Minor change to make a couple of the log messages a bit shorter
293 Revision 1.15 2004/03/08 02:44:09 cheshire
294 <rdar://problem/3579561>: Need to limit service types to fourteen characters
296 Revision 1.14 2004/02/21 02:06:24 cheshire
297 Can't use anonymous unions -- they're non-standard and don't work on all compilers
299 Revision 1.13 2004/02/06 23:04:18 ksekar
300 Basic Dynamic Update support via mDNS_Register (dissabled via
301 UNICAST_REGISTRATION #define)
303 Revision 1.12 2004/02/03 22:37:10 cheshire
304 Delete unused (commented-out) code
306 Revision 1.11 2004/02/03 22:35:34 cheshire
307 <rdar://problem/3548256>: Should not allow empty string for resolve domain
309 Revision 1.10 2004/02/03 19:47:36 ksekar
310 Added an asynchronous state machine mechanism to uDNS.c, including
311 calls to find the parent zone for a domain name. Changes include code
312 in repository previously dissabled via "#if 0 incomplete". Codepath
313 is currently unused, and will be called to create update records, etc.
315 Revision 1.9 2004/01/27 20:15:22 cheshire
316 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
318 Revision 1.8 2004/01/24 23:24:36 cheshire
319 Expanded out the list of local domains to reduce risk of mistakes in future
321 Revision 1.7 2004/01/24 08:32:30 bradley
322 Mask values with 0xFF before casting to avoid runtime truncation errors on Windows debug builds.
323 Separated octal-escaped sequences preceding decimal digits to avoid errors with some compilers wanting
324 to signal potentially hidden errors about the subsequent digit not being part of the octal sequence.
326 Revision 1.6 2004/01/24 04:59:15 cheshire
327 Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
329 Revision 1.5 2004/01/23 23:23:14 ksekar
330 Added TCP support for truncated unicast messages.
332 Revision 1.4 2004/01/22 02:15:33 cheshire
333 <rdar://problem/3536597>: Link-local reverse-mapping domains need to be resolved using link-local multicast
335 Revision 1.3 2004/01/21 21:16:29 cheshire
336 Minor tidy-up: Deleted a bunch of blank lines, trailing spaces, tabs, etc.
338 Revision 1.2 2003/12/13 05:47:48 bradley
339 Made local ptr const to fix error when assigning from const structure. Disable benign conditional
340 expression is constant warning when building with Microsoft compilers.
342 Revision 1.1 2003/12/13 03:05:27 ksekar
343 <rdar://problem/3192548>: DynDNS: Unicast query of service records
347 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
348 #define mDNS_InstantiateInlines 1
349 #include "DNSCommon.h"
351 // Disable certain benign warnings with Microsoft compilers
352 #if (defined(_MSC_VER))
353 // Disable "conditional expression is constant" warning for debug macros.
354 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
355 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
356 #pragma warning(disable:4127)
357 // Disable "array is too small to include a terminating null character" warning
358 // -- domain labels have an initial length byte, not a terminating null character
359 #pragma warning(disable:4295)
362 // ***************************************************************************
363 #if COMPILER_LIKES_PRAGMA_MARK
365 #pragma mark - DNameList copy/deallocation routines
368 mDNSexport DNameListElem
*mDNS_CopyDNameList(const DNameListElem
*orig
)
370 DNameListElem
*copy
= mDNSNULL
, *newelem
;
371 const DNameListElem
*ptr
;
373 for (ptr
= orig
; ptr
; ptr
= ptr
->next
)
375 newelem
= (DNameListElem
*)mDNSPlatformMemAllocate(sizeof(DNameListElem
));
376 if (!newelem
) { LogMsg("ERROR: malloc"); return mDNSNULL
; }
377 AssignDomainName(&newelem
->name
, &ptr
->name
);
378 newelem
->next
= copy
;
384 mDNSexport
void mDNS_FreeDNameList(DNameListElem
*list
)
392 mDNSPlatformMemFree(fptr
);
396 // ***************************************************************************
397 #if COMPILER_LIKES_PRAGMA_MARK
399 #pragma mark - General Utility Functions
402 // return true for RFC1918 private addresses
403 mDNSexport mDNSBool
IsPrivateV4Addr(mDNSAddr
*addr
)
407 if (addr
->type
!= mDNSAddrType_IPv4
) return mDNSfalse
;
410 return ((b
[0] == 10) || // 10/8 prefix
411 (b
[0] == 172 && b
[1] > 15 && b
[1] < 32) || // 172.16/12
412 (b
[0] == 192 && b
[1] == 168)); // 192.168/16
415 mDNSexport
const NetworkInterfaceInfo
*GetFirstActiveInterface(const NetworkInterfaceInfo
*intf
)
417 while (intf
&& !intf
->InterfaceActive
) intf
= intf
->next
;
421 mDNSexport mDNSInterfaceID
GetNextActiveInterfaceID(const NetworkInterfaceInfo
*intf
)
423 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
424 if (next
) return(next
->InterfaceID
); else return(mDNSNULL
);
427 mDNSexport mDNSu32
NumCacheRecordsForInterfaceID(const mDNS
*const m
, mDNSInterfaceID id
)
429 mDNSu32 slot
, used
= 0;
432 FORALL_CACHERECORDS(slot
, cg
, rr
)
433 if (rr
->resrec
.InterfaceID
== id
) used
++;
437 mDNSexport
char *DNSTypeName(mDNSu16 rrtype
)
441 case kDNSType_A
: return("Addr");
442 case kDNSType_NS
: return("NS");
443 case kDNSType_CNAME
:return("CNAME");
444 case kDNSType_SOA
: return("SOA");
445 case kDNSType_NULL
: return("NULL");
446 case kDNSType_PTR
: return("PTR");
447 case kDNSType_HINFO
:return("HINFO");
448 case kDNSType_TXT
: return("TXT");
449 case kDNSType_AAAA
: return("AAAA");
450 case kDNSType_SRV
: return("SRV");
451 case kDNSQType_ANY
: return("ANY");
453 static char buffer
[16];
454 mDNS_snprintf(buffer
, sizeof(buffer
), "(%d)", rrtype
);
460 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
461 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
462 // long as this routine is only used for debugging messages, it probably isn't a big problem.
463 mDNSexport
char *GetRRDisplayString_rdb(const ResourceRecord
*rr
, RDataBody
*rd
, char *buffer
)
466 mDNSu32 length
= mDNS_snprintf(buffer
, 79, "%4d %##s %s ", rr
->rdlength
, rr
->name
->c
, DNSTypeName(rr
->rrtype
));
469 case kDNSType_A
: mDNS_snprintf(buffer
+length
, 79-length
, "%.4a", &rd
->ipv4
); break;
471 case kDNSType_NS
: // Same as PTR
472 case kDNSType_CNAME
:// Same as PTR
473 case kDNSType_PTR
: mDNS_snprintf(buffer
+length
, 79-length
, "%##s", rd
->name
.c
); break;
475 case kDNSType_HINFO
:// Display this the same as TXT (just show first string)
476 case kDNSType_TXT
: mDNS_snprintf(buffer
+length
, 79-length
, "%#s", rd
->txt
.c
); break;
478 case kDNSType_AAAA
: mDNS_snprintf(buffer
+length
, 79-length
, "%.16a", &rd
->ipv6
); break;
479 case kDNSType_SRV
: mDNS_snprintf(buffer
+length
, 79-length
, "%u %u %u %##s",
480 rd
->srv
.priority
, rd
->srv
.weight
, mDNSVal16(rd
->srv
.port
), rd
->srv
.target
.c
); break;
481 default: mDNS_snprintf(buffer
+length
, 79-length
, "RDLen %d: %s", rr
->rdlength
, rd
->data
); break;
483 for (ptr
= buffer
; *ptr
; ptr
++) if (*ptr
< ' ') *ptr
='.';
487 mDNSexport mDNSu32
mDNSRandom(mDNSu32 max
)
489 static mDNSu32 seed
= 0;
495 seed
= mDNSPlatformRandomSeed(); // Pick an initial seed
496 for (i
=0; i
<100; i
++) seed
= seed
* 21 + 1; // And mix it up a bit
498 while (mask
< max
) mask
= (mask
<< 1) | 1;
499 do seed
= seed
* 21 + 1; while ((seed
& mask
) > max
);
500 return (seed
& mask
);
503 mDNSexport mDNSBool
mDNSSameAddress(const mDNSAddr
*ip1
, const mDNSAddr
*ip2
)
505 if (ip1
->type
== ip2
->type
)
509 case mDNSAddrType_None
: return(mDNStrue
); // Empty addresses have no data and are therefore always equal
510 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip1
->ip
.v4
, ip2
->ip
.v4
));
511 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip1
->ip
.v6
, ip2
->ip
.v6
));
517 mDNSexport mDNSBool
mDNSAddrIsDNSMulticast(const mDNSAddr
*ip
)
521 case mDNSAddrType_IPv4
: return(mDNSBool
)(ip
->ip
.v4
.NotAnInteger
== AllDNSLinkGroupv4
.NotAnInteger
);
522 case mDNSAddrType_IPv6
: return(mDNSBool
)(ip
->ip
.v6
.l
[0] == AllDNSLinkGroupv6
.l
[0] &&
523 ip
->ip
.v6
.l
[1] == AllDNSLinkGroupv6
.l
[1] &&
524 ip
->ip
.v6
.l
[2] == AllDNSLinkGroupv6
.l
[2] &&
525 ip
->ip
.v6
.l
[3] == AllDNSLinkGroupv6
.l
[3] );
526 default: return(mDNSfalse
);
530 // ***************************************************************************
531 #if COMPILER_LIKES_PRAGMA_MARK
533 #pragma mark - Domain Name Utility Functions
536 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
539 const int len
= *a
++;
541 if (len
> MAX_DOMAIN_LABEL
)
542 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
544 if (len
!= *b
++) return(mDNSfalse
);
545 for (i
=0; i
<len
; i
++)
549 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
550 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
551 if (ac
!= bc
) return(mDNSfalse
);
556 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
558 const mDNSu8
* a
= d1
->c
;
559 const mDNSu8
* b
= d2
->c
;
560 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
564 if (a
+ 1 + *a
>= max
)
565 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse
); }
566 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
574 mDNSexport mDNSBool
IsLocalDomain(const domainname
*d
)
576 // Domains that are defined to be resolved via link-local multicast are:
577 // local., 254.169.in-addr.arpa., and 0.8.E.F.ip6.arpa.
578 static const domainname
*n0
= (domainname
*)"\x5" "local";
579 static const domainname
*n1
= (domainname
*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
580 static const domainname
*n2
= (domainname
*)"\x1" "0" "\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
582 const domainname
*d1
, *d2
, *d3
, *d4
, *d5
, *d6
; // Top-level domain, second-level domain, etc.
583 d1
= d2
= d3
= d4
= d5
= d6
= mDNSNULL
;
586 d6
= d5
; d5
= d4
; d4
= d3
; d3
= d2
; d2
= d1
; d1
= d
;
587 d
= (domainname
*)(d
->c
+ 1 + d
->c
[0]);
590 if (d1
&& SameDomainName(d1
, n0
)) return(mDNStrue
);
591 if (d4
&& SameDomainName(d4
, n1
)) return(mDNStrue
);
592 if (d6
&& SameDomainName(d6
, n2
)) return(mDNStrue
);
596 // Returns length of a domain name INCLUDING the byte for the final null label
597 // i.e. for the root label "." it returns one
598 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
599 // Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
600 // If the given domainname is invalid, result is 256
601 mDNSexport mDNSu16
DomainNameLength(const domainname
*const name
)
603 const mDNSu8
*src
= name
->c
;
606 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
608 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
610 return((mDNSu16
)(src
- name
->c
+ 1));
613 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
614 // for the final null label i.e. for the root label "." it returns one.
615 // E.g. for the FQDN "foo.com." it returns 9
616 // (length, three data bytes, length, three more data bytes, final zero).
617 // In the case where a parent domain name is provided, and the given name is a child
618 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
619 // of the child name, plus TWO bytes for the compression pointer.
620 // E.g. for the name "foo.com." with parent "com.", it returns 6
621 // (length, three data bytes, two-byte compression pointer).
622 mDNSexport mDNSu16
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
624 const mDNSu8
*src
= name
->c
;
625 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
628 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
629 if (parent
&& SameDomainName((domainname
*)src
, parent
)) return((mDNSu16
)(src
- name
->c
+ 2));
631 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
633 return((mDNSu16
)(src
- name
->c
+ 1));
636 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
637 // The C string contains the label as-is, with no escaping, etc.
638 // Any dots in the name are literal dots, not label separators
639 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
640 // in the domainname bufer (i.e., the next byte after the terminating zero).
641 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
642 // AppendLiteralLabelString returns mDNSNULL.
643 mDNSexport mDNSu8
*AppendLiteralLabelString(domainname
*const name
, const char *cstr
)
645 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
646 const mDNSu8
*const lim1
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
647 const mDNSu8
*const lim2
= ptr
+ 1 + MAX_DOMAIN_LABEL
;
648 const mDNSu8
*const lim
= (lim1
< lim2
) ? lim1
: lim2
;
649 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
651 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the data
652 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
653 *ptr
++ = 0; // Put the null root label on the end
654 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
655 else return(ptr
); // Success: return new value of ptr
658 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
659 // The C string is in conventional DNS syntax:
660 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
661 // If successful, AppendDNSNameString returns a pointer to the next unused byte
662 // in the domainname bufer (i.e., the next byte after the terminating zero).
663 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
664 // AppendDNSNameString returns mDNSNULL.
665 mDNSexport mDNSu8
*AppendDNSNameString(domainname
*const name
, const char *cstring
)
667 const char *cstr
= cstring
;
668 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
669 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
670 while (*cstr
&& ptr
< lim
) // While more characters, and space to put them...
672 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
673 if (*cstr
== '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring
); return(mDNSNULL
); }
674 while (*cstr
&& *cstr
!= '.' && ptr
< lim
) // While we have characters in the label...
676 mDNSu8 c
= (mDNSu8
)*cstr
++; // Read the character
677 if (c
== '\\') // If escape character, check next character
679 c
= (mDNSu8
)*cstr
++; // Assume we'll just take the next character
680 if (mdnsIsDigit(cstr
[-1]) && mdnsIsDigit(cstr
[0]) && mdnsIsDigit(cstr
[1]))
681 { // If three decimal digits,
682 int v0
= cstr
[-1] - '0'; // then interpret as three-digit decimal
683 int v1
= cstr
[ 0] - '0';
684 int v2
= cstr
[ 1] - '0';
685 int val
= v0
* 100 + v1
* 10 + v2
;
686 if (val
<= 255) { c
= (mDNSu8
)val
; cstr
+= 2; } // If valid three-digit decimal value, use it
689 *ptr
++ = c
; // Write the character
691 if (*cstr
) cstr
++; // Skip over the trailing dot (if present)
692 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) // If illegal label, abort
694 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
697 *ptr
++ = 0; // Put the null root label on the end
698 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
699 else return(ptr
); // Success: return new value of ptr
702 // AppendDomainLabel appends a single label to a name.
703 // If successful, AppendDomainLabel returns a pointer to the next unused byte
704 // in the domainname bufer (i.e., the next byte after the terminating zero).
705 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
706 // AppendDomainLabel returns mDNSNULL.
707 mDNSexport mDNSu8
*AppendDomainLabel(domainname
*const name
, const domainlabel
*const label
)
710 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
712 // Check label is legal
713 if (label
->c
[0] > MAX_DOMAIN_LABEL
) return(mDNSNULL
);
715 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
716 if (ptr
+ 1 + label
->c
[0] + 1 > name
->c
+ MAX_DOMAIN_NAME
) return(mDNSNULL
);
718 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
]; // Copy the label data
719 *ptr
++ = 0; // Put the null root label on the end
723 mDNSexport mDNSu8
*AppendDomainName(domainname
*const name
, const domainname
*const append
)
725 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
726 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
727 const mDNSu8
* src
= append
->c
;
731 if (ptr
+ 1 + src
[0] > lim
) return(mDNSNULL
);
732 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
733 *ptr
= 0; // Put the null root label on the end
739 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
740 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
741 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
742 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
743 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
744 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
745 mDNSexport mDNSBool
MakeDomainLabelFromLiteralString(domainlabel
*const label
, const char *cstr
)
747 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
748 const mDNSu8
*const limit
= label
->c
+ 1 + MAX_DOMAIN_LABEL
; // The maximum we can put
749 while (*cstr
&& ptr
< limit
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the label
750 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1); // Set the length byte
751 return(*cstr
== 0); // Return mDNStrue if we successfully consumed all input
754 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
755 // The C string is in conventional DNS syntax:
756 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
757 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
758 // in the domainname bufer (i.e., the next byte after the terminating zero).
759 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
760 // MakeDomainNameFromDNSNameString returns mDNSNULL.
761 mDNSexport mDNSu8
*MakeDomainNameFromDNSNameString(domainname
*const name
, const char *cstr
)
763 name
->c
[0] = 0; // Make an empty domain name
764 return(AppendDNSNameString(name
, cstr
)); // And then add this string to it
767 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
769 const mDNSu8
* src
= label
->c
; // Domain label we're reading
770 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
771 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
772 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
773 while (src
< end
) // While we have characters in the label
778 if (c
== '.' || c
== esc
) // If character is a dot or the escape character
779 *ptr
++ = esc
; // Output escape character
780 else if (c
<= ' ') // If non-printing ascii,
781 { // Output decimal escape sequence
783 *ptr
++ = (char) ('0' + (c
/ 100) );
784 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
785 c
= (mDNSu8
)('0' + (c
) % 10);
788 *ptr
++ = (char)c
; // Copy the character
790 *ptr
= 0; // Null-terminate the string
791 return(ptr
); // and return
794 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes)
795 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
797 const mDNSu8
*src
= name
->c
; // Domain name we're reading
798 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
800 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
802 while (*src
) // While more characters in the domain name
804 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
805 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
806 if (!ptr
) return(mDNSNULL
);
808 *ptr
++ = '.'; // Write the dot after the label
811 *ptr
++ = 0; // Null-terminate the string
812 return(ptr
); // and return
816 // Host names must start with a letter, end with a letter or digit,
817 // and have as interior characters only letters, digits, and hyphen.
818 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
820 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
822 const mDNSu8
* src
= &UTF8Name
[1];
823 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
824 mDNSu8
* ptr
= &hostlabel
->c
[1];
825 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
828 // Delete apostrophes from source name
829 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
830 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
831 { src
+= 3; continue; } // Unicode curly apostrophe
834 if (mdnsValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
835 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
839 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
840 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
843 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
844 const domainlabel
*name
, const domainname
*type
, const domainname
*const domain
)
847 mDNSu8
*dst
= fqdn
->c
;
849 const char *errormsg
;
851 // In the case where there is no name (and ONLY in that case),
852 // a single-label subtype is allowed as the first label of a three-part "type"
855 const mDNSu8
*s0
= type
->c
;
856 if (s0
[0] && s0
[0] < 0x40) // If legal first label (at least one character, and no more than 63)
858 const mDNSu8
* s1
= s0
+ 1 + s0
[0];
859 if (s1
[0] && s1
[0] < 0x40) // and legal second label (at least one character, and no more than 63)
861 const mDNSu8
*s2
= s1
+ 1 + s1
[0];
862 if (s2
[0] && s2
[0] < 0x40 && s2
[1+s2
[0]] == 0) // and we have three and only three labels
864 static const mDNSu8 SubTypeLabel
[5] = "\x04_sub";
865 src
= s0
; // Copy the first label
867 for (i
=0; i
<= len
; i
++) *dst
++ = *src
++;
868 for (i
=0; i
< (int)sizeof(SubTypeLabel
); i
++) *dst
++ = SubTypeLabel
[i
];
869 type
= (domainname
*)s1
;
871 // Special support for queries done by some third-party network monitoring software
872 // For these queries, we retract the "._sub" we just added between the subtype and the main type
873 if (SameDomainName((domainname
*)s0
, (domainname
*)"\x09_services\x07_dns-sd\x04_udp") ||
874 SameDomainName((domainname
*)s0
, (domainname
*)"\x09_services\x05_mdns\x04_udp"))
875 dst
-= sizeof(SubTypeLabel
);
881 if (name
&& name
->c
[0])
883 src
= name
->c
; // Put the service name into the domain name
885 if (len
>= 0x40) { errormsg
="Service instance name too long"; goto fail
; }
886 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
889 name
= (domainlabel
*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
891 src
= type
->c
; // Put the service type into the domain name
893 if (len
< 2 || len
>= 0x40 || (len
> 15 && !SameDomainName(domain
, &localdomain
)))
895 errormsg
="Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
898 if (src
[1] != '_') { errormsg
="Application protocol name must begin with underscore"; goto fail
; }
899 for (i
=2; i
<=len
; i
++)
900 if (!mdnsIsLetter(src
[i
]) && !mdnsIsDigit(src
[i
]) && src
[i
] != '-' && src
[i
] != '_')
901 { errormsg
="Application protocol name must contain only letters, digits, and hyphens"; goto fail
; }
902 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
905 if (!(len
== 4 && src
[1] == '_' &&
906 (((src
[2] | 0x20) == 'u' && (src
[3] | 0x20) == 'd') || ((src
[2] | 0x20) == 't' && (src
[3] | 0x20) == 'c')) &&
907 (src
[4] | 0x20) == 'p'))
908 { errormsg
="Transport protocol name must be _udp or _tcp"; goto fail
; }
909 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
911 if (*src
) { errormsg
="Service type must have only two labels"; goto fail
; }
914 if (!domain
->c
[0]) { errormsg
="Service domain must be non-empty"; goto fail
; }
915 if (SameDomainName(domain
, (domainname
*)"\x05" "local" "\x04" "arpa"))
916 { errormsg
="Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail
; }
917 dst
= AppendDomainName(fqdn
, domain
);
918 if (!dst
) { errormsg
="Service domain too long"; goto fail
; }
922 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg
, name
->c
, type
->c
, domain
->c
);
926 // A service name has the form: instance.application-protocol.transport-protocol.domain
927 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
928 // set or length limits for the protocol names, and the final domain is allowed to be empty.
929 // However, if the given FQDN doesn't contain at least three labels,
930 // DeconstructServiceName will reject it and return mDNSfalse.
931 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
932 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
935 const mDNSu8
*src
= fqdn
->c
;
936 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
939 dst
= name
->c
; // Extract the service name
941 if (!len
) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse
); }
942 if (len
>= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse
); }
943 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
945 dst
= type
->c
; // Extract the service type
947 if (!len
) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse
); }
948 if (len
>= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse
); }
949 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
952 if (!len
) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse
); }
953 if (len
>= 0x40) { debugf("DeconstructServiceName: Transport protocol name too long"); return(mDNSfalse
); }
954 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
955 *dst
++ = 0; // Put terminator on the end of service type
957 dst
= domain
->c
; // Extract the service domain
962 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse
); }
963 if (src
+ 1 + len
+ 1 >= max
)
964 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse
); }
965 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
967 *dst
++ = 0; // Put the null root label on the end
973 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
974 // 10xxxxxx is a continuation byte of a multi-byte character
975 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
976 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
977 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
978 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
979 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
981 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
982 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
983 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
984 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
985 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
987 mDNSexport mDNSu32
TruncateUTF8ToLength(mDNSu8
*string
, mDNSu32 length
, mDNSu32 max
)
991 mDNSu8 c1
= string
[max
]; // First byte after cut point
992 mDNSu8 c2
= (max
+1 < length
) ? string
[max
+1] : 0xB0; // Second byte after cut point
993 length
= max
; // Trim length down
996 // Check if the byte right after the chop point is a UTF-8 continuation byte,
997 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
998 // If so, then we continue to chop more bytes until we get to a legal chop point.
999 mDNSBool continuation
= ((c1
& 0xC0) == 0x80);
1000 mDNSBool secondsurrogate
= (c1
== 0xED && (c2
& 0xF0) == 0xB0);
1001 if (!continuation
&& !secondsurrogate
) break;
1003 c1
= string
[--length
];
1005 // Having truncated characters off the end of our string, also cut off any residual white space
1006 while (length
> 0 && string
[length
-1] <= ' ') length
--;
1011 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1012 // name ends in "-nnn", where n is some decimal number.
1013 mDNSexport mDNSBool
LabelContainsSuffix(const domainlabel
*const name
, const mDNSBool RichText
)
1015 mDNSu16 l
= name
->c
[0];
1019 if (l
< 4) return mDNSfalse
; // Need at least " (2)"
1020 if (name
->c
[l
--] != ')') return mDNSfalse
; // Last char must be ')'
1021 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Preceeded by a digit
1023 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
1024 return (name
->c
[l
] == '(' && name
->c
[l
- 1] == ' ');
1028 if (l
< 2) return mDNSfalse
; // Need at least "-2"
1029 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Last char must be a digit
1031 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
1032 return (name
->c
[l
] == '-');
1036 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1037 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1038 // from the suffix that was removed.
1039 mDNSexport mDNSu32
RemoveLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1041 mDNSu32 val
= 0, multiplier
= 1;
1043 // Chop closing parentheses from RichText suffix
1044 if (RichText
&& name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == ')') name
->c
[0]--;
1046 // Get any existing numerical suffix off the name
1047 while (mdnsIsDigit(name
->c
[name
->c
[0]]))
1048 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
1050 // Chop opening parentheses or dash from suffix
1053 if (name
->c
[0] >= 2 && name
->c
[name
->c
[0]] == '(' && name
->c
[name
->c
[0]-1] == ' ') name
->c
[0] -= 2;
1057 if (name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == '-') name
->c
[0] -= 1;
1063 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1064 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1065 mDNSexport
void AppendLabelSuffix(domainlabel
*name
, mDNSu32 val
, mDNSBool RichText
)
1067 mDNSu32 divisor
= 1, chars
= 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1068 if (RichText
) chars
= 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1070 // Truncate trailing spaces from RichText names
1071 if (RichText
) while (name
->c
[name
->c
[0]] == ' ') name
->c
[0]--;
1073 while (val
>= divisor
* 10) { divisor
*= 10; chars
++; }
1075 name
->c
[0] = (mDNSu8
) TruncateUTF8ToLength(name
->c
+1, name
->c
[0], MAX_DOMAIN_LABEL
- chars
);
1077 if (RichText
) { name
->c
[++name
->c
[0]] = ' '; name
->c
[++name
->c
[0]] = '('; }
1078 else { name
->c
[++name
->c
[0]] = '-'; }
1082 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
1087 if (RichText
) name
->c
[++name
->c
[0]] = ')';
1090 mDNSexport
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1094 if (LabelContainsSuffix(name
, RichText
))
1095 val
= RemoveLabelSuffix(name
, RichText
);
1097 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1098 // If existing suffix in the range 2-9, increment it.
1099 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1100 // so add a random increment to improve the chances of finding an available name next time.
1101 if (val
== 0) val
= 2;
1102 else if (val
< 10) val
++;
1103 else val
+= 1 + mDNSRandom(99);
1105 AppendLabelSuffix(name
, val
, RichText
);
1108 // ***************************************************************************
1109 #if COMPILER_LIKES_PRAGMA_MARK
1111 #pragma mark - Resource Record Utility Functions
1114 mDNSexport mDNSu32
RDataHashValue(mDNSu16
const rdlength
, const RDataBody
*const rdb
)
1118 for (i
=0; i
+1 < rdlength
; i
+=2)
1120 sum
+= (((mDNSu32
)(rdb
->data
[i
])) << 8) | rdb
->data
[i
+1];
1121 sum
= (sum
<<3) | (sum
>>29);
1125 sum
+= ((mDNSu32
)(rdb
->data
[i
])) << 8;
1130 // r1 has to be a full ResourceRecord including rrtype and rdlength
1131 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
1132 mDNSexport mDNSBool
SameRDataBody(const ResourceRecord
*const r1
, const RDataBody
*const r2
)
1136 case kDNSType_CNAME
:// Same as PTR
1137 case kDNSType_PTR
: return(SameDomainName(&r1
->rdata
->u
.name
, &r2
->name
));
1139 case kDNSType_SRV
: return(mDNSBool
)( r1
->rdata
->u
.srv
.priority
== r2
->srv
.priority
&&
1140 r1
->rdata
->u
.srv
.weight
== r2
->srv
.weight
&&
1141 r1
->rdata
->u
.srv
.port
.NotAnInteger
== r2
->srv
.port
.NotAnInteger
&&
1142 SameDomainName(&r1
->rdata
->u
.srv
.target
, &r2
->srv
.target
) );
1144 default: return(mDNSPlatformMemSame(r1
->rdata
->u
.data
, r2
->data
, r1
->rdlength
));
1148 mDNSexport mDNSBool
SameRData(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
1150 if (r1
->rrtype
!= r2
->rrtype
) return(mDNSfalse
);
1151 if (r1
->rdlength
!= r2
->rdlength
) return(mDNSfalse
);
1152 if (r1
->rdatahash
!= r2
->rdatahash
) return(mDNSfalse
);
1153 return(SameRDataBody(r1
, &r2
->rdata
->u
));
1156 mDNSexport mDNSBool
SameResourceRecord(ResourceRecord
*r1
, ResourceRecord
*r2
)
1158 return (r1
->namehash
== r2
->namehash
&&
1159 r1
->rrtype
== r2
->rrtype
&&
1160 SameDomainName(r1
->name
, r2
->name
) &&
1164 mDNSexport mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1166 if (rr
->InterfaceID
&&
1167 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1168 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1170 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1171 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->qtype
&& q
->qtype
!= kDNSQType_ANY
) return(mDNSfalse
);
1172 if ( rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1173 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1176 mDNSexport mDNSu16
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
1178 const RDataBody
*rd
= &rr
->rdata
->u
;
1179 const domainname
*const name
= estimate
? rr
->name
: mDNSNULL
;
1182 case kDNSType_A
: return(sizeof(rd
->ipv4
));
1183 case kDNSType_CNAME
:// Same as PTR
1184 case kDNSType_NS
: // Same as PTR
1185 case kDNSType_PTR
: return(CompressedDomainNameLength(&rd
->name
, name
));
1186 case kDNSType_HINFO
:return(mDNSu16
)(2 + (int)rd
->data
[0] + (int)rd
->data
[1 + (int)rd
->data
[0]]);
1187 case kDNSType_NULL
: // Same as TXT -- not self-describing, so have to just trust rdlength
1188 case kDNSType_TXT
: return(rr
->rdlength
); // TXT is not self-describing, so have to just trust rdlength
1189 case kDNSType_AAAA
: return(sizeof(rd
->ipv6
));
1190 case kDNSType_SRV
: return(mDNSu16
)(6 + CompressedDomainNameLength(&rd
->srv
.target
, name
));
1191 case kDNSType_SOA
: return (mDNSu16
)(CompressedDomainNameLength(&rd
->soa
.mname
, name
) +
1192 CompressedDomainNameLength(&rd
->soa
.rname
, name
) +
1193 5 * sizeof(mDNSOpaque32
));
1194 case kDNSType_OPT
: return(rr
->rdlength
);
1195 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
1196 return(rr
->rdlength
);
1200 mDNSexport mDNSBool
ValidateRData(const mDNSu16 rrtype
, const mDNSu16 rdlength
, const RData
*const rd
)
1206 case kDNSType_A
: return(rdlength
== sizeof(mDNSv4Addr
));
1208 case kDNSType_NS
: // Same as PTR
1209 case kDNSType_MD
: // Same as PTR
1210 case kDNSType_MF
: // Same as PTR
1211 case kDNSType_CNAME
:// Same as PTR
1212 //case kDNSType_SOA not checked
1213 case kDNSType_MB
: // Same as PTR
1214 case kDNSType_MG
: // Same as PTR
1215 case kDNSType_MR
: // Same as PTR
1216 //case kDNSType_NULL not checked (no specified format, so always valid)
1217 //case kDNSType_WKS not checked
1218 case kDNSType_PTR
: if (!rdlength
) return(mDNSfalse
);
1219 len
= DomainNameLength(&rd
->u
.name
);
1220 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== len
);
1222 case kDNSType_HINFO
:// Same as TXT (roughly)
1223 case kDNSType_MINFO
:// Same as TXT (roughly)
1224 case kDNSType_TXT
: if (!rdlength
) return(mDNSfalse
); // TXT record has to be at least one byte (RFC 1035)
1226 const mDNSu8
*ptr
= rd
->u
.txt
.c
;
1227 const mDNSu8
*end
= rd
->u
.txt
.c
+ rdlength
;
1228 while (ptr
< end
) ptr
+= 1 + ptr
[0];
1229 return (ptr
== end
);
1232 case kDNSType_AAAA
: return(rdlength
== sizeof(mDNSv6Addr
));
1234 case kDNSType_MX
: if (!rdlength
) return(mDNSfalse
);
1235 len
= DomainNameLength(&rd
->u
.mx
.exchange
);
1236 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 2+len
);
1238 case kDNSType_SRV
: if (!rdlength
) return(mDNSfalse
);
1239 len
= DomainNameLength(&rd
->u
.srv
.target
);
1240 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 6+len
);
1242 default: return(mDNStrue
); // Allow all other types without checking
1246 // ***************************************************************************
1247 #if COMPILER_LIKES_PRAGMA_MARK
1250 #pragma mark - DNS Message Creation Functions
1253 mDNSexport
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
1257 h
->numQuestions
= 0;
1259 h
->numAuthorities
= 0;
1260 h
->numAdditionals
= 0;
1263 mDNSexport
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
1265 const mDNSu8
*result
= end
- *domname
- 1;
1267 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
1269 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1270 while (result
>= base
)
1272 // If the length byte and first character of the label match, then check further to see
1273 // if this location in the packet will yield a useful name compression pointer.
1274 if (result
[0] == domname
[0] && result
[1] == domname
[1])
1276 const mDNSu8
*name
= domname
;
1277 const mDNSu8
*targ
= result
;
1278 while (targ
+ *name
< end
)
1280 // First see if this label matches
1282 const mDNSu8
*pointertarget
;
1283 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
1284 if (i
<= *name
) break; // If label did not match, bail out
1285 targ
+= 1 + *name
; // Else, did match, so advance target pointer
1286 name
+= 1 + *name
; // and proceed to check next label
1287 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
1288 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
1290 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1291 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
1292 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
1293 if (targ
+1 >= end
) break; // Second byte not present!
1294 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
1295 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
1296 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1297 targ
= pointertarget
;
1300 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
1305 // Put a string of dot-separated labels as length-prefixed labels
1306 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1307 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1308 // end points to the end of the message so far
1309 // ptr points to where we want to put the name
1310 // limit points to one byte past the end of the buffer that we must not overrun
1311 // domainname is the name to put
1312 mDNSexport mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
1313 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
1315 const mDNSu8
*const base
= (const mDNSu8
*)msg
;
1316 const mDNSu8
* np
= name
->c
;
1317 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1318 const mDNSu8
* pointer
= mDNSNULL
;
1319 const mDNSu8
*const searchlimit
= ptr
;
1321 while (*np
&& ptr
< limit
-1) // While we've got characters in the name, and space to write them in the message...
1323 if (*np
> MAX_DOMAIN_LABEL
)
1324 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name
->c
); return(mDNSNULL
); }
1326 // This check correctly allows for the final trailing root label:
1328 // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
1329 // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
1330 // We know that max will be at name->c[255]
1331 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1332 // six bytes, then exit the loop, write the final terminating root label, and the domain
1333 // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
1334 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1335 if (np
+ 1 + *np
>= max
)
1336 { LogMsg("Malformed domain name %##s (more than 255 bytes)", name
->c
); return(mDNSNULL
); }
1338 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
1339 if (pointer
) // Use a compression pointer if we can
1341 mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
1342 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
1343 *ptr
++ = (mDNSu8
)( offset
& 0xFF);
1346 else // Else copy one label and try again
1350 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
1352 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
1356 if (ptr
< limit
) // If we didn't run out of space
1358 *ptr
++ = 0; // Put the final root label
1359 return(ptr
); // and return
1365 mDNSlocal mDNSu8
*putVal16(mDNSu8
*ptr
, mDNSu16 val
)
1367 ptr
[0] = (mDNSu8
)((val
>> 8 ) & 0xFF);
1368 ptr
[1] = (mDNSu8
)((val
) & 0xFF);
1369 return ptr
+ sizeof(mDNSOpaque16
);
1372 mDNSlocal mDNSu8
*putVal32(mDNSu8
*ptr
, mDNSu32 val
)
1374 ptr
[0] = (mDNSu8
)((val
>> 24) & 0xFF);
1375 ptr
[1] = (mDNSu8
)((val
>> 16) & 0xFF);
1376 ptr
[2] = (mDNSu8
)((val
>> 8) & 0xFF);
1377 ptr
[3] = (mDNSu8
)((val
) & 0xFF);
1378 return ptr
+ sizeof(mDNSu32
);
1381 mDNSlocal mDNSu8
*putOptRData(mDNSu8
*ptr
, const mDNSu8
*limit
, ResourceRecord
*rr
)
1386 while (nput
< rr
->rdlength
)
1388 // check if space for opt/optlen
1389 if (ptr
+ (2 * sizeof(mDNSu16
)) > limit
) goto space_err
;
1390 opt
= (rdataOpt
*)(rr
->rdata
->u
.data
+ nput
);
1391 ptr
= putVal16(ptr
, opt
->opt
);
1392 ptr
= putVal16(ptr
, opt
->optlen
);
1393 nput
+= 2 * sizeof(mDNSu16
);
1394 if (opt
->opt
== kDNSOpt_LLQ
)
1396 if (ptr
+ LLQ_OPTLEN
> limit
) goto space_err
;
1397 ptr
= putVal16(ptr
, opt
->OptData
.llq
.vers
);
1398 ptr
= putVal16(ptr
, opt
->OptData
.llq
.llqOp
);
1399 ptr
= putVal16(ptr
, opt
->OptData
.llq
.err
);
1400 mDNSPlatformMemCopy(opt
->OptData
.llq
.id
, ptr
, 8); // 8-byte id
1402 ptr
= putVal32(ptr
, opt
->OptData
.llq
.lease
);
1405 else if (opt
->opt
== kDNSOpt_Lease
)
1407 if (ptr
+ sizeof(mDNSs32
) > limit
) goto space_err
;
1408 ptr
= putVal32(ptr
, opt
->OptData
.lease
);
1409 nput
+= sizeof(mDNSs32
);
1411 else { LogMsg("putOptRData - unknown option %d", opt
->opt
); return mDNSNULL
; }
1417 LogMsg("ERROR: putOptRData - out of space");
1421 mDNSlocal mDNSu16
getVal16(const mDNSu8
**ptr
)
1423 mDNSu16 val
= (mDNSu16
)(((mDNSu16
)(*ptr
)[0]) << 8 | (*ptr
)[1]);
1424 *ptr
+= sizeof(mDNSOpaque16
);
1428 mDNSlocal
const mDNSu8
*getOptRdata(const mDNSu8
*ptr
, const mDNSu8
*const limit
, LargeCacheRecord
*const cr
, mDNSu16 pktRDLen
)
1431 ResourceRecord
*const rr
= &cr
->r
.resrec
;
1432 rdataOpt
*opt
= (rdataOpt
*)rr
->rdata
->u
.data
;
1434 while (nread
< pktRDLen
&& (mDNSu8
*)opt
< rr
->rdata
->u
.data
+ MaximumRDSize
- sizeof(rdataOpt
))
1436 // space for opt + optlen
1437 if (nread
+ (2 * sizeof(mDNSu16
)) > rr
->rdata
->MaxRDLength
) goto space_err
;
1438 opt
->opt
= getVal16(&ptr
);
1439 opt
->optlen
= getVal16(&ptr
);
1440 nread
+= 2 * sizeof(mDNSu16
);
1441 if (opt
->opt
== kDNSOpt_LLQ
)
1443 if ((unsigned)(limit
- ptr
) < LLQ_OPTLEN
) goto space_err
;
1444 opt
->OptData
.llq
.vers
= getVal16(&ptr
);
1445 opt
->OptData
.llq
.llqOp
= getVal16(&ptr
);
1446 opt
->OptData
.llq
.err
= getVal16(&ptr
);
1447 mDNSPlatformMemCopy(ptr
, opt
->OptData
.llq
.id
, 8);
1449 opt
->OptData
.llq
.lease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1450 if (opt
->OptData
.llq
.lease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1451 opt
->OptData
.llq
.lease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1452 ptr
+= sizeof(mDNSOpaque32
);
1453 nread
+= LLQ_OPTLEN
;
1455 else if (opt
->opt
== kDNSOpt_Lease
)
1457 if ((unsigned)(limit
- ptr
) < sizeof(mDNSs32
)) goto space_err
;
1459 opt
->OptData
.lease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1460 if (opt
->OptData
.lease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1461 opt
->OptData
.lease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1462 ptr
+= sizeof(mDNSs32
);
1463 nread
+= sizeof(mDNSs32
);
1465 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt
->opt
); return mDNSNULL
; }
1466 opt
++; // increment pointer into rdatabody
1469 rr
->rdlength
= pktRDLen
;
1473 LogMsg("ERROR: getLLQRdata - out of space");
1477 mDNSexport mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, ResourceRecord
*rr
)
1481 case kDNSType_A
: if (rr
->rdlength
!= 4)
1483 debugf("putRData: Illegal length %d for kDNSType_A", rr
->rdlength
);
1486 if (ptr
+ 4 > limit
) return(mDNSNULL
);
1487 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[0];
1488 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[1];
1489 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[2];
1490 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[3];
1493 case kDNSType_CNAME
:// Same as PTR
1494 case kDNSType_PTR
: return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.name
));
1496 case kDNSType_AAAA
: if (rr
->rdlength
!= sizeof(rr
->rdata
->u
.ipv6
))
1498 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr
->rdlength
);
1501 if (ptr
+ sizeof(rr
->rdata
->u
.ipv6
) > limit
) return(mDNSNULL
);
1502 mDNSPlatformMemCopy(&rr
->rdata
->u
.ipv6
, ptr
, sizeof(rr
->rdata
->u
.ipv6
));
1503 return(ptr
+ sizeof(rr
->rdata
->u
.ipv6
));
1505 case kDNSType_SRV
: if (ptr
+ 6 > limit
) return(mDNSNULL
);
1506 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
>> 8);
1507 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
& 0xFF);
1508 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
>> 8);
1509 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
& 0xFF);
1510 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[0];
1511 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[1];
1512 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.srv
.target
));
1513 case kDNSType_OPT
: return putOptRData(ptr
, limit
, rr
);
1515 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr
->rrtype
);
1516 // Fall through to common code below
1517 case kDNSType_HINFO
:
1519 case kDNSType_TSIG
: if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
1520 mDNSPlatformMemCopy(rr
->rdata
->u
.data
, ptr
, rr
->rdlength
);
1521 return(ptr
+ rr
->rdlength
);
1525 mDNSexport mDNSu8
*PutResourceRecordTTLWithLimit(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32 ttl
, const mDNSu8
*limit
)
1528 mDNSu16 actualLength
;
1530 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
1532 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1536 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
1537 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1538 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
1539 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
1540 ptr
[2] = (mDNSu8
)(rr
->rrclass
>> 8);
1541 ptr
[3] = (mDNSu8
)(rr
->rrclass
& 0xFF);
1542 ptr
[4] = (mDNSu8
)((ttl
>> 24) & 0xFF);
1543 ptr
[5] = (mDNSu8
)((ttl
>> 16) & 0xFF);
1544 ptr
[6] = (mDNSu8
)((ttl
>> 8) & 0xFF);
1545 ptr
[7] = (mDNSu8
)( ttl
& 0xFF);
1546 endofrdata
= putRData(msg
, ptr
+10, limit
, rr
);
1547 if (!endofrdata
) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
)); return(mDNSNULL
); }
1549 // Go back and fill in the actual number of data bytes we wrote
1550 // (actualLength can be less than rdlength when domain name compression is used)
1551 actualLength
= (mDNSu16
)(endofrdata
- ptr
- 10);
1552 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
1553 ptr
[9] = (mDNSu8
)(actualLength
& 0xFF);
1555 if (count
) (*count
)++;
1556 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1560 mDNSexport mDNSu8
*PutResourceRecordCappedTTL(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32
1563 if (maxttl
> rr
->rroriginalttl
) maxttl
= rr
->rroriginalttl
;
1564 return(PutResourceRecordTTL(msg
, ptr
, count
, rr
, maxttl
));
1567 mDNSexport mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1568 mDNSu16
*count
, const AuthRecord
*rr
)
1570 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->resrec
.name
);
1571 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1572 ptr
[0] = (mDNSu8
)(rr
->resrec
.rrtype
>> 8); // Put type
1573 ptr
[1] = (mDNSu8
)(rr
->resrec
.rrtype
& 0xFF);
1574 ptr
[2] = (mDNSu8
)(rr
->resrec
.rrclass
>> 8); // Put class
1575 ptr
[3] = (mDNSu8
)(rr
->resrec
.rrclass
& 0xFF);
1576 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
1577 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
1582 mDNSexport mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
1584 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1585 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1586 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1587 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1588 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1589 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
1590 msg
->h
.numQuestions
++;
1594 // for dynamic updates
1595 mDNSexport mDNSu8
*putZone(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu8
*limit
, const domainname
*zone
, mDNSOpaque16 zoneClass
)
1597 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, zone
);
1598 if (!ptr
|| ptr
+ 4 > limit
) return mDNSNULL
; // If we're out-of-space, return NULL
1599 *ptr
++ = (mDNSu8
)(kDNSType_SOA
>> 8);
1600 *ptr
++ = (mDNSu8
)(kDNSType_SOA
& 0xFF);
1601 *ptr
++ = zoneClass
.b
[0];
1602 *ptr
++ = zoneClass
.b
[1];
1603 msg
->h
.mDNS_numZones
++;
1607 // for dynamic updates
1608 mDNSexport mDNSu8
*putPrereqNameNotInUse(domainname
*name
, DNSMessage
*msg
, mDNSu8
*ptr
, mDNSu8
*end
)
1612 mDNSPlatformMemZero(&prereq
, sizeof(AuthRecord
));
1613 mDNS_SetupResourceRecord(&prereq
, mDNSNULL
, mDNSInterface_Any
, kDNSQType_ANY
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
1614 AssignDomainName(prereq
.resrec
.name
, name
);
1615 prereq
.resrec
.rrtype
= kDNSQType_ANY
;
1616 prereq
.resrec
.rrclass
= kDNSClass_NONE
;
1617 ptr
= putEmptyResourceRecord(msg
, ptr
, end
, &msg
->h
.mDNS_numPrereqs
, &prereq
);
1621 // for dynamic updates
1622 mDNSexport mDNSu8
*putDeletionRecord(DNSMessage
*msg
, mDNSu8
*ptr
, ResourceRecord
*rr
)
1625 // deletion: specify record w/ TTL 0, class NONE
1627 origclass
= rr
->rrclass
;
1628 rr
->rrclass
= kDNSClass_NONE
;
1629 ptr
= PutResourceRecordTTLJumbo(msg
, ptr
, &msg
->h
.mDNS_numUpdates
, rr
, 0);
1630 rr
->rrclass
= origclass
;
1634 mDNSexport mDNSu8
*putDeleteRRSet(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
, mDNSu16 rrtype
)
1636 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1637 mDNSu16
class = kDNSQClass_ANY
;
1639 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1640 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
1641 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1642 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1643 ptr
[2] = (mDNSu8
)(class >> 8);
1644 ptr
[3] = (mDNSu8
)(class & 0xFF);
1645 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
1646 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
1648 msg
->h
.mDNS_numUpdates
++;
1652 // for dynamic updates
1653 mDNSexport mDNSu8
*putDeleteAllRRSets(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
)
1655 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1656 mDNSu16
class = kDNSQClass_ANY
;
1657 mDNSu16 rrtype
= kDNSQType_ANY
;
1659 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1660 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
1661 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1662 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1663 ptr
[2] = (mDNSu8
)(class >> 8);
1664 ptr
[3] = (mDNSu8
)(class & 0xFF);
1665 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
1666 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
1668 msg
->h
.mDNS_numUpdates
++;
1672 // for dynamic updates
1673 mDNSexport mDNSu8
*putUpdateLease(DNSMessage
*msg
, mDNSu8
*end
, mDNSu32 lease
)
1676 ResourceRecord
*opt
= &rr
.resrec
;
1679 mDNSPlatformMemZero(&rr
, sizeof(AuthRecord
));
1680 mDNS_SetupResourceRecord(&rr
, mDNSNULL
, mDNSInterface_Any
, kDNSType_OPT
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
1682 opt
->RecordType
= kDNSRecordTypeKnownUnique
; // to avoid warnings in other layers
1683 opt
->rrtype
= kDNSType_OPT
;
1684 opt
->rdlength
= LEASE_OPT_RDLEN
;
1685 opt
->rdestimate
= LEASE_OPT_RDLEN
;
1687 optRD
= &rr
.resrec
.rdata
->u
.opt
;
1688 optRD
->opt
= kDNSOpt_Lease
;
1689 optRD
->optlen
= sizeof(mDNSs32
);
1690 optRD
->OptData
.lease
= lease
;
1691 end
= PutResourceRecordTTLJumbo(msg
, end
, &msg
->h
.numAdditionals
, opt
, 0);
1692 if (!end
) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL
; }
1697 // ***************************************************************************
1698 #if COMPILER_LIKES_PRAGMA_MARK
1700 #pragma mark - DNS Message Parsing Functions
1703 mDNSexport mDNSu32
DomainNameHashValue(const domainname
*const name
)
1708 for (c
= name
->c
; c
[0] != 0 && c
[1] != 0; c
+= 2)
1710 sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8) |
1711 (mDNSIsUpperCase(c
[1]) ? c
[1] + 'a' - 'A' : c
[1]);
1712 sum
= (sum
<<3) | (sum
>>29);
1714 if (c
[0]) sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8);
1718 mDNSexport
void SetNewRData(ResourceRecord
*const rr
, RData
*NewRData
, mDNSu16 rdlength
)
1723 rr
->rdata
= NewRData
;
1724 rr
->rdlength
= rdlength
;
1726 // Must not try to get target pointer until after updating rr->rdata
1727 target
= GetRRDomainNameTarget(rr
);
1728 rr
->rdlength
= GetRDLength(rr
, mDNSfalse
);
1729 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
1730 rr
->rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
->rdlength
, &rr
->rdata
->u
);
1733 mDNSexport
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
1737 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1738 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1740 while (1) // Read sequence of labels
1742 const mDNSu8 len
= *ptr
++; // Read length of this label
1743 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
1746 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1747 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1748 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
1749 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1754 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
1755 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
1756 case 0xC0: return(ptr
+1);
1761 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
1762 mDNSexport
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
1763 domainname
*const name
)
1765 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
1766 mDNSu8
*np
= name
->c
; // Name pointer
1767 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
1769 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1770 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1772 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1774 while (1) // Read sequence of labels
1776 const mDNSu8 len
= *ptr
++; // Read length of this label
1777 if (len
== 0) break; // If length is zero, that means this name is complete
1783 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1784 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1785 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
1786 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1788 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
1789 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1792 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
1795 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
1797 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
1798 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
1799 ptr
= (mDNSu8
*)msg
+ offset
;
1800 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1801 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
1803 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
1808 if (nextbyte
) return(nextbyte
);
1812 mDNSexport
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1814 mDNSu16 pktrdlength
;
1816 ptr
= skipDomainName(msg
, ptr
, end
);
1817 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1819 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1820 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1822 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1824 return(ptr
+ pktrdlength
);
1827 mDNSexport
const mDNSu8
*GetLargeResourceRecord(mDNS
*const m
, const DNSMessage
* const msg
, const mDNSu8
*ptr
,
1828 const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
, mDNSu8 RecordType
, LargeCacheRecord
*largecr
)
1830 CacheRecord
*rr
= &largecr
->r
;
1831 mDNSu16 pktrdlength
;
1833 if (largecr
== &m
->rec
&& largecr
->r
.resrec
.RecordType
)
1834 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m
, &largecr
->r
));
1836 rr
->next
= mDNSNULL
;
1837 rr
->resrec
.name
= &largecr
->namestorage
;
1839 rr
->NextInKAList
= mDNSNULL
;
1840 rr
->TimeRcvd
= m
? m
->timenow
: 0;
1841 rr
->DelayDelivery
= 0;
1842 rr
->NextRequiredQuery
= m
? m
->timenow
: 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
1843 rr
->LastUsed
= m
? m
->timenow
: 0;
1844 rr
->CRActiveQuestion
= mDNSNULL
;
1845 rr
->UnansweredQueries
= 0;
1846 rr
->LastUnansweredTime
= 0;
1847 rr
->MPUnansweredQ
= 0;
1848 rr
->MPLastUnansweredQT
= 0;
1849 rr
->MPUnansweredKA
= 0;
1850 rr
->MPExpectingKA
= mDNSfalse
;
1851 rr
->NextInCFList
= mDNSNULL
;
1853 rr
->resrec
.InterfaceID
= InterfaceID
;
1854 ptr
= getDomainName(msg
, ptr
, end
, rr
->resrec
.name
);
1855 if (!ptr
) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1857 if (ptr
+ 10 > end
) { debugf("GetResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1859 rr
->resrec
.rrtype
= (mDNSu16
) ((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1860 rr
->resrec
.rrclass
= (mDNSu16
)(((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSClass_Mask
);
1861 rr
->resrec
.rroriginalttl
= (mDNSu32
) ((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
1862 if (rr
->resrec
.rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
&& (mDNSs32
)rr
->resrec
.rroriginalttl
!= -1)
1863 rr
->resrec
.rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1864 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
1865 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
1866 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1867 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8))
1868 RecordType
|= kDNSRecordTypePacketUniqueMask
;
1870 if (ptr
+ pktrdlength
> end
) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1871 end
= ptr
+ pktrdlength
; // Adjust end to indicate the end of the rdata for this resource record
1873 rr
->resrec
.rdata
= (RData
*)&rr
->rdatastorage
;
1874 rr
->resrec
.rdata
->MaxRDLength
= MaximumRDSize
;
1876 if (!RecordType
) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr
->resrec
.name
->c
);
1878 switch (rr
->resrec
.rrtype
)
1880 case kDNSType_A
: rr
->resrec
.rdata
->u
.ipv4
.b
[0] = ptr
[0];
1881 rr
->resrec
.rdata
->u
.ipv4
.b
[1] = ptr
[1];
1882 rr
->resrec
.rdata
->u
.ipv4
.b
[2] = ptr
[2];
1883 rr
->resrec
.rdata
->u
.ipv4
.b
[3] = ptr
[3];
1886 case kDNSType_CNAME
:// Same as PTR
1888 case kDNSType_PTR
: if (!getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.name
))
1889 { debugf("GetResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
1890 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
1893 case kDNSType_NULL
: //Same as TXT
1894 case kDNSType_HINFO
://Same as TXT
1895 case kDNSType_TXT
: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
1897 debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)",
1898 DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
1901 rr
->resrec
.rdlength
= pktrdlength
;
1902 mDNSPlatformMemCopy(ptr
, rr
->resrec
.rdata
->u
.data
, pktrdlength
);
1905 case kDNSType_AAAA
: mDNSPlatformMemCopy(ptr
, &rr
->resrec
.rdata
->u
.ipv6
, sizeof(rr
->resrec
.rdata
->u
.ipv6
));
1908 case kDNSType_SRV
: rr
->resrec
.rdata
->u
.srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1909 rr
->resrec
.rdata
->u
.srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
1910 rr
->resrec
.rdata
->u
.srv
.port
.b
[0] = ptr
[4];
1911 rr
->resrec
.rdata
->u
.srv
.port
.b
[1] = ptr
[5];
1912 if (!getDomainName(msg
, ptr
+6, end
, &rr
->resrec
.rdata
->u
.srv
.target
))
1913 { debugf("GetResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
1914 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
1917 case kDNSType_SOA
: ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.mname
);
1918 if (!ptr
) { debugf("GetResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL
; }
1919 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.rname
);
1920 if (!ptr
) { debugf("GetResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL
; }
1921 if (ptr
+ 0x14 != end
) { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL
; }
1922 rr
->resrec
.rdata
->u
.soa
.serial
= (mDNSs32
) ((mDNSs32
)ptr
[0x00] << 24 | (mDNSs32
)ptr
[0x01] << 16 | (mDNSs32
)ptr
[0x02] << 8 | ptr
[0x03]);
1923 rr
->resrec
.rdata
->u
.soa
.refresh
= (mDNSu32
) ((mDNSu32
)ptr
[0x04] << 24 | (mDNSu32
)ptr
[0x05] << 16 | (mDNSu32
)ptr
[0x06] << 8 | ptr
[0x07]);
1924 rr
->resrec
.rdata
->u
.soa
.retry
= (mDNSu32
) ((mDNSu32
)ptr
[0x08] << 24 | (mDNSu32
)ptr
[0x09] << 16 | (mDNSu32
)ptr
[0x0A] << 8 | ptr
[0x0B]);
1925 rr
->resrec
.rdata
->u
.soa
.expire
= (mDNSu32
) ((mDNSu32
)ptr
[0x0C] << 24 | (mDNSu32
)ptr
[0x0D] << 16 | (mDNSu32
)ptr
[0x0E] << 8 | ptr
[0x0F]);
1926 rr
->resrec
.rdata
->u
.soa
.min
= (mDNSu32
) ((mDNSu32
)ptr
[0x10] << 24 | (mDNSu32
)ptr
[0x11] << 16 | (mDNSu32
)ptr
[0x12] << 8 | ptr
[0x13]);
1929 case kDNSType_OPT
: getOptRdata(ptr
, end
, largecr
, pktrdlength
); break;
1931 default: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
1933 debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
1934 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
1937 debugf("GetResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
1938 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
));
1939 // Note: Just because we don't understand the record type, that doesn't
1940 // mean we fail. The DNS protocol specifies rdlength, so we can
1941 // safely skip over unknown records and ignore them.
1942 // We also grab a binary copy of the rdata anyway, since the caller
1943 // might know how to interpret it even if we don't.
1944 rr
->resrec
.rdlength
= pktrdlength
;
1945 mDNSPlatformMemCopy(ptr
, rr
->resrec
.rdata
->u
.data
, pktrdlength
);
1949 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
1950 SetNewRData(&rr
->resrec
, mDNSNULL
, 0);
1952 // Success! Now fill in RecordType to show this record contains valid data
1953 rr
->resrec
.RecordType
= RecordType
;
1954 return(ptr
+ pktrdlength
);
1957 mDNSexport
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1959 ptr
= skipDomainName(msg
, ptr
, end
);
1960 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
1961 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1965 mDNSexport
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
,
1966 DNSQuestion
*question
)
1968 question
->InterfaceID
= InterfaceID
;
1969 ptr
= getDomainName(msg
, ptr
, end
, &question
->qname
);
1970 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
1971 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1973 question
->qnamehash
= DomainNameHashValue(&question
->qname
);
1974 question
->qtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
1975 question
->qclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
1979 mDNSexport
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1982 const mDNSu8
*ptr
= msg
->data
;
1983 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
1987 mDNSexport
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1990 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
1991 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
1995 mDNSexport
const mDNSu8
*LocateAdditionals(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1998 const mDNSu8
*ptr
= LocateAuthorities(msg
, end
);
1999 for (i
= 0; i
< msg
->h
.numAuthorities
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
2003 // ***************************************************************************
2004 #if COMPILER_LIKES_PRAGMA_MARK
2007 #pragma mark - Packet Sending Functions
2010 mDNSexport mStatus
mDNSSendDNSMessage(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
,
2011 mDNSInterfaceID InterfaceID
, const mDNSAddr
*dst
, mDNSIPPort dstport
, int sd
, uDNS_AuthInfo
*authInfo
)
2017 mDNSu16 numQuestions
= msg
->h
.numQuestions
;
2018 mDNSu16 numAnswers
= msg
->h
.numAnswers
;
2019 mDNSu16 numAuthorities
= msg
->h
.numAuthorities
;
2020 mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
2021 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
2023 // Put all the integer values in IETF byte-order (MSB first, LSB second)
2024 *ptr
++ = (mDNSu8
)(numQuestions
>> 8);
2025 *ptr
++ = (mDNSu8
)(numQuestions
& 0xFF);
2026 *ptr
++ = (mDNSu8
)(numAnswers
>> 8);
2027 *ptr
++ = (mDNSu8
)(numAnswers
& 0xFF);
2028 *ptr
++ = (mDNSu8
)(numAuthorities
>> 8);
2029 *ptr
++ = (mDNSu8
)(numAuthorities
& 0xFF);
2030 *ptr
++ = (mDNSu8
)(numAdditionals
>> 8);
2031 *ptr
++ = (mDNSu8
)(numAdditionals
& 0xFF);
2035 end
= DNSDigest_SignMessage(msg
, &end
, &numAdditionals
, authInfo
);
2036 if (!end
) return mStatus_UnknownErr
;
2039 // Send the packet on the wire
2043 msglen
= (mDNSu16
)(end
- (mDNSu8
*)msg
);
2044 lenbuf
[0] = (mDNSu8
)(msglen
>> 8); // host->network byte conversion
2045 lenbuf
[1] = (mDNSu8
)(msglen
& 0xFF);
2046 nsent
= mDNSPlatformWriteTCP(sd
, (char*)lenbuf
, 2);
2047 //!!!KRS make sure kernel is sending these as 1 packet!
2048 if (nsent
!= 2) goto tcp_error
;
2049 nsent
= mDNSPlatformWriteTCP(sd
, (char *)msg
, msglen
);
2050 if (nsent
!= msglen
) goto tcp_error
;
2051 status
= mStatus_NoError
;
2055 status
= mDNSPlatformSendUDP(m
, msg
, end
, InterfaceID
, dst
, dstport
);
2058 // Put all the integer values back the way they were before we return
2059 msg
->h
.numQuestions
= numQuestions
;
2060 msg
->h
.numAnswers
= numAnswers
;
2061 msg
->h
.numAuthorities
= numAuthorities
;
2062 msg
->h
.numAdditionals
= (mDNSu16
)(authInfo
? numAdditionals
- 1 : numAdditionals
);
2067 LogMsg("mDNSSendDNSMessage: error sending message over tcp");
2068 return mStatus_UnknownErr
;
2071 // ***************************************************************************
2072 #if COMPILER_LIKES_PRAGMA_MARK
2074 #pragma mark - RR List Management & Task Management
2077 mDNSexport
void mDNS_Lock(mDNS
*const m
)
2079 // MUST grab the platform lock FIRST!
2080 mDNSPlatformLock(m
);
2082 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2083 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2084 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2085 // If mDNS_busy != mDNS_reentrancy that's a bad sign
2086 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2087 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2089 // If this is an initial entry into the mDNSCore code, set m->timenow
2090 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
2091 if (m
->mDNS_busy
== 0)
2094 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m
->timenow
, mDNS_TimeNow_NoLock(m
));
2095 m
->timenow
= mDNS_TimeNow_NoLock(m
);
2096 if (m
->timenow
== 0) m
->timenow
= 1;
2098 else if (m
->timenow
== 0)
2100 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
2101 m
->timenow
= mDNS_TimeNow_NoLock(m
);
2102 if (m
->timenow
== 0) m
->timenow
= 1;
2105 if (m
->timenow_last
- m
->timenow
> 0)
2107 m
->timenow_adjust
+= m
->timenow_last
- m
->timenow
;
2108 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m
->timenow_last
- m
->timenow
, m
->timenow_adjust
);
2109 m
->timenow
= m
->timenow_last
;
2111 m
->timenow_last
= m
->timenow
;
2113 // Increment mDNS_busy so we'll recognise re-entrant calls
2117 mDNSlocal mDNSs32
GetNextScheduledEvent(const mDNS
*const m
)
2119 mDNSs32 e
= m
->timenow
+ 0x78000000;
2120 if (m
->mDNSPlatformStatus
!= mStatus_NoError
|| m
->SleepState
) return(e
);
2121 if (m
->NewQuestions
)
2123 if (m
->NewQuestions
->DelayAnswering
) e
= m
->NewQuestions
->DelayAnswering
;
2124 else return(m
->timenow
);
2126 if (m
->NewLocalOnlyQuestions
) return(m
->timenow
);
2127 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
)) return(m
->timenow
);
2128 if (m
->SuppressSending
) return(m
->SuppressSending
);
2129 #ifndef UNICAST_DISABLED
2130 if (e
- m
->uDNS_info
.nextevent
> 0) e
= m
->uDNS_info
.nextevent
;
2132 if (e
- m
->NextCacheCheck
> 0) e
= m
->NextCacheCheck
;
2133 if (e
- m
->NextScheduledQuery
> 0) e
= m
->NextScheduledQuery
;
2134 if (e
- m
->NextScheduledProbe
> 0) e
= m
->NextScheduledProbe
;
2135 if (e
- m
->NextScheduledResponse
> 0) e
= m
->NextScheduledResponse
;
2139 mDNSexport
void mDNS_Unlock(mDNS
*const m
)
2141 // Decrement mDNS_busy
2144 // Check for locking failures
2145 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2146 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2148 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
2149 if (m
->mDNS_busy
== 0)
2151 m
->NextScheduledEvent
= GetNextScheduledEvent(m
);
2152 if (m
->timenow
== 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
2156 // MUST release the platform lock LAST!
2157 mDNSPlatformUnlock(m
);