]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSCore/DNSCommon.c
a1b7dc43d596387399c6e0e418a0095f2ac3e041
[apple/mdnsresponder.git] / mDNSCore / DNSCommon.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23
24 Change History (most recent first):
25
26 $Log: DNSCommon.c,v $
27 Revision 1.92 2005/09/16 21:06:49 cheshire
28 Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place
29
30 Revision 1.91 2005/07/10 22:10:37 cheshire
31 The getOptRdata routine implicitly assumes the destination ResourceRecord is large enough to
32 hold MaximumRDSize bytes, but its parameter was a generic ResourceRecord, which need not be that
33 large. Changing the parameter to a LargeCacheRecord makes it clearer what the routine requires.
34
35 Revision 1.90 2005/03/21 00:33:51 shersche
36 <rdar://problem/4021486> Fix build warnings on Win32 platform
37
38 Revision 1.89 2005/03/17 18:59:38 ksekar
39 <rdar://problem/4012279> Properly parse multiple LLQ Options per packet on Windows
40
41 Revision 1.88 2005/03/16 00:42:32 ksekar
42 <rdar://problem/4012279> Long-lived queries not working on Windows
43
44 Revision 1.87 2005/02/25 04:21:00 cheshire
45 <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
46
47 Revision 1.86 2005/02/18 00:43:12 cheshire
48 <rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
49
50 Revision 1.85 2005/02/10 22:35:17 cheshire
51 <rdar://problem/3727944> Update name
52
53 Revision 1.84 2005/02/03 00:44:38 cheshire
54 <rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
55
56 Revision 1.83 2005/01/27 22:57:55 cheshire
57 Fix compile errors on gcc4
58
59 Revision 1.82 2005/01/19 03:27:03 cheshire
60 <rdar://problem/3961051> CPU Spin in mDNSResponder
61 GetNextScheduledEvent() needs to check LocalRecordReady()
62
63 Revision 1.81 2004/12/18 03:13:45 cheshire
64 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
65
66 Revision 1.80 2004/12/16 21:46:43 cheshire
67 Add DNSTypeName case for kDNSType_SOA
68
69 Revision 1.79 2004/12/16 21:38:37 cheshire
70 Add DNSTypeName case for kDNSType_NS
71
72 Revision 1.78 2004/12/16 21:27:37 ksekar
73 Fixed build failures when compiled with verbose debugging messages
74
75 Revision 1.77 2004/12/16 20:12:59 cheshire
76 <rdar://problem/3324626> Cache memory management improvements
77
78 Revision 1.76 2004/12/16 08:05:29 shersche
79 Remove extranenous semicolons that cause compilation errors on Windows
80
81 Revision 1.75 2004/12/15 02:11:22 ksekar
82 <rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness
83
84 Revision 1.74 2004/12/09 22:49:15 ksekar
85 <rdar://problem/3913653> Wide-Area Goodbyes broken
86
87 Revision 1.73 2004/12/07 22:49:06 cheshire
88 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
89
90 Revision 1.72 2004/12/06 21:15:20 ksekar
91 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
92
93 Revision 1.71 2004/12/04 02:12:45 cheshire
94 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
95
96 Revision 1.70 2004/12/03 19:52:44 ksekar
97 Use PutResourceRecordTTLJumbo for putDeletionRecord()
98
99 Revision 1.69 2004/12/03 07:20:50 ksekar
100 <rdar://problem/3674208> Wide-Area: Registration of large TXT record fails
101
102 Revision 1.68 2004/11/24 00:10:43 cheshire
103 <rdar://problem/3869241> For unicast operations, verify that service types are legal
104
105 Revision 1.67 2004/10/26 03:52:02 cheshire
106 Update checkin comments
107
108 Revision 1.66 2004/10/23 01:16:00 cheshire
109 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
110
111 Revision 1.65 2004/10/20 02:15:09 cheshire
112 Add case in GetRRDisplayString() to display NS rdata
113
114 Revision 1.64 2004/10/13 00:24:02 cheshire
115 Disable "array is too small to include a terminating null character" warning on Windows
116
117 Revision 1.63 2004/10/10 06:57:14 cheshire
118 Change definition of "localdomain" to make code compile a little smaller
119
120 Revision 1.62 2004/10/06 01:44:19 cheshire
121 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
122
123 Revision 1.61 2004/09/30 00:24:56 ksekar
124 <rdar://problem/3695802> Dynamically update default registration domains on config change
125
126 Revision 1.60 2004/09/27 23:25:30 cheshire
127 Fix compiler warning: soa.serial is signed, not unsigned
128
129 Revision 1.59 2004/09/27 22:53:45 ksekar
130 Fixed getLargeResourceRecord for SOA rdata.
131
132 Revision 1.58 2004/09/25 02:41:39 cheshire
133 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
134
135 Revision 1.57 2004/09/25 02:24:27 cheshire
136 Removed unused rr->UseCount
137
138 Revision 1.56 2004/09/24 20:57:39 cheshire
139 <rdar://problem/3680902> Eliminate inappropriate casts that cause misaligned-address errors
140
141 Revision 1.55 2004/09/17 01:08:48 cheshire
142 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
143 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
144 declared in that file are ONLY appropriate to single-address-space embedded applications.
145 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
146
147 Revision 1.54 2004/09/17 00:49:51 cheshire
148 Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use
149 is GetLargeResourceRecord
150
151 Revision 1.53 2004/09/17 00:31:51 cheshire
152 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
153
154 Revision 1.52 2004/09/17 00:19:10 cheshire
155 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
156
157 Revision 1.51 2004/09/16 02:29:39 cheshire
158 Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
159 uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
160
161 Revision 1.50 2004/09/16 01:58:14 cheshire
162 Fix compiler warnings
163
164 Revision 1.49 2004/09/14 23:42:35 cheshire
165 <rdar://problem/3801296> Need to seed random number generator from platform-layer data
166
167 Revision 1.48 2004/09/14 23:27:46 cheshire
168 Fix compile errors
169
170 Revision 1.47 2004/08/25 02:50:04 cheshire
171 <rdar://problem/3561220> Browses are no longer piggybacking on other browses
172 Make mDNSSameAddress() recognise that two mDNSAddrType_None addresses are necessarily equal
173
174 Revision 1.46 2004/08/18 17:35:40 ksekar
175 <rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
176
177 Revision 1.45 2004/08/15 18:26:00 cheshire
178 Don't use strcpy() on "struct domainname" objects; use AssignDomainName() instead
179 (A "struct domainname" is a collection of packed pascal strings, not a C string.)
180
181 Revision 1.44 2004/08/13 23:46:58 cheshire
182 "asyncronous" -> "asynchronous"
183
184 Revision 1.43 2004/08/12 02:55:46 ksekar
185 Fix param order error moving putPrereqNameNotInUse from uDNS.c using
186 ustrcpy macro to DNSCommon.c using mDNSPlatformStrCopy().
187
188 Revision 1.42 2004/08/10 23:19:14 ksekar
189 <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
190 Moved routines/constants to allow extern access for garbage collection daemon
191
192 Revision 1.41 2004/08/10 01:10:01 cheshire
193 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
194 Minor revision from Roger Pantos
195
196 Revision 1.40 2004/08/04 22:10:46 cheshire
197 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
198 Change to use "._sub." instead of ".s." to mark subtypes.
199
200 Revision 1.39 2004/07/13 21:24:24 rpantos
201 Fix for <rdar://problem/3701120>.
202
203 Revision 1.38 2004/06/18 21:08:58 cheshire
204 <rdar://problem/3540040> Applications are registering invalid records
205 Attempts to create domain names like "www..apple.com." now logged to aid debugging
206
207 Revision 1.37 2004/06/18 20:25:42 cheshire
208 <rdar://problem/3488547> Add a syslog message if someone tries to use "local.arpa".
209
210 Revision 1.36 2004/06/18 19:09:59 cheshire
211 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
212
213 Revision 1.35 2004/06/05 00:14:44 cheshire
214 Fix signed/unsigned and other compiler warnings
215
216 Revision 1.34 2004/06/04 00:25:25 cheshire
217 Fix misaligned write exception that occurs on some platforms
218
219 Revision 1.33 2004/06/04 00:16:18 cheshire
220 Remove non-portable use of 'inline'
221
222 Revision 1.32 2004/06/03 03:09:58 ksekar
223 <rdar://problem/3668626>: Garbage Collection for Dynamic Updates
224
225 Revision 1.31 2004/05/28 23:42:36 ksekar
226 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
227
228 Revision 1.30 2004/05/26 09:08:04 bradley
229 Added cast to correct structure pointer when allocating domain name list element to fix C++ builds.
230
231 Revision 1.29 2004/05/18 23:51:25 cheshire
232 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
233
234 Revision 1.28 2004/05/13 04:54:20 ksekar
235 Unified list copy/free code. Added symetric list for
236
237 Revision 1.27 2004/04/22 20:29:07 cheshire
238 Log error message if no count field passed to PutResourceRecordTTL()
239
240 Revision 1.26 2004/04/22 04:07:01 cheshire
241 Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it
242
243 Revision 1.25 2004/04/22 03:05:28 cheshire
244 kDNSClass_ANY should be kDNSQClass_ANY
245
246 Revision 1.24 2004/04/22 02:51:20 cheshire
247 Use common code for HINFO/TXT and TSIG cases in putRData
248
249 Revision 1.23 2004/04/15 00:51:28 bradley
250 Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
251 Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
252
253 Revision 1.22 2004/04/14 23:09:28 ksekar
254 Support for TSIG signed dynamic updates.
255
256 Revision 1.21 2004/04/09 16:47:28 cheshire
257 <rdar://problem/3617655>: mDNSResponder escape handling inconsistent with BIND
258
259 Revision 1.20 2004/04/09 16:37:15 cheshire
260 Suggestion from Bob Bradley:
261 Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers
262
263 Revision 1.19 2004/04/02 19:34:38 cheshire
264 Fix broken comment
265
266 Revision 1.18 2004/03/30 06:45:00 cheshire
267 Compiler warning fixes from Don Woodward at Roku Labs
268
269 Revision 1.17 2004/03/19 22:25:20 cheshire
270 <rdar://problem/3579561>: Need to limit service types to fourteen characters
271 Won't actually do this for now, but keep the code around just in case
272
273 Revision 1.16 2004/03/08 02:45:35 cheshire
274 Minor change to make a couple of the log messages a bit shorter
275
276 Revision 1.15 2004/03/08 02:44:09 cheshire
277 <rdar://problem/3579561>: Need to limit service types to fourteen characters
278
279 Revision 1.14 2004/02/21 02:06:24 cheshire
280 Can't use anonymous unions -- they're non-standard and don't work on all compilers
281
282 Revision 1.13 2004/02/06 23:04:18 ksekar
283 Basic Dynamic Update support via mDNS_Register (dissabled via
284 UNICAST_REGISTRATION #define)
285
286 Revision 1.12 2004/02/03 22:37:10 cheshire
287 Delete unused (commented-out) code
288
289 Revision 1.11 2004/02/03 22:35:34 cheshire
290 <rdar://problem/3548256>: Should not allow empty string for resolve domain
291
292 Revision 1.10 2004/02/03 19:47:36 ksekar
293 Added an asynchronous state machine mechanism to uDNS.c, including
294 calls to find the parent zone for a domain name. Changes include code
295 in repository previously dissabled via "#if 0 incomplete". Codepath
296 is currently unused, and will be called to create update records, etc.
297
298 Revision 1.9 2004/01/27 20:15:22 cheshire
299 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
300
301 Revision 1.8 2004/01/24 23:24:36 cheshire
302 Expanded out the list of local domains to reduce risk of mistakes in future
303
304 Revision 1.7 2004/01/24 08:32:30 bradley
305 Mask values with 0xFF before casting to avoid runtime truncation errors on Windows debug builds.
306 Separated octal-escaped sequences preceding decimal digits to avoid errors with some compilers wanting
307 to signal potentially hidden errors about the subsequent digit not being part of the octal sequence.
308
309 Revision 1.6 2004/01/24 04:59:15 cheshire
310 Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
311
312 Revision 1.5 2004/01/23 23:23:14 ksekar
313 Added TCP support for truncated unicast messages.
314
315 Revision 1.4 2004/01/22 02:15:33 cheshire
316 <rdar://problem/3536597>: Link-local reverse-mapping domains need to be resolved using link-local multicast
317
318 Revision 1.3 2004/01/21 21:16:29 cheshire
319 Minor tidy-up: Deleted a bunch of blank lines, trailing spaces, tabs, etc.
320
321 Revision 1.2 2003/12/13 05:47:48 bradley
322 Made local ptr const to fix error when assigning from const structure. Disable benign conditional
323 expression is constant warning when building with Microsoft compilers.
324
325 Revision 1.1 2003/12/13 03:05:27 ksekar
326 <rdar://problem/3192548>: DynDNS: Unicast query of service records
327
328 */
329
330 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
331 #define mDNS_InstantiateInlines 1
332 #include "DNSCommon.h"
333
334 // Disable certain benign warnings with Microsoft compilers
335 #if (defined(_MSC_VER))
336 // Disable "conditional expression is constant" warning for debug macros.
337 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
338 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
339 #pragma warning(disable:4127)
340 // Disable "array is too small to include a terminating null character" warning
341 // -- domain labels have an initial length byte, not a terminating null character
342 #pragma warning(disable:4295)
343 #endif
344
345 // ***************************************************************************
346 #if COMPILER_LIKES_PRAGMA_MARK
347 #pragma mark -
348 #pragma mark - DNameList copy/deallocation routines
349 #endif
350
351 mDNSexport DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig)
352 {
353 DNameListElem *copy = mDNSNULL, *newelem;
354 const DNameListElem *ptr;
355
356 for (ptr = orig; ptr; ptr = ptr->next)
357 {
358 newelem = (DNameListElem*)mDNSPlatformMemAllocate(sizeof(DNameListElem));
359 if (!newelem) { LogMsg("ERROR: malloc"); return mDNSNULL; }
360 AssignDomainName(&newelem->name, &ptr->name);
361 newelem->next = copy;
362 copy = newelem;
363 }
364 return copy;
365 }
366
367 mDNSexport void mDNS_FreeDNameList(DNameListElem *list)
368 {
369 DNameListElem *fptr;
370
371 while (list)
372 {
373 fptr = list;
374 list = list->next;
375 mDNSPlatformMemFree(fptr);
376 }
377 }
378
379 // ***************************************************************************
380 #if COMPILER_LIKES_PRAGMA_MARK
381 #pragma mark -
382 #pragma mark - General Utility Functions
383 #endif
384
385 // return true for RFC1918 private addresses
386 mDNSexport mDNSBool IsPrivateV4Addr(mDNSAddr *addr)
387 {
388 mDNSu8 *b;
389
390 if (addr->type != mDNSAddrType_IPv4) return mDNSfalse;
391 b = addr->ip.v4.b;
392
393 return ((b[0] == 10) || // 10/8 prefix
394 (b[0] == 172 && b[1] > 15 && b[1] < 32) || // 172.16/12
395 (b[0] == 192 && b[1] == 168)); // 192.168/16
396 }
397
398 mDNSexport const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf)
399 {
400 while (intf && !intf->InterfaceActive) intf = intf->next;
401 return(intf);
402 }
403
404 mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
405 {
406 const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
407 if (next) return(next->InterfaceID); else return(mDNSNULL);
408 }
409
410 mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id)
411 {
412 mDNSu32 slot, used = 0;
413 CacheGroup *cg;
414 CacheRecord *rr;
415 FORALL_CACHERECORDS(slot, cg, rr)
416 if (rr->resrec.InterfaceID == id) used++;
417 return(used);
418 }
419
420 mDNSexport char *DNSTypeName(mDNSu16 rrtype)
421 {
422 switch (rrtype)
423 {
424 case kDNSType_A: return("Addr");
425 case kDNSType_NS: return("NS");
426 case kDNSType_CNAME:return("CNAME");
427 case kDNSType_SOA: return("SOA");
428 case kDNSType_NULL: return("NULL");
429 case kDNSType_PTR: return("PTR");
430 case kDNSType_HINFO:return("HINFO");
431 case kDNSType_TXT: return("TXT");
432 case kDNSType_AAAA: return("AAAA");
433 case kDNSType_SRV: return("SRV");
434 case kDNSQType_ANY: return("ANY");
435 default: {
436 static char buffer[16];
437 mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype);
438 return(buffer);
439 }
440 }
441 }
442
443 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
444 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
445 // long as this routine is only used for debugging messages, it probably isn't a big problem.
446 mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *rr, RDataBody *rd, char *buffer)
447 {
448 char *ptr = buffer;
449 mDNSu32 length = mDNS_snprintf(buffer, 79, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype));
450 switch (rr->rrtype)
451 {
452 case kDNSType_A: mDNS_snprintf(buffer+length, 79-length, "%.4a", &rd->ipv4); break;
453
454 case kDNSType_NS: // Same as PTR
455 case kDNSType_CNAME:// Same as PTR
456 case kDNSType_PTR: mDNS_snprintf(buffer+length, 79-length, "%##s", rd->name.c); break;
457
458 case kDNSType_HINFO:// Display this the same as TXT (just show first string)
459 case kDNSType_TXT: mDNS_snprintf(buffer+length, 79-length, "%#s", rd->txt.c); break;
460
461 case kDNSType_AAAA: mDNS_snprintf(buffer+length, 79-length, "%.16a", &rd->ipv6); break;
462 case kDNSType_SRV: mDNS_snprintf(buffer+length, 79-length, "%##s", rd->srv.target.c); break;
463 default: mDNS_snprintf(buffer+length, 79-length, "RDLen %d: %s", rr->rdlength, rd->data); break;
464 }
465 for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.';
466 return(buffer);
467 }
468
469 mDNSexport mDNSu32 mDNSRandom(mDNSu32 max)
470 {
471 static mDNSu32 seed = 0;
472 mDNSu32 mask = 1;
473
474 if (!seed)
475 {
476 int i;
477 seed = mDNSPlatformRandomSeed(); // Pick an initial seed
478 for (i=0; i<100; i++) seed = seed * 21 + 1; // And mix it up a bit
479 }
480 while (mask < max) mask = (mask << 1) | 1;
481 do seed = seed * 21 + 1; while ((seed & mask) > max);
482 return (seed & mask);
483 }
484
485 mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
486 {
487 if (ip1->type == ip2->type)
488 {
489 switch (ip1->type)
490 {
491 case mDNSAddrType_None : return(mDNStrue); // Empty addresses have no data and are therefore always equal
492 case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4));
493 case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6));
494 }
495 }
496 return(mDNSfalse);
497 }
498
499 mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
500 {
501 switch(ip->type)
502 {
503 case mDNSAddrType_IPv4: return(mDNSBool)(ip->ip.v4.NotAnInteger == AllDNSLinkGroupv4.NotAnInteger);
504 case mDNSAddrType_IPv6: return(mDNSBool)(ip->ip.v6.l[0] == AllDNSLinkGroupv6.l[0] &&
505 ip->ip.v6.l[1] == AllDNSLinkGroupv6.l[1] &&
506 ip->ip.v6.l[2] == AllDNSLinkGroupv6.l[2] &&
507 ip->ip.v6.l[3] == AllDNSLinkGroupv6.l[3] );
508 default: return(mDNSfalse);
509 }
510 }
511
512 // ***************************************************************************
513 #if COMPILER_LIKES_PRAGMA_MARK
514 #pragma mark -
515 #pragma mark - Domain Name Utility Functions
516 #endif
517
518 mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
519 {
520 int i;
521 const int len = *a++;
522
523 if (len > MAX_DOMAIN_LABEL)
524 { debugf("Malformed label (too long)"); return(mDNSfalse); }
525
526 if (len != *b++) return(mDNSfalse);
527 for (i=0; i<len; i++)
528 {
529 mDNSu8 ac = *a++;
530 mDNSu8 bc = *b++;
531 if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
532 if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
533 if (ac != bc) return(mDNSfalse);
534 }
535 return(mDNStrue);
536 }
537
538 mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
539 {
540 const mDNSu8 * a = d1->c;
541 const mDNSu8 * b = d2->c;
542 const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid
543
544 while (*a || *b)
545 {
546 if (a + 1 + *a >= max)
547 { debugf("Malformed domain name (more than 255 characters)"); return(mDNSfalse); }
548 if (!SameDomainLabel(a, b)) return(mDNSfalse);
549 a += 1 + *a;
550 b += 1 + *b;
551 }
552
553 return(mDNStrue);
554 }
555
556 mDNSexport mDNSBool IsLocalDomain(const domainname *d)
557 {
558 // Domains that are defined to be resolved via link-local multicast are:
559 // local., 254.169.in-addr.arpa., and 0.8.E.F.ip6.arpa.
560 static const domainname *n0 = (domainname*)"\x5" "local";
561 static const domainname *n1 = (domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
562 static const domainname *n2 = (domainname*)"\x1" "0" "\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
563
564 const domainname *d1, *d2, *d3, *d4, *d5, *d6; // Top-level domain, second-level domain, etc.
565 d1 = d2 = d3 = d4 = d5 = d6 = mDNSNULL;
566 while (d->c[0])
567 {
568 d6 = d5; d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
569 d = (domainname*)(d->c + 1 + d->c[0]);
570 }
571
572 if (d1 && SameDomainName(d1, n0)) return(mDNStrue);
573 if (d4 && SameDomainName(d4, n1)) return(mDNStrue);
574 if (d6 && SameDomainName(d6, n2)) return(mDNStrue);
575 return(mDNSfalse);
576 }
577
578 // Returns length of a domain name INCLUDING the byte for the final null label
579 // i.e. for the root label "." it returns one
580 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
581 // Legal results are 1 (just root label) to 255 (MAX_DOMAIN_NAME)
582 // If the given domainname is invalid, result is 256
583 mDNSexport mDNSu16 DomainNameLength(const domainname *const name)
584 {
585 const mDNSu8 *src = name->c;
586 while (*src)
587 {
588 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
589 src += 1 + *src;
590 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
591 }
592 return((mDNSu16)(src - name->c + 1));
593 }
594
595 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
596 // for the final null label i.e. for the root label "." it returns one.
597 // E.g. for the FQDN "foo.com." it returns 9
598 // (length, three data bytes, length, three more data bytes, final zero).
599 // In the case where a parent domain name is provided, and the given name is a child
600 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
601 // of the child name, plus TWO bytes for the compression pointer.
602 // E.g. for the name "foo.com." with parent "com.", it returns 6
603 // (length, three data bytes, two-byte compression pointer).
604 mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
605 {
606 const mDNSu8 *src = name->c;
607 if (parent && parent->c[0] == 0) parent = mDNSNULL;
608 while (*src)
609 {
610 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
611 if (parent && SameDomainName((domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
612 src += 1 + *src;
613 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
614 }
615 return((mDNSu16)(src - name->c + 1));
616 }
617
618 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
619 // The C string contains the label as-is, with no escaping, etc.
620 // Any dots in the name are literal dots, not label separators
621 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
622 // in the domainname bufer (i.e., the next byte after the terminating zero).
623 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
624 // AppendLiteralLabelString returns mDNSNULL.
625 mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr)
626 {
627 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
628 const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
629 const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL;
630 const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2;
631 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
632
633 while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data
634 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
635 *ptr++ = 0; // Put the null root label on the end
636 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
637 else return(ptr); // Success: return new value of ptr
638 }
639
640 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
641 // The C string is in conventional DNS syntax:
642 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
643 // If successful, AppendDNSNameString returns a pointer to the next unused byte
644 // in the domainname bufer (i.e., the next byte after the terminating zero).
645 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
646 // AppendDNSNameString returns mDNSNULL.
647 mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring)
648 {
649 const char *cstr = cstring;
650 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
651 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
652 while (*cstr && ptr < lim) // While more characters, and space to put them...
653 {
654 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
655 if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); }
656 while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label...
657 {
658 mDNSu8 c = (mDNSu8)*cstr++; // Read the character
659 if (c == '\\') // If escape character, check next character
660 {
661 c = (mDNSu8)*cstr++; // Assume we'll just take the next character
662 if (mdnsIsDigit(cstr[-1]) && mdnsIsDigit(cstr[0]) && mdnsIsDigit(cstr[1]))
663 { // If three decimal digits,
664 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
665 int v1 = cstr[ 0] - '0';
666 int v2 = cstr[ 1] - '0';
667 int val = v0 * 100 + v1 * 10 + v2;
668 if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it
669 }
670 }
671 *ptr++ = c; // Write the character
672 }
673 if (*cstr) cstr++; // Skip over the trailing dot (if present)
674 if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort
675 return(mDNSNULL);
676 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
677 }
678
679 *ptr++ = 0; // Put the null root label on the end
680 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
681 else return(ptr); // Success: return new value of ptr
682 }
683
684 // AppendDomainLabel appends a single label to a name.
685 // If successful, AppendDomainLabel returns a pointer to the next unused byte
686 // in the domainname bufer (i.e., the next byte after the terminating zero).
687 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
688 // AppendDomainLabel returns mDNSNULL.
689 mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label)
690 {
691 int i;
692 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
693
694 // Check label is legal
695 if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
696
697 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
698 if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL);
699
700 for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data
701 *ptr++ = 0; // Put the null root label on the end
702 return(ptr);
703 }
704
705 mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
706 {
707 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
708 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
709 const mDNSu8 * src = append->c;
710 while(src[0])
711 {
712 int i;
713 if (ptr + 1 + src[0] > lim) return(mDNSNULL);
714 for (i=0; i<=src[0]; i++) *ptr++ = src[i];
715 *ptr = 0; // Put the null root label on the end
716 src += i;
717 }
718 return(ptr);
719 }
720
721 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
722 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
723 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
724 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
725 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
726 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
727 mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr)
728 {
729 mDNSu8 * ptr = label->c + 1; // Where we're putting it
730 const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put
731 while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label
732 label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte
733 return(*cstr == 0); // Return mDNStrue if we successfully consumed all input
734 }
735
736 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
737 // The C string is in conventional DNS syntax:
738 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
739 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
740 // in the domainname bufer (i.e., the next byte after the terminating zero).
741 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 255 bytes)
742 // MakeDomainNameFromDNSNameString returns mDNSNULL.
743 mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr)
744 {
745 name->c[0] = 0; // Make an empty domain name
746 return(AppendDNSNameString(name, cstr)); // And then add this string to it
747 }
748
749 mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
750 {
751 const mDNSu8 * src = label->c; // Domain label we're reading
752 const mDNSu8 len = *src++; // Read length of this (non-null) label
753 const mDNSu8 *const end = src + len; // Work out where the label ends
754 if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
755 while (src < end) // While we have characters in the label
756 {
757 mDNSu8 c = *src++;
758 if (esc)
759 {
760 if (c == '.' || c == esc) // If character is a dot or the escape character
761 *ptr++ = esc; // Output escape character
762 else if (c <= ' ') // If non-printing ascii,
763 { // Output decimal escape sequence
764 *ptr++ = esc;
765 *ptr++ = (char) ('0' + (c / 100) );
766 *ptr++ = (char) ('0' + (c / 10) % 10);
767 c = (mDNSu8)('0' + (c ) % 10);
768 }
769 }
770 *ptr++ = (char)c; // Copy the character
771 }
772 *ptr = 0; // Null-terminate the string
773 return(ptr); // and return
774 }
775
776 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1005 bytes)
777 mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
778 {
779 const mDNSu8 *src = name->c; // Domain name we're reading
780 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
781
782 if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot
783
784 while (*src) // While more characters in the domain name
785 {
786 if (src + 1 + *src >= max) return(mDNSNULL);
787 ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
788 if (!ptr) return(mDNSNULL);
789 src += 1 + *src;
790 *ptr++ = '.'; // Write the dot after the label
791 }
792
793 *ptr++ = 0; // Null-terminate the string
794 return(ptr); // and return
795 }
796
797 // RFC 1034 rules:
798 // Host names must start with a letter, end with a letter or digit,
799 // and have as interior characters only letters, digits, and hyphen.
800 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
801
802 mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
803 {
804 const mDNSu8 * src = &UTF8Name[1];
805 const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0];
806 mDNSu8 * ptr = &hostlabel->c[1];
807 const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL;
808 while (src < end)
809 {
810 // Delete apostrophes from source name
811 if (src[0] == '\'') { src++; continue; } // Standard straight single quote
812 if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99)
813 { src += 3; continue; } // Unicode curly apostrophe
814 if (ptr < lim)
815 {
816 if (mdnsValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
817 else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
818 }
819 src++;
820 }
821 while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks
822 hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
823 }
824
825 mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
826 const domainlabel *name, const domainname *type, const domainname *const domain)
827 {
828 int i, len;
829 mDNSu8 *dst = fqdn->c;
830 const mDNSu8 *src;
831 const char *errormsg;
832
833 // In the case where there is no name (and ONLY in that case),
834 // a single-label subtype is allowed as the first label of a three-part "type"
835 if (!name && type)
836 {
837 const mDNSu8 *s0 = type->c;
838 if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63)
839 {
840 const mDNSu8 * s1 = s0 + 1 + s0[0];
841 if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63)
842 {
843 const mDNSu8 *s2 = s1 + 1 + s1[0];
844 if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels
845 {
846 static const mDNSu8 SubTypeLabel[5] = "\x04_sub";
847 src = s0; // Copy the first label
848 len = *src;
849 for (i=0; i <= len; i++) *dst++ = *src++;
850 for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i];
851 type = (domainname *)s1;
852
853 // Special support for queries done by some third-party network monitoring software
854 // For these queries, we retract the "._sub" we just added between the subtype and the main type
855 if (SameDomainName((domainname*)s0, (domainname*)"\x09_services\x07_dns-sd\x04_udp") ||
856 SameDomainName((domainname*)s0, (domainname*)"\x09_services\x05_mdns\x04_udp"))
857 dst -= sizeof(SubTypeLabel);
858 }
859 }
860 }
861 }
862
863 if (name && name->c[0])
864 {
865 src = name->c; // Put the service name into the domain name
866 len = *src;
867 if (len >= 0x40) { errormsg="Service instance name too long"; goto fail; }
868 for (i=0; i<=len; i++) *dst++ = *src++;
869 }
870 else
871 name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
872
873 src = type->c; // Put the service type into the domain name
874 len = *src;
875 if (len < 2 || len >= 0x40 || (len > 15 && !SameDomainName(domain, (domainname*)"\x05" "local")))
876 {
877 errormsg="Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
878 goto fail;
879 }
880 if (src[1] != '_') { errormsg="Application protocol name must begin with underscore"; goto fail; }
881 for (i=2; i<=len; i++)
882 if (!mdnsIsLetter(src[i]) && !mdnsIsDigit(src[i]) && src[i] != '-' && src[i] != '_')
883 { errormsg="Application protocol name must contain only letters, digits, and hyphens"; goto fail; }
884 for (i=0; i<=len; i++) *dst++ = *src++;
885
886 len = *src;
887 if (!(len == 4 && src[1] == '_' &&
888 (((src[2] | 0x20) == 'u' && (src[3] | 0x20) == 'd') || ((src[2] | 0x20) == 't' && (src[3] | 0x20) == 'c')) &&
889 (src[4] | 0x20) == 'p'))
890 { errormsg="Transport protocol name must be _udp or _tcp"; goto fail; }
891 for (i=0; i<=len; i++) *dst++ = *src++;
892
893 if (*src) { errormsg="Service type must have only two labels"; goto fail; }
894
895 *dst = 0;
896 if (!domain->c[0]) { errormsg="Service domain must be non-empty"; goto fail; }
897 if (SameDomainName(domain, (domainname*)"\x05" "local" "\x04" "arpa"))
898 { errormsg="Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; }
899 dst = AppendDomainName(fqdn, domain);
900 if (!dst) { errormsg="Service domain too long"; goto fail; }
901 return(dst);
902
903 fail:
904 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
905 return(mDNSNULL);
906 }
907
908 mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
909 domainlabel *const name, domainname *const type, domainname *const domain)
910 {
911 int i, len;
912 const mDNSu8 *src = fqdn->c;
913 const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
914 mDNSu8 *dst;
915
916 dst = name->c; // Extract the service name from the domain name
917 len = *src;
918 if (len >= 0x40) { debugf("DeconstructServiceName: service name too long"); return(mDNSfalse); }
919 for (i=0; i<=len; i++) *dst++ = *src++;
920
921 dst = type->c; // Extract the service type from the domain name
922 len = *src;
923 if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); }
924 for (i=0; i<=len; i++) *dst++ = *src++;
925
926 len = *src;
927 if (len >= 0x40) { debugf("DeconstructServiceName: service type too long"); return(mDNSfalse); }
928 for (i=0; i<=len; i++) *dst++ = *src++;
929 *dst++ = 0; // Put the null root label on the end of the service type
930
931 dst = domain->c; // Extract the service domain from the domain name
932 while (*src)
933 {
934 len = *src;
935 if (len >= 0x40)
936 { debugf("DeconstructServiceName: service domain label too long"); return(mDNSfalse); }
937 if (src + 1 + len + 1 >= max)
938 { debugf("DeconstructServiceName: service domain too long"); return(mDNSfalse); }
939 for (i=0; i<=len; i++) *dst++ = *src++;
940 }
941 *dst++ = 0; // Put the null root label on the end
942
943 return(mDNStrue);
944 }
945
946 // Notes on UTF-8:
947 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
948 // 10xxxxxx is a continuation byte of a multi-byte character
949 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
950 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
951 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
952 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
953 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
954 //
955 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
956 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
957 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
958 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
959 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
960
961 mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
962 {
963 if (length > max)
964 {
965 mDNSu8 c1 = string[max]; // First byte after cut point
966 mDNSu8 c2 = (max+1 < length) ? string[max+1] : 0xB0; // Second byte after cut point
967 length = max; // Trim length down
968 while (length > 0)
969 {
970 // Check if the byte right after the chop point is a UTF-8 continuation byte,
971 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
972 // If so, then we continue to chop more bytes until we get to a legal chop point.
973 mDNSBool continuation = ((c1 & 0xC0) == 0x80);
974 mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0);
975 if (!continuation && !secondsurrogate) break;
976 c2 = c1;
977 c1 = string[--length];
978 }
979 // Having truncated characters off the end of our string, also cut off any residual white space
980 while (length > 0 && string[length-1] <= ' ') length--;
981 }
982 return(length);
983 }
984
985 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
986 // name ends in "-nnn", where n is some decimal number.
987 mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
988 {
989 mDNSu16 l = name->c[0];
990
991 if (RichText)
992 {
993 if (l < 4) return mDNSfalse; // Need at least " (2)"
994 if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')'
995 if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit
996 l--;
997 while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
998 return (name->c[l] == '(' && name->c[l - 1] == ' ');
999 }
1000 else
1001 {
1002 if (l < 2) return mDNSfalse; // Need at least "-2"
1003 if (!mdnsIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit
1004 l--;
1005 while (l > 2 && mdnsIsDigit(name->c[l])) l--; // Strip off digits
1006 return (name->c[l] == '-');
1007 }
1008 }
1009
1010 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1011 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1012 // from the suffix that was removed.
1013 mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText)
1014 {
1015 mDNSu32 val = 0, multiplier = 1;
1016
1017 // Chop closing parentheses from RichText suffix
1018 if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
1019
1020 // Get any existing numerical suffix off the name
1021 while (mdnsIsDigit(name->c[name->c[0]]))
1022 { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
1023
1024 // Chop opening parentheses or dash from suffix
1025 if (RichText)
1026 {
1027 if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
1028 }
1029 else
1030 {
1031 if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
1032 }
1033
1034 return(val);
1035 }
1036
1037 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1038 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1039 mDNSexport void AppendLabelSuffix(domainlabel *name, mDNSu32 val, mDNSBool RichText)
1040 {
1041 mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1042 if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1043
1044 // Truncate trailing spaces from RichText names
1045 if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
1046
1047 while (val >= divisor * 10) { divisor *= 10; chars++; }
1048
1049 name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
1050
1051 if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
1052 else { name->c[++name->c[0]] = '-'; }
1053
1054 while (divisor)
1055 {
1056 name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
1057 val %= divisor;
1058 divisor /= 10;
1059 }
1060
1061 if (RichText) name->c[++name->c[0]] = ')';
1062 }
1063
1064 mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
1065 {
1066 mDNSu32 val = 0;
1067
1068 if (LabelContainsSuffix(name, RichText))
1069 val = RemoveLabelSuffix(name, RichText);
1070
1071 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1072 // If existing suffix in the range 2-9, increment it.
1073 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1074 // so add a random increment to improve the chances of finding an available name next time.
1075 if (val == 0) val = 2;
1076 else if (val < 10) val++;
1077 else val += 1 + mDNSRandom(99);
1078
1079 AppendLabelSuffix(name, val, RichText);
1080 }
1081
1082 // ***************************************************************************
1083 #if COMPILER_LIKES_PRAGMA_MARK
1084 #pragma mark -
1085 #pragma mark - Resource Record Utility Functions
1086 #endif
1087
1088 mDNSexport mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb)
1089 {
1090 mDNSu32 sum = 0;
1091 int i;
1092 for (i=0; i+1 < rdlength; i+=2)
1093 {
1094 sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1];
1095 sum = (sum<<3) | (sum>>29);
1096 }
1097 if (i < rdlength)
1098 {
1099 sum += ((mDNSu32)(rdb->data[i])) << 8;
1100 }
1101 return(sum);
1102 }
1103
1104 mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2)
1105 {
1106 if (r1->rrtype != r2->rrtype) return(mDNSfalse);
1107 if (r1->rdlength != r2->rdlength) return(mDNSfalse);
1108 if (r1->rdatahash != r2->rdatahash) return(mDNSfalse);
1109 switch(r1->rrtype)
1110 {
1111 case kDNSType_CNAME:// Same as PTR
1112 case kDNSType_PTR: return(SameDomainName(&r1->rdata->u.name, &r2->rdata->u.name));
1113
1114 case kDNSType_SRV: return(mDNSBool)( r1->rdata->u.srv.priority == r2->rdata->u.srv.priority &&
1115 r1->rdata->u.srv.weight == r2->rdata->u.srv.weight &&
1116 r1->rdata->u.srv.port.NotAnInteger == r2->rdata->u.srv.port.NotAnInteger &&
1117 SameDomainName(&r1->rdata->u.srv.target, &r2->rdata->u.srv.target) );
1118
1119 default: return(mDNSPlatformMemSame(r1->rdata->u.data, r2->rdata->u.data, r1->rdlength));
1120 }
1121 }
1122
1123 mDNSexport mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2)
1124 {
1125 return (r1->namehash == r2->namehash &&
1126 r1->rrtype == r2->rrtype &&
1127 SameDomainName(r1->name, r2->name) &&
1128 SameRData(r1, r2));
1129 }
1130
1131 mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1132 {
1133 if (rr->InterfaceID &&
1134 q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1135 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1136
1137 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1138 if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->qtype && q->qtype != kDNSQType_ANY ) return(mDNSfalse);
1139 if ( rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1140 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1141 }
1142
1143 mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
1144 {
1145 const RDataBody *rd = &rr->rdata->u;
1146 const domainname *const name = estimate ? rr->name : mDNSNULL;
1147 switch (rr->rrtype)
1148 {
1149 case kDNSType_A: return(sizeof(rd->ipv4));
1150 case kDNSType_CNAME:// Same as PTR
1151 case kDNSType_NS: // Same as PTR
1152 case kDNSType_PTR: return(CompressedDomainNameLength(&rd->name, name));
1153 case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
1154 case kDNSType_NULL: // Same as TXT -- not self-describing, so have to just trust rdlength
1155 case kDNSType_TXT: return(rr->rdlength); // TXT is not self-describing, so have to just trust rdlength
1156 case kDNSType_AAAA: return(sizeof(rd->ipv6));
1157 case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
1158 case kDNSType_SOA: return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) +
1159 CompressedDomainNameLength(&rd->soa.rname, name) +
1160 5 * sizeof(mDNSOpaque32));
1161 case kDNSType_OPT: return(rr->rdlength);
1162 default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
1163 return(rr->rdlength);
1164 }
1165 }
1166
1167 mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
1168 {
1169 mDNSu16 len;
1170
1171 switch(rrtype)
1172 {
1173 case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr));
1174
1175 case kDNSType_NS: // Same as PTR
1176 case kDNSType_MD: // Same as PTR
1177 case kDNSType_MF: // Same as PTR
1178 case kDNSType_CNAME:// Same as PTR
1179 //case kDNSType_SOA not checked
1180 case kDNSType_MB: // Same as PTR
1181 case kDNSType_MG: // Same as PTR
1182 case kDNSType_MR: // Same as PTR
1183 //case kDNSType_NULL not checked (no specified format, so always valid)
1184 //case kDNSType_WKS not checked
1185 case kDNSType_PTR: if (!rdlength) return(mDNSfalse);
1186 len = DomainNameLength(&rd->u.name);
1187 return(len <= MAX_DOMAIN_NAME && rdlength == len);
1188
1189 case kDNSType_HINFO:// Same as TXT (roughly)
1190 case kDNSType_MINFO:// Same as TXT (roughly)
1191 case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035)
1192 {
1193 const mDNSu8 *ptr = rd->u.txt.c;
1194 const mDNSu8 *end = rd->u.txt.c + rdlength;
1195 while (ptr < end) ptr += 1 + ptr[0];
1196 return (ptr == end);
1197 }
1198
1199 case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
1200
1201 case kDNSType_MX: if (!rdlength) return(mDNSfalse);
1202 len = DomainNameLength(&rd->u.mx.exchange);
1203 return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
1204
1205 case kDNSType_SRV: if (!rdlength) return(mDNSfalse);
1206 len = DomainNameLength(&rd->u.srv.target);
1207 return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
1208
1209 default: return(mDNStrue); // Allow all other types without checking
1210 }
1211 }
1212
1213 // ***************************************************************************
1214 #if COMPILER_LIKES_PRAGMA_MARK
1215 #pragma mark -
1216 #pragma mark -
1217 #pragma mark - DNS Message Creation Functions
1218 #endif
1219
1220 mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
1221 {
1222 h->id = id;
1223 h->flags = flags;
1224 h->numQuestions = 0;
1225 h->numAnswers = 0;
1226 h->numAuthorities = 0;
1227 h->numAdditionals = 0;
1228 }
1229
1230 mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
1231 {
1232 const mDNSu8 *result = end - *domname - 1;
1233
1234 if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label
1235
1236 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1237 while (result >= base)
1238 {
1239 // If the length byte and first character of the label match, then check further to see
1240 // if this location in the packet will yield a useful name compression pointer.
1241 if (result[0] == domname[0] && result[1] == domname[1])
1242 {
1243 const mDNSu8 *name = domname;
1244 const mDNSu8 *targ = result;
1245 while (targ + *name < end)
1246 {
1247 // First see if this label matches
1248 int i;
1249 const mDNSu8 *pointertarget;
1250 for (i=0; i <= *name; i++) if (targ[i] != name[i]) break;
1251 if (i <= *name) break; // If label did not match, bail out
1252 targ += 1 + *name; // Else, did match, so advance target pointer
1253 name += 1 + *name; // and proceed to check next label
1254 if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match!
1255 if (*name == 0) break; // If no more labels to match, we failed, so bail out
1256
1257 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1258 if (targ[0] < 0x40) continue; // If length value, continue to check next label
1259 if (targ[0] < 0xC0) break; // If 40-BF, not valid
1260 if (targ+1 >= end) break; // Second byte not present!
1261 pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1];
1262 if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet
1263 if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1264 targ = pointertarget;
1265 }
1266 }
1267 result--; // We failed to match at this search position, so back up the tentative result pointer and try again
1268 }
1269 return(mDNSNULL);
1270 }
1271
1272 // Put a string of dot-separated labels as length-prefixed labels
1273 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1274 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1275 // end points to the end of the message so far
1276 // ptr points to where we want to put the name
1277 // limit points to one byte past the end of the buffer that we must not overrun
1278 // domainname is the name to put
1279 mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg,
1280 mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name)
1281 {
1282 const mDNSu8 *const base = (const mDNSu8 *)msg;
1283 const mDNSu8 * np = name->c;
1284 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
1285 const mDNSu8 * pointer = mDNSNULL;
1286 const mDNSu8 *const searchlimit = ptr;
1287
1288 while (*np && ptr < limit-1) // While we've got characters in the name, and space to write them in the message...
1289 {
1290 if (*np > MAX_DOMAIN_LABEL)
1291 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
1292
1293 // This check correctly allows for the final trailing root label:
1294 // e.g.
1295 // Suppose our domain name is exactly 255 bytes long, including the final trailing root label.
1296 // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
1297 // We know that max will be at name->c[255]
1298 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1299 // six bytes, then exit the loop, write the final terminating root label, and the domain
1300 // name we've written is exactly 255 bytes long, exactly at the correct legal limit.
1301 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1302 if (np + 1 + *np >= max)
1303 { LogMsg("Malformed domain name %##s (more than 255 bytes)", name->c); return(mDNSNULL); }
1304
1305 if (base) pointer = FindCompressionPointer(base, searchlimit, np);
1306 if (pointer) // Use a compression pointer if we can
1307 {
1308 mDNSu16 offset = (mDNSu16)(pointer - base);
1309 *ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
1310 *ptr++ = (mDNSu8)( offset & 0xFF);
1311 return(ptr);
1312 }
1313 else // Else copy one label and try again
1314 {
1315 int i;
1316 mDNSu8 len = *np++;
1317 if (ptr + 1 + len >= limit) return(mDNSNULL);
1318 *ptr++ = len;
1319 for (i=0; i<len; i++) *ptr++ = *np++;
1320 }
1321 }
1322
1323 if (ptr < limit) // If we didn't run out of space
1324 {
1325 *ptr++ = 0; // Put the final root label
1326 return(ptr); // and return
1327 }
1328
1329 return(mDNSNULL);
1330 }
1331
1332 mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
1333 {
1334 ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
1335 ptr[1] = (mDNSu8)((val ) & 0xFF);
1336 return ptr + sizeof(mDNSOpaque16);
1337 }
1338
1339 mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
1340 {
1341 ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
1342 ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
1343 ptr[2] = (mDNSu8)((val >> 8) & 0xFF);
1344 ptr[3] = (mDNSu8)((val ) & 0xFF);
1345 return ptr + sizeof(mDNSu32);
1346 }
1347
1348 mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr)
1349 {
1350 int nput = 0;
1351 rdataOpt *opt;
1352
1353 while (nput < rr->rdlength)
1354 {
1355 // check if space for opt/optlen
1356 if (ptr + (2 * sizeof(mDNSu16)) > limit) goto space_err;
1357 opt = (rdataOpt *)(rr->rdata->u.data + nput);
1358 ptr = putVal16(ptr, opt->opt);
1359 ptr = putVal16(ptr, opt->optlen);
1360 nput += 2 * sizeof(mDNSu16);
1361 if (opt->opt == kDNSOpt_LLQ)
1362 {
1363 if (ptr + LLQ_OPTLEN > limit) goto space_err;
1364 ptr = putVal16(ptr, opt->OptData.llq.vers);
1365 ptr = putVal16(ptr, opt->OptData.llq.llqOp);
1366 ptr = putVal16(ptr, opt->OptData.llq.err);
1367 mDNSPlatformMemCopy(opt->OptData.llq.id, ptr, 8); // 8-byte id
1368 ptr += 8;
1369 ptr = putVal32(ptr, opt->OptData.llq.lease);
1370 nput += LLQ_OPTLEN;
1371 }
1372 else if (opt->opt == kDNSOpt_Lease)
1373 {
1374 if (ptr + sizeof(mDNSs32) > limit) goto space_err;
1375 ptr = putVal32(ptr, opt->OptData.lease);
1376 nput += sizeof(mDNSs32);
1377 }
1378 else { LogMsg("putOptRData - unknown option %d", opt->opt); return mDNSNULL; }
1379 }
1380
1381 return ptr;
1382
1383 space_err:
1384 LogMsg("ERROR: putOptRData - out of space");
1385 return mDNSNULL;
1386 }
1387
1388 mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr)
1389 {
1390 mDNSu16 val = (mDNSu16)(((mDNSu16)(*ptr)[0]) << 8 | (*ptr)[1]);
1391 *ptr += sizeof(mDNSOpaque16);
1392 return val;
1393 }
1394
1395 mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *const limit, LargeCacheRecord *const cr, mDNSu16 pktRDLen)
1396 {
1397 int nread = 0;
1398 ResourceRecord *const rr = &cr->r.resrec;
1399 rdataOpt *opt = (rdataOpt *)rr->rdata->u.data;
1400
1401 while (nread < pktRDLen && (mDNSu8 *)opt < rr->rdata->u.data + MaximumRDSize - sizeof(rdataOpt))
1402 {
1403 // space for opt + optlen
1404 if (nread + (2 * sizeof(mDNSu16)) > rr->rdata->MaxRDLength) goto space_err;
1405 opt->opt = getVal16(&ptr);
1406 opt->optlen = getVal16(&ptr);
1407 nread += 2 * sizeof(mDNSu16);
1408 if (opt->opt == kDNSOpt_LLQ)
1409 {
1410 if ((unsigned)(limit - ptr) < LLQ_OPTLEN) goto space_err;
1411 opt->OptData.llq.vers = getVal16(&ptr);
1412 opt->OptData.llq.llqOp = getVal16(&ptr);
1413 opt->OptData.llq.err = getVal16(&ptr);
1414 mDNSPlatformMemCopy(ptr, opt->OptData.llq.id, 8);
1415 ptr += 8;
1416 opt->OptData.llq.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
1417 if (opt->OptData.llq.lease > 0x70000000UL / mDNSPlatformOneSecond)
1418 opt->OptData.llq.lease = 0x70000000UL / mDNSPlatformOneSecond;
1419 ptr += sizeof(mDNSOpaque32);
1420 nread += LLQ_OPTLEN;
1421 }
1422 else if (opt->opt == kDNSOpt_Lease)
1423 {
1424 if ((unsigned)(limit - ptr) < sizeof(mDNSs32)) goto space_err;
1425
1426 opt->OptData.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
1427 if (opt->OptData.lease > 0x70000000UL / mDNSPlatformOneSecond)
1428 opt->OptData.lease = 0x70000000UL / mDNSPlatformOneSecond;
1429 ptr += sizeof(mDNSs32);
1430 nread += sizeof(mDNSs32);
1431 }
1432 else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt->opt); return mDNSNULL; }
1433 opt++; // increment pointer into rdatabody
1434 }
1435
1436 rr->rdlength = pktRDLen;
1437 return ptr;
1438
1439 space_err:
1440 LogMsg("ERROR: getLLQRdata - out of space");
1441 return mDNSNULL;
1442 }
1443
1444 mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr)
1445 {
1446 switch (rr->rrtype)
1447 {
1448 case kDNSType_A: if (rr->rdlength != 4)
1449 {
1450 debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength);
1451 return(mDNSNULL);
1452 }
1453 if (ptr + 4 > limit) return(mDNSNULL);
1454 *ptr++ = rr->rdata->u.ipv4.b[0];
1455 *ptr++ = rr->rdata->u.ipv4.b[1];
1456 *ptr++ = rr->rdata->u.ipv4.b[2];
1457 *ptr++ = rr->rdata->u.ipv4.b[3];
1458 return(ptr);
1459
1460 case kDNSType_CNAME:// Same as PTR
1461 case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.name));
1462
1463 case kDNSType_AAAA: if (rr->rdlength != sizeof(rr->rdata->u.ipv6))
1464 {
1465 debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength);
1466 return(mDNSNULL);
1467 }
1468 if (ptr + sizeof(rr->rdata->u.ipv6) > limit) return(mDNSNULL);
1469 mDNSPlatformMemCopy(&rr->rdata->u.ipv6, ptr, sizeof(rr->rdata->u.ipv6));
1470 return(ptr + sizeof(rr->rdata->u.ipv6));
1471
1472 case kDNSType_SRV: if (ptr + 6 > limit) return(mDNSNULL);
1473 *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority >> 8);
1474 *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority & 0xFF);
1475 *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight >> 8);
1476 *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight & 0xFF);
1477 *ptr++ = rr->rdata->u.srv.port.b[0];
1478 *ptr++ = rr->rdata->u.srv.port.b[1];
1479 return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.srv.target));
1480 case kDNSType_OPT: return putOptRData(ptr, limit, rr);
1481
1482 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype);
1483 // Fall through to common code below
1484 case kDNSType_HINFO:
1485 case kDNSType_TXT:
1486 case kDNSType_TSIG: if (ptr + rr->rdlength > limit) return(mDNSNULL);
1487 mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength);
1488 return(ptr + rr->rdlength);
1489 }
1490 }
1491
1492 mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit)
1493 {
1494 mDNSu8 *endofrdata;
1495 mDNSu16 actualLength;
1496
1497 if (rr->RecordType == kDNSRecordTypeUnregistered)
1498 {
1499 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
1500 return(ptr);
1501 }
1502
1503 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
1504 if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1505 ptr[0] = (mDNSu8)(rr->rrtype >> 8);
1506 ptr[1] = (mDNSu8)(rr->rrtype & 0xFF);
1507 ptr[2] = (mDNSu8)(rr->rrclass >> 8);
1508 ptr[3] = (mDNSu8)(rr->rrclass & 0xFF);
1509 ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF);
1510 ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF);
1511 ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF);
1512 ptr[7] = (mDNSu8)( ttl & 0xFF);
1513 endofrdata = putRData(msg, ptr+10, limit, rr);
1514 if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); }
1515
1516 // Go back and fill in the actual number of data bytes we wrote
1517 // (actualLength can be less than rdlength when domain name compression is used)
1518 actualLength = (mDNSu16)(endofrdata - ptr - 10);
1519 ptr[8] = (mDNSu8)(actualLength >> 8);
1520 ptr[9] = (mDNSu8)(actualLength & 0xFF);
1521
1522 if (count) (*count)++;
1523 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
1524 return(endofrdata);
1525 }
1526
1527 mDNSexport mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32
1528 maxttl)
1529 {
1530 if (maxttl > rr->rroriginalttl) maxttl = rr->rroriginalttl;
1531 return(PutResourceRecordTTL(msg, ptr, count, rr, maxttl));
1532 }
1533
1534 mDNSexport mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit,
1535 mDNSu16 *count, const AuthRecord *rr)
1536 {
1537 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name);
1538 if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1539 ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type
1540 ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF);
1541 ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class
1542 ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF);
1543 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero
1544 ptr[8] = ptr[9] = 0; // RDATA length is zero
1545 (*count)++;
1546 return(ptr + 10);
1547 }
1548
1549 mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
1550 {
1551 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
1552 if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
1553 ptr[0] = (mDNSu8)(rrtype >> 8);
1554 ptr[1] = (mDNSu8)(rrtype & 0xFF);
1555 ptr[2] = (mDNSu8)(rrclass >> 8);
1556 ptr[3] = (mDNSu8)(rrclass & 0xFF);
1557 msg->h.numQuestions++;
1558 return(ptr+4);
1559 }
1560
1561 // for dynamic updates
1562 mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass)
1563 {
1564 ptr = putDomainNameAsLabels(msg, ptr, limit, zone);
1565 if (!ptr || ptr + 4 > limit) return mDNSNULL; // If we're out-of-space, return NULL
1566 *ptr++ = (mDNSu8)(kDNSType_SOA >> 8);
1567 *ptr++ = (mDNSu8)(kDNSType_SOA & 0xFF);
1568 *ptr++ = zoneClass.b[0];
1569 *ptr++ = zoneClass.b[1];
1570 msg->h.mDNS_numZones++;
1571 return ptr;
1572 }
1573
1574 // for dynamic updates
1575 mDNSexport mDNSu8 *putPrereqNameNotInUse(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end)
1576 {
1577 AuthRecord prereq;
1578
1579 mDNSPlatformMemZero(&prereq, sizeof(AuthRecord));
1580 mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
1581 AssignDomainName(prereq.resrec.name, name);
1582 prereq.resrec.rrtype = kDNSQType_ANY;
1583 prereq.resrec.rrclass = kDNSClass_NONE;
1584 ptr = putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq);
1585 return ptr;
1586 }
1587
1588 // for dynamic updates
1589 mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
1590 {
1591 mDNSu16 origclass;
1592 // deletion: specify record w/ TTL 0, class NONE
1593
1594 origclass = rr->rrclass;
1595 rr->rrclass = kDNSClass_NONE;
1596 ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
1597 rr->rrclass = origclass;
1598 return ptr;
1599 }
1600
1601 mDNSexport mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype)
1602 {
1603 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
1604 mDNSu16 class = kDNSQClass_ANY;
1605
1606 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
1607 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
1608 ptr[0] = (mDNSu8)(rrtype >> 8);
1609 ptr[1] = (mDNSu8)(rrtype & 0xFF);
1610 ptr[2] = (mDNSu8)(class >> 8);
1611 ptr[3] = (mDNSu8)(class & 0xFF);
1612 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
1613 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
1614
1615 msg->h.mDNS_numUpdates++;
1616 return ptr + 10;
1617 }
1618
1619 // for dynamic updates
1620 mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name)
1621 {
1622 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
1623 mDNSu16 class = kDNSQClass_ANY;
1624 mDNSu16 rrtype = kDNSQType_ANY;
1625
1626 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
1627 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
1628 ptr[0] = (mDNSu8)(rrtype >> 8);
1629 ptr[1] = (mDNSu8)(rrtype & 0xFF);
1630 ptr[2] = (mDNSu8)(class >> 8);
1631 ptr[3] = (mDNSu8)(class & 0xFF);
1632 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
1633 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
1634
1635 msg->h.mDNS_numUpdates++;
1636 return ptr + 10;
1637 }
1638
1639 // for dynamic updates
1640 mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
1641 {
1642 AuthRecord rr;
1643 ResourceRecord *opt = &rr.resrec;
1644 rdataOpt *optRD;
1645
1646 mDNSPlatformMemZero(&rr, sizeof(AuthRecord));
1647 mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, 0, mDNSNULL, mDNSNULL);
1648
1649 opt->RecordType = kDNSRecordTypeKnownUnique; // to avoid warnings in other layers
1650 opt->rrtype = kDNSType_OPT;
1651 opt->rdlength = LEASE_OPT_RDLEN;
1652 opt->rdestimate = LEASE_OPT_RDLEN;
1653
1654 optRD = &rr.resrec.rdata->u.opt;
1655 optRD->opt = kDNSOpt_Lease;
1656 optRD->optlen = sizeof(mDNSs32);
1657 optRD->OptData.lease = lease;
1658 end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, opt, 0);
1659 if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; }
1660
1661 return end;
1662 }
1663
1664 // ***************************************************************************
1665 #if COMPILER_LIKES_PRAGMA_MARK
1666 #pragma mark -
1667 #pragma mark - DNS Message Parsing Functions
1668 #endif
1669
1670 mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name)
1671 {
1672 mDNSu32 sum = 0;
1673 const mDNSu8 *c;
1674
1675 for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
1676 {
1677 sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) |
1678 (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]);
1679 sum = (sum<<3) | (sum>>29);
1680 }
1681 if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
1682 return(sum);
1683 }
1684
1685 mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
1686 {
1687 domainname *target;
1688 if (NewRData)
1689 {
1690 rr->rdata = NewRData;
1691 rr->rdlength = rdlength;
1692 }
1693 // Must not try to get target pointer until after updating rr->rdata
1694 target = GetRRDomainNameTarget(rr);
1695 rr->rdlength = GetRDLength(rr, mDNSfalse);
1696 rr->rdestimate = GetRDLength(rr, mDNStrue);
1697 rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->rdlength, &rr->rdata->u);
1698 }
1699
1700 mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
1701 {
1702 mDNSu16 total = 0;
1703
1704 if (ptr < (mDNSu8*)msg || ptr >= end)
1705 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
1706
1707 while (1) // Read sequence of labels
1708 {
1709 const mDNSu8 len = *ptr++; // Read length of this label
1710 if (len == 0) return(ptr); // If length is zero, that means this name is complete
1711 switch (len & 0xC0)
1712 {
1713 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
1714 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
1715 if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label
1716 { debugf("skipDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
1717 ptr += len;
1718 total += 1 + len;
1719 break;
1720
1721 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL);
1722 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL);
1723 case 0xC0: return(ptr+1);
1724 }
1725 }
1726 }
1727
1728 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
1729 mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
1730 domainname *const name)
1731 {
1732 const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers
1733 mDNSu8 *np = name->c; // Name pointer
1734 const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer
1735
1736 if (ptr < (mDNSu8*)msg || ptr >= end)
1737 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
1738
1739 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1740
1741 while (1) // Read sequence of labels
1742 {
1743 const mDNSu8 len = *ptr++; // Read length of this label
1744 if (len == 0) break; // If length is zero, that means this name is complete
1745 switch (len & 0xC0)
1746 {
1747 int i;
1748 mDNSu16 offset;
1749
1750 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
1751 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
1752 if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label
1753 { debugf("getDomainName: Malformed domain name (more than 255 characters)"); return(mDNSNULL); }
1754 *np++ = len;
1755 for (i=0; i<len; i++) *np++ = *ptr++;
1756 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
1757 break;
1758
1759 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
1760 return(mDNSNULL);
1761
1762 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
1763
1764 case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
1765 if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers
1766 ptr = (mDNSu8 *)msg + offset;
1767 if (ptr < (mDNSu8*)msg || ptr >= end)
1768 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
1769 if (*ptr & 0xC0)
1770 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
1771 break;
1772 }
1773 }
1774
1775 if (nextbyte) return(nextbyte);
1776 else return(ptr);
1777 }
1778
1779 mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
1780 {
1781 mDNSu16 pktrdlength;
1782
1783 ptr = skipDomainName(msg, ptr, end);
1784 if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
1785
1786 if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
1787 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
1788 ptr += 10;
1789 if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
1790
1791 return(ptr + pktrdlength);
1792 }
1793
1794 mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr,
1795 const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *largecr)
1796 {
1797 CacheRecord *rr = &largecr->r;
1798 mDNSu16 pktrdlength;
1799
1800 if (largecr == &m->rec && rr->resrec.RecordType)
1801 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use");
1802
1803 rr->next = mDNSNULL;
1804 rr->resrec.RecordType = RecordType;
1805 rr->resrec.name = &largecr->namestorage;
1806
1807 rr->NextInKAList = mDNSNULL;
1808 rr->TimeRcvd = m ? m->timenow : 0;
1809 rr->DelayDelivery = 0;
1810 rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTime()
1811 rr->LastUsed = m ? m->timenow : 0;
1812 rr->CRActiveQuestion = mDNSNULL;
1813 rr->UnansweredQueries = 0;
1814 rr->LastUnansweredTime= 0;
1815 rr->MPUnansweredQ = 0;
1816 rr->MPLastUnansweredQT= 0;
1817 rr->MPUnansweredKA = 0;
1818 rr->MPExpectingKA = mDNSfalse;
1819 rr->NextInCFList = mDNSNULL;
1820
1821 rr->resrec.InterfaceID = InterfaceID;
1822 ptr = getDomainName(msg, ptr, end, rr->resrec.name);
1823 if (!ptr) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL); }
1824
1825 if (ptr + 10 > end) { debugf("GetResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
1826
1827 rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]);
1828 rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask);
1829 rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
1830 if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1)
1831 rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
1832 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
1833 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
1834 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
1835 if (ptr[2] & (kDNSClass_UniqueRRSet >> 8))
1836 rr->resrec.RecordType |= kDNSRecordTypePacketUniqueMask;
1837 ptr += 10;
1838 if (ptr + pktrdlength > end) { debugf("GetResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
1839 end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record
1840
1841 rr->resrec.rdata = (RData*)&rr->rdatastorage;
1842 rr->resrec.rdata->MaxRDLength = MaximumRDSize;
1843
1844 if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c);
1845
1846 switch (rr->resrec.rrtype)
1847 {
1848 case kDNSType_A: rr->resrec.rdata->u.ipv4.b[0] = ptr[0];
1849 rr->resrec.rdata->u.ipv4.b[1] = ptr[1];
1850 rr->resrec.rdata->u.ipv4.b[2] = ptr[2];
1851 rr->resrec.rdata->u.ipv4.b[3] = ptr[3];
1852 break;
1853
1854 case kDNSType_CNAME:// Same as PTR
1855 case kDNSType_NS:
1856 case kDNSType_PTR: if (!getDomainName(msg, ptr, end, &rr->resrec.rdata->u.name))
1857 { debugf("GetResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); }
1858 //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
1859 break;
1860
1861 case kDNSType_NULL: //Same as TXT
1862 case kDNSType_HINFO://Same as TXT
1863 case kDNSType_TXT: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
1864 {
1865 debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)",
1866 DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
1867 return(mDNSNULL);
1868 }
1869 rr->resrec.rdlength = pktrdlength;
1870 mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength);
1871 break;
1872
1873 case kDNSType_AAAA: mDNSPlatformMemCopy(ptr, &rr->resrec.rdata->u.ipv6, sizeof(rr->resrec.rdata->u.ipv6));
1874 break;
1875
1876 case kDNSType_SRV: rr->resrec.rdata->u.srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
1877 rr->resrec.rdata->u.srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
1878 rr->resrec.rdata->u.srv.port.b[0] = ptr[4];
1879 rr->resrec.rdata->u.srv.port.b[1] = ptr[5];
1880 if (!getDomainName(msg, ptr+6, end, &rr->resrec.rdata->u.srv.target))
1881 { debugf("GetResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); }
1882 //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
1883 break;
1884
1885 case kDNSType_SOA: ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.mname);
1886 if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL; }
1887 ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.rname);
1888 if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL; }
1889 if (ptr + 0x14 != end) { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL; }
1890 rr->resrec.rdata->u.soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]);
1891 rr->resrec.rdata->u.soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]);
1892 rr->resrec.rdata->u.soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]);
1893 rr->resrec.rdata->u.soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]);
1894 rr->resrec.rdata->u.soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
1895 break;
1896
1897 case kDNSType_OPT: getOptRdata(ptr, end, largecr, pktrdlength); break;
1898
1899 default: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
1900 {
1901 debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
1902 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
1903 return(mDNSNULL);
1904 }
1905 debugf("GetResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
1906 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
1907 // Note: Just because we don't understand the record type, that doesn't
1908 // mean we fail. The DNS protocol specifies rdlength, so we can
1909 // safely skip over unknown records and ignore them.
1910 // We also grab a binary copy of the rdata anyway, since the caller
1911 // might know how to interpret it even if we don't.
1912 rr->resrec.rdlength = pktrdlength;
1913 mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength);
1914 break;
1915 }
1916
1917 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
1918 SetNewRData(&rr->resrec, mDNSNULL, 0);
1919
1920 return(ptr + pktrdlength);
1921 }
1922
1923 mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
1924 {
1925 ptr = skipDomainName(msg, ptr, end);
1926 if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); }
1927 if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
1928 return(ptr+4);
1929 }
1930
1931 mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
1932 DNSQuestion *question)
1933 {
1934 question->InterfaceID = InterfaceID;
1935 ptr = getDomainName(msg, ptr, end, &question->qname);
1936 if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
1937 if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
1938
1939 question->qnamehash = DomainNameHashValue(&question->qname);
1940 question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type
1941 question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class
1942 return(ptr+4);
1943 }
1944
1945 mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
1946 {
1947 int i;
1948 const mDNSu8 *ptr = msg->data;
1949 for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
1950 return(ptr);
1951 }
1952
1953 mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
1954 {
1955 int i;
1956 const mDNSu8 *ptr = LocateAnswers(msg, end);
1957 for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
1958 return(ptr);
1959 }
1960
1961 mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end)
1962 {
1963 int i;
1964 const mDNSu8 *ptr = LocateAuthorities(msg, end);
1965 for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end);
1966 return (ptr);
1967 }
1968
1969 // ***************************************************************************
1970 #if COMPILER_LIKES_PRAGMA_MARK
1971 #pragma mark -
1972 #pragma mark -
1973 #pragma mark - Packet Sending Functions
1974 #endif
1975
1976 mDNSexport mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
1977 mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo)
1978 {
1979 mStatus status;
1980 int nsent;
1981 mDNSs32 msglen;
1982 mDNSu8 lenbuf[2];
1983 mDNSu16 numQuestions = msg->h.numQuestions;
1984 mDNSu16 numAnswers = msg->h.numAnswers;
1985 mDNSu16 numAuthorities = msg->h.numAuthorities;
1986 mDNSu16 numAdditionals = msg->h.numAdditionals;
1987 mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
1988
1989 // Put all the integer values in IETF byte-order (MSB first, LSB second)
1990 *ptr++ = (mDNSu8)(numQuestions >> 8);
1991 *ptr++ = (mDNSu8)(numQuestions & 0xFF);
1992 *ptr++ = (mDNSu8)(numAnswers >> 8);
1993 *ptr++ = (mDNSu8)(numAnswers & 0xFF);
1994 *ptr++ = (mDNSu8)(numAuthorities >> 8);
1995 *ptr++ = (mDNSu8)(numAuthorities & 0xFF);
1996 *ptr++ = (mDNSu8)(numAdditionals >> 8);
1997 *ptr++ = (mDNSu8)(numAdditionals & 0xFF);
1998
1999 if (authInfo)
2000 {
2001 end = DNSDigest_SignMessage(msg, &end, &numAdditionals, authInfo);
2002 if (!end) return mStatus_UnknownErr;
2003 }
2004
2005 // Send the packet on the wire
2006
2007 if (sd >= 0)
2008 {
2009 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
2010 lenbuf[0] = (mDNSu8)(msglen >> 8); // host->network byte conversion
2011 lenbuf[1] = (mDNSu8)(msglen & 0xFF);
2012 nsent = mDNSPlatformWriteTCP(sd, (char*)lenbuf, 2);
2013 //!!!KRS make sure kernel is sending these as 1 packet!
2014 if (nsent != 2) goto tcp_error;
2015 nsent = mDNSPlatformWriteTCP(sd, (char *)msg, msglen);
2016 if (nsent != msglen) goto tcp_error;
2017 status = mStatus_NoError;
2018 }
2019 else
2020 {
2021 status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, dst, dstport);
2022 }
2023
2024 // Put all the integer values back the way they were before we return
2025 msg->h.numQuestions = numQuestions;
2026 msg->h.numAnswers = numAnswers;
2027 msg->h.numAuthorities = numAuthorities;
2028 msg->h.numAdditionals = (mDNSu16)(authInfo ? numAdditionals - 1 : numAdditionals);
2029
2030 return(status);
2031
2032 tcp_error:
2033 LogMsg("mDNSSendDNSMessage: error sending message over tcp");
2034 return mStatus_UnknownErr;
2035 }
2036
2037 // ***************************************************************************
2038 #if COMPILER_LIKES_PRAGMA_MARK
2039 #pragma mark -
2040 #pragma mark - RR List Management & Task Management
2041 #endif
2042
2043 mDNSexport void mDNS_Lock(mDNS *const m)
2044 {
2045 // MUST grab the platform lock FIRST!
2046 mDNSPlatformLock(m);
2047
2048 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
2049 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
2050 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
2051 // If mDNS_busy != mDNS_reentrancy that's a bad sign
2052 if (m->mDNS_busy != m->mDNS_reentrancy)
2053 LogMsg("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
2054
2055 // If this is an initial entry into the mDNSCore code, set m->timenow
2056 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
2057 if (m->mDNS_busy == 0)
2058 {
2059 if (m->timenow)
2060 LogMsg("mDNS_Lock: m->timenow already set (%ld/%ld)", m->timenow, mDNS_TimeNow_NoLock(m));
2061 m->timenow = mDNS_TimeNow_NoLock(m);
2062 if (m->timenow == 0) m->timenow = 1;
2063 }
2064 else if (m->timenow == 0)
2065 {
2066 LogMsg("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy);
2067 m->timenow = mDNS_TimeNow_NoLock(m);
2068 if (m->timenow == 0) m->timenow = 1;
2069 }
2070
2071 if (m->timenow_last - m->timenow > 0)
2072 {
2073 m->timenow_adjust += m->timenow_last - m->timenow;
2074 LogMsg("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", m->timenow_last - m->timenow, m->timenow_adjust);
2075 m->timenow = m->timenow_last;
2076 }
2077 m->timenow_last = m->timenow;
2078
2079 // Increment mDNS_busy so we'll recognise re-entrant calls
2080 m->mDNS_busy++;
2081 }
2082
2083 mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
2084 {
2085 mDNSs32 e = m->timenow + 0x78000000;
2086 if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) return(e);
2087 if (m->NewQuestions)
2088 {
2089 if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering;
2090 else return(m->timenow);
2091 }
2092 if (m->NewLocalOnlyQuestions) return(m->timenow);
2093 if (m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords)) return(m->timenow);
2094 if (m->SuppressSending) return(m->SuppressSending);
2095 #ifndef UNICAST_DISABLED
2096 if (e - m->uDNS_info.nextevent > 0) e = m->uDNS_info.nextevent;
2097 #endif
2098 if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck;
2099 if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery;
2100 if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe;
2101 if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
2102 return(e);
2103 }
2104
2105 mDNSexport void mDNS_Unlock(mDNS *const m)
2106 {
2107 // Decrement mDNS_busy
2108 m->mDNS_busy--;
2109
2110 // Check for locking failures
2111 if (m->mDNS_busy != m->mDNS_reentrancy)
2112 LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
2113
2114 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
2115 if (m->mDNS_busy == 0)
2116 {
2117 m->NextScheduledEvent = GetNextScheduledEvent(m);
2118 if (m->timenow == 0) LogMsg("mDNS_Unlock: ERROR! m->timenow aready zero");
2119 m->timenow = 0;
2120 }
2121
2122 // MUST release the platform lock LAST!
2123 mDNSPlatformUnlock(m);
2124 }