]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSCore/DNSCommon.c
mDNSResponder-107.tar.gz
[apple/mdnsresponder.git] / mDNSCore / DNSCommon.c
1 /*
2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22
23 Change History (most recent first):
24
25 $Log: DNSCommon.c,v $
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
28
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
31
32 Revision 1.85 2005/02/10 22:35:17 cheshire
33 <rdar://problem/3727944> Update name
34
35 Revision 1.84 2005/02/03 00:44:38 cheshire
36 <rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
37
38 Revision 1.83 2005/01/27 22:57:55 cheshire
39 Fix compile errors on gcc4
40
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()
44
45 Revision 1.81 2004/12/18 03:13:45 cheshire
46 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
47
48 Revision 1.80 2004/12/16 21:46:43 cheshire
49 Add DNSTypeName case for kDNSType_SOA
50
51 Revision 1.79 2004/12/16 21:38:37 cheshire
52 Add DNSTypeName case for kDNSType_NS
53
54 Revision 1.78 2004/12/16 21:27:37 ksekar
55 Fixed build failures when compiled with verbose debugging messages
56
57 Revision 1.77 2004/12/16 20:12:59 cheshire
58 <rdar://problem/3324626> Cache memory management improvements
59
60 Revision 1.76 2004/12/16 08:05:29 shersche
61 Remove extranenous semicolons that cause compilation errors on Windows
62
63 Revision 1.75 2004/12/15 02:11:22 ksekar
64 <rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness
65
66 Revision 1.74 2004/12/09 22:49:15 ksekar
67 <rdar://problem/3913653> Wide-Area Goodbyes broken
68
69 Revision 1.73 2004/12/07 22:49:06 cheshire
70 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
71
72 Revision 1.72 2004/12/06 21:15:20 ksekar
73 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
74
75 Revision 1.71 2004/12/04 02:12:45 cheshire
76 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
77
78 Revision 1.70 2004/12/03 19:52:44 ksekar
79 Use PutResourceRecordTTLJumbo for putDeletionRecord()
80
81 Revision 1.69 2004/12/03 07:20:50 ksekar
82 <rdar://problem/3674208> Wide-Area: Registration of large TXT record fails
83
84 Revision 1.68 2004/11/24 00:10:43 cheshire
85 <rdar://problem/3869241> For unicast operations, verify that service types are legal
86
87 Revision 1.67 2004/10/26 03:52:02 cheshire
88 Update checkin comments
89
90 Revision 1.66 2004/10/23 01:16:00 cheshire
91 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
92
93 Revision 1.65 2004/10/20 02:15:09 cheshire
94 Add case in GetRRDisplayString() to display NS rdata
95
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
98
99 Revision 1.63 2004/10/10 06:57:14 cheshire
100 Change definition of "localdomain" to make code compile a little smaller
101
102 Revision 1.62 2004/10/06 01:44:19 cheshire
103 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
104
105 Revision 1.61 2004/09/30 00:24:56 ksekar
106 <rdar://problem/3695802> Dynamically update default registration domains on config change
107
108 Revision 1.60 2004/09/27 23:25:30 cheshire
109 Fix compiler warning: soa.serial is signed, not unsigned
110
111 Revision 1.59 2004/09/27 22:53:45 ksekar
112 Fixed getLargeResourceRecord for SOA rdata.
113
114 Revision 1.58 2004/09/25 02:41:39 cheshire
115 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
116
117 Revision 1.57 2004/09/25 02:24:27 cheshire
118 Removed unused rr->UseCount
119
120 Revision 1.56 2004/09/24 20:57:39 cheshire
121 <rdar://problem/3680902> Eliminate inappropriate casts that cause misaligned-address errors
122
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.
128
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
132
133 Revision 1.53 2004/09/17 00:31:51 cheshire
134 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
135
136 Revision 1.52 2004/09/17 00:19:10 cheshire
137 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
138
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
142
143 Revision 1.50 2004/09/16 01:58:14 cheshire
144 Fix compiler warnings
145
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
148
149 Revision 1.48 2004/09/14 23:27:46 cheshire
150 Fix compile errors
151
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
155
156 Revision 1.46 2004/08/18 17:35:40 ksekar
157 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
158
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.)
162
163 Revision 1.44 2004/08/13 23:46:58 cheshire
164 "asyncronous" -> "asynchronous"
165
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().
169
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
173
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
177
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.
181
182 Revision 1.39 2004/07/13 21:24:24 rpantos
183 Fix for <rdar://problem/3701120>.
184
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
188
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".
191
192 Revision 1.36 2004/06/18 19:09:59 cheshire
193 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
194
195 Revision 1.35 2004/06/05 00:14:44 cheshire
196 Fix signed/unsigned and other compiler warnings
197
198 Revision 1.34 2004/06/04 00:25:25 cheshire
199 Fix misaligned write exception that occurs on some platforms
200
201 Revision 1.33 2004/06/04 00:16:18 cheshire
202 Remove non-portable use of 'inline'
203
204 Revision 1.32 2004/06/03 03:09:58 ksekar
205 <rdar://problem/3668626>: Garbage Collection for Dynamic Updates
206
207 Revision 1.31 2004/05/28 23:42:36 ksekar
208 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
209
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.
212
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
215
216 Revision 1.28 2004/05/13 04:54:20 ksekar
217 Unified list copy/free code. Added symetric list for
218
219 Revision 1.27 2004/04/22 20:29:07 cheshire
220 Log error message if no count field passed to PutResourceRecordTTL()
221
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
224
225 Revision 1.25 2004/04/22 03:05:28 cheshire
226 kDNSClass_ANY should be kDNSQClass_ANY
227
228 Revision 1.24 2004/04/22 02:51:20 cheshire
229 Use common code for HINFO/TXT and TSIG cases in putRData
230
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.
234
235 Revision 1.22 2004/04/14 23:09:28 ksekar
236 Support for TSIG signed dynamic updates.
237
238 Revision 1.21 2004/04/09 16:47:28 cheshire
239 <rdar://problem/3617655>: mDNSResponder escape handling inconsistent with BIND
240
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
244
245 Revision 1.19 2004/04/02 19:34:38 cheshire
246 Fix broken comment
247
248 Revision 1.18 2004/03/30 06:45:00 cheshire
249 Compiler warning fixes from Don Woodward at Roku Labs
250
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
254
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
257
258 Revision 1.15 2004/03/08 02:44:09 cheshire
259 <rdar://problem/3579561>: Need to limit service types to fourteen characters
260
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
263
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)
267
268 Revision 1.12 2004/02/03 22:37:10 cheshire
269 Delete unused (commented-out) code
270
271 Revision 1.11 2004/02/03 22:35:34 cheshire
272 <rdar://problem/3548256>: Should not allow empty string for resolve domain
273
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.
279
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
282
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
285
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.
290
291 Revision 1.6 2004/01/24 04:59:15 cheshire
292 Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
293
294 Revision 1.5 2004/01/23 23:23:14 ksekar
295 Added TCP support for truncated unicast messages.
296
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
299
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.
302
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.
306
307 Revision 1.1 2003/12/13 03:05:27 ksekar
308 <rdar://problem/3192548>: DynDNS: Unicast query of service records
309
310 */
311
312 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
313 #define mDNS_InstantiateInlines 1
314 #include "DNSCommon.h"
315
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)
325 #endif
326
327 // ***************************************************************************
328 #if COMPILER_LIKES_PRAGMA_MARK
329 #pragma mark -
330 #pragma mark - DNameList copy/deallocation routines
331 #endif
332
333 mDNSexport DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig)
334 {
335 DNameListElem *copy = mDNSNULL, *newelem;
336 const DNameListElem *ptr;
337
338 for (ptr = orig; ptr; ptr = ptr->next)
339 {
340 newelem = (DNameListElem*)mDNSPlatformMemAllocate(sizeof(DNameListElem));
341 if (!newelem) { LogMsg("ERROR: malloc"); return mDNSNULL; }
342 AssignDomainName(&newelem->name, &ptr->name);
343 newelem->next = copy;
344 copy = newelem;
345 }
346 return copy;
347 }
348
349 mDNSexport void mDNS_FreeDNameList(DNameListElem *list)
350 {
351 DNameListElem *fptr;
352
353 while (list)
354 {
355 fptr = list;
356 list = list->next;
357 mDNSPlatformMemFree(fptr);
358 }
359 }
360
361 // ***************************************************************************
362 #if COMPILER_LIKES_PRAGMA_MARK
363 #pragma mark -
364 #pragma mark - General Utility Functions
365 #endif
366
367 // return true for RFC1918 private addresses
368 mDNSexport mDNSBool IsPrivateV4Addr(mDNSAddr *addr)
369 {
370 mDNSu8 *b;
371
372 if (addr->type != mDNSAddrType_IPv4) return mDNSfalse;
373 b = addr->ip.v4.b;
374
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
378 }
379
380 mDNSexport const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf)
381 {
382 while (intf && !intf->InterfaceActive) intf = intf->next;
383 return(intf);
384 }
385
386 mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
387 {
388 const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
389 if (next) return(next->InterfaceID); else return(mDNSNULL);
390 }
391
392 mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id)
393 {
394 mDNSu32 slot, used = 0;
395 CacheGroup *cg;
396 CacheRecord *rr;
397 FORALL_CACHERECORDS(slot, cg, rr)
398 if (rr->resrec.InterfaceID == id) used++;
399 return(used);
400 }
401
402 mDNSexport char *DNSTypeName(mDNSu16 rrtype)
403 {
404 switch (rrtype)
405 {
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");
417 default: {
418 static char buffer[16];
419 mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype);
420 return(buffer);
421 }
422 }
423 }
424
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)
429 {
430 char *ptr = buffer;
431 mDNSu32 length = mDNS_snprintf(buffer, 79, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype));
432 switch (rr->rrtype)
433 {
434 case kDNSType_A: mDNS_snprintf(buffer+length, 79-length, "%.4a", &rd->ipv4); break;
435
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;
439
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;
442
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;
446 }
447 for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.';
448 return(buffer);
449 }
450
451 mDNSexport mDNSu32 mDNSRandom(mDNSu32 max)
452 {
453 static mDNSu32 seed = 0;
454 mDNSu32 mask = 1;
455
456 if (!seed)
457 {
458 int i;
459 seed = mDNSPlatformRandomSeed(); // Pick an initial seed
460 for (i=0; i<100; i++) seed = seed * 21 + 1; // And mix it up a bit
461 }
462 while (mask < max) mask = (mask << 1) | 1;
463 do seed = seed * 21 + 1; while ((seed & mask) > max);
464 return (seed & mask);
465 }
466
467 mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
468 {
469 if (ip1->type == ip2->type)
470 {
471 switch (ip1->type)
472 {
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));
476 }
477 }
478 return(mDNSfalse);
479 }
480
481 mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
482 {
483 switch(ip->type)
484 {
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);
491 }
492 }
493
494 // ***************************************************************************
495 #if COMPILER_LIKES_PRAGMA_MARK
496 #pragma mark -
497 #pragma mark - Domain Name Utility Functions
498 #endif
499
500 mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
501 {
502 int i;
503 const int len = *a++;
504
505 if (len > MAX_DOMAIN_LABEL)
506 { debugf("Malformed label (too long)"); return(mDNSfalse); }
507
508 if (len != *b++) return(mDNSfalse);
509 for (i=0; i<len; i++)
510 {
511 mDNSu8 ac = *a++;
512 mDNSu8 bc = *b++;
513 if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
514 if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
515 if (ac != bc) return(mDNSfalse);
516 }
517 return(mDNStrue);
518 }
519
520 mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
521 {
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
525
526 while (*a || *b)
527 {
528 if (a + 1 + *a >= max)
529 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse); }
530 if (!SameDomainLabel(a, b)) return(mDNSfalse);
531 a += 1 + *a;
532 b += 1 + *b;
533 }
534
535 return(mDNStrue);
536 }
537
538 mDNSexport mDNSBool IsLocalDomain(const domainname *d)
539 {
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";
545
546 const domainname *d1, *d2, *d3, *d4, *d5, *d6; // Top-level domain, second-level domain, etc.
547 d1 = d2 = d3 = d4 = d5 = d6 = mDNSNULL;
548 while (d->c[0])
549 {
550 d6 = d5; d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
551 d = (domainname*)(d->c + 1 + d->c[0]);
552 }
553
554 if (d1 && SameDomainName(d1, n0)) return(mDNStrue);
555 if (d4 && SameDomainName(d4, n1)) return(mDNStrue);
556 if (d6 && SameDomainName(d6, n2)) return(mDNStrue);
557 return(mDNSfalse);
558 }
559
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)
566 {
567 const mDNSu8 *src = name->c;
568 while (*src)
569 {
570 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
571 src += 1 + *src;
572 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
573 }
574 return((mDNSu16)(src - name->c + 1));
575 }
576
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)
587 {
588 const mDNSu8 *src = name->c;
589 if (parent && parent->c[0] == 0) parent = mDNSNULL;
590 while (*src)
591 {
592 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
593 if (parent && SameDomainName((domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
594 src += 1 + *src;
595 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
596 }
597 return((mDNSu16)(src - name->c + 1));
598 }
599
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)
608 {
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
614
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
620 }
621
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)
630 {
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...
635 {
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...
639 {
640 mDNSu8 c = (mDNSu8)*cstr++; // Read the character
641 if (c == '\\') // If escape character, check next character
642 {
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
651 }
652 }
653 *ptr++ = c; // Write the character
654 }
655 if (*cstr) cstr++; // Skip over the trailing dot (if present)
656 if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort
657 return(mDNSNULL);
658 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
659 }
660
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
664 }
665
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)
672 {
673 int i;
674 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
675
676 // Check label is legal
677 if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
678
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);
681
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
684 return(ptr);
685 }
686
687 mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
688 {
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;
692 while(src[0])
693 {
694 int i;
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
698 src += i;
699 }
700 return(ptr);
701 }
702
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)
710 {
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
716 }
717
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)
726 {
727 name->c[0] = 0; // Make an empty domain name
728 return(AppendDNSNameString(name, cstr)); // And then add this string to it
729 }
730
731 mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
732 {
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
738 {
739 mDNSu8 c = *src++;
740 if (esc)
741 {
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
746 *ptr++ = esc;
747 *ptr++ = (char) ('0' + (c / 100) );
748 *ptr++ = (char) ('0' + (c / 10) % 10);
749 c = (mDNSu8)('0' + (c ) % 10);
750 }
751 }
752 *ptr++ = (char)c; // Copy the character
753 }
754 *ptr = 0; // Null-terminate the string
755 return(ptr); // and return
756 }
757
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)
760 {
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
763
764 if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot
765
766 while (*src) // While more characters in the domain name
767 {
768 if (src + 1 + *src >= max) return(mDNSNULL);
769 ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
770 if (!ptr) return(mDNSNULL);
771 src += 1 + *src;
772 *ptr++ = '.'; // Write the dot after the label
773 }
774
775 *ptr++ = 0; // Null-terminate the string
776 return(ptr); // and return
777 }
778
779 // RFC 1034 rules:
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
783
784 mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
785 {
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;
790 while (src < end)
791 {
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
796 if (ptr < lim)
797 {
798 if (mdnsValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
799 else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
800 }
801 src++;
802 }
803 while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks
804 hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
805 }
806
807 mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
808 const domainlabel *name, const domainname *type, const domainname *const domain)
809 {
810 int i, len;
811 mDNSu8 *dst = fqdn->c;
812 const mDNSu8 *src;
813 const char *errormsg;
814
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"
817 if (!name && type)
818 {
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)
821 {
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)
824 {
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
827 {
828 static const mDNSu8 SubTypeLabel[5] = "\x04_sub";
829 src = s0; // Copy the first label
830 len = *src;
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;
834
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);
840 }
841 }
842 }
843 }
844
845 if (name && name->c[0])
846 {
847 src = name->c; // Put the service name into the domain name
848 len = *src;
849 if (len >= 0x40) { errormsg="Service instance name too long"; goto fail; }
850 for (i=0; i<=len; i++) *dst++ = *src++;
851 }
852 else
853 name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
854
855 src = type->c; // Put the service type into the domain name
856 len = *src;
857 if (len < 2 || len >= 0x40 || (len > 15 && !SameDomainName(domain, (domainname*)"\x05" "local")))
858 {
859 errormsg="Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
860 goto fail;
861 }
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++;
867
868 len = *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++;
874
875 if (*src) { errormsg="Service type must have only two labels"; goto fail; }
876
877 *dst = 0;
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; }
883 return(dst);
884
885 fail:
886 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
887 return(mDNSNULL);
888 }
889
890 mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
891 domainlabel *const name, domainname *const type, domainname *const domain)
892 {
893 int i, len;
894 const mDNSu8 *src = fqdn->c;
895 const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
896 mDNSu8 *dst;
897
898 dst = name->c; // Extract the service name from the domain name
899 len = *src;
900 if (len >= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse); }
901 for (i=0; i<=len; i++) *dst++ = *src++;
902
903 dst = type->c; // Extract the service type from the domain name
904 len = *src;
905 if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); }
906 for (i=0; i<=len; i++) *dst++ = *src++;
907
908 len = *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
912
913 dst = domain->c; // Extract the service domain from the domain name
914 while (*src)
915 {
916 len = *src;
917 if (len >= 0x40)
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++;
922 }
923 *dst++ = 0; // Put the null root label on the end
924
925 return(mDNStrue);
926 }
927
928 // Notes on UTF-8:
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)
936 //
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).
942
943 mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
944 {
945 if (length > max)
946 {
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
950 while (length > 0)
951 {
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;
958 c2 = c1;
959 c1 = string[--length];
960 }
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--;
963 }
964 return(length);
965 }
966
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)
970 {
971 mDNSu16 l = name->c[0];
972
973 if (RichText)
974 {
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
978 l--;
979 while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
980 return (name->c[l] == '(' && name->c[l - 1] == ' ');
981 }
982 else
983 {
984 if (l < 2) return mDNSfalse; // Need at least "-2"
985 if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit
986 l--;
987 while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
988 return (name->c[l] == '-');
989 }
990 }
991
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)
996 {
997 mDNSu32 val = 0, multiplier = 1;
998
999 // Chop closing parentheses from RichText suffix
1000 if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
1001
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]--; }
1005
1006 // Chop opening parentheses or dash from suffix
1007 if (RichText)
1008 {
1009 if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
1010 }
1011 else
1012 {
1013 if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
1014 }
1015
1016 return(val);
1017 }
1018
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)
1022 {
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)")
1025
1026 // Truncate trailing spaces from RichText names
1027 if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
1028
1029 while (val >= divisor * 10) { divisor *= 10; chars++; }
1030
1031 name->c[0] = TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
1032
1033 if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
1034 else { name->c[++name->c[0]] = '-'; }
1035
1036 while (divisor)
1037 {
1038 name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
1039 val %= divisor;
1040 divisor /= 10;
1041 }
1042
1043 if (RichText) name->c[++name->c[0]] = ')';
1044 }
1045
1046 mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
1047 {
1048 mDNSu32 val = 0;
1049
1050 if (LabelContainsSuffix(name, RichText))
1051 val = RemoveLabelSuffix(name, RichText);
1052
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);
1060
1061 AppendLabelSuffix(name, val, RichText);
1062 }
1063
1064 // ***************************************************************************
1065 #if COMPILER_LIKES_PRAGMA_MARK
1066 #pragma mark -
1067 #pragma mark - Resource Record Utility Functions
1068 #endif
1069
1070 mDNSexport mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb)
1071 {
1072 mDNSu32 sum = 0;
1073 int i;
1074 for (i=0; i+1 < rdlength; i+=2)
1075 {
1076 sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1];
1077 sum = (sum<<3) | (sum>>29);
1078 }
1079 if (i < rdlength)
1080 {
1081 sum += ((mDNSu32)(rdb->data[i])) << 8;
1082 }
1083 return(sum);
1084 }
1085
1086 mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2)
1087 {
1088 if (r1->rrtype != r2->rrtype) return(mDNSfalse);
1089 if (r1->rdlength != r2->rdlength) return(mDNSfalse);
1090 if (r1->rdatahash != r2->rdatahash) return(mDNSfalse);
1091 switch(r1->rrtype)
1092 {
1093 case kDNSType_CNAME:// Same as PTR
1094 case kDNSType_PTR: return(SameDomainName(&r1->rdata->u.name, &r2->rdata->u.name));
1095
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) );
1100
1101 default: return(mDNSPlatformMemSame(r1->rdata->u.data, r2->rdata->u.data, r1->rdlength));
1102 }
1103 }
1104
1105 mDNSexport mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2)
1106 {
1107 return (r1->namehash == r2->namehash &&
1108 r1->rrtype == r2->rrtype &&
1109 SameDomainName(r1->name, r2->name) &&
1110 SameRData(r1, r2));
1111 }
1112
1113 mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1114 {
1115 if (rr->InterfaceID &&
1116 q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1117 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1118
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));
1123 }
1124
1125 mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
1126 {
1127 const RDataBody *rd = &rr->rdata->u;
1128 const domainname *const name = estimate ? rr->name : mDNSNULL;
1129 switch (rr->rrtype)
1130 {
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);
1146 }
1147 }
1148
1149 mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
1150 {
1151 mDNSu16 len;
1152
1153 switch(rrtype)
1154 {
1155 case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr));
1156
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);
1170
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)
1174 {
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);
1179 }
1180
1181 case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
1182
1183 case kDNSType_MX: if (!rdlength) return(mDNSfalse);
1184 len = DomainNameLength(&rd->u.mx.exchange);
1185 return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
1186
1187 case kDNSType_SRV: if (!rdlength) return(mDNSfalse);
1188 len = DomainNameLength(&rd->u.srv.target);
1189 return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
1190
1191 default: return(mDNStrue); // Allow all other types without checking
1192 }
1193 }
1194
1195 // ***************************************************************************
1196 #if COMPILER_LIKES_PRAGMA_MARK
1197 #pragma mark -
1198 #pragma mark -
1199 #pragma mark - DNS Message Creation Functions
1200 #endif
1201
1202 mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
1203 {
1204 h->id = id;
1205 h->flags = flags;
1206 h->numQuestions = 0;
1207 h->numAnswers = 0;
1208 h->numAuthorities = 0;
1209 h->numAdditionals = 0;
1210 }
1211
1212 mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
1213 {
1214 const mDNSu8 *result = end - *domname - 1;
1215
1216 if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label
1217
1218 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1219 while (result >= base)
1220 {
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])
1224 {
1225 const mDNSu8 *name = domname;
1226 const mDNSu8 *targ = result;
1227 while (targ + *name < end)
1228 {
1229 // First see if this label matches
1230 int i;
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
1238
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;
1247 }
1248 }
1249 result--; // We failed to match at this search position, so back up the tentative result pointer and try again
1250 }
1251 return(mDNSNULL);
1252 }
1253
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)
1263 {
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;
1269
1270 while (*np && ptr < limit-1) // While we've got characters in the name, and space to write them in the message...
1271 {
1272 if (*np > MAX_DOMAIN_LABEL)
1273 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
1274
1275 // This check correctly allows for the final trailing root label:
1276 // e.g.
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); }
1286
1287 if (base) pointer = FindCompressionPointer(base, searchlimit, np);
1288 if (pointer) // Use a compression pointer if we can
1289 {
1290 mDNSu16 offset = (mDNSu16)(pointer - base);
1291 *ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
1292 *ptr++ = (mDNSu8)( offset & 0xFF);
1293 return(ptr);
1294 }
1295 else // Else copy one label and try again
1296 {
1297 int i;
1298 mDNSu8 len = *np++;
1299 if (ptr + 1 + len >= limit) return(mDNSNULL);
1300 *ptr++ = len;
1301 for (i=0; i<len; i++) *ptr++ = *np++;
1302 }
1303 }
1304
1305 if (ptr < limit) // If we didn't run out of space
1306 {
1307 *ptr++ = 0; // Put the final root label
1308 return(ptr); // and return
1309 }
1310
1311 return(mDNSNULL);
1312 }
1313
1314 mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
1315 {
1316 ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
1317 ptr[1] = (mDNSu8)((val ) & 0xFF);
1318 return ptr + sizeof(mDNSOpaque16);
1319 }
1320
1321 mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
1322 {
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);
1328 }
1329
1330 mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr)
1331 {
1332 int nput = 0;
1333 rdataOpt *opt;
1334
1335 while (nput < rr->rdlength)
1336 {
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)
1344 {
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
1350 ptr += 8;
1351 ptr = putVal32(ptr, opt->OptData.llq.lease);
1352 nput += sizeof(LLQOptData);
1353 }
1354 else if (opt->opt == kDNSOpt_Lease)
1355 {
1356 if (ptr + sizeof(mDNSs32) > limit) goto space_err;
1357 ptr = putVal32(ptr, opt->OptData.lease);
1358 nput += sizeof(mDNSs32);
1359 }
1360 else { LogMsg("putOptRData - unknown option %d", opt->opt); return mDNSNULL; }
1361 }
1362
1363 return ptr;
1364
1365 space_err:
1366 LogMsg("ERROR: putOptRData - out of space");
1367 return mDNSNULL;
1368 }
1369
1370 mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr)
1371 {
1372 mDNSu16 val = (mDNSu16)(((mDNSu16)(*ptr)[0]) << 8 | (*ptr)[1]);
1373 *ptr += sizeof(mDNSOpaque16);
1374 return val;
1375 }
1376
1377 mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr, mDNSu16 pktRDLen)
1378 {
1379 int nread = 0;
1380 rdataOpt *opt;
1381
1382 while (nread < pktRDLen)
1383 {
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)
1391 {
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);
1397 ptr += 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);
1403 }
1404 else if (opt->opt == kDNSOpt_Lease)
1405 {
1406 if ((unsigned)(limit - ptr) < sizeof(mDNSs32)) goto space_err;
1407
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);
1413 }
1414 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt->opt); return mDNSNULL; }
1415 }
1416
1417 rr->rdlength = pktRDLen;
1418 return ptr;
1419
1420 space_err:
1421 LogMsg("ERROR: getLLQRdata - out of space");
1422 return mDNSNULL;
1423 }
1424
1425 mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr)
1426 {
1427 switch (rr->rrtype)
1428 {
1429 case kDNSType_A: if (rr->rdlength != 4)
1430 {
1431 debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength);
1432 return(mDNSNULL);
1433 }
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];
1439 return(ptr);
1440
1441 case kDNSType_CNAME:// Same as PTR
1442 case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.name));
1443
1444 case kDNSType_AAAA: if (rr->rdlength != sizeof(rr->rdata->u.ipv6))
1445 {
1446 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength);
1447 return(mDNSNULL);
1448 }
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));
1452
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);
1462
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:
1466 case kDNSType_TXT:
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);
1470 }
1471 }
1472
1473 mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit)
1474 {
1475 mDNSu8 *endofrdata;
1476 mDNSu16 actualLength;
1477
1478 if (rr->RecordType == kDNSRecordTypeUnregistered)
1479 {
1480 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
1481 return(ptr);
1482 }
1483
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); }
1496
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);
1502
1503 if (count) (*count)++;
1504 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
1505 return(endofrdata);
1506 }
1507
1508 mDNSexport mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32
1509 maxttl)
1510 {
1511 if (maxttl > rr->rroriginalttl) maxttl = rr->rroriginalttl;
1512 return(PutResourceRecordTTL(msg, ptr, count, rr, maxttl));
1513 }
1514
1515 mDNSexport mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit,
1516 mDNSu16 *count, const AuthRecord *rr)
1517 {
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
1526 (*count)++;
1527 return(ptr + 10);
1528 }
1529
1530 mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
1531 {
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++;
1539 return(ptr+4);
1540 }
1541
1542 // for dynamic updates
1543 mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass)
1544 {
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++;
1552 return ptr;
1553 }
1554
1555 // for dynamic updates
1556 mDNSexport mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end)
1557 {
1558 AuthRecord prereq;
1559
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);
1566 return ptr;
1567 }
1568
1569 // for dynamic updates
1570 mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
1571 {
1572 mDNSu16 origclass;
1573 // deletion: specify record w/ TTL 0, class NONE
1574
1575 origclass = rr->rrclass;
1576 rr->rrclass = kDNSClass_NONE;
1577 ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
1578 rr->rrclass = origclass;
1579 return ptr;
1580 }
1581
1582 mDNSexport mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype)
1583 {
1584 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
1585 mDNSu16 class = kDNSQClass_ANY;
1586
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
1595
1596 msg->h.mDNS_numUpdates++;
1597 return ptr + 10;
1598 }
1599
1600 // for dynamic updates
1601 mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name)
1602 {
1603 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
1604 mDNSu16 class = kDNSQClass_ANY;
1605 mDNSu16 rrtype = kDNSQType_ANY;
1606
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
1615
1616 msg->h.mDNS_numUpdates++;
1617 return ptr + 10;
1618 }
1619
1620 // for dynamic updates
1621 mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
1622 {
1623 AuthRecord rr;
1624 ResourceRecord *opt = &rr.resrec;
1625 rdataOpt *optRD;
1626
1627 mDNSPlatformMemZero(&rr, sizeof(AuthRecord));
1628 mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, 0, mDNSNULL, mDNSNULL);
1629
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;
1634
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; }
1641
1642 return end;
1643 }
1644
1645 // ***************************************************************************
1646 #if COMPILER_LIKES_PRAGMA_MARK
1647 #pragma mark -
1648 #pragma mark - DNS Message Parsing Functions
1649 #endif
1650
1651 mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name)
1652 {
1653 mDNSu32 sum = 0;
1654 const mDNSu8 *c;
1655
1656 for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
1657 {
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);
1661 }
1662 if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
1663 return(sum);
1664 }
1665
1666 mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
1667 {
1668 domainname *target;
1669 if (NewRData)
1670 {
1671 rr->rdata = NewRData;
1672 rr->rdlength = rdlength;
1673 }
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);
1679 }
1680
1681 mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
1682 {
1683 mDNSu16 total = 0;
1684
1685 if (ptr < (mDNSu8*)msg || ptr >= end)
1686 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
1687
1688 while (1) // Read sequence of labels
1689 {
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
1692 switch (len & 0xC0)
1693 {
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); }
1698 ptr += len;
1699 total += 1 + len;
1700 break;
1701
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);
1705 }
1706 }
1707 }
1708
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)
1712 {
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
1716
1717 if (ptr < (mDNSu8*)msg || ptr >= end)
1718 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
1719
1720 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1721
1722 while (1) // Read sequence of labels
1723 {
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
1726 switch (len & 0xC0)
1727 {
1728 int i;
1729 mDNSu16 offset;
1730
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); }
1735 *np++ = len;
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)
1738 break;
1739
1740 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
1741 return(mDNSNULL);
1742
1743 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
1744
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); }
1750 if (*ptr & 0xC0)
1751 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
1752 break;
1753 }
1754 }
1755
1756 if (nextbyte) return(nextbyte);
1757 else return(ptr);
1758 }
1759
1760 mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
1761 {
1762 mDNSu16 pktrdlength;
1763
1764 ptr = skipDomainName(msg, ptr, end);
1765 if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
1766
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]);
1769 ptr += 10;
1770 if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
1771
1772 return(ptr + pktrdlength);
1773 }
1774
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)
1777 {
1778 CacheRecord *rr = &largecr->r;
1779 mDNSu16 pktrdlength;
1780
1781 if (largecr == &m->rec && rr->resrec.RecordType)
1782 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use");
1783
1784 rr->next = mDNSNULL;
1785 rr->resrec.RecordType = RecordType;
1786 rr->resrec.name = &largecr->namestorage;
1787
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;
1801
1802 rr->resrec.InterfaceID = InterfaceID;
1803 ptr = getDomainName(msg, ptr, end, rr->resrec.name);
1804 if (!ptr) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL); }
1805
1806 if (ptr + 10 > end) { debugf("GetResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
1807
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;
1818 ptr += 10;
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
1821
1822 rr->resrec.rdata = (RData*)&rr->rdatastorage;
1823 rr->resrec.rdata->MaxRDLength = MaximumRDSize;
1824
1825 if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c);
1826
1827 switch (rr->resrec.rrtype)
1828 {
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];
1833 break;
1834
1835 case kDNSType_CNAME:// Same as PTR
1836 case kDNSType_NS:
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);
1840 break;
1841
1842 case kDNSType_NULL: //Same as TXT
1843 case kDNSType_HINFO://Same as TXT
1844 case kDNSType_TXT: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
1845 {
1846 debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)",
1847 DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
1848 return(mDNSNULL);
1849 }
1850 rr->resrec.rdlength = pktrdlength;
1851 mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength);
1852 break;
1853
1854 case kDNSType_AAAA: mDNSPlatformMemCopy(ptr, &rr->resrec.rdata->u.ipv6, sizeof(rr->resrec.rdata->u.ipv6));
1855 break;
1856
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);
1864 break;
1865
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]);
1876 break;
1877
1878 case kDNSType_OPT: getOptRdata(ptr, end, &rr->resrec, pktrdlength); break;
1879
1880 default: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
1881 {
1882 debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
1883 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
1884 return(mDNSNULL);
1885 }
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);
1895 break;
1896 }
1897
1898 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
1899 SetNewRData(&rr->resrec, mDNSNULL, 0);
1900
1901 return(ptr + pktrdlength);
1902 }
1903
1904 mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
1905 {
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); }
1909 return(ptr+4);
1910 }
1911
1912 mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
1913 DNSQuestion *question)
1914 {
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); }
1919
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
1923 return(ptr+4);
1924 }
1925
1926 mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
1927 {
1928 int i;
1929 const mDNSu8 *ptr = msg->data;
1930 for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
1931 return(ptr);
1932 }
1933
1934 mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
1935 {
1936 int i;
1937 const mDNSu8 *ptr = LocateAnswers(msg, end);
1938 for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
1939 return(ptr);
1940 }
1941
1942 mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end)
1943 {
1944 int i;
1945 const mDNSu8 *ptr = LocateAuthorities(msg, end);
1946 for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end);
1947 return (ptr);
1948 }
1949
1950 // ***************************************************************************
1951 #if COMPILER_LIKES_PRAGMA_MARK
1952 #pragma mark -
1953 #pragma mark -
1954 #pragma mark - Packet Sending Functions
1955 #endif
1956
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)
1959 {
1960 mStatus status;
1961 int nsent;
1962 mDNSs32 msglen;
1963 mDNSu8 lenbuf[2];
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;
1969
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);
1979
1980 if (authInfo)
1981 {
1982 end = DNSDigest_SignMessage(msg, &end, &numAdditionals, authInfo);
1983 if (!end) return mStatus_UnknownErr;
1984 }
1985
1986 // Send the packet on the wire
1987
1988 if (sd >= 0)
1989 {
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;
1999 }
2000 else
2001 {
2002 status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, dst, dstport);
2003 }
2004
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);
2010
2011 return(status);
2012
2013 tcp_error:
2014 LogMsg("mDNSSendDNSMessage: error sending message over tcp");
2015 return mStatus_UnknownErr;
2016 }
2017
2018 // ***************************************************************************
2019 #if COMPILER_LIKES_PRAGMA_MARK
2020 #pragma mark -
2021 #pragma mark - RR List Management & Task Management
2022 #endif
2023
2024 mDNSexport void mDNS_Lock(mDNS *const m)
2025 {
2026 // MUST grab the platform lock FIRST!
2027 mDNSPlatformLock(m);
2028
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);
2035
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)
2039 {
2040 if (m->timenow)
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;
2044 }
2045 else if (m->timenow == 0)
2046 {
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;
2050 }
2051
2052 if (m->timenow_last - m->timenow > 0)
2053 {
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;
2057 }
2058 m->timenow_last = m->timenow;
2059
2060 // Increment mDNS_busy so we'll recognise re-entrant calls
2061 m->mDNS_busy++;
2062 }
2063
2064 mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
2065 {
2066 mDNSs32 e = m->timenow + 0x78000000;
2067 if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) return(e);
2068 if (m->NewQuestions)
2069 {
2070 if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering;
2071 else return(m->timenow);
2072 }
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;
2078 #endif
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;
2083 return(e);
2084 }
2085
2086 mDNSexport void mDNS_Unlock(mDNS *const m)
2087 {
2088 // Decrement mDNS_busy
2089 m->mDNS_busy--;
2090
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);
2094
2095 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
2096 if (m->mDNS_busy == 0)
2097 {
2098 m->NextScheduledEvent = GetNextScheduledEvent(m);
2099 if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
2100 m->timenow = 0;
2101 }
2102
2103 // MUST release the platform lock LAST!
2104 mDNSPlatformUnlock(m);
2105 }