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