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