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.87 2005/02/25 04:21:00 cheshire
27 <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
29 Revision 1.86 2005/02/18 00:43:12 cheshire
30 <rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
32 Revision 1.85 2005/02/10 22:35:17 cheshire
33 <rdar://problem/3727944> Update name
35 Revision 1.84 2005/02/03 00:44:38 cheshire
36 <rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
38 Revision 1.83 2005/01/27 22:57:55 cheshire
39 Fix compile errors on gcc4
41 Revision 1.82 2005/01/19 03:27:03 cheshire
42 <rdar://problem/3961051> CPU Spin in mDNSResponder
43 GetNextScheduledEvent() needs to check LocalRecordReady()
45 Revision 1.81 2004/12/18 03:13:45 cheshire
46 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
48 Revision 1.80 2004/12/16 21:46:43 cheshire
49 Add DNSTypeName case for kDNSType_SOA
51 Revision 1.79 2004/12/16 21:38:37 cheshire
52 Add DNSTypeName case for kDNSType_NS
54 Revision 1.78 2004/12/16 21:27:37 ksekar
55 Fixed build failures when compiled with verbose debugging messages
57 Revision 1.77 2004/12/16 20:12:59 cheshire
58 <rdar://problem/3324626> Cache memory management improvements
60 Revision 1.76 2004/12/16 08:05:29 shersche
61 Remove extranenous semicolons that cause compilation errors on Windows
63 Revision 1.75 2004/12/15 02:11:22 ksekar
64 <rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness
66 Revision 1.74 2004/12/09 22:49:15 ksekar
67 <rdar://problem/3913653> Wide-Area Goodbyes broken
69 Revision 1.73 2004/12/07 22:49:06 cheshire
70 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
72 Revision 1.72 2004/12/06 21:15:20 ksekar
73 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
75 Revision 1.71 2004/12/04 02:12:45 cheshire
76 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
78 Revision 1.70 2004/12/03 19:52:44 ksekar
79 Use PutResourceRecordTTLJumbo for putDeletionRecord()
81 Revision 1.69 2004/12/03 07:20:50 ksekar
82 <rdar://problem/3674208> Wide-Area: Registration of large TXT record fails
84 Revision 1.68 2004/11/24 00:10:43 cheshire
85 <rdar://problem/3869241> For unicast operations, verify that service types are legal
87 Revision 1.67 2004/10/26 03:52:02 cheshire
88 Update checkin comments
90 Revision 1.66 2004/10/23 01:16:00 cheshire
91 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
93 Revision 1.65 2004/10/20 02:15:09 cheshire
94 Add case in GetRRDisplayString() to display NS rdata
96 Revision 1.64 2004/10/13 00:24:02 cheshire
97 Disable "array is too small to include a terminating null character" warning on Windows
99 Revision 1.63 2004/10/10 06:57:14 cheshire
100 Change definition of "localdomain" to make code compile a little smaller
102 Revision 1.62 2004/10/06 01:44:19 cheshire
103 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
105 Revision 1.61 2004/09/30 00:24:56 ksekar
106 <rdar://problem/3695802> Dynamically update default registration domains on config change
108 Revision 1.60 2004/09/27 23:25:30 cheshire
109 Fix compiler warning: soa.serial is signed, not unsigned
111 Revision 1.59 2004/09/27 22:53:45 ksekar
112 Fixed getLargeResourceRecord for SOA rdata.
114 Revision 1.58 2004/09/25 02:41:39 cheshire
115 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
117 Revision 1.57 2004/09/25 02:24:27 cheshire
118 Removed unused rr->UseCount
120 Revision 1.56 2004/09/24 20:57:39 cheshire
121 <rdar://problem/3680902> Eliminate inappropriate casts that cause misaligned-address errors
123 Revision 1.55 2004/09/17 01:08:48 cheshire
124 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
125 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
126 declared in that file are ONLY appropriate to single-address-space embedded applications.
127 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
129 Revision 1.54 2004/09/17 00:49:51 cheshire
130 Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use
131 is GetLargeResourceRecord
133 Revision 1.53 2004/09/17 00:31:51 cheshire
134 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
136 Revision 1.52 2004/09/17 00:19:10 cheshire
137 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
139 Revision 1.51 2004/09/16 02:29:39 cheshire
140 Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
141 uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
143 Revision 1.50 2004/09/16 01:58:14 cheshire
144 Fix compiler warnings
146 Revision 1.49 2004/09/14 23:42:35 cheshire
147 <rdar://problem/3801296> Need to seed random number generator from platform-layer data
149 Revision 1.48 2004/09/14 23:27:46 cheshire
152 Revision 1.47 2004/08/25 02:50:04 cheshire
153 <rdar://problem/3561220> Browses are no longer piggybacking on other browses
154 Make mDNSSameAddress() recognise that two mDNSAddrType_None addresses are necessarily equal
156 Revision 1.46 2004/08/18 17:35:40 ksekar
157 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
159 Revision 1.45 2004/08/15 18:26:00 cheshire
160 Don't use strcpy() on "struct domainname" objects; use AssignDomainName() instead
161 (A "struct domainname" is a collection of packed pascal strings, not a C string.)
163 Revision 1.44 2004/08/13 23:46:58 cheshire
164 "asyncronous" -> "asynchronous"
166 Revision 1.43 2004/08/12 02:55:46 ksekar
167 Fix param order error moving putPrereqNameNotInUse from uDNS.c using
168 ustrcpy macro to DNSCommon.c using mDNSPlatformStrCopy().
170 Revision 1.42 2004/08/10 23:19:14 ksekar
171 <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
172 Moved routines/constants to allow extern access for garbage collection daemon
174 Revision 1.41 2004/08/10 01:10:01 cheshire
175 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
176 Minor revision from Roger Pantos
178 Revision 1.40 2004/08/04 22:10:46 cheshire
179 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
180 Change to use "._sub." instead of ".s." to mark subtypes.
182 Revision 1.39 2004/07/13 21:24:24 rpantos
183 Fix for <rdar://problem/3701120>.
185 Revision 1.38 2004/06/18 21:08:58 cheshire
186 <rdar://problem/3540040> Applications are registering invalid records
187 Attempts to create domain names like "www..apple.com." now logged to aid debugging
189 Revision 1.37 2004/06/18 20:25:42 cheshire
190 <rdar://problem/3488547> Add a syslog message if someone tries to use "local.arpa".
192 Revision 1.36 2004/06/18 19:09:59 cheshire
193 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
195 Revision 1.35 2004/06/05 00:14:44 cheshire
196 Fix signed/unsigned and other compiler warnings
198 Revision 1.34 2004/06/04 00:25:25 cheshire
199 Fix misaligned write exception that occurs on some platforms
201 Revision 1.33 2004/06/04 00:16:18 cheshire
202 Remove non-portable use of 'inline'
204 Revision 1.32 2004/06/03 03:09:58 ksekar
205 <rdar://problem/3668626>: Garbage Collection for Dynamic Updates
207 Revision 1.31 2004/05/28 23:42:36 ksekar
208 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
210 Revision 1.30 2004/05/26 09:08:04 bradley
211 Added cast to correct structure pointer when allocating domain name list element to fix C++ builds.
213 Revision 1.29 2004/05/18 23:51:25 cheshire
214 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
216 Revision 1.28 2004/05/13 04:54:20 ksekar
217 Unified list copy/free code. Added symetric list for
219 Revision 1.27 2004/04/22 20:29:07 cheshire
220 Log error message if no count field passed to PutResourceRecordTTL()
222 Revision 1.26 2004/04/22 04:07:01 cheshire
223 Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it
225 Revision 1.25 2004/04/22 03:05:28 cheshire
226 kDNSClass_ANY should be kDNSQClass_ANY
228 Revision 1.24 2004/04/22 02:51:20 cheshire
229 Use common code for HINFO/TXT and TSIG cases in putRData
231 Revision 1.23 2004/04/15 00:51:28 bradley
232 Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
233 Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
235 Revision 1.22 2004/04/14 23:09:28 ksekar
236 Support for TSIG signed dynamic updates.
238 Revision 1.21 2004/04/09 16:47:28 cheshire
239 <rdar://problem/3617655>: mDNSResponder escape handling inconsistent with BIND
241 Revision 1.20 2004/04/09 16:37:15 cheshire
242 Suggestion from Bob Bradley:
243 Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers
245 Revision 1.19 2004/04/02 19:34:38 cheshire
248 Revision 1.18 2004/03/30 06:45:00 cheshire
249 Compiler warning fixes from Don Woodward at Roku Labs
251 Revision 1.17 2004/03/19 22:25:20 cheshire
252 <rdar://problem/3579561>: Need to limit service types to fourteen characters
253 Won't actually do this for now, but keep the code around just in case
255 Revision 1.16 2004/03/08 02:45:35 cheshire
256 Minor change to make a couple of the log messages a bit shorter
258 Revision 1.15 2004/03/08 02:44:09 cheshire
259 <rdar://problem/3579561>: Need to limit service types to fourteen characters
261 Revision 1.14 2004/02/21 02:06:24 cheshire
262 Can't use anonymous unions -- they're non-standard and don't work on all compilers
264 Revision 1.13 2004/02/06 23:04:18 ksekar
265 Basic Dynamic Update support via mDNS_Register (dissabled via
266 UNICAST_REGISTRATION #define)
268 Revision 1.12 2004/02/03 22:37:10 cheshire
269 Delete unused (commented-out) code
271 Revision 1.11 2004/02/03 22:35:34 cheshire
272 <rdar://problem/3548256>: Should not allow empty string for resolve domain
274 Revision 1.10 2004/02/03 19:47:36 ksekar
275 Added an asynchronous state machine mechanism to uDNS.c, including
276 calls to find the parent zone for a domain name. Changes include code
277 in repository previously dissabled via "#if 0 incomplete". Codepath
278 is currently unused, and will be called to create update records, etc.
280 Revision 1.9 2004/01/27 20:15:22 cheshire
281 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
283 Revision 1.8 2004/01/24 23:24:36 cheshire
284 Expanded out the list of local domains to reduce risk of mistakes in future
286 Revision 1.7 2004/01/24 08:32:30 bradley
287 Mask values with 0xFF before casting to avoid runtime truncation errors on Windows debug builds.
288 Separated octal-escaped sequences preceding decimal digits to avoid errors with some compilers wanting
289 to signal potentially hidden errors about the subsequent digit not being part of the octal sequence.
291 Revision 1.6 2004/01/24 04:59:15 cheshire
292 Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
294 Revision 1.5 2004/01/23 23:23:14 ksekar
295 Added TCP support for truncated unicast messages.
297 Revision 1.4 2004/01/22 02:15:33 cheshire
298 <rdar://problem/3536597>: Link-local reverse-mapping domains need to be resolved using link-local multicast
300 Revision 1.3 2004/01/21 21:16:29 cheshire
301 Minor tidy-up: Deleted a bunch of blank lines, trailing spaces, tabs, etc.
303 Revision 1.2 2003/12/13 05:47:48 bradley
304 Made local ptr const to fix error when assigning from const structure. Disable benign conditional
305 expression is constant warning when building with Microsoft compilers.
307 Revision 1.1 2003/12/13 03:05:27 ksekar
308 <rdar://problem/3192548>: DynDNS: Unicast query of service records
312 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
313 #define mDNS_InstantiateInlines 1
314 #include "DNSCommon.h"
316 // Disable certain benign warnings with Microsoft compilers
317 #if (defined(_MSC_VER))
318 // Disable "conditional expression is constant" warning for debug macros.
319 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
320 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
321 #pragma warning(disable:4127)
322 // Disable "array is too small to include a terminating null character" warning
323 // -- domain labels have an initial length byte, not a terminating null character
324 #pragma warning(disable:4295)
327 // ***************************************************************************
328 #if COMPILER_LIKES_PRAGMA_MARK
330 #pragma mark - DNameList copy/deallocation routines
333 mDNSexport DNameListElem
*mDNS_CopyDNameList(const DNameListElem
*orig
)
335 DNameListElem
*copy
= mDNSNULL
, *newelem
;
336 const DNameListElem
*ptr
;
338 for (ptr
= orig
; ptr
; ptr
= ptr
->next
)
340 newelem
= (DNameListElem
*)mDNSPlatformMemAllocate(sizeof(DNameListElem
));
341 if (!newelem
) { LogMsg("ERROR: malloc"); return mDNSNULL
; }
342 AssignDomainName(&newelem
->name
, &ptr
->name
);
343 newelem
->next
= copy
;
349 mDNSexport
void mDNS_FreeDNameList(DNameListElem
*list
)
357 mDNSPlatformMemFree(fptr
);
361 // ***************************************************************************
362 #if COMPILER_LIKES_PRAGMA_MARK
364 #pragma mark - General Utility Functions
367 // return true for RFC1918 private addresses
368 mDNSexport mDNSBool
IsPrivateV4Addr(mDNSAddr
*addr
)
372 if (addr
->type
!= mDNSAddrType_IPv4
) return mDNSfalse
;
375 return ((b
[0] == 10) || // 10/8 prefix
376 (b
[0] == 172 && b
[1] > 15 && b
[1] < 32) || // 172.16/12
377 (b
[0] == 192 && b
[1] == 168)); // 192.168/16
380 mDNSexport
const NetworkInterfaceInfo
*GetFirstActiveInterface(const NetworkInterfaceInfo
*intf
)
382 while (intf
&& !intf
->InterfaceActive
) intf
= intf
->next
;
386 mDNSexport mDNSInterfaceID
GetNextActiveInterfaceID(const NetworkInterfaceInfo
*intf
)
388 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
389 if (next
) return(next
->InterfaceID
); else return(mDNSNULL
);
392 mDNSexport mDNSu32
NumCacheRecordsForInterfaceID(const mDNS
*const m
, mDNSInterfaceID id
)
394 mDNSu32 slot
, used
= 0;
397 FORALL_CACHERECORDS(slot
, cg
, rr
)
398 if (rr
->resrec
.InterfaceID
== id
) used
++;
402 mDNSexport
char *DNSTypeName(mDNSu16 rrtype
)
406 case kDNSType_A
: return("Addr");
407 case kDNSType_NS
: return("NS");
408 case kDNSType_CNAME
:return("CNAME");
409 case kDNSType_SOA
: return("SOA");
410 case kDNSType_NULL
: return("NULL");
411 case kDNSType_PTR
: return("PTR");
412 case kDNSType_HINFO
:return("HINFO");
413 case kDNSType_TXT
: return("TXT");
414 case kDNSType_AAAA
: return("AAAA");
415 case kDNSType_SRV
: return("SRV");
416 case kDNSQType_ANY
: return("ANY");
418 static char buffer
[16];
419 mDNS_snprintf(buffer
, sizeof(buffer
), "(%d)", rrtype
);
425 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
426 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
427 // long as this routine is only used for debugging messages, it probably isn't a big problem.
428 mDNSexport
char *GetRRDisplayString_rdb(const ResourceRecord
*rr
, RDataBody
*rd
, char *buffer
)
431 mDNSu32 length
= mDNS_snprintf(buffer
, 79, "%4d %##s %s ", rr
->rdlength
, rr
->name
->c
, DNSTypeName(rr
->rrtype
));
434 case kDNSType_A
: mDNS_snprintf(buffer
+length
, 79-length
, "%.4a", &rd
->ipv4
); break;
436 case kDNSType_NS
: // Same as PTR
437 case kDNSType_CNAME
:// Same as PTR
438 case kDNSType_PTR
: mDNS_snprintf(buffer
+length
, 79-length
, "%##s", rd
->name
.c
); break;
440 case kDNSType_HINFO
:// Display this the same as TXT (just show first string)
441 case kDNSType_TXT
: mDNS_snprintf(buffer
+length
, 79-length
, "%#s", rd
->txt
.c
); break;
443 case kDNSType_AAAA
: mDNS_snprintf(buffer
+length
, 79-length
, "%.16a", &rd
->ipv6
); break;
444 case kDNSType_SRV
: mDNS_snprintf(buffer
+length
, 79-length
, "%##s", rd
->srv
.target
.c
); break;
445 default: mDNS_snprintf(buffer
+length
, 79-length
, "RDLen %d: %s", rr
->rdlength
, rd
->data
); break;
447 for (ptr
= buffer
; *ptr
; ptr
++) if (*ptr
< ' ') *ptr
='.';
451 mDNSexport mDNSu32
mDNSRandom(mDNSu32 max
)
453 static mDNSu32 seed
= 0;
459 seed
= mDNSPlatformRandomSeed(); // Pick an initial seed
460 for (i
=0; i
<100; i
++) seed
= seed
* 21 + 1; // And mix it up a bit
462 while (mask
< max
) mask
= (mask
<< 1) | 1;
463 do seed
= seed
* 21 + 1; while ((seed
& mask
) > max
);
464 return (seed
& mask
);
467 mDNSexport mDNSBool
mDNSSameAddress(const mDNSAddr
*ip1
, const mDNSAddr
*ip2
)
469 if (ip1
->type
== ip2
->type
)
473 case mDNSAddrType_None
: return(mDNStrue
); // Empty addresses have no data and are therefore always equal
474 case mDNSAddrType_IPv4
: return(mDNSBool
)(mDNSSameIPv4Address(ip1
->ip
.v4
, ip2
->ip
.v4
));
475 case mDNSAddrType_IPv6
: return(mDNSBool
)(mDNSSameIPv6Address(ip1
->ip
.v6
, ip2
->ip
.v6
));
481 mDNSexport mDNSBool
mDNSAddrIsDNSMulticast(const mDNSAddr
*ip
)
485 case mDNSAddrType_IPv4
: return(mDNSBool
)(ip
->ip
.v4
.NotAnInteger
== AllDNSLinkGroupv4
.NotAnInteger
);
486 case mDNSAddrType_IPv6
: return(mDNSBool
)(ip
->ip
.v6
.l
[0] == AllDNSLinkGroupv6
.l
[0] &&
487 ip
->ip
.v6
.l
[1] == AllDNSLinkGroupv6
.l
[1] &&
488 ip
->ip
.v6
.l
[2] == AllDNSLinkGroupv6
.l
[2] &&
489 ip
->ip
.v6
.l
[3] == AllDNSLinkGroupv6
.l
[3] );
490 default: return(mDNSfalse
);
494 // ***************************************************************************
495 #if COMPILER_LIKES_PRAGMA_MARK
497 #pragma mark - Domain Name Utility Functions
500 mDNSexport mDNSBool
SameDomainLabel(const mDNSu8
*a
, const mDNSu8
*b
)
503 const int len
= *a
++;
505 if (len
> MAX_DOMAIN_LABEL
)
506 { debugf("Malformed label (too long)"); return(mDNSfalse
); }
508 if (len
!= *b
++) return(mDNSfalse
);
509 for (i
=0; i
<len
; i
++)
513 if (mDNSIsUpperCase(ac
)) ac
+= 'a' - 'A';
514 if (mDNSIsUpperCase(bc
)) bc
+= 'a' - 'A';
515 if (ac
!= bc
) return(mDNSfalse
);
520 mDNSexport mDNSBool
SameDomainName(const domainname
*const d1
, const domainname
*const d2
)
522 const mDNSu8
* a
= d1
->c
;
523 const mDNSu8
* b
= d2
->c
;
524 const mDNSu8
*const max
= d1
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
528 if (a
+ 1 + *a
>= max
)
529 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse
); }
530 if (!SameDomainLabel(a
, b
)) return(mDNSfalse
);
538 mDNSexport mDNSBool
IsLocalDomain(const domainname
*d
)
540 // Domains that are defined to be resolved via link-local multicast are:
541 // local., 254.169.in-addr.arpa., and 0.8.E.F.ip6.arpa.
542 static const domainname
*n0
= (domainname
*)"\x5" "local";
543 static const domainname
*n1
= (domainname
*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
544 static const domainname
*n2
= (domainname
*)"\x1" "0" "\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
546 const domainname
*d1
, *d2
, *d3
, *d4
, *d5
, *d6
; // Top-level domain, second-level domain, etc.
547 d1
= d2
= d3
= d4
= d5
= d6
= mDNSNULL
;
550 d6
= d5
; d5
= d4
; d4
= d3
; d3
= d2
; d2
= d1
; d1
= d
;
551 d
= (domainname
*)(d
->c
+ 1 + d
->c
[0]);
554 if (d1
&& SameDomainName(d1
, n0
)) return(mDNStrue
);
555 if (d4
&& SameDomainName(d4
, n1
)) return(mDNStrue
);
556 if (d6
&& SameDomainName(d6
, n2
)) return(mDNStrue
);
560 // Returns length of a domain name INCLUDING the byte for the final null label
561 // i.e. for the root label "." it returns one
562 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
563 // Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
564 // If the given domainname is invalid, result is 256
565 mDNSexport mDNSu16
DomainNameLength(const domainname
*const name
)
567 const mDNSu8
*src
= name
->c
;
570 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
572 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
574 return((mDNSu16
)(src
- name
->c
+ 1));
577 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
578 // for the final null label i.e. for the root label "." it returns one.
579 // E.g. for the FQDN "foo.com." it returns 9
580 // (length, three data bytes, length, three more data bytes, final zero).
581 // In the case where a parent domain name is provided, and the given name is a child
582 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
583 // of the child name, plus TWO bytes for the compression pointer.
584 // E.g. for the name "foo.com." with parent "com.", it returns 6
585 // (length, three data bytes, two-byte compression pointer).
586 mDNSexport mDNSu16
CompressedDomainNameLength(const domainname
*const name
, const domainname
*parent
)
588 const mDNSu8
*src
= name
->c
;
589 if (parent
&& parent
->c
[0] == 0) parent
= mDNSNULL
;
592 if (*src
> MAX_DOMAIN_LABEL
) return(MAX_DOMAIN_NAME
+1);
593 if (parent
&& SameDomainName((domainname
*)src
, parent
)) return((mDNSu16
)(src
- name
->c
+ 2));
595 if (src
- name
->c
>= MAX_DOMAIN_NAME
) return(MAX_DOMAIN_NAME
+1);
597 return((mDNSu16
)(src
- name
->c
+ 1));
600 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
601 // The C string contains the label as-is, with no escaping, etc.
602 // Any dots in the name are literal dots, not label separators
603 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
604 // in the domainname bufer (i.e., the next byte after the terminating zero).
605 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
606 // AppendLiteralLabelString returns mDNSNULL.
607 mDNSexport mDNSu8
*AppendLiteralLabelString(domainname
*const name
, const char *cstr
)
609 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
610 const mDNSu8
*const lim1
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
611 const mDNSu8
*const lim2
= ptr
+ 1 + MAX_DOMAIN_LABEL
;
612 const mDNSu8
*const lim
= (lim1
< lim2
) ? lim1
: lim2
;
613 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
615 while (*cstr
&& ptr
< lim
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the data
616 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
617 *ptr
++ = 0; // Put the null root label on the end
618 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
619 else return(ptr
); // Success: return new value of ptr
622 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
623 // The C string is in conventional DNS syntax:
624 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
625 // If successful, AppendDNSNameString returns a pointer to the next unused byte
626 // in the domainname bufer (i.e., the next byte after the terminating zero).
627 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
628 // AppendDNSNameString returns mDNSNULL.
629 mDNSexport mDNSu8
*AppendDNSNameString(domainname
*const name
, const char *cstring
)
631 const char *cstr
= cstring
;
632 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
633 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
634 while (*cstr
&& ptr
< lim
) // While more characters, and space to put them...
636 mDNSu8
*lengthbyte
= ptr
++; // Record where the length is going to go
637 if (*cstr
== '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring
); return(mDNSNULL
); }
638 while (*cstr
&& *cstr
!= '.' && ptr
< lim
) // While we have characters in the label...
640 mDNSu8 c
= (mDNSu8
)*cstr
++; // Read the character
641 if (c
== '\\') // If escape character, check next character
643 c
= (mDNSu8
)*cstr
++; // Assume we'll just take the next character
644 if (mdnsIsDigit(cstr
[-1]) && mdnsIsDigit(cstr
[0]) && mdnsIsDigit(cstr
[1]))
645 { // If three decimal digits,
646 int v0
= cstr
[-1] - '0'; // then interpret as three-digit decimal
647 int v1
= cstr
[ 0] - '0';
648 int v2
= cstr
[ 1] - '0';
649 int val
= v0
* 100 + v1
* 10 + v2
;
650 if (val
<= 255) { c
= (mDNSu8
)val
; cstr
+= 2; } // If valid three-digit decimal value, use it
653 *ptr
++ = c
; // Write the character
655 if (*cstr
) cstr
++; // Skip over the trailing dot (if present)
656 if (ptr
- lengthbyte
- 1 > MAX_DOMAIN_LABEL
) // If illegal label, abort
658 *lengthbyte
= (mDNSu8
)(ptr
- lengthbyte
- 1); // Fill in the length byte
661 *ptr
++ = 0; // Put the null root label on the end
662 if (*cstr
) return(mDNSNULL
); // Failure: We didn't successfully consume all input
663 else return(ptr
); // Success: return new value of ptr
666 // AppendDomainLabel appends a single label to a name.
667 // If successful, AppendDomainLabel returns a pointer to the next unused byte
668 // in the domainname bufer (i.e., the next byte after the terminating zero).
669 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
670 // AppendDomainLabel returns mDNSNULL.
671 mDNSexport mDNSu8
*AppendDomainLabel(domainname
*const name
, const domainlabel
*const label
)
674 mDNSu8
*ptr
= name
->c
+ DomainNameLength(name
) - 1;
676 // Check label is legal
677 if (label
->c
[0] > MAX_DOMAIN_LABEL
) return(mDNSNULL
);
679 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
680 if (ptr
+ 1 + label
->c
[0] + 1 > name
->c
+ MAX_DOMAIN_NAME
) return(mDNSNULL
);
682 for (i
=0; i
<=label
->c
[0]; i
++) *ptr
++ = label
->c
[i
]; // Copy the label data
683 *ptr
++ = 0; // Put the null root label on the end
687 mDNSexport mDNSu8
*AppendDomainName(domainname
*const name
, const domainname
*const append
)
689 mDNSu8
* ptr
= name
->c
+ DomainNameLength(name
) - 1; // Find end of current name
690 const mDNSu8
*const lim
= name
->c
+ MAX_DOMAIN_NAME
- 1; // Limit of how much we can add (not counting final zero)
691 const mDNSu8
* src
= append
->c
;
695 if (ptr
+ 1 + src
[0] > lim
) return(mDNSNULL
);
696 for (i
=0; i
<=src
[0]; i
++) *ptr
++ = src
[i
];
697 *ptr
= 0; // Put the null root label on the end
703 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
704 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
705 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
706 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
707 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
708 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
709 mDNSexport mDNSBool
MakeDomainLabelFromLiteralString(domainlabel
*const label
, const char *cstr
)
711 mDNSu8
* ptr
= label
->c
+ 1; // Where we're putting it
712 const mDNSu8
*const limit
= label
->c
+ 1 + MAX_DOMAIN_LABEL
; // The maximum we can put
713 while (*cstr
&& ptr
< limit
) *ptr
++ = (mDNSu8
)*cstr
++; // Copy the label
714 label
->c
[0] = (mDNSu8
)(ptr
- label
->c
- 1); // Set the length byte
715 return(*cstr
== 0); // Return mDNStrue if we successfully consumed all input
718 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
719 // The C string is in conventional DNS syntax:
720 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
721 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
722 // in the domainname bufer (i.e., the next byte after the terminating zero).
723 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
724 // MakeDomainNameFromDNSNameString returns mDNSNULL.
725 mDNSexport mDNSu8
*MakeDomainNameFromDNSNameString(domainname
*const name
, const char *cstr
)
727 name
->c
[0] = 0; // Make an empty domain name
728 return(AppendDNSNameString(name
, cstr
)); // And then add this string to it
731 mDNSexport
char *ConvertDomainLabelToCString_withescape(const domainlabel
*const label
, char *ptr
, char esc
)
733 const mDNSu8
* src
= label
->c
; // Domain label we're reading
734 const mDNSu8 len
= *src
++; // Read length of this (non-null) label
735 const mDNSu8
*const end
= src
+ len
; // Work out where the label ends
736 if (len
> MAX_DOMAIN_LABEL
) return(mDNSNULL
); // If illegal label, abort
737 while (src
< end
) // While we have characters in the label
742 if (c
== '.' || c
== esc
) // If character is a dot or the escape character
743 *ptr
++ = esc
; // Output escape character
744 else if (c
<= ' ') // If non-printing ascii,
745 { // Output decimal escape sequence
747 *ptr
++ = (char) ('0' + (c
/ 100) );
748 *ptr
++ = (char) ('0' + (c
/ 10) % 10);
749 c
= (mDNSu8
)('0' + (c
) % 10);
752 *ptr
++ = (char)c
; // Copy the character
754 *ptr
= 0; // Null-terminate the string
755 return(ptr
); // and return
758 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes)
759 mDNSexport
char *ConvertDomainNameToCString_withescape(const domainname
*const name
, char *ptr
, char esc
)
761 const mDNSu8
*src
= name
->c
; // Domain name we're reading
762 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
764 if (*src
== 0) *ptr
++ = '.'; // Special case: For root, just write a dot
766 while (*src
) // While more characters in the domain name
768 if (src
+ 1 + *src
>= max
) return(mDNSNULL
);
769 ptr
= ConvertDomainLabelToCString_withescape((const domainlabel
*)src
, ptr
, esc
);
770 if (!ptr
) return(mDNSNULL
);
772 *ptr
++ = '.'; // Write the dot after the label
775 *ptr
++ = 0; // Null-terminate the string
776 return(ptr
); // and return
780 // Host names must start with a letter, end with a letter or digit,
781 // and have as interior characters only letters, digits, and hyphen.
782 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
784 mDNSexport
void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name
[], domainlabel
*const hostlabel
)
786 const mDNSu8
* src
= &UTF8Name
[1];
787 const mDNSu8
*const end
= &UTF8Name
[1] + UTF8Name
[0];
788 mDNSu8
* ptr
= &hostlabel
->c
[1];
789 const mDNSu8
*const lim
= &hostlabel
->c
[1] + MAX_DOMAIN_LABEL
;
792 // Delete apostrophes from source name
793 if (src
[0] == '\'') { src
++; continue; } // Standard straight single quote
794 if (src
+ 2 < end
&& src
[0] == 0xE2 && src
[1] == 0x80 && src
[2] == 0x99)
795 { src
+= 3; continue; } // Unicode curly apostrophe
798 if (mdnsValidHostChar(*src
, (ptr
> &hostlabel
->c
[1]), (src
< end
-1))) *ptr
++ = *src
;
799 else if (ptr
> &hostlabel
->c
[1] && ptr
[-1] != '-') *ptr
++ = '-';
803 while (ptr
> &hostlabel
->c
[1] && ptr
[-1] == '-') ptr
--; // Truncate trailing '-' marks
804 hostlabel
->c
[0] = (mDNSu8
)(ptr
- &hostlabel
->c
[1]);
807 mDNSexport mDNSu8
*ConstructServiceName(domainname
*const fqdn
,
808 const domainlabel
*name
, const domainname
*type
, const domainname
*const domain
)
811 mDNSu8
*dst
= fqdn
->c
;
813 const char *errormsg
;
815 // In the case where there is no name (and ONLY in that case),
816 // a single-label subtype is allowed as the first label of a three-part "type"
819 const mDNSu8
*s0
= type
->c
;
820 if (s0
[0] && s0
[0] < 0x40) // If legal first label (at least one character, and no more than 63)
822 const mDNSu8
* s1
= s0
+ 1 + s0
[0];
823 if (s1
[0] && s1
[0] < 0x40) // and legal second label (at least one character, and no more than 63)
825 const mDNSu8
*s2
= s1
+ 1 + s1
[0];
826 if (s2
[0] && s2
[0] < 0x40 && s2
[1+s2
[0]] == 0) // and we have three and only three labels
828 static const mDNSu8 SubTypeLabel
[5] = "\x04_sub";
829 src
= s0
; // Copy the first label
831 for (i
=0; i
<= len
; i
++) *dst
++ = *src
++;
832 for (i
=0; i
< (int)sizeof(SubTypeLabel
); i
++) *dst
++ = SubTypeLabel
[i
];
833 type
= (domainname
*)s1
;
835 // Special support for queries done by some third-party network monitoring software
836 // For these queries, we retract the "._sub" we just added between the subtype and the main type
837 if (SameDomainName((domainname
*)s0
, (domainname
*)"\x09_services\x07_dns-sd\x04_udp") ||
838 SameDomainName((domainname
*)s0
, (domainname
*)"\x09_services\x05_mdns\x04_udp"))
839 dst
-= sizeof(SubTypeLabel
);
845 if (name
&& name
->c
[0])
847 src
= name
->c
; // Put the service name into the domain name
849 if (len
>= 0x40) { errormsg
="Service instance name too long"; goto fail
; }
850 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
853 name
= (domainlabel
*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
855 src
= type
->c
; // Put the service type into the domain name
857 if (len
< 2 || len
>= 0x40 || (len
> 15 && !SameDomainName(domain
, (domainname
*)"\x05" "local")))
859 errormsg
="Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
862 if (src
[1] != '_') { errormsg
="Application protocol name must begin with underscore"; goto fail
; }
863 for (i
=2; i
<=len
; i
++)
864 if (!mdnsIsLetter(src
[i
]) && !mdnsIsDigit(src
[i
]) && src
[i
] != '-' && src
[i
] != '_')
865 { errormsg
="Application protocol name must contain only letters, digits, and hyphens"; goto fail
; }
866 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
869 if (!(len
== 4 && src
[1] == '_' &&
870 (((src
[2] | 0x20) == 'u' && (src
[3] | 0x20) == 'd') || ((src
[2] | 0x20) == 't' && (src
[3] | 0x20) == 'c')) &&
871 (src
[4] | 0x20) == 'p'))
872 { errormsg
="Transport protocol name must be _udp or _tcp"; goto fail
; }
873 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
875 if (*src
) { errormsg
="Service type must have only two labels"; goto fail
; }
878 if (!domain
->c
[0]) { errormsg
="Service domain must be non-empty"; goto fail
; }
879 if (SameDomainName(domain
, (domainname
*)"\x05" "local" "\x04" "arpa"))
880 { errormsg
="Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail
; }
881 dst
= AppendDomainName(fqdn
, domain
);
882 if (!dst
) { errormsg
="Service domain too long"; goto fail
; }
886 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg
, name
->c
, type
->c
, domain
->c
);
890 mDNSexport mDNSBool
DeconstructServiceName(const domainname
*const fqdn
,
891 domainlabel
*const name
, domainname
*const type
, domainname
*const domain
)
894 const mDNSu8
*src
= fqdn
->c
;
895 const mDNSu8
*max
= fqdn
->c
+ MAX_DOMAIN_NAME
;
898 dst
= name
->c
; // Extract the service name from the domain name
900 if (len
>= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse
); }
901 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
903 dst
= type
->c
; // Extract the service type from the domain name
905 if (len
>= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse
); }
906 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
909 if (len
>= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse
); }
910 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
911 *dst
++ = 0; // Put the null root label on the end of the service type
913 dst
= domain
->c
; // Extract the service domain from the domain name
918 { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse
); }
919 if (src
+ 1 + len
+ 1 >= max
)
920 { debugf("DeconstructServiceName: service domain too long"); return(mDNSfalse
); }
921 for (i
=0; i
<=len
; i
++) *dst
++ = *src
++;
923 *dst
++ = 0; // Put the null root label on the end
929 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
930 // 10xxxxxx is a continuation byte of a multi-byte character
931 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
932 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
933 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
934 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
935 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
937 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
938 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
939 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
940 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
941 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
943 mDNSexport mDNSu32
TruncateUTF8ToLength(mDNSu8
*string
, mDNSu32 length
, mDNSu32 max
)
947 mDNSu8 c1
= string
[max
]; // First byte after cut point
948 mDNSu8 c2
= (max
+1 < length
) ? string
[max
+1] : 0xB0; // Second byte after cut point
949 length
= max
; // Trim length down
952 // Check if the byte right after the chop point is a UTF-8 continuation byte,
953 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
954 // If so, then we continue to chop more bytes until we get to a legal chop point.
955 mDNSBool continuation
= ((c1
& 0xC0) == 0x80);
956 mDNSBool secondsurrogate
= (c1
== 0xED && (c2
& 0xF0) == 0xB0);
957 if (!continuation
&& !secondsurrogate
) break;
959 c1
= string
[--length
];
961 // Having truncated characters off the end of our string, also cut off any residual white space
962 while (length
> 0 && string
[length
-1] <= ' ') length
--;
967 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
968 // name ends in "-nnn", where n is some decimal number.
969 mDNSexport mDNSBool
LabelContainsSuffix(const domainlabel
*const name
, const mDNSBool RichText
)
971 mDNSu16 l
= name
->c
[0];
975 if (l
< 4) return mDNSfalse
; // Need at least " (2)"
976 if (name
->c
[l
--] != ')') return mDNSfalse
; // Last char must be ')'
977 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Preceeded by a digit
979 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
980 return (name
->c
[l
] == '(' && name
->c
[l
- 1] == ' ');
984 if (l
< 2) return mDNSfalse
; // Need at least "-2"
985 if (!mdnsIsDigit(name
->c
[l
])) return mDNSfalse
; // Last char must be a digit
987 while (l
> 2 && mdnsIsDigit(name
->c
[l
])) l
--; // Strip off digits
988 return (name
->c
[l
] == '-');
992 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
993 // responsible for ensuring that the label does indeed contain a suffix. returns the number
994 // from the suffix that was removed.
995 mDNSexport mDNSu32
RemoveLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
997 mDNSu32 val
= 0, multiplier
= 1;
999 // Chop closing parentheses from RichText suffix
1000 if (RichText
&& name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == ')') name
->c
[0]--;
1002 // Get any existing numerical suffix off the name
1003 while (mdnsIsDigit(name
->c
[name
->c
[0]]))
1004 { val
+= (name
->c
[name
->c
[0]] - '0') * multiplier
; multiplier
*= 10; name
->c
[0]--; }
1006 // Chop opening parentheses or dash from suffix
1009 if (name
->c
[0] >= 2 && name
->c
[name
->c
[0]] == '(' && name
->c
[name
->c
[0]-1] == ' ') name
->c
[0] -= 2;
1013 if (name
->c
[0] >= 1 && name
->c
[name
->c
[0]] == '-') name
->c
[0] -= 1;
1019 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1020 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1021 mDNSexport
void AppendLabelSuffix(domainlabel
*name
, mDNSu32 val
, mDNSBool RichText
)
1023 mDNSu32 divisor
= 1, chars
= 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1024 if (RichText
) chars
= 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1026 // Truncate trailing spaces from RichText names
1027 if (RichText
) while (name
->c
[name
->c
[0]] == ' ') name
->c
[0]--;
1029 while (val
>= divisor
* 10) { divisor
*= 10; chars
++; }
1031 name
->c
[0] = TruncateUTF8ToLength(name
->c
+1, name
->c
[0], MAX_DOMAIN_LABEL
- chars
);
1033 if (RichText
) { name
->c
[++name
->c
[0]] = ' '; name
->c
[++name
->c
[0]] = '('; }
1034 else { name
->c
[++name
->c
[0]] = '-'; }
1038 name
->c
[++name
->c
[0]] = (mDNSu8
)('0' + val
/ divisor
);
1043 if (RichText
) name
->c
[++name
->c
[0]] = ')';
1046 mDNSexport
void IncrementLabelSuffix(domainlabel
*name
, mDNSBool RichText
)
1050 if (LabelContainsSuffix(name
, RichText
))
1051 val
= RemoveLabelSuffix(name
, RichText
);
1053 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1054 // If existing suffix in the range 2-9, increment it.
1055 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1056 // so add a random increment to improve the chances of finding an available name next time.
1057 if (val
== 0) val
= 2;
1058 else if (val
< 10) val
++;
1059 else val
+= 1 + mDNSRandom(99);
1061 AppendLabelSuffix(name
, val
, RichText
);
1064 // ***************************************************************************
1065 #if COMPILER_LIKES_PRAGMA_MARK
1067 #pragma mark - Resource Record Utility Functions
1070 mDNSexport mDNSu32
RDataHashValue(mDNSu16
const rdlength
, const RDataBody
*const rdb
)
1074 for (i
=0; i
+1 < rdlength
; i
+=2)
1076 sum
+= (((mDNSu32
)(rdb
->data
[i
])) << 8) | rdb
->data
[i
+1];
1077 sum
= (sum
<<3) | (sum
>>29);
1081 sum
+= ((mDNSu32
)(rdb
->data
[i
])) << 8;
1086 mDNSexport mDNSBool
SameRData(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
1088 if (r1
->rrtype
!= r2
->rrtype
) return(mDNSfalse
);
1089 if (r1
->rdlength
!= r2
->rdlength
) return(mDNSfalse
);
1090 if (r1
->rdatahash
!= r2
->rdatahash
) return(mDNSfalse
);
1093 case kDNSType_CNAME
:// Same as PTR
1094 case kDNSType_PTR
: return(SameDomainName(&r1
->rdata
->u
.name
, &r2
->rdata
->u
.name
));
1096 case kDNSType_SRV
: return(mDNSBool
)( r1
->rdata
->u
.srv
.priority
== r2
->rdata
->u
.srv
.priority
&&
1097 r1
->rdata
->u
.srv
.weight
== r2
->rdata
->u
.srv
.weight
&&
1098 r1
->rdata
->u
.srv
.port
.NotAnInteger
== r2
->rdata
->u
.srv
.port
.NotAnInteger
&&
1099 SameDomainName(&r1
->rdata
->u
.srv
.target
, &r2
->rdata
->u
.srv
.target
) );
1101 default: return(mDNSPlatformMemSame(r1
->rdata
->u
.data
, r2
->rdata
->u
.data
, r1
->rdlength
));
1105 mDNSexport mDNSBool
SameResourceRecord(ResourceRecord
*r1
, ResourceRecord
*r2
)
1107 return (r1
->namehash
== r2
->namehash
&&
1108 r1
->rrtype
== r2
->rrtype
&&
1109 SameDomainName(r1
->name
, r2
->name
) &&
1113 mDNSexport mDNSBool
ResourceRecordAnswersQuestion(const ResourceRecord
*const rr
, const DNSQuestion
*const q
)
1115 if (rr
->InterfaceID
&&
1116 q
->InterfaceID
&& q
->InterfaceID
!= mDNSInterface_LocalOnly
&&
1117 rr
->InterfaceID
!= q
->InterfaceID
) return(mDNSfalse
);
1119 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1120 if (rr
->rrtype
!= kDNSType_CNAME
&& rr
->rrtype
!= q
->qtype
&& q
->qtype
!= kDNSQType_ANY
) return(mDNSfalse
);
1121 if ( rr
->rrclass
!= q
->qclass
&& q
->qclass
!= kDNSQClass_ANY
) return(mDNSfalse
);
1122 return(rr
->namehash
== q
->qnamehash
&& SameDomainName(rr
->name
, &q
->qname
));
1125 mDNSexport mDNSu16
GetRDLength(const ResourceRecord
*const rr
, mDNSBool estimate
)
1127 const RDataBody
*rd
= &rr
->rdata
->u
;
1128 const domainname
*const name
= estimate
? rr
->name
: mDNSNULL
;
1131 case kDNSType_A
: return(sizeof(rd
->ipv4
));
1132 case kDNSType_CNAME
:// Same as PTR
1133 case kDNSType_NS
: // Same as PTR
1134 case kDNSType_PTR
: return(CompressedDomainNameLength(&rd
->name
, name
));
1135 case kDNSType_HINFO
:return(mDNSu16
)(2 + (int)rd
->data
[0] + (int)rd
->data
[1 + (int)rd
->data
[0]]);
1136 case kDNSType_NULL
: // Same as TXT -- not self-describing, so have to just trust rdlength
1137 case kDNSType_TXT
: return(rr
->rdlength
); // TXT is not self-describing, so have to just trust rdlength
1138 case kDNSType_AAAA
: return(sizeof(rd
->ipv6
));
1139 case kDNSType_SRV
: return(mDNSu16
)(6 + CompressedDomainNameLength(&rd
->srv
.target
, name
));
1140 case kDNSType_SOA
: return (mDNSu16
)(CompressedDomainNameLength(&rd
->soa
.mname
, name
) +
1141 CompressedDomainNameLength(&rd
->soa
.rname
, name
) +
1142 5 * sizeof(mDNSOpaque32
));
1143 case kDNSType_OPT
: return(rr
->rdlength
);
1144 default: debugf("Warning! Don't know how to get length of resource type %d", rr
->rrtype
);
1145 return(rr
->rdlength
);
1149 mDNSexport mDNSBool
ValidateRData(const mDNSu16 rrtype
, const mDNSu16 rdlength
, const RData
*const rd
)
1155 case kDNSType_A
: return(rdlength
== sizeof(mDNSv4Addr
));
1157 case kDNSType_NS
: // Same as PTR
1158 case kDNSType_MD
: // Same as PTR
1159 case kDNSType_MF
: // Same as PTR
1160 case kDNSType_CNAME
:// Same as PTR
1161 //case kDNSType_SOA not checked
1162 case kDNSType_MB
: // Same as PTR
1163 case kDNSType_MG
: // Same as PTR
1164 case kDNSType_MR
: // Same as PTR
1165 //case kDNSType_NULL not checked (no specified format, so always valid)
1166 //case kDNSType_WKS not checked
1167 case kDNSType_PTR
: if (!rdlength
) return(mDNSfalse
);
1168 len
= DomainNameLength(&rd
->u
.name
);
1169 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== len
);
1171 case kDNSType_HINFO
:// Same as TXT (roughly)
1172 case kDNSType_MINFO
:// Same as TXT (roughly)
1173 case kDNSType_TXT
: if (!rdlength
) return(mDNSfalse
); // TXT record has to be at least one byte (RFC 1035)
1175 const mDNSu8
*ptr
= rd
->u
.txt
.c
;
1176 const mDNSu8
*end
= rd
->u
.txt
.c
+ rdlength
;
1177 while (ptr
< end
) ptr
+= 1 + ptr
[0];
1178 return (ptr
== end
);
1181 case kDNSType_AAAA
: return(rdlength
== sizeof(mDNSv6Addr
));
1183 case kDNSType_MX
: if (!rdlength
) return(mDNSfalse
);
1184 len
= DomainNameLength(&rd
->u
.mx
.exchange
);
1185 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 2+len
);
1187 case kDNSType_SRV
: if (!rdlength
) return(mDNSfalse
);
1188 len
= DomainNameLength(&rd
->u
.srv
.target
);
1189 return(len
<= MAX_DOMAIN_NAME
&& rdlength
== 6+len
);
1191 default: return(mDNStrue
); // Allow all other types without checking
1195 // ***************************************************************************
1196 #if COMPILER_LIKES_PRAGMA_MARK
1199 #pragma mark - DNS Message Creation Functions
1202 mDNSexport
void InitializeDNSMessage(DNSMessageHeader
*h
, mDNSOpaque16 id
, mDNSOpaque16 flags
)
1206 h
->numQuestions
= 0;
1208 h
->numAuthorities
= 0;
1209 h
->numAdditionals
= 0;
1212 mDNSexport
const mDNSu8
*FindCompressionPointer(const mDNSu8
*const base
, const mDNSu8
*const end
, const mDNSu8
*const domname
)
1214 const mDNSu8
*result
= end
- *domname
- 1;
1216 if (*domname
== 0) return(mDNSNULL
); // There's no point trying to match just the root label
1218 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1219 while (result
>= base
)
1221 // If the length byte and first character of the label match, then check further to see
1222 // if this location in the packet will yield a useful name compression pointer.
1223 if (result
[0] == domname
[0] && result
[1] == domname
[1])
1225 const mDNSu8
*name
= domname
;
1226 const mDNSu8
*targ
= result
;
1227 while (targ
+ *name
< end
)
1229 // First see if this label matches
1231 const mDNSu8
*pointertarget
;
1232 for (i
=0; i
<= *name
; i
++) if (targ
[i
] != name
[i
]) break;
1233 if (i
<= *name
) break; // If label did not match, bail out
1234 targ
+= 1 + *name
; // Else, did match, so advance target pointer
1235 name
+= 1 + *name
; // and proceed to check next label
1236 if (*name
== 0 && *targ
== 0) return(result
); // If no more labels, we found a match!
1237 if (*name
== 0) break; // If no more labels to match, we failed, so bail out
1239 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1240 if (targ
[0] < 0x40) continue; // If length value, continue to check next label
1241 if (targ
[0] < 0xC0) break; // If 40-BF, not valid
1242 if (targ
+1 >= end
) break; // Second byte not present!
1243 pointertarget
= base
+ (((mDNSu16
)(targ
[0] & 0x3F)) << 8) + targ
[1];
1244 if (targ
< pointertarget
) break; // Pointertarget must point *backwards* in the packet
1245 if (pointertarget
[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1246 targ
= pointertarget
;
1249 result
--; // We failed to match at this search position, so back up the tentative result pointer and try again
1254 // Put a string of dot-separated labels as length-prefixed labels
1255 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1256 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1257 // end points to the end of the message so far
1258 // ptr points to where we want to put the name
1259 // limit points to one byte past the end of the buffer that we must not overrun
1260 // domainname is the name to put
1261 mDNSexport mDNSu8
*putDomainNameAsLabels(const DNSMessage
*const msg
,
1262 mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
)
1264 const mDNSu8
*const base
= (const mDNSu8
*)msg
;
1265 const mDNSu8
* np
= name
->c
;
1266 const mDNSu8
*const max
= name
->c
+ MAX_DOMAIN_NAME
; // Maximum that's valid
1267 const mDNSu8
* pointer
= mDNSNULL
;
1268 const mDNSu8
*const searchlimit
= ptr
;
1270 while (*np
&& ptr
< limit
-1) // While we've got characters in the name, and space to write them in the message...
1272 if (*np
> MAX_DOMAIN_LABEL
)
1273 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name
->c
); return(mDNSNULL
); }
1275 // This check correctly allows for the final trailing root label:
1277 // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
1278 // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
1279 // We know that max will be at name->c[255]
1280 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1281 // six bytes, then exit the loop, write the final terminating root label, and the domain
1282 // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
1283 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1284 if (np
+ 1 + *np
>= max
)
1285 { LogMsg("Malformed domain name %##s (more than 255 bytes)", name
->c
); return(mDNSNULL
); }
1287 if (base
) pointer
= FindCompressionPointer(base
, searchlimit
, np
);
1288 if (pointer
) // Use a compression pointer if we can
1290 mDNSu16 offset
= (mDNSu16
)(pointer
- base
);
1291 *ptr
++ = (mDNSu8
)(0xC0 | (offset
>> 8));
1292 *ptr
++ = (mDNSu8
)( offset
& 0xFF);
1295 else // Else copy one label and try again
1299 if (ptr
+ 1 + len
>= limit
) return(mDNSNULL
);
1301 for (i
=0; i
<len
; i
++) *ptr
++ = *np
++;
1305 if (ptr
< limit
) // If we didn't run out of space
1307 *ptr
++ = 0; // Put the final root label
1308 return(ptr
); // and return
1314 mDNSlocal mDNSu8
*putVal16(mDNSu8
*ptr
, mDNSu16 val
)
1316 ptr
[0] = (mDNSu8
)((val
>> 8 ) & 0xFF);
1317 ptr
[1] = (mDNSu8
)((val
) & 0xFF);
1318 return ptr
+ sizeof(mDNSOpaque16
);
1321 mDNSlocal mDNSu8
*putVal32(mDNSu8
*ptr
, mDNSu32 val
)
1323 ptr
[0] = (mDNSu8
)((val
>> 24) & 0xFF);
1324 ptr
[1] = (mDNSu8
)((val
>> 16) & 0xFF);
1325 ptr
[2] = (mDNSu8
)((val
>> 8) & 0xFF);
1326 ptr
[3] = (mDNSu8
)((val
) & 0xFF);
1327 return ptr
+ sizeof(mDNSu32
);
1330 mDNSlocal mDNSu8
*putOptRData(mDNSu8
*ptr
, const mDNSu8
*limit
, ResourceRecord
*rr
)
1335 while (nput
< rr
->rdlength
)
1337 // check if space for opt/optlen
1338 if (ptr
+ (2 * sizeof(mDNSu16
)) > limit
) goto space_err
;
1339 opt
= (rdataOpt
*)(rr
->rdata
->u
.data
+ nput
);
1340 ptr
= putVal16(ptr
, opt
->opt
);
1341 ptr
= putVal16(ptr
, opt
->optlen
);
1342 nput
+= 2 * sizeof(mDNSu16
);
1343 if (opt
->opt
== kDNSOpt_LLQ
)
1345 if (ptr
+ sizeof(LLQOptData
) > limit
) goto space_err
;
1346 ptr
= putVal16(ptr
, opt
->OptData
.llq
.vers
);
1347 ptr
= putVal16(ptr
, opt
->OptData
.llq
.llqOp
);
1348 ptr
= putVal16(ptr
, opt
->OptData
.llq
.err
);
1349 mDNSPlatformMemCopy(opt
->OptData
.llq
.id
, ptr
, 8); // 8-byte id
1351 ptr
= putVal32(ptr
, opt
->OptData
.llq
.lease
);
1352 nput
+= sizeof(LLQOptData
);
1354 else if (opt
->opt
== kDNSOpt_Lease
)
1356 if (ptr
+ sizeof(mDNSs32
) > limit
) goto space_err
;
1357 ptr
= putVal32(ptr
, opt
->OptData
.lease
);
1358 nput
+= sizeof(mDNSs32
);
1360 else { LogMsg("putOptRData - unknown option %d", opt
->opt
); return mDNSNULL
; }
1366 LogMsg("ERROR: putOptRData - out of space");
1370 mDNSlocal mDNSu16
getVal16(const mDNSu8
**ptr
)
1372 mDNSu16 val
= (mDNSu16
)(((mDNSu16
)(*ptr
)[0]) << 8 | (*ptr
)[1]);
1373 *ptr
+= sizeof(mDNSOpaque16
);
1377 mDNSlocal
const mDNSu8
*getOptRdata(const mDNSu8
*ptr
, const mDNSu8
*limit
, ResourceRecord
*rr
, mDNSu16 pktRDLen
)
1382 while (nread
< pktRDLen
)
1384 opt
= (rdataOpt
*)(rr
->rdata
->u
.data
+ nread
);
1385 // space for opt + optlen
1386 if (nread
+ (2 * sizeof(mDNSu16
)) > rr
->rdata
->MaxRDLength
) goto space_err
;
1387 opt
->opt
= getVal16(&ptr
);
1388 opt
->optlen
= getVal16(&ptr
);
1389 nread
+= 2 * sizeof(mDNSu16
);
1390 if (opt
->opt
== kDNSOpt_LLQ
)
1392 if ((unsigned)(limit
- ptr
) < sizeof(LLQOptData
)) goto space_err
;
1393 opt
->OptData
.llq
.vers
= getVal16(&ptr
);
1394 opt
->OptData
.llq
.llqOp
= getVal16(&ptr
);
1395 opt
->OptData
.llq
.err
= getVal16(&ptr
);
1396 mDNSPlatformMemCopy(ptr
, opt
->OptData
.llq
.id
, 8);
1398 opt
->OptData
.llq
.lease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1399 if (opt
->OptData
.llq
.lease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1400 opt
->OptData
.llq
.lease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1401 ptr
+= sizeof(mDNSOpaque32
);
1402 nread
+= sizeof(LLQOptData
);
1404 else if (opt
->opt
== kDNSOpt_Lease
)
1406 if ((unsigned)(limit
- ptr
) < sizeof(mDNSs32
)) goto space_err
;
1408 opt
->OptData
.lease
= (mDNSu32
) ((mDNSu32
)ptr
[0] << 24 | (mDNSu32
)ptr
[1] << 16 | (mDNSu32
)ptr
[2] << 8 | ptr
[3]);
1409 if (opt
->OptData
.lease
> 0x70000000UL
/ mDNSPlatformOneSecond
)
1410 opt
->OptData
.lease
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1411 ptr
+= sizeof(mDNSs32
);
1412 nread
+= sizeof(mDNSs32
);
1414 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt
->opt
); return mDNSNULL
; }
1417 rr
->rdlength
= pktRDLen
;
1421 LogMsg("ERROR: getLLQRdata - out of space");
1425 mDNSexport mDNSu8
*putRData(const DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, ResourceRecord
*rr
)
1429 case kDNSType_A
: if (rr
->rdlength
!= 4)
1431 debugf("putRData: Illegal length %d for kDNSType_A", rr
->rdlength
);
1434 if (ptr
+ 4 > limit
) return(mDNSNULL
);
1435 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[0];
1436 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[1];
1437 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[2];
1438 *ptr
++ = rr
->rdata
->u
.ipv4
.b
[3];
1441 case kDNSType_CNAME
:// Same as PTR
1442 case kDNSType_PTR
: return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.name
));
1444 case kDNSType_AAAA
: if (rr
->rdlength
!= sizeof(rr
->rdata
->u
.ipv6
))
1446 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr
->rdlength
);
1449 if (ptr
+ sizeof(rr
->rdata
->u
.ipv6
) > limit
) return(mDNSNULL
);
1450 mDNSPlatformMemCopy(&rr
->rdata
->u
.ipv6
, ptr
, sizeof(rr
->rdata
->u
.ipv6
));
1451 return(ptr
+ sizeof(rr
->rdata
->u
.ipv6
));
1453 case kDNSType_SRV
: if (ptr
+ 6 > limit
) return(mDNSNULL
);
1454 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
>> 8);
1455 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.priority
& 0xFF);
1456 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
>> 8);
1457 *ptr
++ = (mDNSu8
)(rr
->rdata
->u
.srv
.weight
& 0xFF);
1458 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[0];
1459 *ptr
++ = rr
->rdata
->u
.srv
.port
.b
[1];
1460 return(putDomainNameAsLabels(msg
, ptr
, limit
, &rr
->rdata
->u
.srv
.target
));
1461 case kDNSType_OPT
: return putOptRData(ptr
, limit
, rr
);
1463 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr
->rrtype
);
1464 // Fall through to common code below
1465 case kDNSType_HINFO
:
1467 case kDNSType_TSIG
: if (ptr
+ rr
->rdlength
> limit
) return(mDNSNULL
);
1468 mDNSPlatformMemCopy(rr
->rdata
->u
.data
, ptr
, rr
->rdlength
);
1469 return(ptr
+ rr
->rdlength
);
1473 mDNSexport mDNSu8
*PutResourceRecordTTLWithLimit(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32 ttl
, const mDNSu8
*limit
)
1476 mDNSu16 actualLength
;
1478 if (rr
->RecordType
== kDNSRecordTypeUnregistered
)
1480 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1484 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->name
);
1485 if (!ptr
|| ptr
+ 10 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1486 ptr
[0] = (mDNSu8
)(rr
->rrtype
>> 8);
1487 ptr
[1] = (mDNSu8
)(rr
->rrtype
& 0xFF);
1488 ptr
[2] = (mDNSu8
)(rr
->rrclass
>> 8);
1489 ptr
[3] = (mDNSu8
)(rr
->rrclass
& 0xFF);
1490 ptr
[4] = (mDNSu8
)((ttl
>> 24) & 0xFF);
1491 ptr
[5] = (mDNSu8
)((ttl
>> 16) & 0xFF);
1492 ptr
[6] = (mDNSu8
)((ttl
>> 8) & 0xFF);
1493 ptr
[7] = (mDNSu8
)( ttl
& 0xFF);
1494 endofrdata
= putRData(msg
, ptr
+10, limit
, rr
);
1495 if (!endofrdata
) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
)); return(mDNSNULL
); }
1497 // Go back and fill in the actual number of data bytes we wrote
1498 // (actualLength can be less than rdlength when domain name compression is used)
1499 actualLength
= (mDNSu16
)(endofrdata
- ptr
- 10);
1500 ptr
[8] = (mDNSu8
)(actualLength
>> 8);
1501 ptr
[9] = (mDNSu8
)(actualLength
& 0xFF);
1503 if (count
) (*count
)++;
1504 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr
->name
->c
, DNSTypeName(rr
->rrtype
));
1508 mDNSexport mDNSu8
*PutResourceRecordCappedTTL(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu16
*count
, ResourceRecord
*rr
, mDNSu32
1511 if (maxttl
> rr
->rroriginalttl
) maxttl
= rr
->rroriginalttl
;
1512 return(PutResourceRecordTTL(msg
, ptr
, count
, rr
, maxttl
));
1515 mDNSexport mDNSu8
*putEmptyResourceRecord(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
,
1516 mDNSu16
*count
, const AuthRecord
*rr
)
1518 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, rr
->resrec
.name
);
1519 if (!ptr
|| ptr
+ 10 > limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1520 ptr
[0] = (mDNSu8
)(rr
->resrec
.rrtype
>> 8); // Put type
1521 ptr
[1] = (mDNSu8
)(rr
->resrec
.rrtype
& 0xFF);
1522 ptr
[2] = (mDNSu8
)(rr
->resrec
.rrclass
>> 8); // Put class
1523 ptr
[3] = (mDNSu8
)(rr
->resrec
.rrclass
& 0xFF);
1524 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // TTL is zero
1525 ptr
[8] = ptr
[9] = 0; // RDATA length is zero
1530 mDNSexport mDNSu8
*putQuestion(DNSMessage
*const msg
, mDNSu8
*ptr
, const mDNSu8
*const limit
, const domainname
*const name
, mDNSu16 rrtype
, mDNSu16 rrclass
)
1532 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1533 if (!ptr
|| ptr
+4 >= limit
) return(mDNSNULL
); // If we're out-of-space, return mDNSNULL
1534 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1535 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1536 ptr
[2] = (mDNSu8
)(rrclass
>> 8);
1537 ptr
[3] = (mDNSu8
)(rrclass
& 0xFF);
1538 msg
->h
.numQuestions
++;
1542 // for dynamic updates
1543 mDNSexport mDNSu8
*putZone(DNSMessage
*const msg
, mDNSu8
*ptr
, mDNSu8
*limit
, const domainname
*zone
, mDNSOpaque16 zoneClass
)
1545 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, zone
);
1546 if (!ptr
|| ptr
+ 4 > limit
) return mDNSNULL
; // If we're out-of-space, return NULL
1547 *ptr
++ = (mDNSu8
)(kDNSType_SOA
>> 8);
1548 *ptr
++ = (mDNSu8
)(kDNSType_SOA
& 0xFF);
1549 *ptr
++ = zoneClass
.b
[0];
1550 *ptr
++ = zoneClass
.b
[1];
1551 msg
->h
.mDNS_numZones
++;
1555 // for dynamic updates
1556 mDNSexport mDNSu8
*putPrereqNameNotInUse(domainname
*name
, DNSMessage
*msg
, mDNSu8
*ptr
, mDNSu8
*end
)
1560 mDNSPlatformMemZero(&prereq
, sizeof(AuthRecord
));
1561 mDNS_SetupResourceRecord(&prereq
, mDNSNULL
, mDNSInterface_Any
, kDNSQType_ANY
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
1562 AssignDomainName(prereq
.resrec
.name
, name
);
1563 prereq
.resrec
.rrtype
= kDNSQType_ANY
;
1564 prereq
.resrec
.rrclass
= kDNSClass_NONE
;
1565 ptr
= putEmptyResourceRecord(msg
, ptr
, end
, &msg
->h
.mDNS_numPrereqs
, &prereq
);
1569 // for dynamic updates
1570 mDNSexport mDNSu8
*putDeletionRecord(DNSMessage
*msg
, mDNSu8
*ptr
, ResourceRecord
*rr
)
1573 // deletion: specify record w/ TTL 0, class NONE
1575 origclass
= rr
->rrclass
;
1576 rr
->rrclass
= kDNSClass_NONE
;
1577 ptr
= PutResourceRecordTTLJumbo(msg
, ptr
, &msg
->h
.mDNS_numUpdates
, rr
, 0);
1578 rr
->rrclass
= origclass
;
1582 mDNSexport mDNSu8
*putDeleteRRSet(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
, mDNSu16 rrtype
)
1584 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1585 mDNSu16
class = kDNSQClass_ANY
;
1587 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1588 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
1589 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1590 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1591 ptr
[2] = (mDNSu8
)(class >> 8);
1592 ptr
[3] = (mDNSu8
)(class & 0xFF);
1593 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
1594 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
1596 msg
->h
.mDNS_numUpdates
++;
1600 // for dynamic updates
1601 mDNSexport mDNSu8
*putDeleteAllRRSets(DNSMessage
*msg
, mDNSu8
*ptr
, const domainname
*name
)
1603 const mDNSu8
*limit
= msg
->data
+ AbsoluteMaxDNSMessageData
;
1604 mDNSu16
class = kDNSQClass_ANY
;
1605 mDNSu16 rrtype
= kDNSQType_ANY
;
1607 ptr
= putDomainNameAsLabels(msg
, ptr
, limit
, name
);
1608 if (!ptr
|| ptr
+ 10 >= limit
) return mDNSNULL
; // If we're out-of-space, return mDNSNULL
1609 ptr
[0] = (mDNSu8
)(rrtype
>> 8);
1610 ptr
[1] = (mDNSu8
)(rrtype
& 0xFF);
1611 ptr
[2] = (mDNSu8
)(class >> 8);
1612 ptr
[3] = (mDNSu8
)(class & 0xFF);
1613 ptr
[4] = ptr
[5] = ptr
[6] = ptr
[7] = 0; // zero ttl
1614 ptr
[8] = ptr
[9] = 0; // zero rdlength/rdata
1616 msg
->h
.mDNS_numUpdates
++;
1620 // for dynamic updates
1621 mDNSexport mDNSu8
*putUpdateLease(DNSMessage
*msg
, mDNSu8
*end
, mDNSu32 lease
)
1624 ResourceRecord
*opt
= &rr
.resrec
;
1627 mDNSPlatformMemZero(&rr
, sizeof(AuthRecord
));
1628 mDNS_SetupResourceRecord(&rr
, mDNSNULL
, mDNSInterface_Any
, kDNSType_OPT
, kStandardTTL
, 0, mDNSNULL
, mDNSNULL
);
1630 opt
->RecordType
= kDNSRecordTypeKnownUnique
; // to avoid warnings in other layers
1631 opt
->rrtype
= kDNSType_OPT
;
1632 opt
->rdlength
= LEASE_OPT_SIZE
;
1633 opt
->rdestimate
= LEASE_OPT_SIZE
;
1635 optRD
= &rr
.resrec
.rdata
->u
.opt
;
1636 optRD
->opt
= kDNSOpt_Lease
;
1637 optRD
->optlen
= sizeof(mDNSs32
);
1638 optRD
->OptData
.lease
= lease
;
1639 end
= PutResourceRecordTTLJumbo(msg
, end
, &msg
->h
.numAdditionals
, opt
, 0);
1640 if (!end
) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL
; }
1645 // ***************************************************************************
1646 #if COMPILER_LIKES_PRAGMA_MARK
1648 #pragma mark - DNS Message Parsing Functions
1651 mDNSexport mDNSu32
DomainNameHashValue(const domainname
*const name
)
1656 for (c
= name
->c
; c
[0] != 0 && c
[1] != 0; c
+= 2)
1658 sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8) |
1659 (mDNSIsUpperCase(c
[1]) ? c
[1] + 'a' - 'A' : c
[1]);
1660 sum
= (sum
<<3) | (sum
>>29);
1662 if (c
[0]) sum
+= ((mDNSIsUpperCase(c
[0]) ? c
[0] + 'a' - 'A' : c
[0]) << 8);
1666 mDNSexport
void SetNewRData(ResourceRecord
*const rr
, RData
*NewRData
, mDNSu16 rdlength
)
1671 rr
->rdata
= NewRData
;
1672 rr
->rdlength
= rdlength
;
1674 // Must not try to get target pointer until after updating rr->rdata
1675 target
= GetRRDomainNameTarget(rr
);
1676 rr
->rdlength
= GetRDLength(rr
, mDNSfalse
);
1677 rr
->rdestimate
= GetRDLength(rr
, mDNStrue
);
1678 rr
->rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
->rdlength
, &rr
->rdata
->u
);
1681 mDNSexport
const mDNSu8
*skipDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
)
1685 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1686 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1688 while (1) // Read sequence of labels
1690 const mDNSu8 len
= *ptr
++; // Read length of this label
1691 if (len
== 0) return(ptr
); // If length is zero, that means this name is complete
1694 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1695 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1696 if (total
+ 1 + len
>= MAX_DOMAIN_NAME
) // Remember: expect at least one more byte for the root label
1697 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1702 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len
); return(mDNSNULL
);
1703 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len
); return(mDNSNULL
);
1704 case 0xC0: return(ptr
+1);
1709 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
1710 mDNSexport
const mDNSu8
*getDomainName(const DNSMessage
*const msg
, const mDNSu8
*ptr
, const mDNSu8
*const end
,
1711 domainname
*const name
)
1713 const mDNSu8
*nextbyte
= mDNSNULL
; // Record where we got to before we started following pointers
1714 mDNSu8
*np
= name
->c
; // Name pointer
1715 const mDNSu8
*const limit
= np
+ MAX_DOMAIN_NAME
; // Limit so we don't overrun buffer
1717 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1718 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL
); }
1720 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1722 while (1) // Read sequence of labels
1724 const mDNSu8 len
= *ptr
++; // Read length of this label
1725 if (len
== 0) break; // If length is zero, that means this name is complete
1731 case 0x00: if (ptr
+ len
>= end
) // Remember: expect at least one more byte for the root label
1732 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL
); }
1733 if (np
+ 1 + len
>= limit
) // Remember: expect at least one more byte for the root label
1734 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL
); }
1736 for (i
=0; i
<len
; i
++) *np
++ = *ptr
++;
1737 *np
= 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1740 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len
, name
->c
);
1743 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len
, name
->c
); return(mDNSNULL
);
1745 case 0xC0: offset
= (mDNSu16
)((((mDNSu16
)(len
& 0x3F)) << 8) | *ptr
++);
1746 if (!nextbyte
) nextbyte
= ptr
; // Record where we got to before we started following pointers
1747 ptr
= (mDNSu8
*)msg
+ offset
;
1748 if (ptr
< (mDNSu8
*)msg
|| ptr
>= end
)
1749 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL
); }
1751 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL
); }
1756 if (nextbyte
) return(nextbyte
);
1760 mDNSexport
const mDNSu8
*skipResourceRecord(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1762 mDNSu16 pktrdlength
;
1764 ptr
= skipDomainName(msg
, ptr
, end
);
1765 if (!ptr
) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1767 if (ptr
+ 10 > end
) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1768 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1770 if (ptr
+ pktrdlength
> end
) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1772 return(ptr
+ pktrdlength
);
1775 mDNSexport
const mDNSu8
*GetLargeResourceRecord(mDNS
*const m
, const DNSMessage
* const msg
, const mDNSu8
*ptr
,
1776 const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
, mDNSu8 RecordType
, LargeCacheRecord
*largecr
)
1778 CacheRecord
*rr
= &largecr
->r
;
1779 mDNSu16 pktrdlength
;
1781 if (largecr
== &m
->rec
&& rr
->resrec
.RecordType
)
1782 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use");
1784 rr
->next
= mDNSNULL
;
1785 rr
->resrec
.RecordType
= RecordType
;
1786 rr
->resrec
.name
= &largecr
->namestorage
;
1788 rr
->NextInKAList
= mDNSNULL
;
1789 rr
->TimeRcvd
= m
? m
->timenow
: 0;
1790 rr
->DelayDelivery
= 0;
1791 rr
->NextRequiredQuery
= m
? m
->timenow
: 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
1792 rr
->LastUsed
= m
? m
->timenow
: 0;
1793 rr
->CRActiveQuestion
= mDNSNULL
;
1794 rr
->UnansweredQueries
= 0;
1795 rr
->LastUnansweredTime
= 0;
1796 rr
->MPUnansweredQ
= 0;
1797 rr
->MPLastUnansweredQT
= 0;
1798 rr
->MPUnansweredKA
= 0;
1799 rr
->MPExpectingKA
= mDNSfalse
;
1800 rr
->NextInCFList
= mDNSNULL
;
1802 rr
->resrec
.InterfaceID
= InterfaceID
;
1803 ptr
= getDomainName(msg
, ptr
, end
, rr
->resrec
.name
);
1804 if (!ptr
) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL
); }
1806 if (ptr
+ 10 > end
) { debugf("GetResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL
); }
1808 rr
->resrec
.rrtype
= (mDNSu16
) ((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1809 rr
->resrec
.rrclass
= (mDNSu16
)(((mDNSu16
)ptr
[2] << 8 | ptr
[3]) & kDNSClass_Mask
);
1810 rr
->resrec
.rroriginalttl
= (mDNSu32
) ((mDNSu32
)ptr
[4] << 24 | (mDNSu32
)ptr
[5] << 16 | (mDNSu32
)ptr
[6] << 8 | ptr
[7]);
1811 if (rr
->resrec
.rroriginalttl
> 0x70000000UL
/ mDNSPlatformOneSecond
&& (mDNSs32
)rr
->resrec
.rroriginalttl
!= -1)
1812 rr
->resrec
.rroriginalttl
= 0x70000000UL
/ mDNSPlatformOneSecond
;
1813 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
1814 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
1815 pktrdlength
= (mDNSu16
)((mDNSu16
)ptr
[8] << 8 | ptr
[9]);
1816 if (ptr
[2] & (kDNSClass_UniqueRRSet
>> 8))
1817 rr
->resrec
.RecordType
|= kDNSRecordTypePacketUniqueMask
;
1819 if (ptr
+ pktrdlength
> end
) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL
); }
1820 end
= ptr
+ pktrdlength
; // Adjust end to indicate the end of the rdata for this resource record
1822 rr
->resrec
.rdata
= (RData
*)&rr
->rdatastorage
;
1823 rr
->resrec
.rdata
->MaxRDLength
= MaximumRDSize
;
1825 if (!RecordType
) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr
->resrec
.name
->c
);
1827 switch (rr
->resrec
.rrtype
)
1829 case kDNSType_A
: rr
->resrec
.rdata
->u
.ipv4
.b
[0] = ptr
[0];
1830 rr
->resrec
.rdata
->u
.ipv4
.b
[1] = ptr
[1];
1831 rr
->resrec
.rdata
->u
.ipv4
.b
[2] = ptr
[2];
1832 rr
->resrec
.rdata
->u
.ipv4
.b
[3] = ptr
[3];
1835 case kDNSType_CNAME
:// Same as PTR
1837 case kDNSType_PTR
: if (!getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.name
))
1838 { debugf("GetResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL
); }
1839 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
1842 case kDNSType_NULL
: //Same as TXT
1843 case kDNSType_HINFO
://Same as TXT
1844 case kDNSType_TXT
: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
1846 debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)",
1847 DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
1850 rr
->resrec
.rdlength
= pktrdlength
;
1851 mDNSPlatformMemCopy(ptr
, rr
->resrec
.rdata
->u
.data
, pktrdlength
);
1854 case kDNSType_AAAA
: mDNSPlatformMemCopy(ptr
, &rr
->resrec
.rdata
->u
.ipv6
, sizeof(rr
->resrec
.rdata
->u
.ipv6
));
1857 case kDNSType_SRV
: rr
->resrec
.rdata
->u
.srv
.priority
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
1858 rr
->resrec
.rdata
->u
.srv
.weight
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
1859 rr
->resrec
.rdata
->u
.srv
.port
.b
[0] = ptr
[4];
1860 rr
->resrec
.rdata
->u
.srv
.port
.b
[1] = ptr
[5];
1861 if (!getDomainName(msg
, ptr
+6, end
, &rr
->resrec
.rdata
->u
.srv
.target
))
1862 { debugf("GetResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL
); }
1863 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
1866 case kDNSType_SOA
: ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.mname
);
1867 if (!ptr
) { debugf("GetResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL
; }
1868 ptr
= getDomainName(msg
, ptr
, end
, &rr
->resrec
.rdata
->u
.soa
.rname
);
1869 if (!ptr
) { debugf("GetResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL
; }
1870 if (ptr
+ 0x14 != end
) { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL
; }
1871 rr
->resrec
.rdata
->u
.soa
.serial
= (mDNSs32
) ((mDNSs32
)ptr
[0x00] << 24 | (mDNSs32
)ptr
[0x01] << 16 | (mDNSs32
)ptr
[0x02] << 8 | ptr
[0x03]);
1872 rr
->resrec
.rdata
->u
.soa
.refresh
= (mDNSu32
) ((mDNSu32
)ptr
[0x04] << 24 | (mDNSu32
)ptr
[0x05] << 16 | (mDNSu32
)ptr
[0x06] << 8 | ptr
[0x07]);
1873 rr
->resrec
.rdata
->u
.soa
.retry
= (mDNSu32
) ((mDNSu32
)ptr
[0x08] << 24 | (mDNSu32
)ptr
[0x09] << 16 | (mDNSu32
)ptr
[0x0A] << 8 | ptr
[0x0B]);
1874 rr
->resrec
.rdata
->u
.soa
.expire
= (mDNSu32
) ((mDNSu32
)ptr
[0x0C] << 24 | (mDNSu32
)ptr
[0x0D] << 16 | (mDNSu32
)ptr
[0x0E] << 8 | ptr
[0x0F]);
1875 rr
->resrec
.rdata
->u
.soa
.min
= (mDNSu32
) ((mDNSu32
)ptr
[0x10] << 24 | (mDNSu32
)ptr
[0x11] << 16 | (mDNSu32
)ptr
[0x12] << 8 | ptr
[0x13]);
1878 case kDNSType_OPT
: getOptRdata(ptr
, end
, &rr
->resrec
, pktrdlength
); break;
1880 default: if (pktrdlength
> rr
->resrec
.rdata
->MaxRDLength
)
1882 debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
1883 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
), pktrdlength
, rr
->resrec
.rdata
->MaxRDLength
);
1886 debugf("GetResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
1887 rr
->resrec
.rrtype
, DNSTypeName(rr
->resrec
.rrtype
));
1888 // Note: Just because we don't understand the record type, that doesn't
1889 // mean we fail. The DNS protocol specifies rdlength, so we can
1890 // safely skip over unknown records and ignore them.
1891 // We also grab a binary copy of the rdata anyway, since the caller
1892 // might know how to interpret it even if we don't.
1893 rr
->resrec
.rdlength
= pktrdlength
;
1894 mDNSPlatformMemCopy(ptr
, rr
->resrec
.rdata
->u
.data
, pktrdlength
);
1898 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
1899 SetNewRData(&rr
->resrec
, mDNSNULL
, 0);
1901 return(ptr
+ pktrdlength
);
1904 mDNSexport
const mDNSu8
*skipQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
)
1906 ptr
= skipDomainName(msg
, ptr
, end
);
1907 if (!ptr
) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL
); }
1908 if (ptr
+4 > end
) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1912 mDNSexport
const mDNSu8
*getQuestion(const DNSMessage
*msg
, const mDNSu8
*ptr
, const mDNSu8
*end
, const mDNSInterfaceID InterfaceID
,
1913 DNSQuestion
*question
)
1915 question
->InterfaceID
= InterfaceID
;
1916 ptr
= getDomainName(msg
, ptr
, end
, &question
->qname
);
1917 if (!ptr
) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL
); }
1918 if (ptr
+4 > end
) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL
); }
1920 question
->qnamehash
= DomainNameHashValue(&question
->qname
);
1921 question
->qtype
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]); // Get type
1922 question
->qclass
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]); // and class
1926 mDNSexport
const mDNSu8
*LocateAnswers(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1929 const mDNSu8
*ptr
= msg
->data
;
1930 for (i
= 0; i
< msg
->h
.numQuestions
&& ptr
; i
++) ptr
= skipQuestion(msg
, ptr
, end
);
1934 mDNSexport
const mDNSu8
*LocateAuthorities(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1937 const mDNSu8
*ptr
= LocateAnswers(msg
, end
);
1938 for (i
= 0; i
< msg
->h
.numAnswers
&& ptr
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
1942 mDNSexport
const mDNSu8
*LocateAdditionals(const DNSMessage
*const msg
, const mDNSu8
*const end
)
1945 const mDNSu8
*ptr
= LocateAuthorities(msg
, end
);
1946 for (i
= 0; i
< msg
->h
.numAuthorities
; i
++) ptr
= skipResourceRecord(msg
, ptr
, end
);
1950 // ***************************************************************************
1951 #if COMPILER_LIKES_PRAGMA_MARK
1954 #pragma mark - Packet Sending Functions
1957 mDNSexport mStatus
mDNSSendDNSMessage(const mDNS
*const m
, DNSMessage
*const msg
, mDNSu8
*end
,
1958 mDNSInterfaceID InterfaceID
, const mDNSAddr
*dst
, mDNSIPPort dstport
, int sd
, uDNS_AuthInfo
*authInfo
)
1964 mDNSu16 numQuestions
= msg
->h
.numQuestions
;
1965 mDNSu16 numAnswers
= msg
->h
.numAnswers
;
1966 mDNSu16 numAuthorities
= msg
->h
.numAuthorities
;
1967 mDNSu16 numAdditionals
= msg
->h
.numAdditionals
;
1968 mDNSu8
*ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
1970 // Put all the integer values in IETF byte-order (MSB first, LSB second)
1971 *ptr
++ = (mDNSu8
)(numQuestions
>> 8);
1972 *ptr
++ = (mDNSu8
)(numQuestions
& 0xFF);
1973 *ptr
++ = (mDNSu8
)(numAnswers
>> 8);
1974 *ptr
++ = (mDNSu8
)(numAnswers
& 0xFF);
1975 *ptr
++ = (mDNSu8
)(numAuthorities
>> 8);
1976 *ptr
++ = (mDNSu8
)(numAuthorities
& 0xFF);
1977 *ptr
++ = (mDNSu8
)(numAdditionals
>> 8);
1978 *ptr
++ = (mDNSu8
)(numAdditionals
& 0xFF);
1982 end
= DNSDigest_SignMessage(msg
, &end
, &numAdditionals
, authInfo
);
1983 if (!end
) return mStatus_UnknownErr
;
1986 // Send the packet on the wire
1990 msglen
= (mDNSu16
)(end
- (mDNSu8
*)msg
);
1991 lenbuf
[0] = (mDNSu8
)(msglen
>> 8); // host->network byte conversion
1992 lenbuf
[1] = (mDNSu8
)(msglen
& 0xFF);
1993 nsent
= mDNSPlatformWriteTCP(sd
, (char*)lenbuf
, 2);
1994 //!!!KRS make sure kernel is sending these as 1 packet!
1995 if (nsent
!= 2) goto tcp_error
;
1996 nsent
= mDNSPlatformWriteTCP(sd
, (char *)msg
, msglen
);
1997 if (nsent
!= msglen
) goto tcp_error
;
1998 status
= mStatus_NoError
;
2002 status
= mDNSPlatformSendUDP(m
, msg
, end
, InterfaceID
, dst
, dstport
);
2005 // Put all the integer values back the way they were before we return
2006 msg
->h
.numQuestions
= numQuestions
;
2007 msg
->h
.numAnswers
= numAnswers
;
2008 msg
->h
.numAuthorities
= numAuthorities
;
2009 msg
->h
.numAdditionals
= (mDNSu16
)(authInfo
? numAdditionals
- 1 : numAdditionals
);
2014 LogMsg("mDNSSendDNSMessage: error sending message over tcp");
2015 return mStatus_UnknownErr
;
2018 // ***************************************************************************
2019 #if COMPILER_LIKES_PRAGMA_MARK
2021 #pragma mark - RR List Management & Task Management
2024 mDNSexport
void mDNS_Lock(mDNS
*const m
)
2026 // MUST grab the platform lock FIRST!
2027 mDNSPlatformLock(m
);
2029 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2030 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2031 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2032 // If mDNS_busy != mDNS_reentrancy that's a bad sign
2033 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2034 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2036 // If this is an initial entry into the mDNSCore code, set m->timenow
2037 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
2038 if (m
->mDNS_busy
== 0)
2041 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m
->timenow
, mDNSPlatformRawTime() + m
->timenow_adjust
);
2042 m
->timenow
= mDNSPlatformRawTime() + m
->timenow_adjust
;
2043 if (m
->timenow
== 0) m
->timenow
= 1;
2045 else if (m
->timenow
== 0)
2047 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
2048 m
->timenow
= mDNSPlatformRawTime() + m
->timenow_adjust
;
2049 if (m
->timenow
== 0) m
->timenow
= 1;
2052 if (m
->timenow_last
- m
->timenow
> 0)
2054 m
->timenow_adjust
+= m
->timenow_last
- m
->timenow
;
2055 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m
->timenow_last
- m
->timenow
, m
->timenow_adjust
);
2056 m
->timenow
= m
->timenow_last
;
2058 m
->timenow_last
= m
->timenow
;
2060 // Increment mDNS_busy so we'll recognise re-entrant calls
2064 mDNSlocal mDNSs32
GetNextScheduledEvent(const mDNS
*const m
)
2066 mDNSs32 e
= m
->timenow
+ 0x78000000;
2067 if (m
->mDNSPlatformStatus
!= mStatus_NoError
|| m
->SleepState
) return(e
);
2068 if (m
->NewQuestions
)
2070 if (m
->NewQuestions
->DelayAnswering
) e
= m
->NewQuestions
->DelayAnswering
;
2071 else return(m
->timenow
);
2073 if (m
->NewLocalOnlyQuestions
) return(m
->timenow
);
2074 if (m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
)) return(m
->timenow
);
2075 if (m
->SuppressSending
) return(m
->SuppressSending
);
2076 #ifndef UNICAST_DISABLED
2077 if (e
- m
->uDNS_info
.nextevent
> 0) e
= m
->uDNS_info
.nextevent
;
2079 if (e
- m
->NextCacheCheck
> 0) e
= m
->NextCacheCheck
;
2080 if (e
- m
->NextScheduledQuery
> 0) e
= m
->NextScheduledQuery
;
2081 if (e
- m
->NextScheduledProbe
> 0) e
= m
->NextScheduledProbe
;
2082 if (e
- m
->NextScheduledResponse
> 0) e
= m
->NextScheduledResponse
;
2086 mDNSexport
void mDNS_Unlock(mDNS
*const m
)
2088 // Decrement mDNS_busy
2091 // Check for locking failures
2092 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
)
2093 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
2095 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
2096 if (m
->mDNS_busy
== 0)
2098 m
->NextScheduledEvent
= GetNextScheduledEvent(m
);
2099 if (m
->timenow
== 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
2103 // MUST release the platform lock LAST!
2104 mDNSPlatformUnlock(m
);