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