]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSCore/DNSCommon.c
mDNSResponder-379.38.1.tar.gz
[apple/mdnsresponder.git] / mDNSCore / DNSCommon.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
19 #define mDNS_InstantiateInlines 1
20 #include "DNSCommon.h"
21 #include "CryptoAlg.h"
22
23 // Disable certain benign warnings with Microsoft compilers
24 #if (defined(_MSC_VER))
25 // Disable "conditional expression is constant" warning for debug macros.
26 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
27 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
28 #pragma warning(disable:4127)
29 // Disable "array is too small to include a terminating null character" warning
30 // -- domain labels have an initial length byte, not a terminating null character
31 #pragma warning(disable:4295)
32 #endif
33
34 // ***************************************************************************
35 #if COMPILER_LIKES_PRAGMA_MARK
36 #pragma mark - Program Constants
37 #endif
38
39 mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0;
40 mDNSexport const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)-1;
41 mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2;
42 mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)-3;
43 mDNSexport const mDNSInterfaceID mDNSInterface_P2P = (mDNSInterfaceID)-4;
44
45 // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
46 // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
47 // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
48 // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
49 // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
50 // with Microsoft's LLMNR client code.
51
52 #define DiscardPortAsNumber 9
53 #define SSHPortAsNumber 22
54 #define UnicastDNSPortAsNumber 53
55 #define SSDPPortAsNumber 1900
56 #define IPSECPortAsNumber 4500
57 #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback
58 #define NATPMPAnnouncementPortAsNumber 5350
59 #define NATPMPPortAsNumber 5351
60 #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
61 #define MulticastDNSPortAsNumber 5353
62 #define LoopbackIPCPortAsNumber 5354
63 //#define MulticastDNSPortAsNumber 5355 // LLMNR
64 #define PrivateDNSPortAsNumber 5533
65
66 mDNSexport const mDNSIPPort DiscardPort = { { DiscardPortAsNumber >> 8, DiscardPortAsNumber & 0xFF } };
67 mDNSexport const mDNSIPPort SSHPort = { { SSHPortAsNumber >> 8, SSHPortAsNumber & 0xFF } };
68 mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } };
69 mDNSexport const mDNSIPPort SSDPPort = { { SSDPPortAsNumber >> 8, SSDPPortAsNumber & 0xFF } };
70 mDNSexport const mDNSIPPort IPSECPort = { { IPSECPortAsNumber >> 8, IPSECPortAsNumber & 0xFF } };
71 mDNSexport const mDNSIPPort NSIPCPort = { { NSIPCPortAsNumber >> 8, NSIPCPortAsNumber & 0xFF } };
72 mDNSexport const mDNSIPPort NATPMPAnnouncementPort = { { NATPMPAnnouncementPortAsNumber >> 8, NATPMPAnnouncementPortAsNumber & 0xFF } };
73 mDNSexport const mDNSIPPort NATPMPPort = { { NATPMPPortAsNumber >> 8, NATPMPPortAsNumber & 0xFF } };
74 mDNSexport const mDNSIPPort DNSEXTPort = { { DNSEXTPortAsNumber >> 8, DNSEXTPortAsNumber & 0xFF } };
75 mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } };
76 mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } };
77 mDNSexport const mDNSIPPort PrivateDNSPort = { { PrivateDNSPortAsNumber >> 8, PrivateDNSPortAsNumber & 0xFF } };
78
79 mDNSexport const OwnerOptData zeroOwner = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } };
80
81 mDNSexport const mDNSIPPort zeroIPPort = { { 0 } };
82 mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } };
83 mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } };
84 mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } };
85 mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } };
86 mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
87 mDNSexport const mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255, 255 } };
88 mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} };
89
90 mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
91 mDNSexport const mDNSv4Addr AllHosts_v4 = { { 224, 0, 0, 1 } }; // For NAT-PMP Annoucements
92 mDNSexport const mDNSv6Addr AllHosts_v6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } };
93 mDNSexport const mDNSv6Addr NDP_prefix = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01, 0xFF,0x00,0x00,0xFB } }; // FF02:0:0:0:0:1:FF00::/104
94 mDNSexport const mDNSEthAddr AllHosts_v6_Eth = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } };
95 mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } };
96 //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR
97 mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
98 //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
99
100 mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } };
101 mDNSexport const mDNSOpaque16 onesID = { { 255, 255 } };
102 mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } };
103 mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } };
104 mDNSexport const mDNSOpaque16 DNSSecQFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, kDNSFlag1_CD } };
105 mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
106 mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } };
107 mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } };
108
109 mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } };
110
111 // ***************************************************************************
112 #if COMPILER_LIKES_PRAGMA_MARK
113 #pragma mark -
114 #pragma mark - General Utility Functions
115 #endif
116
117 // return true for RFC1918 private addresses
118 mDNSexport mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr)
119 {
120 return ((addr->b[0] == 10) || // 10/8 prefix
121 (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) || // 172.16/12
122 (addr->b[0] == 192 && addr->b[1] == 168)); // 192.168/16
123 }
124
125 mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf)
126 {
127 while (intf && !intf->InterfaceActive) intf = intf->next;
128 return(intf);
129 }
130
131 mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
132 {
133 const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
134 if (next) return(next->InterfaceID);else return(mDNSNULL);
135 }
136
137 mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id)
138 {
139 mDNSu32 slot, used = 0;
140 CacheGroup *cg;
141 const CacheRecord *rr;
142 FORALL_CACHERECORDS(slot, cg, rr)
143 {
144 if (rr->resrec.InterfaceID == id)
145 used++;
146 }
147 return(used);
148 }
149
150 mDNSexport char *DNSTypeName(mDNSu16 rrtype)
151 {
152 switch (rrtype)
153 {
154 case kDNSType_A: return("Addr");
155 case kDNSType_NS: return("NS");
156 case kDNSType_CNAME: return("CNAME");
157 case kDNSType_SOA: return("SOA");
158 case kDNSType_NULL: return("NULL");
159 case kDNSType_PTR: return("PTR");
160 case kDNSType_HINFO: return("HINFO");
161 case kDNSType_TXT: return("TXT");
162 case kDNSType_AAAA: return("AAAA");
163 case kDNSType_SRV: return("SRV");
164 case kDNSType_OPT: return("OPT");
165 case kDNSType_NSEC: return("NSEC");
166 case kDNSType_TSIG: return("TSIG");
167 case kDNSType_RRSIG: return("RRSIG");
168 case kDNSType_DNSKEY: return("DNSKEY");
169 case kDNSType_DS: return("DS");
170 case kDNSQType_ANY: return("ANY");
171 default: {
172 static char buffer[16];
173 mDNS_snprintf(buffer, sizeof(buffer), "TYPE%d", rrtype);
174 return(buffer);
175 }
176 }
177 }
178
179 mDNSlocal char *DNSSECAlgName(mDNSu8 alg)
180 {
181 switch (alg)
182 {
183 case CRYPTO_RSA_SHA1: return "RSA_SHA1";
184 case CRYPTO_DSA_NSEC3_SHA1: return "DSA_NSEC3_SHA1";
185 case CRYPTO_RSA_NSEC3_SHA1: return "RSA_NSEC3_SHA1";
186 case CRYPTO_RSA_SHA256: return "RSA_SHA256";
187 case CRYPTO_RSA_SHA512: return "RSA_SHA512";
188 default: {
189 static char algbuffer[16];
190 mDNS_snprintf(algbuffer, sizeof(algbuffer), "ALG%d", alg);
191 return(algbuffer);
192 }
193 }
194 }
195
196 mDNSlocal char *DNSSECDigestName(mDNSu8 digest)
197 {
198 switch (digest)
199 {
200 case SHA1_DIGEST_TYPE: return "SHA1";
201 case SHA256_DIGEST_TYPE: return "SHA256";
202 default: {
203 static char digbuffer[16];
204 mDNS_snprintf(digbuffer, sizeof(digbuffer), "DIG%d", digest);
205 return(digbuffer);
206 }
207 }
208 }
209
210 mDNSexport mDNSu32 swap32(mDNSu32 x)
211 {
212 mDNSu8 *ptr = (mDNSu8 *)&x;
213 return (mDNSu32)((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
214 }
215
216 mDNSexport mDNSu16 swap16(mDNSu16 x)
217 {
218 mDNSu8 *ptr = (mDNSu8 *)&x;
219 return (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
220 }
221
222 // RFC 4034 Appendix B: Get the keyid of a DNS KEY. It is not transmitted
223 // explicitly on the wire.
224 //
225 // Note: This just helps narrow down the list of keys to look at. It is possible
226 // for two DNS keys to have the same ID i.e., key ID is not a unqiue tag. We ignore
227 // MD5 keys.
228 //
229 // 1st argument - the RDATA part of the DNSKEY RR
230 // 2nd argument - the RDLENGTH
231 //
232 mDNSlocal mDNSu32 keytag(mDNSu8 *key, mDNSu32 keysize)
233 {
234 unsigned long ac;
235 unsigned int i;
236
237 for (ac = 0, i = 0; i < keysize; ++i)
238 ac += (i & 1) ? key[i] : key[i] << 8;
239 ac += (ac >> 16) & 0xFFFF;
240 return ac & 0xFFFF;
241 }
242
243 mDNSlocal int base64Encode(char *buffer, int blen, mDNSu8 *data, int len)
244 {
245 AlgContext *ctx;
246 mDNSu8 *outputBuffer;
247 int length;
248
249 ctx = AlgCreate(ENC_ALG, ENC_BASE64);
250 if (!ctx)
251 {
252 LogMsg("base64Encode: AlgCreate failed\n");
253 return 0;
254 }
255 AlgAdd(ctx, data, len);
256 outputBuffer = AlgEncode(ctx);
257 length = 0;
258 if (outputBuffer)
259 length = mDNS_snprintf(buffer, blen, " %s", outputBuffer);
260 AlgDestroy(ctx);
261 return length;
262 }
263
264 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
265 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
266 // long as this routine is only used for debugging messages, it probably isn't a big problem.
267 mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer)
268 {
269 const RDataBody2 *const rd = (RDataBody2 *)rd1;
270 #define RemSpc (MaxMsg-1-length)
271 char *ptr = buffer;
272 mDNSu32 length = mDNS_snprintf(buffer, MaxMsg-1, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype));
273 if (rr->RecordType == kDNSRecordTypePacketNegative) return(buffer);
274 if (!rr->rdlength) { mDNS_snprintf(buffer+length, RemSpc, "<< ZERO RDATA LENGTH >>"); return(buffer); }
275
276 switch (rr->rrtype)
277 {
278 case kDNSType_A: mDNS_snprintf(buffer+length, RemSpc, "%.4a", &rd->ipv4); break;
279
280 case kDNSType_NS: // Same as PTR
281 case kDNSType_CNAME: // Same as PTR
282 case kDNSType_PTR: mDNS_snprintf(buffer+length, RemSpc, "%##s", rd->name.c); break;
283
284 case kDNSType_SOA: mDNS_snprintf(buffer+length, RemSpc, "%##s %##s %d %d %d %d %d",
285 rd->soa.mname.c, rd->soa.rname.c,
286 rd->soa.serial, rd->soa.refresh, rd->soa.retry, rd->soa.expire, rd->soa.min);
287 break;
288
289 case kDNSType_HINFO: // Display this the same as TXT (show all constituent strings)
290 case kDNSType_TXT: {
291 const mDNSu8 *t = rd->txt.c;
292 while (t < rd->txt.c + rr->rdlength)
293 {
294 length += mDNS_snprintf(buffer+length, RemSpc, "%s%#s", t > rd->txt.c ? "¦" : "", t);
295 t += 1 + t[0];
296 }
297 } break;
298
299 case kDNSType_AAAA: mDNS_snprintf(buffer+length, RemSpc, "%.16a", &rd->ipv6); break;
300 case kDNSType_SRV: mDNS_snprintf(buffer+length, RemSpc, "%u %u %u %##s",
301 rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break;
302
303 case kDNSType_OPT: {
304 const rdataOPT *opt;
305 const rdataOPT *const end = (const rdataOPT *)&rd->data[rr->rdlength];
306 length += mDNS_snprintf(buffer+length, RemSpc, "Max %d", rr->rrclass);
307 for (opt = &rd->opt[0]; opt < end; opt++)
308 {
309 switch(opt->opt)
310 {
311 case kDNSOpt_LLQ:
312 length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.llq.vers);
313 length += mDNS_snprintf(buffer+length, RemSpc, " Op %d", opt->u.llq.llqOp);
314 length += mDNS_snprintf(buffer+length, RemSpc, " Err/Port %d", opt->u.llq.err);
315 length += mDNS_snprintf(buffer+length, RemSpc, " ID %08X%08X", opt->u.llq.id.l[0], opt->u.llq.id.l[1]);
316 length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.llq.llqlease);
317 break;
318 case kDNSOpt_Lease:
319 length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.updatelease);
320 break;
321 case kDNSOpt_Owner:
322 length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.owner.vers);
323 length += mDNS_snprintf(buffer+length, RemSpc, " Seq %3d", (mDNSu8)opt->u.owner.seq); // Display as unsigned
324 length += mDNS_snprintf(buffer+length, RemSpc, " MAC %.6a", opt->u.owner.HMAC.b);
325 if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
326 {
327 length += mDNS_snprintf(buffer+length, RemSpc, " I-MAC %.6a", opt->u.owner.IMAC.b);
328 if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
329 length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b);
330 }
331 break;
332 default:
333 length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d", opt->opt);
334 break;
335 }
336 }
337 }
338 break;
339
340 case kDNSType_NSEC: {
341 domainname *next = (domainname *)rd->data;
342 int len, bitmaplen;
343 int win, wlen, type;
344 mDNSu8 *bmap;
345 len = DomainNameLength(next);
346 bitmaplen = rr->rdlength - len;
347 bmap = (mDNSu8 *)((mDNSu8 *)next + len);
348
349 if (UNICAST_NSEC(rr))
350 length += mDNS_snprintf(buffer+length, RemSpc, "%##s ", next->c);
351
352 while (bitmaplen > 0)
353 {
354 int i;
355
356 if (bitmaplen < 3)
357 {
358 LogMsg("GetRRDisplayString_rdb: malformed nsec, bitmaplen %d short", bitmaplen);
359 break;
360 }
361
362 win = *bmap++;
363 wlen = *bmap++;
364 bitmaplen -= 2;
365 if (bitmaplen < wlen || wlen < 1 || wlen > 32)
366 {
367 LogInfo("GetRRDisplayString_rdb: malformed nsec, bitmaplen %d wlen %d", bitmaplen, wlen);
368 break;
369 }
370 if (win < 0 || win >= 256)
371 {
372 LogInfo("GetRRDisplayString_rdb: malformed nsec, bad window win %d", win);
373 break;
374 }
375 type = win * 256;
376 for (i = 0; i < wlen * 8; i++)
377 {
378 if (bmap[i>>3] & (128 >> (i&7)))
379 length += mDNS_snprintf(buffer+length, RemSpc, "%s ", DNSTypeName(type + i));
380 }
381 bmap += wlen;
382 bitmaplen -= wlen;
383 }
384 }
385 break;
386 case kDNSType_RRSIG: {
387 rdataRRSig *rrsig = (rdataRRSig *)rd->data;
388 mDNSu8 expTimeBuf[64];
389 mDNSu8 inceptTimeBuf[64];
390 unsigned long inceptClock;
391 unsigned long expClock;
392 int len;
393
394 expClock = (unsigned long)swap32(rrsig->sigExpireTime);
395 mDNSPlatformFormatTime(expClock, expTimeBuf, sizeof(expTimeBuf));
396
397 inceptClock = (unsigned long)swap32(rrsig->sigInceptTime);
398 mDNSPlatformFormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf));
399
400 length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %s %d %d %s %s %d %##s",
401 DNSTypeName(swap16(rrsig->typeCovered)), DNSSECAlgName(rrsig->alg), rrsig->labels, swap32(rrsig->origTTL),
402 expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag), ((domainname *)(&rrsig->signerName))->c);
403
404 len = DomainNameLength((domainname *)&rrsig->signerName);
405 length += base64Encode(buffer + length, RemSpc, (mDNSu8 *)(rd->data + len + RRSIG_FIXED_SIZE),
406 rr->rdlength - (len + RRSIG_FIXED_SIZE));
407 }
408 break;
409 case kDNSType_DNSKEY: {
410 rdataDNSKey *rrkey = (rdataDNSKey *)rd->data;
411 length += mDNS_snprintf(buffer+length, RemSpc, "\t%d %d %s %u", swap16(rrkey->flags), rrkey->proto,
412 DNSSECAlgName(rrkey->alg), (unsigned int)keytag((mDNSu8 *)rrkey, rr->rdlength));
413 length += base64Encode(buffer + length, RemSpc, (mDNSu8 *)(rd->data + DNSKEY_FIXED_SIZE),
414 rr->rdlength - DNSKEY_FIXED_SIZE);
415 }
416 break;
417 case kDNSType_DS: {
418 mDNSu8 *p;
419 int i;
420 rdataDS *rrds = (rdataDS *)rd->data;
421
422 length += mDNS_snprintf(buffer+length, RemSpc, "\t%s\t%d\t%s ", DNSSECAlgName(rrds->alg), swap16(rrds->keyTag),
423 DNSSECDigestName(rrds->digestType));
424
425 p = (mDNSu8 *)(rd->data + DS_FIXED_SIZE);
426 for (i = 0; i < (rr->rdlength - DS_FIXED_SIZE); i++)
427 {
428 length += mDNS_snprintf(buffer+length, RemSpc, "%x", p[i]);
429 }
430 }
431 break;
432
433 default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %s", rr->rdlength, rd->data);
434 // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
435 for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.';
436 break;
437 }
438 return(buffer);
439 }
440
441 // See comments in mDNSEmbeddedAPI.h
442 #if _PLATFORM_HAS_STRONG_PRNG_
443 #define mDNSRandomNumber mDNSPlatformRandomNumber
444 #else
445 mDNSlocal mDNSu32 mDNSRandomFromSeed(mDNSu32 seed)
446 {
447 return seed * 21 + 1;
448 }
449
450 mDNSlocal mDNSu32 mDNSMixRandomSeed(mDNSu32 seed, mDNSu8 iteration)
451 {
452 return iteration ? mDNSMixRandomSeed(mDNSRandomFromSeed(seed), --iteration) : seed;
453 }
454
455 mDNSlocal mDNSu32 mDNSRandomNumber()
456 {
457 static mDNSBool seeded = mDNSfalse;
458 static mDNSu32 seed = 0;
459 if (!seeded)
460 {
461 seed = mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100);
462 seeded = mDNStrue;
463 }
464 return (seed = mDNSRandomFromSeed(seed));
465 }
466 #endif // ! _PLATFORM_HAS_STRONG_PRNG_
467
468 mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) // Returns pseudo-random result from zero to max inclusive
469 {
470 mDNSu32 ret = 0;
471 mDNSu32 mask = 1;
472
473 while (mask < max) mask = (mask << 1) | 1;
474
475 do ret = mDNSRandomNumber() & mask;
476 while (ret > max);
477
478 return ret;
479 }
480
481 mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
482 {
483 if (ip1->type == ip2->type)
484 {
485 switch (ip1->type)
486 {
487 case mDNSAddrType_None: return(mDNStrue); // Empty addresses have no data and are therefore always equal
488 case mDNSAddrType_IPv4: return (mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4));
489 case mDNSAddrType_IPv6: return (mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6));
490 }
491 }
492 return(mDNSfalse);
493 }
494
495 mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
496 {
497 switch(ip->type)
498 {
499 case mDNSAddrType_IPv4: return (mDNSBool)(mDNSSameIPv4Address(ip->ip.v4, AllDNSLinkGroup_v4.ip.v4));
500 case mDNSAddrType_IPv6: return (mDNSBool)(mDNSSameIPv6Address(ip->ip.v6, AllDNSLinkGroup_v6.ip.v6));
501 default: return(mDNSfalse);
502 }
503 }
504
505 // ***************************************************************************
506 #if COMPILER_LIKES_PRAGMA_MARK
507 #pragma mark -
508 #pragma mark - Domain Name Utility Functions
509 #endif
510
511 mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
512 {
513 int i;
514 const int len = *a++;
515
516 if (len > MAX_DOMAIN_LABEL)
517 { debugf("Malformed label (too long)"); return(mDNSfalse); }
518
519 if (len != *b++) return(mDNSfalse);
520 for (i=0; i<len; i++)
521 {
522 mDNSu8 ac = *a++;
523 mDNSu8 bc = *b++;
524 if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
525 if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
526 if (ac != bc) return(mDNSfalse);
527 }
528 return(mDNStrue);
529 }
530
531 mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
532 {
533 const mDNSu8 * a = d1->c;
534 const mDNSu8 * b = d2->c;
535 const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid
536
537 while (*a || *b)
538 {
539 if (a + 1 + *a >= max)
540 { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse); }
541 if (!SameDomainLabel(a, b)) return(mDNSfalse);
542 a += 1 + *a;
543 b += 1 + *b;
544 }
545
546 return(mDNStrue);
547 }
548
549 mDNSexport mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2)
550 {
551 mDNSu16 l1 = DomainNameLength(d1);
552 mDNSu16 l2 = DomainNameLength(d2);
553 return(l1 <= MAX_DOMAIN_NAME && l1 == l2 && mDNSPlatformMemSame(d1, d2, l1));
554 }
555
556 mDNSexport mDNSBool IsLocalDomain(const domainname *d)
557 {
558 // Domains that are defined to be resolved via link-local multicast are:
559 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
560 static const domainname *nL = (const domainname*)"\x5" "local";
561 static const domainname *nR = (const domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
562 static const domainname *n8 = (const domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
563 static const domainname *n9 = (const domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
564 static const domainname *nA = (const domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
565 static const domainname *nB = (const domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
566
567 const domainname *d1, *d2, *d3, *d4, *d5; // Top-level domain, second-level domain, etc.
568 d1 = d2 = d3 = d4 = d5 = mDNSNULL;
569 while (d->c[0])
570 {
571 d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
572 d = (const domainname*)(d->c + 1 + d->c[0]);
573 }
574
575 if (d1 && SameDomainName(d1, nL)) return(mDNStrue);
576 if (d4 && SameDomainName(d4, nR)) return(mDNStrue);
577 if (d5 && SameDomainName(d5, n8)) return(mDNStrue);
578 if (d5 && SameDomainName(d5, n9)) return(mDNStrue);
579 if (d5 && SameDomainName(d5, nA)) return(mDNStrue);
580 if (d5 && SameDomainName(d5, nB)) return(mDNStrue);
581 return(mDNSfalse);
582 }
583
584 mDNSexport const mDNSu8 *LastLabel(const domainname *d)
585 {
586 const mDNSu8 *p = d->c;
587 while (d->c[0])
588 {
589 p = d->c;
590 d = (const domainname*)(d->c + 1 + d->c[0]);
591 }
592 return(p);
593 }
594
595 // Returns length of a domain name INCLUDING the byte for the final null label
596 // e.g. for the root label "." it returns one
597 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
598 // Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME)
599 // If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1)
600 mDNSexport mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit)
601 {
602 const mDNSu8 *src = name->c;
603 while (src < limit && *src <= MAX_DOMAIN_LABEL)
604 {
605 if (*src == 0) return((mDNSu16)(src - name->c + 1));
606 src += 1 + *src;
607 }
608 return(MAX_DOMAIN_NAME+1);
609 }
610
611 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
612 // for the final null label, e.g. for the root label "." it returns one.
613 // E.g. for the FQDN "foo.com." it returns 9
614 // (length, three data bytes, length, three more data bytes, final zero).
615 // In the case where a parent domain name is provided, and the given name is a child
616 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
617 // of the child name, plus TWO bytes for the compression pointer.
618 // E.g. for the name "foo.com." with parent "com.", it returns 6
619 // (length, three data bytes, two-byte compression pointer).
620 mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
621 {
622 const mDNSu8 *src = name->c;
623 if (parent && parent->c[0] == 0) parent = mDNSNULL;
624 while (*src)
625 {
626 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
627 if (parent && SameDomainName((const domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
628 src += 1 + *src;
629 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
630 }
631 return((mDNSu16)(src - name->c + 1));
632 }
633
634 // CountLabels() returns number of labels in name, excluding final root label
635 // (e.g. for "apple.com." CountLabels returns 2.)
636 mDNSexport int CountLabels(const domainname *d)
637 {
638 int count = 0;
639 const mDNSu8 *ptr;
640 for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++;
641 return count;
642 }
643
644 // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
645 // returning a pointer to the suffix with 'skip' labels removed.
646 mDNSexport const domainname *SkipLeadingLabels(const domainname *d, int skip)
647 {
648 while (skip > 0 && d->c[0]) { d = (const domainname *)(d->c + 1 + d->c[0]); skip--; }
649 return(d);
650 }
651
652 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
653 // The C string contains the label as-is, with no escaping, etc.
654 // Any dots in the name are literal dots, not label separators
655 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
656 // in the domainname bufer (i.e. the next byte after the terminating zero).
657 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
658 // AppendLiteralLabelString returns mDNSNULL.
659 mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr)
660 {
661 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
662 const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
663 const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL;
664 const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2;
665 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
666
667 while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data
668 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
669 *ptr++ = 0; // Put the null root label on the end
670 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
671 else return(ptr); // Success: return new value of ptr
672 }
673
674 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
675 // The C string is in conventional DNS syntax:
676 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
677 // If successful, AppendDNSNameString returns a pointer to the next unused byte
678 // in the domainname bufer (i.e. the next byte after the terminating zero).
679 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
680 // AppendDNSNameString returns mDNSNULL.
681 mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring)
682 {
683 const char *cstr = cstring;
684 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
685 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
686 while (*cstr && ptr < lim) // While more characters, and space to put them...
687 {
688 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
689 if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); }
690 while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label...
691 {
692 mDNSu8 c = (mDNSu8)*cstr++; // Read the character
693 if (c == '\\') // If escape character, check next character
694 {
695 c = (mDNSu8)*cstr++; // Assume we'll just take the next character
696 if (mDNSIsDigit(cstr[-1]) && mDNSIsDigit(cstr[0]) && mDNSIsDigit(cstr[1]))
697 { // If three decimal digits,
698 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
699 int v1 = cstr[ 0] - '0';
700 int v2 = cstr[ 1] - '0';
701 int val = v0 * 100 + v1 * 10 + v2;
702 if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it
703 }
704 }
705 *ptr++ = c; // Write the character
706 }
707 if (*cstr) cstr++; // Skip over the trailing dot (if present)
708 if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort
709 return(mDNSNULL);
710 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
711 }
712
713 *ptr++ = 0; // Put the null root label on the end
714 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
715 else return(ptr); // Success: return new value of ptr
716 }
717
718 // AppendDomainLabel appends a single label to a name.
719 // If successful, AppendDomainLabel returns a pointer to the next unused byte
720 // in the domainname bufer (i.e. the next byte after the terminating zero).
721 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
722 // AppendDomainLabel returns mDNSNULL.
723 mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label)
724 {
725 int i;
726 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
727
728 // Check label is legal
729 if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
730
731 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
732 if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL);
733
734 for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data
735 *ptr++ = 0; // Put the null root label on the end
736 return(ptr);
737 }
738
739 mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
740 {
741 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
742 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
743 const mDNSu8 * src = append->c;
744 while (src[0])
745 {
746 int i;
747 if (ptr + 1 + src[0] > lim) return(mDNSNULL);
748 for (i=0; i<=src[0]; i++) *ptr++ = src[i];
749 *ptr = 0; // Put the null root label on the end
750 src += i;
751 }
752 return(ptr);
753 }
754
755 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
756 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
757 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
758 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
759 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
760 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
761 mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr)
762 {
763 mDNSu8 * ptr = label->c + 1; // Where we're putting it
764 const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put
765 while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label
766 label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte
767 return(*cstr == 0); // Return mDNStrue if we successfully consumed all input
768 }
769
770 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
771 // The C string is in conventional DNS syntax:
772 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
773 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
774 // in the domainname bufer (i.e. the next byte after the terminating zero).
775 // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
776 // MakeDomainNameFromDNSNameString returns mDNSNULL.
777 mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr)
778 {
779 name->c[0] = 0; // Make an empty domain name
780 return(AppendDNSNameString(name, cstr)); // And then add this string to it
781 }
782
783 mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
784 {
785 const mDNSu8 * src = label->c; // Domain label we're reading
786 const mDNSu8 len = *src++; // Read length of this (non-null) label
787 const mDNSu8 *const end = src + len; // Work out where the label ends
788 if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
789 while (src < end) // While we have characters in the label
790 {
791 mDNSu8 c = *src++;
792 if (esc)
793 {
794 if (c == '.' || c == esc) // If character is a dot or the escape character
795 *ptr++ = esc; // Output escape character
796 else if (c <= ' ') // If non-printing ascii,
797 { // Output decimal escape sequence
798 *ptr++ = esc;
799 *ptr++ = (char) ('0' + (c / 100) );
800 *ptr++ = (char) ('0' + (c / 10) % 10);
801 c = (mDNSu8)('0' + (c ) % 10);
802 }
803 }
804 *ptr++ = (char)c; // Copy the character
805 }
806 *ptr = 0; // Null-terminate the string
807 return(ptr); // and return
808 }
809
810 // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1009 bytes)
811 mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
812 {
813 const mDNSu8 *src = name->c; // Domain name we're reading
814 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
815
816 if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot
817
818 while (*src) // While more characters in the domain name
819 {
820 if (src + 1 + *src >= max) return(mDNSNULL);
821 ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
822 if (!ptr) return(mDNSNULL);
823 src += 1 + *src;
824 *ptr++ = '.'; // Write the dot after the label
825 }
826
827 *ptr++ = 0; // Null-terminate the string
828 return(ptr); // and return
829 }
830
831 // RFC 1034 rules:
832 // Host names must start with a letter, end with a letter or digit,
833 // and have as interior characters only letters, digits, and hyphen.
834 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
835
836 mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
837 {
838 const mDNSu8 * src = &UTF8Name[1];
839 const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0];
840 mDNSu8 * ptr = &hostlabel->c[1];
841 const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL;
842 while (src < end)
843 {
844 // Delete apostrophes from source name
845 if (src[0] == '\'') { src++; continue; } // Standard straight single quote
846 if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99)
847 { src += 3; continue; } // Unicode curly apostrophe
848 if (ptr < lim)
849 {
850 if (mDNSValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
851 else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
852 }
853 src++;
854 }
855 while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks
856 hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
857 }
858
859 #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
860 ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
861 ((X)[4] | 0x20) == 'p')
862
863 mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
864 const domainlabel *name, const domainname *type, const domainname *const domain)
865 {
866 int i, len;
867 mDNSu8 *dst = fqdn->c;
868 const mDNSu8 *src;
869 const char *errormsg;
870 #if APPLE_OSX_mDNSResponder
871 mDNSBool loggedUnderscore = mDNSfalse;
872 static char typeBuf[MAX_ESCAPED_DOMAIN_NAME];
873 #endif
874
875 // In the case where there is no name (and ONLY in that case),
876 // a single-label subtype is allowed as the first label of a three-part "type"
877 if (!name && type)
878 {
879 const mDNSu8 *s0 = type->c;
880 if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63)
881 {
882 const mDNSu8 * s1 = s0 + 1 + s0[0];
883 if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63)
884 {
885 const mDNSu8 *s2 = s1 + 1 + s1[0];
886 if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels
887 {
888 static const mDNSu8 SubTypeLabel[5] = "\x04_sub";
889 src = s0; // Copy the first label
890 len = *src;
891 for (i=0; i <= len; i++) *dst++ = *src++;
892 for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i];
893 type = (const domainname *)s1;
894
895 // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
896 // For these queries, we retract the "._sub" we just added between the subtype and the main type
897 // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
898 if (SameDomainName((domainname*)s0, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
899 dst -= sizeof(SubTypeLabel);
900 }
901 }
902 }
903 }
904
905 if (name && name->c[0])
906 {
907 src = name->c; // Put the service name into the domain name
908 len = *src;
909 if (len >= 0x40) { errormsg = "Service instance name too long"; goto fail; }
910 for (i=0; i<=len; i++) *dst++ = *src++;
911 }
912 else
913 name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
914
915 src = type->c; // Put the service type into the domain name
916 len = *src;
917 if (len < 2 || len > 16)
918 {
919 LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-15 characters. "
920 "See <http://www.dns-sd.org/ServiceTypes.html>", name->c, type->c, domain->c);
921 #if APPLE_OSX_mDNSResponder
922 ConvertDomainNameToCString(type, typeBuf);
923 mDNSASLLog(mDNSNULL, "serviceType.nameTooLong", "noop", typeBuf, "");
924 #endif
925 }
926 if (len < 2 || len >= 0x40 || (len > 16 && !SameDomainName(domain, &localdomain))) return(mDNSNULL);
927 if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; }
928 for (i=2; i<=len; i++)
929 {
930 // Letters and digits are allowed anywhere
931 if (mDNSIsLetter(src[i]) || mDNSIsDigit(src[i])) continue;
932 // Hyphens are only allowed as interior characters
933 // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
934 // with the same rule as hyphens
935 if ((src[i] == '-' || src[i] == '_') && i > 2 && i < len)
936 {
937 #if APPLE_OSX_mDNSResponder
938 if (src[i] == '_' && loggedUnderscore == mDNSfalse)
939 {
940 ConvertDomainNameToCString(type, typeBuf);
941 mDNSASLLog(mDNSNULL, "serviceType.nameWithUnderscore", "noop", typeBuf, "");
942 loggedUnderscore = mDNStrue;
943 }
944 #endif
945 continue;
946 }
947 errormsg = "Application protocol name must contain only letters, digits, and hyphens";
948 #if APPLE_OSX_mDNSResponder
949 {
950 ConvertDomainNameToCString(type, typeBuf);
951 mDNSASLLog(mDNSNULL, "serviceType.nameWithIllegalCharacters", "noop", typeBuf, "");
952 }
953 #endif
954 goto fail;
955 }
956 for (i=0; i<=len; i++) *dst++ = *src++;
957
958 len = *src;
959 if (!ValidTransportProtocol(src)) { errormsg = "Transport protocol name must be _udp or _tcp"; goto fail; }
960 for (i=0; i<=len; i++) *dst++ = *src++;
961
962 if (*src) { errormsg = "Service type must have only two labels"; goto fail; }
963
964 *dst = 0;
965 if (!domain->c[0]) { errormsg = "Service domain must be non-empty"; goto fail; }
966 if (SameDomainName(domain, (const domainname*)"\x05" "local" "\x04" "arpa"))
967 { errormsg = "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; }
968 dst = AppendDomainName(fqdn, domain);
969 if (!dst) { errormsg = "Service domain too long"; goto fail; }
970 return(dst);
971
972 fail:
973 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
974 return(mDNSNULL);
975 }
976
977 // A service name has the form: instance.application-protocol.transport-protocol.domain
978 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
979 // set or length limits for the protocol names, and the final domain is allowed to be empty.
980 // However, if the given FQDN doesn't contain at least three labels,
981 // DeconstructServiceName will reject it and return mDNSfalse.
982 mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
983 domainlabel *const name, domainname *const type, domainname *const domain)
984 {
985 int i, len;
986 const mDNSu8 *src = fqdn->c;
987 const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
988 mDNSu8 *dst;
989
990 dst = name->c; // Extract the service name
991 len = *src;
992 if (!len) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse); }
993 if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); }
994 for (i=0; i<=len; i++) *dst++ = *src++;
995
996 dst = type->c; // Extract the service type
997 len = *src;
998 if (!len) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse); }
999 if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); }
1000 if (src[1] != '_') { debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse); }
1001 for (i=0; i<=len; i++) *dst++ = *src++;
1002
1003 len = *src;
1004 if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); }
1005 if (!ValidTransportProtocol(src))
1006 { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); }
1007 for (i=0; i<=len; i++) *dst++ = *src++;
1008 *dst++ = 0; // Put terminator on the end of service type
1009
1010 dst = domain->c; // Extract the service domain
1011 while (*src)
1012 {
1013 len = *src;
1014 if (len >= 0x40)
1015 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); }
1016 if (src + 1 + len + 1 >= max)
1017 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); }
1018 for (i=0; i<=len; i++) *dst++ = *src++;
1019 }
1020 *dst++ = 0; // Put the null root label on the end
1021
1022 return(mDNStrue);
1023 }
1024
1025 // Notes on UTF-8:
1026 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
1027 // 10xxxxxx is a continuation byte of a multi-byte character
1028 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
1029 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
1030 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
1031 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
1032 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
1033 //
1034 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
1035 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
1036 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
1037 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
1038 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
1039
1040 mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
1041 {
1042 if (length > max)
1043 {
1044 mDNSu8 c1 = string[max]; // First byte after cut point
1045 mDNSu8 c2 = (max+1 < length) ? string[max+1] : (mDNSu8)0xB0; // Second byte after cut point
1046 length = max; // Trim length down
1047 while (length > 0)
1048 {
1049 // Check if the byte right after the chop point is a UTF-8 continuation byte,
1050 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
1051 // If so, then we continue to chop more bytes until we get to a legal chop point.
1052 mDNSBool continuation = ((c1 & 0xC0) == 0x80);
1053 mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0);
1054 if (!continuation && !secondsurrogate) break;
1055 c2 = c1;
1056 c1 = string[--length];
1057 }
1058 // Having truncated characters off the end of our string, also cut off any residual white space
1059 while (length > 0 && string[length-1] <= ' ') length--;
1060 }
1061 return(length);
1062 }
1063
1064 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
1065 // name ends in "-nnn", where n is some decimal number.
1066 mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
1067 {
1068 mDNSu16 l = name->c[0];
1069
1070 if (RichText)
1071 {
1072 if (l < 4) return mDNSfalse; // Need at least " (2)"
1073 if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')'
1074 if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit
1075 l--;
1076 while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits
1077 return (name->c[l] == '(' && name->c[l - 1] == ' ');
1078 }
1079 else
1080 {
1081 if (l < 2) return mDNSfalse; // Need at least "-2"
1082 if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit
1083 l--;
1084 while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits
1085 return (name->c[l] == '-');
1086 }
1087 }
1088
1089 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
1090 // responsible for ensuring that the label does indeed contain a suffix. returns the number
1091 // from the suffix that was removed.
1092 mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText)
1093 {
1094 mDNSu32 val = 0, multiplier = 1;
1095
1096 // Chop closing parentheses from RichText suffix
1097 if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
1098
1099 // Get any existing numerical suffix off the name
1100 while (mDNSIsDigit(name->c[name->c[0]]))
1101 { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
1102
1103 // Chop opening parentheses or dash from suffix
1104 if (RichText)
1105 {
1106 if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
1107 }
1108 else
1109 {
1110 if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
1111 }
1112
1113 return(val);
1114 }
1115
1116 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
1117 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
1118 mDNSexport void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText)
1119 {
1120 mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
1121 if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)")
1122
1123 // Truncate trailing spaces from RichText names
1124 if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
1125
1126 while (divisor < 0xFFFFFFFFUL/10 && val >= divisor * 10) { divisor *= 10; chars++; }
1127
1128 name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
1129
1130 if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
1131 else { name->c[++name->c[0]] = '-'; }
1132
1133 while (divisor)
1134 {
1135 name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
1136 val %= divisor;
1137 divisor /= 10;
1138 }
1139
1140 if (RichText) name->c[++name->c[0]] = ')';
1141 }
1142
1143 mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
1144 {
1145 mDNSu32 val = 0;
1146
1147 if (LabelContainsSuffix(name, RichText))
1148 val = RemoveLabelSuffix(name, RichText);
1149
1150 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
1151 // If existing suffix in the range 2-9, increment it.
1152 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
1153 // so add a random increment to improve the chances of finding an available name next time.
1154 if (val == 0) val = 2;
1155 else if (val < 10) val++;
1156 else val += 1 + mDNSRandom(99);
1157
1158 AppendLabelSuffix(name, val, RichText);
1159 }
1160
1161 // ***************************************************************************
1162 #if COMPILER_LIKES_PRAGMA_MARK
1163 #pragma mark -
1164 #pragma mark - Resource Record Utility Functions
1165 #endif
1166
1167 // Set up a AuthRecord with sensible default values.
1168 // These defaults may be overwritten with new values before mDNS_Register is called
1169 mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
1170 mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context)
1171 {
1172 //
1173 // LocalOnly auth record can be created with LocalOnly InterfaceID or a valid InterfaceID.
1174 // Most of the applications normally create with LocalOnly InterfaceID and we store them as
1175 // such, so that we can deliver the response to questions that specify LocalOnly InterfaceID.
1176 // LocalOnly resource records can also be created with valid InterfaceID which happens today
1177 // when we create LocalOnly records for /etc/hosts.
1178
1179 if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly)
1180 {
1181 LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype);
1182 return;
1183 }
1184 else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P)
1185 {
1186 LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype);
1187 return;
1188 }
1189 else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly))
1190 {
1191 LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype);
1192 return;
1193 }
1194
1195 // Don't try to store a TTL bigger than we can represent in platform time units
1196 if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond)
1197 ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond;
1198 else if (ttl == 0) // And Zero TTL is illegal
1199 ttl = DefaultTTLforRRType(rrtype);
1200
1201 // Field Group 1: The actual information pertaining to this resource record
1202 rr->resrec.RecordType = RecordType;
1203 rr->resrec.InterfaceID = InterfaceID;
1204 rr->resrec.name = &rr->namestorage;
1205 rr->resrec.rrtype = rrtype;
1206 rr->resrec.rrclass = kDNSClass_IN;
1207 rr->resrec.rroriginalttl = ttl;
1208 rr->resrec.rDNSServer = mDNSNULL;
1209 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
1210 // rr->resrec.rdestimate = set in mDNS_Register_internal
1211 // rr->resrec.rdata = MUST be set by client
1212
1213 if (RDataStorage)
1214 rr->resrec.rdata = RDataStorage;
1215 else
1216 {
1217 rr->resrec.rdata = &rr->rdatastorage;
1218 rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
1219 }
1220
1221 // Field Group 2: Persistent metadata for Authoritative Records
1222 rr->Additional1 = mDNSNULL;
1223 rr->Additional2 = mDNSNULL;
1224 rr->DependentOn = mDNSNULL;
1225 rr->RRSet = mDNSNULL;
1226 rr->RecordCallback = Callback;
1227 rr->RecordContext = Context;
1228
1229 rr->AutoTarget = Target_Manual;
1230 rr->AllowRemoteQuery = mDNSfalse;
1231 rr->ForceMCast = mDNSfalse;
1232
1233 rr->WakeUp = zeroOwner;
1234 rr->AddressProxy = zeroAddr;
1235 rr->TimeRcvd = 0;
1236 rr->TimeExpire = 0;
1237 rr->ARType = artype;
1238
1239 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
1240 // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
1241
1242 // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
1243 // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
1244 // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
1245 rr->state = regState_Zero;
1246 rr->uselease = 0;
1247 rr->expire = 0;
1248 rr->Private = 0;
1249 rr->updateid = zeroID;
1250 rr->zone = rr->resrec.name;
1251 rr->nta = mDNSNULL;
1252 rr->tcp = mDNSNULL;
1253 rr->OrigRData = 0;
1254 rr->OrigRDLen = 0;
1255 rr->InFlightRData = 0;
1256 rr->InFlightRDLen = 0;
1257 rr->QueuedRData = 0;
1258 rr->QueuedRDLen = 0;
1259 mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo));
1260 rr->SRVChanged = mDNSfalse;
1261 rr->mState = mergeState_Zero;
1262
1263 rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register()
1264 }
1265
1266 mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name,
1267 const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context)
1268 {
1269 q->InterfaceID = InterfaceID;
1270 q->flags = 0;
1271 q->Target = zeroAddr;
1272 AssignDomainName(&q->qname, name);
1273 q->qtype = qtype;
1274 q->qclass = kDNSClass_IN;
1275 q->LongLived = (qtype == kDNSType_PTR);
1276 q->ExpectUnique = (qtype != kDNSType_PTR);
1277 q->ForceMCast = mDNSfalse;
1278 q->ReturnIntermed = mDNSfalse;
1279 q->SuppressUnusable = mDNSfalse;
1280 q->SearchListIndex = 0;
1281 q->AppendSearchDomains = 0;
1282 q->RetryWithSearchDomains = mDNSfalse;
1283 q->TimeoutQuestion = 0;
1284 q->WakeOnResolve = 0;
1285 q->UseBrackgroundTrafficClass = mDNSfalse;
1286 q->ValidationRequired = 0;
1287 q->ValidatingResponse = 0;
1288 q->qnameOrig = mDNSNULL;
1289 q->QuestionCallback = callback;
1290 q->QuestionContext = context;
1291 }
1292
1293 mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr)
1294 {
1295 int len = rr->rdlength;
1296 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
1297 const mDNSu8 *ptr = rdb->data;
1298 mDNSu32 sum = 0;
1299
1300 switch(rr->rrtype)
1301 {
1302 case kDNSType_NS:
1303 case kDNSType_MD:
1304 case kDNSType_MF:
1305 case kDNSType_CNAME:
1306 case kDNSType_MB:
1307 case kDNSType_MG:
1308 case kDNSType_MR:
1309 case kDNSType_PTR:
1310 case kDNSType_NSAP_PTR:
1311 case kDNSType_DNAME: return DomainNameHashValue(&rdb->name);
1312
1313 case kDNSType_SOA: return rdb->soa.serial +
1314 rdb->soa.refresh +
1315 rdb->soa.retry +
1316 rdb->soa.expire +
1317 rdb->soa.min +
1318 DomainNameHashValue(&rdb->soa.mname) +
1319 DomainNameHashValue(&rdb->soa.rname);
1320
1321 case kDNSType_MX:
1322 case kDNSType_AFSDB:
1323 case kDNSType_RT:
1324 case kDNSType_KX: return DomainNameHashValue(&rdb->mx.exchange);
1325
1326 case kDNSType_MINFO:
1327 case kDNSType_RP: return DomainNameHashValue(&rdb->rp.mbox) + DomainNameHashValue(&rdb->rp.txt);
1328
1329 case kDNSType_PX: return DomainNameHashValue(&rdb->px.map822) + DomainNameHashValue(&rdb->px.mapx400);
1330
1331 case kDNSType_SRV: return DomainNameHashValue(&rdb->srv.target);
1332
1333 case kDNSType_OPT: return 0; // OPT is a pseudo-RR container structure; makes no sense to compare
1334
1335 case kDNSType_NSEC: {
1336 int dlen;
1337 dlen = DomainNameLength((domainname *)rdb->data);
1338 sum = DomainNameHashValue((domainname *)rdb->data);
1339 ptr += dlen;
1340 len -= dlen;
1341 /* FALLTHROUGH */
1342 }
1343
1344 default:
1345 {
1346 int i;
1347 for (i=0; i+1 < len; i+=2)
1348 {
1349 sum += (((mDNSu32)(ptr[i])) << 8) | ptr[i+1];
1350 sum = (sum<<3) | (sum>>29);
1351 }
1352 if (i < len)
1353 {
1354 sum += ((mDNSu32)(ptr[i])) << 8;
1355 }
1356 return(sum);
1357 }
1358 }
1359 }
1360
1361 // r1 has to be a full ResourceRecord including rrtype and rdlength
1362 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
1363 mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename)
1364 {
1365 const RDataBody2 *const b1 = (RDataBody2 *)r1->rdata->u.data;
1366 const RDataBody2 *const b2 = (RDataBody2 *)r2;
1367 switch(r1->rrtype)
1368 {
1369 case kDNSType_NS:
1370 case kDNSType_MD:
1371 case kDNSType_MF:
1372 case kDNSType_CNAME:
1373 case kDNSType_MB:
1374 case kDNSType_MG:
1375 case kDNSType_MR:
1376 case kDNSType_PTR:
1377 case kDNSType_NSAP_PTR:
1378 case kDNSType_DNAME: return(SameDomainName(&b1->name, &b2->name));
1379
1380 case kDNSType_SOA: return (mDNSBool)( b1->soa.serial == b2->soa.serial &&
1381 b1->soa.refresh == b2->soa.refresh &&
1382 b1->soa.retry == b2->soa.retry &&
1383 b1->soa.expire == b2->soa.expire &&
1384 b1->soa.min == b2->soa.min &&
1385 samename(&b1->soa.mname, &b2->soa.mname) &&
1386 samename(&b1->soa.rname, &b2->soa.rname));
1387
1388 case kDNSType_MX:
1389 case kDNSType_AFSDB:
1390 case kDNSType_RT:
1391 case kDNSType_KX: return (mDNSBool)( b1->mx.preference == b2->mx.preference &&
1392 samename(&b1->mx.exchange, &b2->mx.exchange));
1393
1394 case kDNSType_MINFO:
1395 case kDNSType_RP: return (mDNSBool)( samename(&b1->rp.mbox, &b2->rp.mbox) &&
1396 samename(&b1->rp.txt, &b2->rp.txt));
1397
1398 case kDNSType_PX: return (mDNSBool)( b1->px.preference == b2->px.preference &&
1399 samename(&b1->px.map822, &b2->px.map822) &&
1400 samename(&b1->px.mapx400, &b2->px.mapx400));
1401
1402 case kDNSType_SRV: return (mDNSBool)( b1->srv.priority == b2->srv.priority &&
1403 b1->srv.weight == b2->srv.weight &&
1404 mDNSSameIPPort(b1->srv.port, b2->srv.port) &&
1405 samename(&b1->srv.target, &b2->srv.target));
1406
1407 case kDNSType_OPT: return mDNSfalse; // OPT is a pseudo-RR container structure; makes no sense to compare
1408 case kDNSType_NSEC: {
1409 // If the "nxt" name changes in case, we want to delete the old
1410 // and store just the new one. If the caller passes in SameDomainCS for "samename",
1411 // we would return "false" when the only change between the two rdata is the case
1412 // change in "nxt".
1413 //
1414 // Note: rdlength of both the RData are same (ensured by the caller) and hence we can
1415 // use just r1->rdlength below
1416
1417 int dlen1 = DomainNameLength((domainname *)b1->data);
1418 int dlen2 = DomainNameLength((domainname *)b2->data);
1419 return (mDNSBool)(dlen1 == dlen2 &&
1420 samename((domainname *)b1->data, (domainname *)b2->data) &&
1421 mDNSPlatformMemSame(b1->data + dlen1, b2->data + dlen2, r1->rdlength - dlen1));
1422 }
1423
1424 default: return(mDNSPlatformMemSame(b1->data, b2->data, r1->rdlength));
1425 }
1426 }
1427
1428 // Don't call this function if the resource record is not NSEC. It will return false
1429 // which means that the type does not exist.
1430 mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type)
1431 {
1432 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
1433 mDNSu8 *nsec = (mDNSu8 *)rdb->data;
1434 int len, bitmaplen;
1435 int win, wlen;
1436 mDNSu8 *bmap;
1437 int wintype;
1438
1439 if (rr->rrtype != kDNSType_NSEC) return mDNSfalse;
1440
1441 len = DomainNameLength((domainname *)nsec);
1442
1443 // The window that this type belongs to. NSEC has 256 windows that
1444 // comprises of 256 types.
1445 wintype = type >> 8;
1446
1447 bitmaplen = rr->rdlength - len;
1448 bmap = nsec + len;
1449 while (bitmaplen > 0)
1450 {
1451 if (bitmaplen < 3)
1452 {
1453 LogInfo("RRAssertsExistence: malformed nsec, bitmaplen %d short", bitmaplen);
1454 return mDNSfalse;
1455 }
1456
1457 win = *bmap++;
1458 wlen = *bmap++;
1459 bitmaplen -= 2;
1460 if (bitmaplen < wlen || wlen < 1 || wlen > 32)
1461 {
1462 LogInfo("RRAssertsExistence: malformed nsec, bitmaplen %d wlen %d, win %d", bitmaplen, wlen, win);
1463 return mDNSfalse;
1464 }
1465 if (win < 0 || win >= 256)
1466 {
1467 LogInfo("RRAssertsExistence: malformed nsec, wlen %d", wlen);
1468 return mDNSfalse;
1469 }
1470 if (win == wintype)
1471 {
1472 // First byte in the window serves 0 to 7, the next one serves 8 to 15 and so on.
1473 // Calculate the right byte offset first.
1474 int boff = (type & 0xff ) >> 3;
1475 if (wlen <= boff)
1476 return mDNSfalse;
1477 // The last three bits values 0 to 7 corresponds to bit positions
1478 // within the byte.
1479 return (bmap[boff] & (0x80 >> (type & 7)));
1480 }
1481 else
1482 {
1483 // If the windows are ordered, then we could check to see
1484 // if wintype > win and then return early.
1485 bmap += wlen;
1486 bitmaplen -= wlen;
1487 }
1488 }
1489 return mDNSfalse;
1490 }
1491
1492 // Don't call this function if the resource record is not NSEC. It will return false
1493 // which means that the type exists.
1494 mDNSexport mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type)
1495 {
1496 if (rr->rrtype != kDNSType_NSEC) return mDNSfalse;
1497
1498 return !RRAssertsExistence(rr, type);
1499 }
1500
1501 // Checks whether the RRSIG or NSEC record answers the question "q".
1502 mDNSlocal mDNSBool DNSSECRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q, mDNSBool *checkType)
1503 {
1504 *checkType = mDNStrue;
1505
1506 // This function is called for all questions and as long as the type matches,
1507 // return true. For the types (RRSIG and NSEC) that are specifically checked in
1508 // this function, returning true still holds good.
1509 if (q->qtype == rr->rrtype)
1510 return mDNStrue;
1511
1512 // If we are validating a response using DNSSEC, we might already have the records
1513 // for the "q->qtype" in the cache but we issued a query with DO bit set
1514 // to get the RRSIGs e.g., if you have two questions one of which does not require
1515 // DNSSEC validation. When the RRSIG is added to the cache, we need to deliver
1516 // the response to the question. The RRSIG type won't match the q->qtype and hence
1517 // we need to bypass the check in that case.
1518 if (rr->rrtype == kDNSType_RRSIG && q->ValidatingResponse)
1519 {
1520 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
1521 rdataRRSig *rrsig = (rdataRRSig *)rdb->data;
1522 if (q->qtype == kDNSType_CNAME || swap16(rrsig->typeCovered) != q->qtype)
1523 {
1524 debugf("DNSSECRecordAnswersQuestion: Question %##s (%s) did not match record %##s (RRSIG)", q->qname.c,
1525 DNSTypeName(q->qtype), rr->name->c);
1526 return mDNSfalse;
1527 }
1528 LogInfo("DNSSECRecordAnswersQuestion: Question %##s (%s) matched record %##s (RRSIG)", q->qname.c,
1529 DNSTypeName(q->qtype), rr->name->c);
1530 *checkType = mDNSfalse;
1531 return mDNStrue;
1532 }
1533 // If the NSEC record asserts the non-existence of a name looked up by the question, we would
1534 // typically answer that e.g., the bitmap asserts that q->qtype does not exist. If we have
1535 // to prove the non-existence as required by ValidatingResponse and ValidationRequired question,
1536 // then we should not answer that as it may not be the right one always. We may need more than
1537 // one NSEC to prove the non-existence.
1538 if (rr->rrtype == kDNSType_NSEC && DNSSECQuestion(q))
1539 {
1540 debugf("DNSSECRecordAnswersQuestion: Question %##s (%s) matched record %##s (NSEC)", q->qname.c,
1541 DNSTypeName(q->qtype), rr->name->c);
1542 return mDNSfalse;
1543 }
1544 return mDNStrue;
1545 }
1546
1547 // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
1548 // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
1549 // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
1550 // because it has to check all the way to the end of the names to be sure.
1551 // In cases where we know in advance that the names match it's especially advantageous to skip the
1552 // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
1553
1554 mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1555 {
1556 mDNSBool checkType = mDNStrue;
1557
1558 // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
1559 // are handled in LocalOnlyRecordAnswersQuestion
1560 if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
1561 {
1562 LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
1563 return mDNSfalse;
1564 }
1565 if (QuerySuppressed(q))
1566 return mDNSfalse;
1567
1568 if (rr->InterfaceID &&
1569 q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1570 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1571
1572 // Resource record received via unicast, the resolver group ID should match ?
1573 if (!rr->InterfaceID)
1574 {
1575 mDNSu16 idr = (rr->rDNSServer ? rr->rDNSServer->resGroupID : 0);
1576 mDNSu16 idq = (q->qDNSServer ? q->qDNSServer->resGroupID : 0);
1577 if (idr != idq) return(mDNSfalse);
1578 if (!DNSSECRecordAnswersQuestion(rr, q, &checkType)) return mDNSfalse;
1579 }
1580
1581 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1582 if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1583
1584 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1585 if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
1586 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1587
1588 #if APPLE_OSX_mDNSResponder
1589 if (!mDNSPlatformValidRecordForQuestion(rr, q))
1590 return mDNSfalse;
1591 #endif // APPLE_OSX_mDNSResponder
1592
1593 return(mDNStrue);
1594 }
1595
1596 mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1597 {
1598 if (!SameNameRecordAnswersQuestion(rr, q))
1599 return mDNSfalse;
1600
1601 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1602 }
1603
1604 // We have a separate function to handle LocalOnly AuthRecords because they can be created with
1605 // a valid InterfaceID (e.g., scoped /etc/hosts) and can be used to answer unicast questions unlike
1606 // multicast resource records (which has a valid InterfaceID) which can't be used to answer
1607 // unicast questions. ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion can't tell whether
1608 // a resource record is multicast or LocalOnly by just looking at the ResourceRecord because
1609 // LocalOnly records are truly identified by ARType in the AuthRecord. As P2P and LocalOnly record
1610 // are kept in the same hash table, we use the same function to make it easy for the callers when
1611 // they walk the hash table to answer LocalOnly/P2P questions
1612 //
1613 mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const DNSQuestion *const q)
1614 {
1615 ResourceRecord *rr = &ar->resrec;
1616
1617 // mDNSInterface_Any questions can be answered with LocalOnly/P2P records in this function. AuthRecord_Any
1618 // records are handled in ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion
1619 if (RRAny(ar))
1620 {
1621 LogMsg("LocalOnlyRecordAnswersQuestion: ERROR!! called with regular AuthRecordAny %##s", rr->name->c);
1622 return mDNSfalse;
1623 }
1624
1625 // Questions with mDNSInterface_LocalOnly InterfaceID should be answered with all resource records that are
1626 // *local* to the machine. These include resource records that have InterfaceID set to mDNSInterface_LocalOnly,
1627 // mDNSInterface_Any and any other real InterfaceID. Hence, LocalOnly questions should not be checked against
1628 // the InterfaceID in the resource record.
1629 //
1630 // mDNSInterface_Unicast does not indicate any scope and hence treat them like mDNSInterface_Any.
1631
1632 if (rr->InterfaceID &&
1633 q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && q->InterfaceID != mDNSInterface_Unicast &&
1634 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1635
1636 // Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records
1637 // may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set
1638 // to mDNSInterface_Any, mDNSInterface_LocalOnly or a real InterfaceID (scoped).
1639 //
1640 // 1) Question: Any, LocalOnly Record: no scope. This question should be answered with this record.
1641 //
1642 // 2) Question: Any, LocalOnly Record: scoped. This question should be answered with the record because
1643 // traditionally applications never specify scope e.g., getaddrinfo, but need to be able
1644 // to get to /etc/hosts entries.
1645 //
1646 // 3) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: no scope. This is the inverse of (2).
1647 // If we register a LocalOnly record, we need to answer a LocalOnly question. If the /etc/hosts has a
1648 // non scoped entry, it may not make sense to answer a scoped question. But we can't tell these two
1649 // cases apart. As we currently answer LocalOnly question with LocalOnly record, we continue to do so.
1650 //
1651 // 4) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: scoped. LocalOnly questions should be
1652 // answered with any resource record where as if it has a valid InterfaceID, the scope should match.
1653 //
1654 // (1) and (2) is bypassed because we check for a non-NULL InterfaceID above. For (3), the InterfaceID is NULL
1655 // and hence bypassed above. For (4) we bypassed LocalOnly questions and checked the scope of the record
1656 // against the question.
1657 //
1658 // For P2P, InterfaceIDs of the question and the record should match.
1659
1660 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
1661 // LocalOnly authoritative answers are exempt. LocalOnly authoritative answers are used for /etc/host entries.
1662 // We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com" which would then
1663 // cause other applications (e.g. Safari) to connect to the wrong address. The rpc to register records filters out records
1664 // with names that don't end in local and have mDNSInterface_LocalOnly set.
1665 //
1666 // Note: The check is bypassed for LocalOnly and for P2P it is not needed as only .local records are registered and for
1667 // a question to match its names, it also has to end in .local and that question can't be a unicast question (See
1668 // Question_uDNS macro and its usage). As P2P does not enforce .local only registrations we still make this check
1669 // and also makes it future proof.
1670
1671 if (ar->ARType != AuthRecordLocalOnly && rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1672
1673 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1674 if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
1675 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1676
1677 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1678 }
1679
1680 mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1681 {
1682 // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
1683 // are handled in LocalOnlyRecordAnswersQuestion
1684 if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
1685 {
1686 LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
1687 return mDNSfalse;
1688 }
1689 if (rr->InterfaceID &&
1690 q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1691 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1692
1693 // Resource record received via unicast, the resolver group ID should match ?
1694 // Note that Auth Records are normally setup with NULL InterfaceID and
1695 // both the DNSServers are assumed to be NULL in that case
1696 if (!rr->InterfaceID)
1697 {
1698 mDNSu16 idr = (rr->rDNSServer ? rr->rDNSServer->resGroupID : 0);
1699 mDNSu16 idq = (q->qDNSServer ? q->qDNSServer->resGroupID : 0);
1700 if (idr != idq) return(mDNSfalse);
1701 }
1702
1703 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1704 if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1705
1706 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1707
1708 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1709 }
1710
1711 // This is called with both unicast resource record and multicast resource record. The question that
1712 // received the unicast response could be the regular unicast response from a DNS server or a response
1713 // to a mDNS QU query. The main reason we need this function is that we can't compare DNSServers between the
1714 // question and the resource record because the resource record is not completely initialized in
1715 // mDNSCoreReceiveResponse when this function is called.
1716 mDNSexport mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q)
1717 {
1718 mDNSBool checkType = mDNStrue;
1719
1720 if (QuerySuppressed(q))
1721 return mDNSfalse;
1722
1723 // For resource records created using multicast, the InterfaceIDs have to match
1724 if (rr->InterfaceID &&
1725 q->InterfaceID && rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1726
1727 // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
1728 if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1729
1730 if (!DNSSECRecordAnswersQuestion(rr, q, &checkType)) return mDNSfalse;
1731
1732 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1733 if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
1734
1735 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1736
1737 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1738 }
1739
1740 mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
1741 {
1742 const RDataBody2 *const rd = (RDataBody2 *)rr->rdata->u.data;
1743 const domainname *const name = estimate ? rr->name : mDNSNULL;
1744 if (rr->rrclass == kDNSQClass_ANY) return(rr->rdlength); // Used in update packets to mean "Delete An RRset" (RFC 2136)
1745 else switch (rr->rrtype)
1746 {
1747 case kDNSType_A: return(sizeof(rd->ipv4));
1748
1749 case kDNSType_NS:
1750 case kDNSType_CNAME:
1751 case kDNSType_PTR:
1752 case kDNSType_DNAME: return(CompressedDomainNameLength(&rd->name, name));
1753
1754 case kDNSType_SOA: return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) +
1755 CompressedDomainNameLength(&rd->soa.rname, name) +
1756 5 * sizeof(mDNSOpaque32));
1757
1758 case kDNSType_NULL:
1759 case kDNSType_TSIG:
1760 case kDNSType_TXT:
1761 case kDNSType_X25:
1762 case kDNSType_ISDN:
1763 case kDNSType_LOC:
1764 case kDNSType_DHCID: return(rr->rdlength); // Not self-describing, so have to just trust rdlength
1765
1766 case kDNSType_HINFO: return (mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
1767
1768 case kDNSType_MX:
1769 case kDNSType_AFSDB:
1770 case kDNSType_RT:
1771 case kDNSType_KX: return (mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name));
1772
1773 case kDNSType_RP: return (mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) +
1774 CompressedDomainNameLength(&rd->rp.txt, name));
1775
1776 case kDNSType_PX: return (mDNSu16)(2 + CompressedDomainNameLength(&rd->px.map822, name) +
1777 CompressedDomainNameLength(&rd->px.mapx400, name));
1778
1779 case kDNSType_AAAA: return(sizeof(rd->ipv6));
1780
1781 case kDNSType_SRV: return (mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
1782
1783 case kDNSType_OPT: return(rr->rdlength);
1784
1785 case kDNSType_NSEC: {
1786 domainname *next = (domainname *)rd->data;
1787 int dlen = DomainNameLength(next);
1788 //
1789 if (UNICAST_NSEC(rr))
1790 return (mDNSu16)(CompressedDomainNameLength(next, name) + rr->rdlength - dlen);
1791 else
1792 return (mDNSu16)((estimate ? 2 : dlen) + rr->rdlength - dlen);
1793 }
1794
1795 default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
1796 return(rr->rdlength);
1797 }
1798 }
1799
1800 // When a local client registers (or updates) a record, we use this routine to do some simple validation checks
1801 // to help reduce the risk of bogus malformed data on the network
1802 mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
1803 {
1804 mDNSu16 len;
1805
1806 switch(rrtype)
1807 {
1808 case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr));
1809
1810 case kDNSType_NS: // Same as PTR
1811 case kDNSType_MD: // Same as PTR
1812 case kDNSType_MF: // Same as PTR
1813 case kDNSType_CNAME: // Same as PTR
1814 //case kDNSType_SOA not checked
1815 case kDNSType_MB: // Same as PTR
1816 case kDNSType_MG: // Same as PTR
1817 case kDNSType_MR: // Same as PTR
1818 //case kDNSType_NULL not checked (no specified format, so always valid)
1819 //case kDNSType_WKS not checked
1820 case kDNSType_PTR: len = DomainNameLengthLimit(&rd->u.name, rd->u.data + rdlength);
1821 return(len <= MAX_DOMAIN_NAME && rdlength == len);
1822
1823 case kDNSType_HINFO: // Same as TXT (roughly)
1824 case kDNSType_MINFO: // Same as TXT (roughly)
1825 case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035)
1826 {
1827 const mDNSu8 *ptr = rd->u.txt.c;
1828 const mDNSu8 *end = rd->u.txt.c + rdlength;
1829 while (ptr < end) ptr += 1 + ptr[0];
1830 return (ptr == end);
1831 }
1832
1833 case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
1834
1835 case kDNSType_MX: // Must be at least two-byte preference, plus domainname
1836 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1837 len = DomainNameLengthLimit(&rd->u.mx.exchange, rd->u.data + rdlength);
1838 return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
1839
1840 case kDNSType_SRV: // Must be at least priority+weight+port, plus domainname
1841 // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
1842 len = DomainNameLengthLimit(&rd->u.srv.target, rd->u.data + rdlength);
1843 return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
1844
1845 //case kDNSType_NSEC not checked
1846
1847 default: return(mDNStrue); // Allow all other types without checking
1848 }
1849 }
1850
1851 // ***************************************************************************
1852 #if COMPILER_LIKES_PRAGMA_MARK
1853 #pragma mark -
1854 #pragma mark - DNS Message Creation Functions
1855 #endif
1856
1857 mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
1858 {
1859 h->id = id;
1860 h->flags = flags;
1861 h->numQuestions = 0;
1862 h->numAnswers = 0;
1863 h->numAuthorities = 0;
1864 h->numAdditionals = 0;
1865 }
1866
1867 mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
1868 {
1869 const mDNSu8 *result = end - *domname - 1;
1870
1871 if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label
1872
1873 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
1874 while (result >= base)
1875 {
1876 // If the length byte and first character of the label match, then check further to see
1877 // if this location in the packet will yield a useful name compression pointer.
1878 if (result[0] == domname[0] && result[1] == domname[1])
1879 {
1880 const mDNSu8 *name = domname;
1881 const mDNSu8 *targ = result;
1882 while (targ + *name < end)
1883 {
1884 // First see if this label matches
1885 int i;
1886 const mDNSu8 *pointertarget;
1887 for (i=0; i <= *name; i++) if (targ[i] != name[i]) break;
1888 if (i <= *name) break; // If label did not match, bail out
1889 targ += 1 + *name; // Else, did match, so advance target pointer
1890 name += 1 + *name; // and proceed to check next label
1891 if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match!
1892 if (*name == 0) break; // If no more labels to match, we failed, so bail out
1893
1894 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
1895 if (targ[0] < 0x40) continue; // If length value, continue to check next label
1896 if (targ[0] < 0xC0) break; // If 40-BF, not valid
1897 if (targ+1 >= end) break; // Second byte not present!
1898 pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1];
1899 if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet
1900 if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte
1901 targ = pointertarget;
1902 }
1903 }
1904 result--; // We failed to match at this search position, so back up the tentative result pointer and try again
1905 }
1906 return(mDNSNULL);
1907 }
1908
1909 // Put a string of dot-separated labels as length-prefixed labels
1910 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
1911 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
1912 // end points to the end of the message so far
1913 // ptr points to where we want to put the name
1914 // limit points to one byte past the end of the buffer that we must not overrun
1915 // domainname is the name to put
1916 mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg,
1917 mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name)
1918 {
1919 const mDNSu8 *const base = (const mDNSu8 *)msg;
1920 const mDNSu8 * np = name->c;
1921 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
1922 const mDNSu8 * pointer = mDNSNULL;
1923 const mDNSu8 *const searchlimit = ptr;
1924
1925 if (!ptr) { LogMsg("putDomainNameAsLabels %##s ptr is null", name->c); return(mDNSNULL); }
1926
1927 if (!*np) // If just writing one-byte root label, make sure we have space for that
1928 {
1929 if (ptr >= limit) return(mDNSNULL);
1930 }
1931 else // else, loop through writing labels and/or a compression offset
1932 {
1933 do {
1934 if (*np > MAX_DOMAIN_LABEL)
1935 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
1936
1937 // This check correctly allows for the final trailing root label:
1938 // e.g.
1939 // Suppose our domain name is exactly 256 bytes long, including the final trailing root label.
1940 // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local").
1941 // We know that max will be at name->c[256]
1942 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
1943 // six bytes, then exit the loop, write the final terminating root label, and the domain
1944 // name we've written is exactly 256 bytes long, exactly at the correct legal limit.
1945 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
1946 if (np + 1 + *np >= max)
1947 { LogMsg("Malformed domain name %##s (more than 256 bytes)", name->c); return(mDNSNULL); }
1948
1949 if (base) pointer = FindCompressionPointer(base, searchlimit, np);
1950 if (pointer) // Use a compression pointer if we can
1951 {
1952 const mDNSu16 offset = (mDNSu16)(pointer - base);
1953 if (ptr+2 > limit) return(mDNSNULL); // If we don't have two bytes of space left, give up
1954 *ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
1955 *ptr++ = (mDNSu8)( offset & 0xFF);
1956 return(ptr);
1957 }
1958 else // Else copy one label and try again
1959 {
1960 int i;
1961 mDNSu8 len = *np++;
1962 // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up
1963 if (ptr + 1 + len >= limit) return(mDNSNULL);
1964 *ptr++ = len;
1965 for (i=0; i<len; i++) *ptr++ = *np++;
1966 }
1967 } while (*np); // While we've got characters remaining in the name, continue
1968 }
1969
1970 *ptr++ = 0; // Put the final root label
1971 return(ptr);
1972 }
1973
1974 mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
1975 {
1976 ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
1977 ptr[1] = (mDNSu8)((val ) & 0xFF);
1978 return ptr + sizeof(mDNSOpaque16);
1979 }
1980
1981 mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
1982 {
1983 ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
1984 ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
1985 ptr[2] = (mDNSu8)((val >> 8) & 0xFF);
1986 ptr[3] = (mDNSu8)((val ) & 0xFF);
1987 return ptr + sizeof(mDNSu32);
1988 }
1989
1990 // Copy the RDATA information. The actual in memory storage for the data might be bigger than what the rdlength
1991 // says. Hence, the only way to copy out the data from a resource record is to use putRData.
1992 // msg points to the message we're building (pass mDNSNULL for "msg" if we don't want to use compression pointers)
1993 mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr)
1994 {
1995 const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
1996 switch (rr->rrtype)
1997 {
1998 case kDNSType_A: if (rr->rdlength != 4)
1999 { debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); return(mDNSNULL); }
2000 if (ptr + 4 > limit) return(mDNSNULL);
2001 *ptr++ = rdb->ipv4.b[0];
2002 *ptr++ = rdb->ipv4.b[1];
2003 *ptr++ = rdb->ipv4.b[2];
2004 *ptr++ = rdb->ipv4.b[3];
2005 return(ptr);
2006
2007 case kDNSType_NS:
2008 case kDNSType_CNAME:
2009 case kDNSType_PTR:
2010 case kDNSType_DNAME: return(putDomainNameAsLabels(msg, ptr, limit, &rdb->name));
2011
2012 case kDNSType_SOA: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.mname);
2013 if (!ptr) return(mDNSNULL);
2014 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.rname);
2015 if (!ptr || ptr + 20 > limit) return(mDNSNULL);
2016 ptr = putVal32(ptr, rdb->soa.serial);
2017 ptr = putVal32(ptr, rdb->soa.refresh);
2018 ptr = putVal32(ptr, rdb->soa.retry);
2019 ptr = putVal32(ptr, rdb->soa.expire);
2020 ptr = putVal32(ptr, rdb->soa.min);
2021 return(ptr);
2022
2023 case kDNSType_NULL:
2024 case kDNSType_HINFO:
2025 case kDNSType_TSIG:
2026 case kDNSType_TXT:
2027 case kDNSType_X25:
2028 case kDNSType_ISDN:
2029 case kDNSType_LOC:
2030 case kDNSType_DHCID: if (ptr + rr->rdlength > limit) return(mDNSNULL);
2031 mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
2032 return(ptr + rr->rdlength);
2033
2034 case kDNSType_MX:
2035 case kDNSType_AFSDB:
2036 case kDNSType_RT:
2037 case kDNSType_KX: if (ptr + 3 > limit) return(mDNSNULL);
2038 ptr = putVal16(ptr, rdb->mx.preference);
2039 return(putDomainNameAsLabels(msg, ptr, limit, &rdb->mx.exchange));
2040
2041 case kDNSType_RP: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.mbox);
2042 if (!ptr) return(mDNSNULL);
2043 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.txt);
2044 return(ptr);
2045
2046 case kDNSType_PX: if (ptr + 5 > limit) return(mDNSNULL);
2047 ptr = putVal16(ptr, rdb->px.preference);
2048 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.map822);
2049 if (!ptr) return(mDNSNULL);
2050 ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.mapx400);
2051 return(ptr);
2052
2053 case kDNSType_AAAA: if (rr->rdlength != sizeof(rdb->ipv6))
2054 { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); return(mDNSNULL); }
2055 if (ptr + sizeof(rdb->ipv6) > limit) return(mDNSNULL);
2056 mDNSPlatformMemCopy(ptr, &rdb->ipv6, sizeof(rdb->ipv6));
2057 return(ptr + sizeof(rdb->ipv6));
2058
2059 case kDNSType_SRV: if (ptr + 7 > limit) return(mDNSNULL);
2060 *ptr++ = (mDNSu8)(rdb->srv.priority >> 8);
2061 *ptr++ = (mDNSu8)(rdb->srv.priority & 0xFF);
2062 *ptr++ = (mDNSu8)(rdb->srv.weight >> 8);
2063 *ptr++ = (mDNSu8)(rdb->srv.weight & 0xFF);
2064 *ptr++ = rdb->srv.port.b[0];
2065 *ptr++ = rdb->srv.port.b[1];
2066 return(putDomainNameAsLabels(msg, ptr, limit, &rdb->srv.target));
2067
2068 case kDNSType_OPT: {
2069 int len = 0;
2070 const rdataOPT *opt;
2071 const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength];
2072 for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) len += DNSOpt_Data_Space(opt);
2073 if (ptr + len > limit) { LogMsg("ERROR: putOptRData - out of space"); return mDNSNULL; }
2074
2075 for (opt = &rr->rdata->u.opt[0]; opt < end; opt++)
2076 {
2077 const int space = DNSOpt_Data_Space(opt);
2078 ptr = putVal16(ptr, opt->opt);
2079 ptr = putVal16(ptr, (mDNSu16)space - 4);
2080 switch (opt->opt)
2081 {
2082 case kDNSOpt_LLQ:
2083 ptr = putVal16(ptr, opt->u.llq.vers);
2084 ptr = putVal16(ptr, opt->u.llq.llqOp);
2085 ptr = putVal16(ptr, opt->u.llq.err);
2086 mDNSPlatformMemCopy(ptr, opt->u.llq.id.b, 8); // 8-byte id
2087 ptr += 8;
2088 ptr = putVal32(ptr, opt->u.llq.llqlease);
2089 break;
2090 case kDNSOpt_Lease:
2091 ptr = putVal32(ptr, opt->u.updatelease);
2092 break;
2093 case kDNSOpt_Owner:
2094 *ptr++ = opt->u.owner.vers;
2095 *ptr++ = opt->u.owner.seq;
2096 mDNSPlatformMemCopy(ptr, opt->u.owner.HMAC.b, 6); // 6-byte Host identifier
2097 ptr += 6;
2098 if (space >= DNSOpt_OwnerData_ID_Wake_Space)
2099 {
2100 mDNSPlatformMemCopy(ptr, opt->u.owner.IMAC.b, 6); // 6-byte interface MAC
2101 ptr += 6;
2102 if (space > DNSOpt_OwnerData_ID_Wake_Space)
2103 {
2104 mDNSPlatformMemCopy(ptr, opt->u.owner.password.b, space - DNSOpt_OwnerData_ID_Wake_Space);
2105 ptr += space - DNSOpt_OwnerData_ID_Wake_Space;
2106 }
2107 }
2108 break;
2109 }
2110 }
2111 return ptr;
2112 }
2113
2114 case kDNSType_NSEC: {
2115 // For NSEC records, rdlength represents the exact number of bytes
2116 // of in memory storage.
2117 int len = rr->rdlength;
2118 mDNSu8 *nsec = (mDNSu8 *)rdb->data;
2119 domainname *name = (domainname *)nsec;
2120 int dlen;
2121
2122 dlen = DomainNameLength(name);
2123 len -= dlen;
2124 nsec += dlen;
2125 // This function is called when we are sending a NSEC record as part of mDNS,
2126 // or to copy the data to any other buffer needed which could be a mDNS or uDNS
2127 // NSEC record. The only time compression is used that when we are sending it
2128 // in mDNS (indicated by non-NULL "msg") and hence we handle mDNS case
2129 // separately.
2130 if (!UNICAST_NSEC(rr))
2131 {
2132 mDNSu8 *save = ptr;
2133 int i, j, wlen;
2134 wlen = *(nsec + 1);
2135 nsec += 2; // Skip the window number and len
2136 len -= 2;
2137
2138 // For our simplified use of NSEC synthetic records:
2139 //
2140 // nextname is always the record's own name,
2141 // the block number is always 0,
2142 // the count byte is a value in the range 1-32,
2143 // followed by the 1-32 data bytes
2144 //
2145 // Note: When we send the NSEC record in mDNS, the window size is set to 32.
2146 // We need to find out what the last non-NULL byte is. If we are copying out
2147 // from an RDATA, we have the right length. As we need to handle both the case,
2148 // we loop to find the right value instead of blindly using len to copy.
2149
2150 for (i=wlen; i>0; i--) if (nsec[i-1]) break;
2151
2152 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
2153 if (!ptr) { LogInfo("putRData: Can't put name, Length %d, record %##s", limit - save, rr->name->c); return(mDNSNULL); }
2154 if (i) // Only put a block if at least one type exists for this name
2155 {
2156 if (ptr + 2 + i > limit) { LogInfo("putRData: Can't put window, Length %d, i %d, record %##s", limit - ptr, i, rr->name->c); return(mDNSNULL); }
2157 *ptr++ = 0;
2158 *ptr++ = (mDNSu8)i;
2159 for (j=0; j<i; j++) *ptr++ = nsec[j];
2160 }
2161 return ptr;
2162 }
2163 else
2164 {
2165 int win, wlen;
2166
2167 // Sanity check whether the bitmap is good
2168 while (len)
2169 {
2170 if (len < 3)
2171 { LogMsg("putRData: invalid length %d", len); return mDNSNULL; }
2172
2173 win = *nsec++;
2174 wlen = *nsec++;
2175 len -= 2;
2176 if (len < wlen || wlen < 1 || wlen > 32)
2177 { LogMsg("putRData: invalid window length %d", wlen); return mDNSNULL; }
2178 if (win < 0 || win >= 256)
2179 { LogMsg("putRData: invalid window %d", win); return mDNSNULL; }
2180
2181 nsec += wlen;
2182 len -= wlen;
2183 }
2184 if (ptr + rr->rdlength > limit) { LogMsg("putRData: NSEC rdlength beyond limit %##s (%s), ptr %p, rdlength %d, limit %p", rr->name->c, DNSTypeName(rr->rrtype), ptr, rr->rdlength, limit); return(mDNSNULL);}
2185
2186 // No compression allowed for "nxt", just copy the data.
2187 mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
2188 return(ptr + rr->rdlength);
2189 }
2190 }
2191
2192 default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype);
2193 if (ptr + rr->rdlength > limit) return(mDNSNULL);
2194 mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
2195 return(ptr + rr->rdlength);
2196 }
2197 }
2198
2199 #define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update)
2200
2201 mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit)
2202 {
2203 mDNSu8 *endofrdata;
2204 mDNSu16 actualLength;
2205 // When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782)
2206 const DNSMessage *const rdatacompressionbase = (IsUnicastUpdate(msg) && rr->rrtype == kDNSType_SRV) ? mDNSNULL : msg;
2207
2208 if (rr->RecordType == kDNSRecordTypeUnregistered)
2209 {
2210 LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
2211 return(ptr);
2212 }
2213
2214 if (!ptr) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL); }
2215
2216 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
2217 if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
2218 ptr[0] = (mDNSu8)(rr->rrtype >> 8);
2219 ptr[1] = (mDNSu8)(rr->rrtype & 0xFF);
2220 ptr[2] = (mDNSu8)(rr->rrclass >> 8);
2221 ptr[3] = (mDNSu8)(rr->rrclass & 0xFF);
2222 ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF);
2223 ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF);
2224 ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF);
2225 ptr[7] = (mDNSu8)( ttl & 0xFF);
2226 // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes
2227
2228 endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr);
2229 if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); }
2230
2231 // Go back and fill in the actual number of data bytes we wrote
2232 // (actualLength can be less than rdlength when domain name compression is used)
2233 actualLength = (mDNSu16)(endofrdata - ptr - 10);
2234 ptr[8] = (mDNSu8)(actualLength >> 8);
2235 ptr[9] = (mDNSu8)(actualLength & 0xFF);
2236
2237 if (count) (*count)++;
2238 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
2239 return(endofrdata);
2240 }
2241
2242 mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr)
2243 {
2244 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name);
2245 if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
2246 ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type
2247 ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF);
2248 ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class
2249 ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF);
2250 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero
2251 ptr[8] = ptr[9] = 0; // RDATA length is zero
2252 (*count)++;
2253 return(ptr + 10);
2254 }
2255
2256 mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
2257 {
2258 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
2259 if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
2260 ptr[0] = (mDNSu8)(rrtype >> 8);
2261 ptr[1] = (mDNSu8)(rrtype & 0xFF);
2262 ptr[2] = (mDNSu8)(rrclass >> 8);
2263 ptr[3] = (mDNSu8)(rrclass & 0xFF);
2264 msg->h.numQuestions++;
2265 return(ptr+4);
2266 }
2267
2268 // for dynamic updates
2269 mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass)
2270 {
2271 ptr = putDomainNameAsLabels(msg, ptr, limit, zone);
2272 if (!ptr || ptr + 4 > limit) return mDNSNULL; // If we're out-of-space, return NULL
2273 *ptr++ = (mDNSu8)(kDNSType_SOA >> 8);
2274 *ptr++ = (mDNSu8)(kDNSType_SOA & 0xFF);
2275 *ptr++ = zoneClass.b[0];
2276 *ptr++ = zoneClass.b[1];
2277 msg->h.mDNS_numZones++;
2278 return ptr;
2279 }
2280
2281 // for dynamic updates
2282 mDNSexport mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end)
2283 {
2284 AuthRecord prereq;
2285 mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
2286 AssignDomainName(&prereq.namestorage, name);
2287 prereq.resrec.rrtype = kDNSQType_ANY;
2288 prereq.resrec.rrclass = kDNSClass_NONE;
2289 return putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq);
2290 }
2291
2292 // for dynamic updates
2293 mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
2294 {
2295 // deletion: specify record w/ TTL 0, class NONE
2296 const mDNSu16 origclass = rr->rrclass;
2297 rr->rrclass = kDNSClass_NONE;
2298 ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
2299 rr->rrclass = origclass;
2300 return ptr;
2301 }
2302
2303 // for dynamic updates
2304 mDNSexport mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit)
2305 {
2306 // deletion: specify record w/ TTL 0, class NONE
2307 const mDNSu16 origclass = rr->rrclass;
2308 rr->rrclass = kDNSClass_NONE;
2309 ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0, limit);
2310 rr->rrclass = origclass;
2311 return ptr;
2312 }
2313
2314 mDNSexport mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit)
2315 {
2316 mDNSu16 class = kDNSQClass_ANY;
2317
2318 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
2319 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
2320 ptr[0] = (mDNSu8)(rrtype >> 8);
2321 ptr[1] = (mDNSu8)(rrtype & 0xFF);
2322 ptr[2] = (mDNSu8)(class >> 8);
2323 ptr[3] = (mDNSu8)(class & 0xFF);
2324 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
2325 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
2326
2327 msg->h.mDNS_numUpdates++;
2328 return ptr + 10;
2329 }
2330
2331 // for dynamic updates
2332 mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name)
2333 {
2334 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
2335 mDNSu16 class = kDNSQClass_ANY;
2336 mDNSu16 rrtype = kDNSQType_ANY;
2337
2338 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
2339 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
2340 ptr[0] = (mDNSu8)(rrtype >> 8);
2341 ptr[1] = (mDNSu8)(rrtype & 0xFF);
2342 ptr[2] = (mDNSu8)(class >> 8);
2343 ptr[3] = (mDNSu8)(class & 0xFF);
2344 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
2345 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
2346
2347 msg->h.mDNS_numUpdates++;
2348 return ptr + 10;
2349 }
2350
2351 // for dynamic updates
2352 mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
2353 {
2354 AuthRecord rr;
2355 mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
2356 rr.resrec.rrclass = NormalMaxDNSMessageData;
2357 rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
2358 rr.resrec.rdestimate = sizeof(rdataOPT);
2359 rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease;
2360 rr.resrec.rdata->u.opt[0].u.updatelease = lease;
2361 end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, &rr.resrec, 0);
2362 if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; }
2363 return end;
2364 }
2365
2366 // for dynamic updates
2367 mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease, mDNSu8 *limit)
2368 {
2369 AuthRecord rr;
2370 mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
2371 rr.resrec.rrclass = NormalMaxDNSMessageData;
2372 rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
2373 rr.resrec.rdestimate = sizeof(rdataOPT);
2374 rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease;
2375 rr.resrec.rdata->u.opt[0].u.updatelease = lease;
2376 end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, 0, limit);
2377 if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; }
2378 return end;
2379 }
2380
2381 mDNSexport mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit)
2382 {
2383 AuthRecord rr;
2384 mDNSu32 ttl = 0;
2385
2386 mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
2387 // It is still not clear what the right size is. We will have to fine tune this once we do
2388 // a lot of testing with DNSSEC.
2389 rr.resrec.rrclass = 4096;
2390 rr.resrec.rdlength = 0;
2391 rr.resrec.rdestimate = 0;
2392 // set the DO bit
2393 ttl |= 0x8000;
2394 end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, ttl, limit);
2395 if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; }
2396 return end;
2397 }
2398
2399 mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, DomainAuthInfo *authInfo, mDNSu8 *limit)
2400 {
2401 if (authInfo && authInfo->AutoTunnel)
2402 {
2403 AuthRecord hinfo;
2404 mDNSu8 *h = hinfo.rdatastorage.u.data;
2405 mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0];
2406 mDNSu8 *newptr;
2407 mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
2408 AppendDomainLabel(&hinfo.namestorage, &m->hostlabel);
2409 AppendDomainName (&hinfo.namestorage, &authInfo->domain);
2410 hinfo.resrec.rroriginalttl = 0;
2411 mDNSPlatformMemCopy(h, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]);
2412 h += 1 + (int)h[0];
2413 mDNSPlatformMemCopy(h, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]);
2414 hinfo.resrec.rdlength = len;
2415 hinfo.resrec.rdestimate = len;
2416 newptr = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &hinfo.resrec, 0, limit);
2417 return newptr;
2418 }
2419 else
2420 return end;
2421 }
2422
2423 // ***************************************************************************
2424 #if COMPILER_LIKES_PRAGMA_MARK
2425 #pragma mark -
2426 #pragma mark - DNS Message Parsing Functions
2427 #endif
2428
2429 mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name)
2430 {
2431 mDNSu32 sum = 0;
2432 const mDNSu8 *c;
2433
2434 for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
2435 {
2436 sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) |
2437 (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]);
2438 sum = (sum<<3) | (sum>>29);
2439 }
2440 if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
2441 return(sum);
2442 }
2443
2444 mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
2445 {
2446 domainname *target;
2447 if (NewRData)
2448 {
2449 rr->rdata = NewRData;
2450 rr->rdlength = rdlength;
2451 }
2452 // Must not try to get target pointer until after updating rr->rdata
2453 target = GetRRDomainNameTarget(rr);
2454 rr->rdlength = GetRDLength(rr, mDNSfalse);
2455 rr->rdestimate = GetRDLength(rr, mDNStrue);
2456 rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr);
2457 }
2458
2459 mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
2460 {
2461 mDNSu16 total = 0;
2462
2463 if (ptr < (mDNSu8*)msg || ptr >= end)
2464 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
2465
2466 while (1) // Read sequence of labels
2467 {
2468 const mDNSu8 len = *ptr++; // Read length of this label
2469 if (len == 0) return(ptr); // If length is zero, that means this name is complete
2470 switch (len & 0xC0)
2471 {
2472 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
2473 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
2474 if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label
2475 { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
2476 ptr += len;
2477 total += 1 + len;
2478 break;
2479
2480 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL);
2481 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL);
2482 case 0xC0: return(ptr+1);
2483 }
2484 }
2485 }
2486
2487 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
2488 mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
2489 domainname *const name)
2490 {
2491 const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers
2492 mDNSu8 *np = name->c; // Name pointer
2493 const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer
2494
2495 if (ptr < (mDNSu8*)msg || ptr >= end)
2496 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
2497
2498 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2499
2500 while (1) // Read sequence of labels
2501 {
2502 const mDNSu8 len = *ptr++; // Read length of this label
2503 if (len == 0) break; // If length is zero, that means this name is complete
2504 switch (len & 0xC0)
2505 {
2506 int i;
2507 mDNSu16 offset;
2508
2509 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
2510 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
2511 if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label
2512 { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
2513 *np++ = len;
2514 for (i=0; i<len; i++) *np++ = *ptr++;
2515 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
2516 break;
2517
2518 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
2519 return(mDNSNULL);
2520
2521 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
2522
2523 case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
2524 if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers
2525 ptr = (mDNSu8 *)msg + offset;
2526 if (ptr < (mDNSu8*)msg || ptr >= end)
2527 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
2528 if (*ptr & 0xC0)
2529 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
2530 break;
2531 }
2532 }
2533
2534 if (nextbyte) return(nextbyte);
2535 else return(ptr);
2536 }
2537
2538 mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
2539 {
2540 mDNSu16 pktrdlength;
2541
2542 ptr = skipDomainName(msg, ptr, end);
2543 if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
2544
2545 if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
2546 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
2547 ptr += 10;
2548 if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
2549
2550 return(ptr + pktrdlength);
2551 }
2552
2553 // This function is called with "msg" when we receive a DNS message and needs to parse a single resource record
2554 // pointed to by "ptr". Some resource records like SOA, SRV are converted to host order and also expanded
2555 // (domainnames are expanded to 255 bytes) when stored in memory.
2556 //
2557 // This function can also be called with "NULL" msg to parse a single resource record pointed to by ptr.
2558 // The caller can do this only if the names in the resource records are compressed and validity of the
2559 // resource record has already been done before. DNSSEC currently uses it this way.
2560 mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end,
2561 LargeCacheRecord *const largecr, mDNSu16 rdlength)
2562 {
2563 CacheRecord *const rr = &largecr->r;
2564 RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data;
2565
2566 switch (rr->resrec.rrtype)
2567 {
2568 case kDNSType_A:
2569 if (rdlength != sizeof(mDNSv4Addr))
2570 goto fail;
2571 rdb->ipv4.b[0] = ptr[0];
2572 rdb->ipv4.b[1] = ptr[1];
2573 rdb->ipv4.b[2] = ptr[2];
2574 rdb->ipv4.b[3] = ptr[3];
2575 break;
2576
2577 case kDNSType_NS:
2578 case kDNSType_MD:
2579 case kDNSType_MF:
2580 case kDNSType_CNAME:
2581 case kDNSType_MB:
2582 case kDNSType_MG:
2583 case kDNSType_MR:
2584 case kDNSType_PTR:
2585 case kDNSType_NSAP_PTR:
2586 case kDNSType_DNAME:
2587 if (msg)
2588 {
2589 ptr = getDomainName(msg, ptr, end, &rdb->name);
2590 }
2591 else
2592 {
2593 AssignDomainName(&rdb->name, (domainname *)ptr);
2594 ptr += DomainNameLength(&rdb->name);
2595 }
2596 if (ptr != end)
2597 {
2598 debugf("SetRData: Malformed CNAME/PTR RDATA name");
2599 goto fail;
2600 }
2601 break;
2602
2603 case kDNSType_SOA:
2604 if (msg)
2605 {
2606 ptr = getDomainName(msg, ptr, end, &rdb->soa.mname);
2607 }
2608 else
2609 {
2610 AssignDomainName(&rdb->soa.mname, (domainname *)ptr);
2611 ptr += DomainNameLength(&rdb->soa.mname);
2612 }
2613 if (!ptr)
2614 {
2615 debugf("SetRData: Malformed SOA RDATA mname");
2616 goto fail;
2617 }
2618 if (msg)
2619 {
2620 ptr = getDomainName(msg, ptr, end, &rdb->soa.rname);
2621 }
2622 else
2623 {
2624 AssignDomainName(&rdb->soa.rname, (domainname *)ptr);
2625 ptr += DomainNameLength(&rdb->soa.rname);
2626 }
2627 if (!ptr)
2628 {
2629 debugf("SetRData: Malformed SOA RDATA rname");
2630 goto fail;
2631 }
2632 if (ptr + 0x14 != end)
2633 {
2634 debugf("SetRData: Malformed SOA RDATA");
2635 goto fail;
2636 }
2637 rdb->soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]);
2638 rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]);
2639 rdb->soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]);
2640 rdb->soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]);
2641 rdb->soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
2642 break;
2643
2644 case kDNSType_NULL:
2645 case kDNSType_HINFO:
2646 case kDNSType_TXT:
2647 case kDNSType_X25:
2648 case kDNSType_ISDN:
2649 case kDNSType_LOC:
2650 case kDNSType_DHCID:
2651 rr->resrec.rdlength = rdlength;
2652 mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
2653 break;
2654
2655 case kDNSType_MX:
2656 case kDNSType_AFSDB:
2657 case kDNSType_RT:
2658 case kDNSType_KX:
2659 // Preference + domainname
2660 if (rdlength < 3)
2661 goto fail;
2662 rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2663 ptr += 2;
2664 if (msg)
2665 {
2666 ptr = getDomainName(msg, ptr, end, &rdb->mx.exchange);
2667 }
2668 else
2669 {
2670 AssignDomainName(&rdb->mx.exchange, (domainname *)ptr);
2671 ptr += DomainNameLength(&rdb->mx.exchange);
2672 }
2673 if (ptr != end)
2674 {
2675 debugf("SetRData: Malformed MX name");
2676 goto fail;
2677 }
2678 break;
2679
2680 case kDNSType_MINFO:
2681 case kDNSType_RP:
2682 // Domainname + domainname
2683 if (msg)
2684 {
2685 ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox);
2686 }
2687 else
2688 {
2689 AssignDomainName(&rdb->rp.mbox, (domainname *)ptr);
2690 ptr += DomainNameLength(&rdb->rp.mbox);
2691 }
2692 if (!ptr)
2693 {
2694 debugf("SetRData: Malformed RP mbox");
2695 goto fail;
2696 }
2697 if (msg)
2698 {
2699 ptr = getDomainName(msg, ptr, end, &rdb->rp.txt);
2700 }
2701 else
2702 {
2703 AssignDomainName(&rdb->rp.txt, (domainname *)ptr);
2704 ptr += DomainNameLength(&rdb->rp.txt);
2705 }
2706 if (ptr != end)
2707 {
2708 debugf("SetRData: Malformed RP txt");
2709 goto fail;
2710 }
2711 break;
2712
2713 case kDNSType_PX:
2714 // Preference + domainname + domainname
2715 if (rdlength < 4)
2716 goto fail;
2717 rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2718 ptr += 2;
2719 if (msg)
2720 {
2721 ptr = getDomainName(msg, ptr, end, &rdb->px.map822);
2722 }
2723 else
2724 {
2725 AssignDomainName(&rdb->px.map822, (domainname *)ptr);
2726 ptr += DomainNameLength(&rdb->px.map822);
2727 }
2728 if (!ptr)
2729 {
2730 debugf("SetRData: Malformed PX map822");
2731 goto fail;
2732 }
2733 if (msg)
2734 {
2735 ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400);
2736 }
2737 else
2738 {
2739 AssignDomainName(&rdb->px.mapx400, (domainname *)ptr);
2740 ptr += DomainNameLength(&rdb->px.mapx400);
2741 }
2742 if (ptr != end)
2743 {
2744 debugf("SetRData: Malformed PX mapx400");
2745 goto fail;
2746 }
2747 break;
2748
2749 case kDNSType_AAAA:
2750 if (rdlength != sizeof(mDNSv6Addr))
2751 goto fail;
2752 mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6));
2753 break;
2754
2755 case kDNSType_SRV:
2756 // Priority + weight + port + domainname
2757 if (rdlength < 7)
2758 goto fail;
2759 rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2760 rdb->srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
2761 rdb->srv.port.b[0] = ptr[4];
2762 rdb->srv.port.b[1] = ptr[5];
2763 ptr += 6;
2764 if (msg)
2765 {
2766 ptr = getDomainName(msg, ptr, end, &rdb->srv.target);
2767 }
2768 else
2769 {
2770 AssignDomainName(&rdb->srv.target, (domainname *)ptr);
2771 ptr += DomainNameLength(&rdb->srv.target);
2772 }
2773 if (ptr != end)
2774 {
2775 debugf("SetRData: Malformed SRV RDATA name");
2776 goto fail;
2777 }
2778 break;
2779
2780 case kDNSType_NAPTR:
2781 {
2782 int savelen, len;
2783 domainname name;
2784 const mDNSu8 *orig = ptr;
2785 const mDNSu8 *save;
2786
2787 // Make sure the data is parseable and within the limits. DNSSEC code looks at
2788 // the domain name in the end for a valid domainname.
2789 //
2790 // Fixed length: Order, preference (4 bytes)
2791 // Variable length: flags, service, regexp, domainname
2792
2793 if (rdlength < 8)
2794 goto fail;
2795 // Order, preference.
2796 ptr += 4;
2797 // Parse flags, Service and Regexp
2798 // length in the first byte does not include the length byte itself
2799 len = *ptr + 1;
2800 ptr += len;
2801 if (ptr >= end)
2802 {
2803 LogInfo("SetRData: Malformed NAPTR flags");
2804 goto fail;
2805 }
2806
2807 // Service
2808 len = *ptr + 1;
2809 ptr += len;
2810 if (ptr >= end)
2811 {
2812 LogInfo("SetRData: Malformed NAPTR service");
2813 goto fail;
2814 }
2815
2816 // Regexp
2817 len = *ptr + 1;
2818 ptr += len;
2819 if (ptr >= end)
2820 {
2821 LogInfo("SetRData: Malformed NAPTR regexp");
2822 goto fail;
2823 }
2824
2825 save = ptr;
2826 savelen = ptr - orig;
2827
2828 // RFC 2915 states that name compression is not allowed for this field. But RFC 3597
2829 // states that for NAPTR we should decompress. We make sure that we store the full
2830 // name rather than the compressed name
2831 if (msg)
2832 {
2833 ptr = getDomainName(msg, ptr, end, &name);
2834 }
2835 else
2836 {
2837 AssignDomainName(&name, (domainname *)ptr);
2838 ptr += DomainNameLength(&name);
2839 }
2840 if (ptr != end)
2841 {
2842 LogInfo("SetRData: Malformed NAPTR RDATA name");
2843 goto fail;
2844 }
2845
2846 rr->resrec.rdlength = savelen + DomainNameLength(&name);
2847 // The uncompressed size should not exceed the limits
2848 if (rr->resrec.rdlength > MaximumRDSize)
2849 {
2850 LogInfo("SetRData: Malformed NAPTR rdlength %d, rr->resrec.rdlength %d, "
2851 "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c);
2852 goto fail;
2853 }
2854 mDNSPlatformMemCopy(rdb->data, orig, savelen);
2855 AssignDomainName((domainname *)(rdb->data + savelen), &name);
2856 break;
2857 }
2858 case kDNSType_OPT: {
2859 mDNSu8 *dataend = rr->resrec.rdata->u.data;
2860 rdataOPT *opt = rr->resrec.rdata->u.opt;
2861 rr->resrec.rdlength = 0;
2862 while (ptr < end && (mDNSu8 *)(opt+1) < &dataend[MaximumRDSize])
2863 {
2864 const rdataOPT *const currentopt = opt;
2865 if (ptr + 4 > end) { LogInfo("SetRData: OPT RDATA ptr + 4 > end"); goto fail; }
2866 opt->opt = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2867 opt->optlen = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
2868 ptr += 4;
2869 if (ptr + opt->optlen > end) { LogInfo("SetRData: ptr + opt->optlen > end"); goto fail; }
2870 switch (opt->opt)
2871 {
2872 case kDNSOpt_LLQ:
2873 if (opt->optlen == DNSOpt_LLQData_Space - 4)
2874 {
2875 opt->u.llq.vers = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2876 opt->u.llq.llqOp = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
2877 opt->u.llq.err = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
2878 mDNSPlatformMemCopy(opt->u.llq.id.b, ptr+6, 8);
2879 opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[14] << 24 | (mDNSu32)ptr[15] << 16 | (mDNSu32)ptr[16] << 8 | ptr[17]);
2880 if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond)
2881 opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond;
2882 opt++;
2883 }
2884 break;
2885 case kDNSOpt_Lease:
2886 if (opt->optlen == DNSOpt_LeaseData_Space - 4)
2887 {
2888 opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
2889 if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond)
2890 opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond;
2891 opt++;
2892 }
2893 break;
2894 case kDNSOpt_Owner:
2895 if (ValidOwnerLength(opt->optlen))
2896 {
2897 opt->u.owner.vers = ptr[0];
2898 opt->u.owner.seq = ptr[1];
2899 mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6); // 6-byte MAC address
2900 mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6); // 6-byte MAC address
2901 opt->u.owner.password = zeroEthAddr;
2902 if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
2903 {
2904 mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6); // 6-byte MAC address
2905 // This mDNSPlatformMemCopy is safe because the ValidOwnerLength(opt->optlen) check above
2906 // ensures that opt->optlen is no more than DNSOpt_OwnerData_ID_Wake_PW6_Space - 4
2907 if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
2908 mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4));
2909 }
2910 opt++;
2911 }
2912 break;
2913 }
2914 ptr += currentopt->optlen;
2915 }
2916 rr->resrec.rdlength = (mDNSu16)((mDNSu8*)opt - rr->resrec.rdata->u.data);
2917 if (ptr != end) { LogInfo("SetRData: Malformed OptRdata"); goto fail; }
2918 break;
2919 }
2920
2921 case kDNSType_NSEC: {
2922 domainname name;
2923 int win, wlen;
2924 int len = rdlength;
2925 int bmaplen, dlen;
2926 const mDNSu8 *orig = ptr;
2927 const mDNSu8 *bmap;
2928
2929 if (msg)
2930 {
2931 ptr = getDomainName(msg, ptr, end, &name);
2932 }
2933 else
2934 {
2935 AssignDomainName(&name, (domainname *)ptr);
2936 ptr += DomainNameLength(&name);
2937 }
2938 if (!ptr)
2939 {
2940 LogInfo("SetRData: Malformed NSEC nextname");
2941 goto fail;
2942 }
2943
2944 dlen = DomainNameLength(&name);
2945
2946 // Multicast NSECs use name compression for this field unlike the unicast case which
2947 // does not use compression. And multicast case always succeeds in compression. So,
2948 // the rdlength includes only the compressed space in that case. So, can't
2949 // use the DomainNameLength of name to reduce the length here.
2950 len -= (ptr - orig);
2951 bmaplen = len; // Save the length of the bitmap
2952 bmap = ptr;
2953 // Sanity check whether the bitmap is good
2954 while (ptr < end)
2955 {
2956 if (len < 3)
2957 {
2958 LogInfo("SetRData: invalid length %d", len);
2959 goto fail;
2960 }
2961
2962 win = *ptr++;
2963 wlen = *ptr++;
2964 len -= 2;
2965 if (len < wlen || wlen < 1 || wlen > 32)
2966 {
2967 LogInfo("SetRData: invalid window length %d", wlen);
2968 goto fail;
2969 }
2970 if (win < 0 || win >= 256)
2971 {
2972 LogInfo("SetRData: invalid window %d", win);
2973 goto fail;
2974 }
2975
2976 ptr += wlen;
2977 len -= wlen;
2978 }
2979 if (ptr != end)
2980 {
2981 LogInfo("SetRData: Malformed NSEC length not right");
2982 goto fail;
2983 }
2984
2985 // Initialize the right length here. When we call SetNewRData below which in turn calls
2986 // GetRDLength and for NSEC case, it assumes that rdlength is intitialized
2987 rr->resrec.rdlength = DomainNameLength(&name) + bmaplen;
2988
2989 // Do we have space after the name expansion ?
2990 if (rr->resrec.rdlength > MaximumRDSize)
2991 {
2992 LogInfo("SetRData: Malformed NSEC rdlength %d, rr->resrec.rdlength %d, "
2993 "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c);
2994 goto fail;
2995 }
2996 AssignDomainName((domainname *)rdb->data, &name);
2997 mDNSPlatformMemCopy(rdb->data + dlen, bmap, bmaplen);
2998 break;
2999 }
3000 case kDNSType_TKEY:
3001 case kDNSType_TSIG:
3002 {
3003 domainname name;
3004 int dlen, rlen;
3005
3006 // The name should not be compressed. But we take the conservative approach
3007 // and uncompress the name before we store it.
3008 if (msg)
3009 {
3010 ptr = getDomainName(msg, ptr, end, &name);
3011 }
3012 else
3013 {
3014 AssignDomainName(&name, (domainname *)ptr);
3015 ptr += DomainNameLength(&name);
3016 }
3017 if (!ptr)
3018 {
3019 LogInfo("SetRData: Malformed name for TSIG/TKEY type %d", rr->resrec.rrtype);
3020 goto fail;
3021 }
3022 dlen = DomainNameLength(&name);
3023 rlen = end - ptr;
3024 rr->resrec.rdlength = dlen + rlen;
3025 AssignDomainName((domainname *)rdb->data, &name);
3026 mDNSPlatformMemCopy(rdb->data + dlen, ptr, rlen);
3027 break;
3028 }
3029 case kDNSType_RRSIG:
3030 {
3031 const mDNSu8 *sig = ptr + RRSIG_FIXED_SIZE;
3032 const mDNSu8 *orig = sig;
3033 domainname name;
3034 if (rdlength < RRSIG_FIXED_SIZE + 1)
3035 {
3036 LogInfo("SetRData: RRSIG too small length %d", rdlength);
3037 goto fail;
3038 }
3039 if (msg)
3040 {
3041 sig = getDomainName(msg, sig, end, &name);
3042 }
3043 else
3044 {
3045 AssignDomainName(&name, (domainname *)sig);
3046 sig += DomainNameLength(&name);
3047 }
3048 if (!sig)
3049 {
3050 LogInfo("SetRData: Malformed RRSIG record");
3051 goto fail;
3052 }
3053
3054 if ((sig - orig) != DomainNameLength(&name))
3055 {
3056 LogInfo("SetRData: Malformed RRSIG record, signer name compression");
3057 goto fail;
3058 }
3059 // Just ensure that we have at least one byte of the signature
3060 if (sig + 1 >= end)
3061 {
3062 LogInfo("SetRData: Not enough bytes for signature type %d", rr->resrec.rrtype);
3063 goto fail;
3064 }
3065 rr->resrec.rdlength = rdlength;
3066 mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3067 break;
3068 }
3069 case kDNSType_DNSKEY:
3070 {
3071 if (rdlength < DNSKEY_FIXED_SIZE + 1)
3072 {
3073 LogInfo("SetRData: DNSKEY too small length %d", rdlength);
3074 goto fail;
3075 }
3076 rr->resrec.rdlength = rdlength;
3077 mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3078 break;
3079 }
3080 case kDNSType_DS:
3081 {
3082 if (rdlength < DS_FIXED_SIZE + 1)
3083 {
3084 LogInfo("SetRData: DS too small length %d", rdlength);
3085 goto fail;
3086 }
3087 rr->resrec.rdlength = rdlength;
3088 mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3089 break;
3090 }
3091
3092 default:
3093 debugf("SetRData: Warning! Reading resource type %d (%s) as opaque data",
3094 rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
3095 // Note: Just because we don't understand the record type, that doesn't
3096 // mean we fail. The DNS protocol specifies rdlength, so we can
3097 // safely skip over unknown records and ignore them.
3098 // We also grab a binary copy of the rdata anyway, since the caller
3099 // might know how to interpret it even if we don't.
3100 rr->resrec.rdlength = rdlength;
3101 mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3102 break;
3103 }
3104 return mDNStrue;
3105 fail:
3106 return mDNSfalse;
3107 }
3108
3109 mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr,
3110 const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr)
3111 {
3112 CacheRecord *const rr = &largecr->r;
3113 mDNSu16 pktrdlength;
3114
3115 if (largecr == &m->rec && m->rec.r.resrec.RecordType)
3116 {
3117 LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r));
3118 #if ForceAlerts
3119 *(long*)0 = 0;
3120 #endif
3121 }
3122
3123 rr->next = mDNSNULL;
3124 rr->resrec.name = &largecr->namestorage;
3125
3126 rr->NextInKAList = mDNSNULL;
3127 rr->TimeRcvd = m ? m->timenow : 0;
3128 rr->DelayDelivery = 0;
3129 rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord()
3130 rr->LastUsed = m ? m->timenow : 0;
3131 rr->CRActiveQuestion = mDNSNULL;
3132 rr->UnansweredQueries = 0;
3133 rr->LastUnansweredTime= 0;
3134 #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
3135 rr->MPUnansweredQ = 0;
3136 rr->MPLastUnansweredQT= 0;
3137 rr->MPUnansweredKA = 0;
3138 rr->MPExpectingKA = mDNSfalse;
3139 #endif
3140 rr->NextInCFList = mDNSNULL;
3141
3142 rr->resrec.InterfaceID = InterfaceID;
3143 rr->resrec.rDNSServer = mDNSNULL;
3144
3145 ptr = getDomainName(msg, ptr, end, &largecr->namestorage); // Will bail out correctly if ptr is NULL
3146 if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); }
3147 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
3148
3149 if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
3150
3151 rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]);
3152 rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask);
3153 rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
3154 if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1)
3155 rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
3156 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
3157 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
3158 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
3159
3160 // If mDNS record has cache-flush bit set, we mark it unique
3161 // For uDNS records, all are implicitly deemed unique (a single DNS server is always
3162 // authoritative for the entire RRSet), unless this is a truncated response
3163 if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || (!InterfaceID && !(msg->h.flags.b[0] & kDNSFlag0_TC)))
3164 RecordType |= kDNSRecordTypePacketUniqueMask;
3165 ptr += 10;
3166 if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
3167 end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record
3168
3169 rr->resrec.rdata = (RData*)&rr->smallrdatastorage;
3170 rr->resrec.rdata->MaxRDLength = MaximumRDSize;
3171
3172 if (pktrdlength > MaximumRDSize)
3173 {
3174 LogInfo("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
3175 DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
3176 goto fail;
3177 }
3178
3179 if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c);
3180
3181 // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
3182 // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
3183 // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
3184 // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
3185 // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
3186 if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0) // Used in update packets to mean "Delete An RRset" (RFC 2136)
3187 rr->resrec.rdlength = 0;
3188 else if (!SetRData(msg, ptr, end, largecr, pktrdlength))
3189 goto fail;
3190
3191 SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rdlength, rdestimate, rdatahash for us
3192
3193 // Success! Now fill in RecordType to show this record contains valid data
3194 rr->resrec.RecordType = RecordType;
3195 return(end);
3196
3197 fail:
3198 // If we were unable to parse the rdata in this record, we indicate that by
3199 // returing a 'kDNSRecordTypePacketNegative' record with rdlength set to zero
3200 rr->resrec.RecordType = kDNSRecordTypePacketNegative;
3201 rr->resrec.rdlength = 0;
3202 rr->resrec.rdestimate = 0;
3203 rr->resrec.rdatahash = 0;
3204 return(end);
3205 }
3206
3207 mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
3208 {
3209 ptr = skipDomainName(msg, ptr, end);
3210 if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); }
3211 if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
3212 return(ptr+4);
3213 }
3214
3215 mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
3216 DNSQuestion *question)
3217 {
3218 mDNSPlatformMemZero(question, sizeof(*question));
3219 question->InterfaceID = InterfaceID;
3220 if (!InterfaceID) question->TargetQID = onesID; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast
3221 ptr = getDomainName(msg, ptr, end, &question->qname);
3222 if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
3223 if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
3224
3225 question->qnamehash = DomainNameHashValue(&question->qname);
3226 question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type
3227 question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class
3228 return(ptr+4);
3229 }
3230
3231 mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
3232 {
3233 int i;
3234 const mDNSu8 *ptr = msg->data;
3235 for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
3236 return(ptr);
3237 }
3238
3239 mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
3240 {
3241 int i;
3242 const mDNSu8 *ptr = LocateAnswers(msg, end);
3243 for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
3244 return(ptr);
3245 }
3246
3247 mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end)
3248 {
3249 int i;
3250 const mDNSu8 *ptr = LocateAuthorities(msg, end);
3251 for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end);
3252 return (ptr);
3253 }
3254
3255 mDNSexport const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize)
3256 {
3257 int i;
3258 const mDNSu8 *ptr = LocateAdditionals(msg, end);
3259
3260 // Locate the OPT record.
3261 // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
3262 // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
3263 // but not necessarily the *last* entry in the Additional Section.
3264 for (i = 0; ptr && i < msg->h.numAdditionals; i++)
3265 {
3266 if (ptr + DNSOpt_Header_Space + minsize <= end && // Make sure we have 11+minsize bytes of data
3267 ptr[0] == 0 && // Name must be root label
3268 ptr[1] == (kDNSType_OPT >> 8 ) && // rrtype OPT
3269 ptr[2] == (kDNSType_OPT & 0xFF) &&
3270 ((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)minsize)
3271 return(ptr);
3272 else
3273 ptr = skipResourceRecord(msg, ptr, end);
3274 }
3275 return(mDNSNULL);
3276 }
3277
3278 // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
3279 // it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use
3280 // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
3281 // The code that currently calls this assumes there's only one, instead of iterating through the set
3282 mDNSexport const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end)
3283 {
3284 const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LLQData_Space);
3285 if (ptr)
3286 {
3287 ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
3288 if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) return(&m->rec.r.resrec.rdata->u.opt[0]);
3289 }
3290 return(mDNSNULL);
3291 }
3292
3293 // Get the lease life of records in a dynamic update
3294 // returns 0 on error or if no lease present
3295 mDNSexport mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end)
3296 {
3297 mDNSu32 result = 0;
3298 const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space);
3299 if (ptr) ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
3300 if (ptr && m->rec.r.resrec.rdlength >= DNSOpt_LeaseData_Space && m->rec.r.resrec.rdata->u.opt[0].opt == kDNSOpt_Lease)
3301 result = m->rec.r.resrec.rdata->u.opt[0].u.updatelease;
3302 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
3303 return(result);
3304 }
3305
3306 mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label)
3307 {
3308 int i;
3309 LogMsg("%2d %s", count, label);
3310 for (i = 0; i < count && ptr; i++)
3311 {
3312 // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
3313 // but since it's only used for debugging (and probably only on OS X, not on
3314 // embedded systems) putting a 9kB object on the stack isn't a big problem.
3315 LargeCacheRecord largecr;
3316 ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr);
3317 if (ptr) LogMsg("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r));
3318 }
3319 if (!ptr) LogMsg("DumpRecords: ERROR: Premature end of packet data");
3320 return(ptr);
3321 }
3322
3323 #define DNS_OP_Name(X) ( \
3324 (X) == kDNSFlag0_OP_StdQuery ? "" : \
3325 (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
3326 (X) == kDNSFlag0_OP_Status ? "Status " : \
3327 (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \
3328 (X) == kDNSFlag0_OP_Notify ? "Notify " : \
3329 (X) == kDNSFlag0_OP_Update ? "Update " : "?? " )
3330
3331 #define DNS_RC_Name(X) ( \
3332 (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \
3333 (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \
3334 (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \
3335 (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \
3336 (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \
3337 (X) == kDNSFlag1_RC_Refused ? "Refused" : \
3338 (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \
3339 (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \
3340 (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \
3341 (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
3342 (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" )
3343
3344 // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
3345 mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport,
3346 const mDNSAddr *srcaddr, mDNSIPPort srcport,
3347 const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end)
3348 {
3349 mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update);
3350 const mDNSu8 *ptr = msg->data;
3351 int i;
3352 DNSQuestion q;
3353 char tbuffer[64], sbuffer[64], dbuffer[64] = "";
3354 if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received" )] = 0;
3355 else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receiv")] = 0;
3356 if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port " )] = 0;
3357 else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0;
3358 if (dstaddr || !mDNSIPPortIsZero(dstport))
3359 dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0;
3360
3361 LogMsg("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --",
3362 tbuffer, transport,
3363 DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask),
3364 msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query",
3365 msg->h.flags.b[0], msg->h.flags.b[1],
3366 DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask),
3367 msg->h.flags.b[1] & kDNSFlag1_RC_Mask,
3368 msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "",
3369 msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "",
3370 msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "",
3371 msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "",
3372 msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "",
3373 msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "",
3374 mDNSVal16(msg->h.id),
3375 end - msg->data,
3376 sbuffer, mDNSVal16(srcport), dbuffer,
3377 (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : ""
3378 );
3379
3380 LogMsg("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions");
3381 for (i = 0; i < msg->h.numQuestions && ptr; i++)
3382 {
3383 ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q);
3384 if (ptr) LogMsg("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype));
3385 }
3386 ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers");
3387 ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities");
3388 ptr = DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals");
3389 LogMsg("--------------");
3390 }
3391
3392 // ***************************************************************************
3393 #if COMPILER_LIKES_PRAGMA_MARK
3394 #pragma mark -
3395 #pragma mark - Packet Sending Functions
3396 #endif
3397
3398 // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
3399 struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ };
3400
3401 struct UDPSocket_struct
3402 {
3403 mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
3404 };
3405
3406 // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
3407 // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
3408 mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
3409 mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst,
3410 mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo,
3411 mDNSBool useBackgroundTrafficClass)
3412 {
3413 mStatus status = mStatus_NoError;
3414 const mDNSu16 numAdditionals = msg->h.numAdditionals;
3415 mDNSu8 *newend;
3416 mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
3417
3418 // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code
3419 if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
3420 {
3421 LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data);
3422 return mStatus_BadParamErr;
3423 }
3424
3425 newend = putHINFO(m, msg, end, authInfo, limit);
3426 if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed msg %p end %p, limit %p", msg->data, end, limit); // Not fatal
3427 else end = newend;
3428
3429 // Put all the integer values in IETF byte-order (MSB first, LSB second)
3430 SwapDNSHeaderBytes(msg);
3431
3432 if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0); // DNSDigest_SignMessage operates on message in network byte order
3433 if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; }
3434 else
3435 {
3436 // Send the packet on the wire
3437 if (!sock)
3438 status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, src, dst, dstport, useBackgroundTrafficClass);
3439 else
3440 {
3441 mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
3442 mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) };
3443 long nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2); // Should do scatter/gather here -- this is probably going out as two packets
3444 if (nsent != 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); status = mStatus_ConnFailed; }
3445 else
3446 {
3447 nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen);
3448 if (nsent != msglen) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); status = mStatus_ConnFailed; }
3449 }
3450 }
3451 }
3452
3453 // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage)
3454 SwapDNSHeaderBytes(msg);
3455
3456 // Dump the packet with the HINFO and TSIG
3457 if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id))
3458 DumpPacket(m, status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end);
3459
3460 // put the number of additionals back the way it was
3461 msg->h.numAdditionals = numAdditionals;
3462
3463 return(status);
3464 }
3465
3466 // ***************************************************************************
3467 #if COMPILER_LIKES_PRAGMA_MARK
3468 #pragma mark -
3469 #pragma mark - RR List Management & Task Management
3470 #endif
3471
3472 mDNSexport void mDNS_Lock_(mDNS *const m, const char * const functionname)
3473 {
3474 // MUST grab the platform lock FIRST!
3475 mDNSPlatformLock(m);
3476
3477 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
3478 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
3479 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
3480 // If mDNS_busy != mDNS_reentrancy that's a bad sign
3481 if (m->mDNS_busy != m->mDNS_reentrancy)
3482 {
3483 LogMsg("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy);
3484 #if ForceAlerts
3485 *(long*)0 = 0;
3486 #endif
3487 }
3488
3489 // If this is an initial entry into the mDNSCore code, set m->timenow
3490 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
3491 if (m->mDNS_busy == 0)
3492 {
3493 if (m->timenow)
3494 LogMsg("%s: mDNS_Lock: m->timenow already set (%ld/%ld)", functionname, m->timenow, mDNS_TimeNow_NoLock(m));
3495 m->timenow = mDNS_TimeNow_NoLock(m);
3496 if (m->timenow == 0) m->timenow = 1;
3497 }
3498 else if (m->timenow == 0)
3499 {
3500 LogMsg("%s: mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", functionname, m->mDNS_busy);
3501 m->timenow = mDNS_TimeNow_NoLock(m);
3502 if (m->timenow == 0) m->timenow = 1;
3503 }
3504
3505 if (m->timenow_last - m->timenow > 0)
3506 {
3507 m->timenow_adjust += m->timenow_last - m->timenow;
3508 LogMsg("%s: mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", functionname, m->timenow_last - m->timenow, m->timenow_adjust);
3509 m->timenow = m->timenow_last;
3510 }
3511 m->timenow_last = m->timenow;
3512
3513 // Increment mDNS_busy so we'll recognise re-entrant calls
3514 m->mDNS_busy++;
3515 }
3516
3517 mDNSlocal AuthRecord *AnyLocalRecordReady(const mDNS *const m)
3518 {
3519 AuthRecord *rr;
3520 for (rr = m->NewLocalRecords; rr; rr = rr->next)
3521 if (LocalRecordReady(rr)) return rr;
3522 return mDNSNULL;
3523 }
3524
3525 mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
3526 {
3527 mDNSs32 e = m->timenow + 0x78000000;
3528 if (m->mDNSPlatformStatus != mStatus_NoError) return(e);
3529 if (m->NewQuestions)
3530 {
3531 if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering;
3532 else return(m->timenow);
3533 }
3534 if (m->NewLocalOnlyQuestions) return(m->timenow);
3535 if (m->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow);
3536 if (m->NewLocalOnlyRecords) return(m->timenow);
3537 if (m->SPSProxyListChanged) return(m->timenow);
3538 if (m->LocalRemoveEvents) return(m->timenow);
3539
3540 #ifndef UNICAST_DISABLED
3541 if (e - m->NextuDNSEvent > 0) e = m->NextuDNSEvent;
3542 if (e - m->NextScheduledNATOp > 0) e = m->NextScheduledNATOp;
3543 if (m->NextSRVUpdate && e - m->NextSRVUpdate > 0) e = m->NextSRVUpdate;
3544 #endif
3545
3546 if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck;
3547 if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS;
3548 if (e - m->NextScheduledKA > 0) e = m->NextScheduledKA;
3549 if (m->clearIgnoreNA && (e - m->clearIgnoreNA > 0)) e = m->clearIgnoreNA;
3550
3551 // NextScheduledSPRetry only valid when DelaySleep not set
3552 if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry;
3553 if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep;
3554
3555 if (m->SuppressSending)
3556 {
3557 if (e - m->SuppressSending > 0) e = m->SuppressSending;
3558 }
3559 else
3560 {
3561 if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery;
3562 if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe;
3563 if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
3564 }
3565 if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime;
3566 return(e);
3567 }
3568
3569 mDNSexport void ShowTaskSchedulingError(mDNS *const m)
3570 {
3571 AuthRecord *rr;
3572 mDNS_Lock(m);
3573
3574 LogMsg("Task Scheduling Error: Continuously busy for more than a second");
3575
3576 // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above
3577
3578 if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0))
3579 LogMsg("Task Scheduling Error: NewQuestion %##s (%s)",
3580 m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
3581
3582 if (m->NewLocalOnlyQuestions)
3583 LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
3584 m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
3585
3586 if (m->NewLocalRecords)
3587 {
3588 rr = AnyLocalRecordReady(m);
3589 if (rr) LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr));
3590 }
3591
3592 if (m->NewLocalOnlyRecords) LogMsg("Task Scheduling Error: NewLocalOnlyRecords");
3593
3594 if (m->SPSProxyListChanged) LogMsg("Task Scheduling Error: SPSProxyListChanged");
3595 if (m->LocalRemoveEvents) LogMsg("Task Scheduling Error: LocalRemoveEvents");
3596
3597 if (m->timenow - m->NextScheduledEvent >= 0)
3598 LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m->timenow - m->NextScheduledEvent);
3599
3600 #ifndef UNICAST_DISABLED
3601 if (m->timenow - m->NextuDNSEvent >= 0)
3602 LogMsg("Task Scheduling Error: m->NextuDNSEvent %d", m->timenow - m->NextuDNSEvent);
3603 if (m->timenow - m->NextScheduledNATOp >= 0)
3604 LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp);
3605 if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0)
3606 LogMsg("Task Scheduling Error: m->NextSRVUpdate %d", m->timenow - m->NextSRVUpdate);
3607 #endif
3608
3609 if (m->timenow - m->NextCacheCheck >= 0)
3610 LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck);
3611 if (m->timenow - m->NextScheduledSPS >= 0)
3612 LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS);
3613 if (m->timenow - m->NextScheduledKA >= 0)
3614 LogMsg("Task Scheduling Error: m->NextScheduledKA %d", m->timenow - m->NextScheduledKA);
3615 if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0)
3616 LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry);
3617 if (m->DelaySleep && m->timenow - m->DelaySleep >= 0)
3618 LogMsg("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep);
3619
3620 if (m->SuppressSending && m->timenow - m->SuppressSending >= 0)
3621 LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending);
3622 if (m->timenow - m->NextScheduledQuery >= 0)
3623 LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery);
3624 if (m->timenow - m->NextScheduledProbe >= 0)
3625 LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe);
3626 if (m->timenow - m->NextScheduledResponse >= 0)
3627 LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse);
3628 if (m->clearIgnoreNA && m->timenow - m->clearIgnoreNA >= 0)
3629 LogMsg("Task Scheduling Error: m->clearIgnoreNA %d", m->timenow - m->clearIgnoreNA);
3630 mDNS_Unlock(m);
3631 }
3632
3633 mDNSexport void mDNS_Unlock_(mDNS *const m, const char * const functionname)
3634 {
3635 // Decrement mDNS_busy
3636 m->mDNS_busy--;
3637
3638 // Check for locking failures
3639 if (m->mDNS_busy != m->mDNS_reentrancy)
3640 {
3641 LogMsg("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy);
3642 #if ForceAlerts
3643 *(long*)0 = 0;
3644 #endif
3645 }
3646
3647 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
3648 if (m->mDNS_busy == 0)
3649 {
3650 m->NextScheduledEvent = GetNextScheduledEvent(m);
3651 if (m->timenow == 0) LogMsg("%s: mDNS_Unlock: ERROR! m->timenow aready zero", functionname);
3652 m->timenow = 0;
3653 }
3654
3655 // MUST release the platform lock LAST!
3656 mDNSPlatformUnlock(m);
3657 }
3658
3659 // ***************************************************************************
3660 #if COMPILER_LIKES_PRAGMA_MARK
3661 #pragma mark -
3662 #pragma mark - Specialized mDNS version of vsnprintf
3663 #endif
3664
3665 static const struct mDNSprintf_format
3666 {
3667 unsigned leftJustify : 1;
3668 unsigned forceSign : 1;
3669 unsigned zeroPad : 1;
3670 unsigned havePrecision : 1;
3671 unsigned hSize : 1;
3672 unsigned lSize : 1;
3673 char altForm;
3674 char sign; // +, - or space
3675 unsigned int fieldWidth;
3676 unsigned int precision;
3677 } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
3678
3679 mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
3680 {
3681 mDNSu32 nwritten = 0;
3682 int c;
3683 if (buflen == 0) return(0);
3684 buflen--; // Pre-reserve one space in the buffer for the terminating null
3685 if (buflen == 0) goto exit;
3686
3687 for (c = *fmt; c != 0; c = *++fmt)
3688 {
3689 if (c != '%')
3690 {
3691 *sbuffer++ = (char)c;
3692 if (++nwritten >= buflen) goto exit;
3693 }
3694 else
3695 {
3696 unsigned int i=0, j;
3697 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
3698 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
3699 // The size needs to be enough for a 256-byte domain name plus some error text.
3700 #define mDNS_VACB_Size 300
3701 char mDNS_VACB[mDNS_VACB_Size];
3702 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
3703 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
3704 char *s = mDNS_VACB_Lim, *digits;
3705 struct mDNSprintf_format F = mDNSprintf_format_default;
3706
3707 while (1) // decode flags
3708 {
3709 c = *++fmt;
3710 if (c == '-') F.leftJustify = 1;
3711 else if (c == '+') F.forceSign = 1;
3712 else if (c == ' ') F.sign = ' ';
3713 else if (c == '#') F.altForm++;
3714 else if (c == '0') F.zeroPad = 1;
3715 else break;
3716 }
3717
3718 if (c == '*') // decode field width
3719 {
3720 int f = va_arg(arg, int);
3721 if (f < 0) { f = -f; F.leftJustify = 1; }
3722 F.fieldWidth = (unsigned int)f;
3723 c = *++fmt;
3724 }
3725 else
3726 {
3727 for (; c >= '0' && c <= '9'; c = *++fmt)
3728 F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
3729 }
3730
3731 if (c == '.') // decode precision
3732 {
3733 if ((c = *++fmt) == '*')
3734 { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
3735 else for (; c >= '0' && c <= '9'; c = *++fmt)
3736 F.precision = (10 * F.precision) + (c - '0');
3737 F.havePrecision = 1;
3738 }
3739
3740 if (F.leftJustify) F.zeroPad = 0;
3741
3742 conv:
3743 switch (c) // perform appropriate conversion
3744 {
3745 unsigned long n;
3746 case 'h': F.hSize = 1; c = *++fmt; goto conv;
3747 case 'l': // fall through
3748 case 'L': F.lSize = 1; c = *++fmt; goto conv;
3749 case 'd':
3750 case 'i': if (F.lSize) n = (unsigned long)va_arg(arg, long);
3751 else n = (unsigned long)va_arg(arg, int);
3752 if (F.hSize) n = (short) n;
3753 if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
3754 else if (F.forceSign) F.sign = '+';
3755 goto decimal;
3756 case 'u': if (F.lSize) n = va_arg(arg, unsigned long);
3757 else n = va_arg(arg, unsigned int);
3758 if (F.hSize) n = (unsigned short) n;
3759 F.sign = 0;
3760 goto decimal;
3761 decimal: if (!F.havePrecision)
3762 {
3763 if (F.zeroPad)
3764 {
3765 F.precision = F.fieldWidth;
3766 if (F.sign) --F.precision;
3767 }
3768 if (F.precision < 1) F.precision = 1;
3769 }
3770 if (F.precision > mDNS_VACB_Size - 1)
3771 F.precision = mDNS_VACB_Size - 1;
3772 for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
3773 for (; i < F.precision; i++) *--s = '0';
3774 if (F.sign) { *--s = F.sign; i++; }
3775 break;
3776
3777 case 'o': if (F.lSize) n = va_arg(arg, unsigned long);
3778 else n = va_arg(arg, unsigned int);
3779 if (F.hSize) n = (unsigned short) n;
3780 if (!F.havePrecision)
3781 {
3782 if (F.zeroPad) F.precision = F.fieldWidth;
3783 if (F.precision < 1) F.precision = 1;
3784 }
3785 if (F.precision > mDNS_VACB_Size - 1)
3786 F.precision = mDNS_VACB_Size - 1;
3787 for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
3788 if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
3789 for (; i < F.precision; i++) *--s = '0';
3790 break;
3791
3792 case 'a': {
3793 unsigned char *a = va_arg(arg, unsigned char *);
3794 if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
3795 else
3796 {
3797 s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
3798 if (F.altForm)
3799 {
3800 mDNSAddr *ip = (mDNSAddr*)a;
3801 switch (ip->type)
3802 {
3803 case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break;
3804 case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
3805 default: F.precision = 0; break;
3806 }
3807 }
3808 if (F.altForm && !F.precision)
3809 i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "«ZERO ADDRESS»");
3810 else switch (F.precision)
3811 {
3812 case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d",
3813 a[0], a[1], a[2], a[3]); break;
3814 case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
3815 a[0], a[1], a[2], a[3], a[4], a[5]); break;
3816 case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB),
3817 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
3818 a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7],
3819 a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break;
3820 default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify"
3821 " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
3822 }
3823 }
3824 }
3825 break;
3826
3827 case 'p': F.havePrecision = F.lSize = 1;
3828 F.precision = sizeof(void*) * 2; // 8 characters on 32-bit; 16 characters on 64-bit
3829 case 'X': digits = "0123456789ABCDEF";
3830 goto hexadecimal;
3831 case 'x': digits = "0123456789abcdef";
3832 hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long);
3833 else n = va_arg(arg, unsigned int);
3834 if (F.hSize) n = (unsigned short) n;
3835 if (!F.havePrecision)
3836 {
3837 if (F.zeroPad)
3838 {
3839 F.precision = F.fieldWidth;
3840 if (F.altForm) F.precision -= 2;
3841 }
3842 if (F.precision < 1) F.precision = 1;
3843 }
3844 if (F.precision > mDNS_VACB_Size - 1)
3845 F.precision = mDNS_VACB_Size - 1;
3846 for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
3847 for (; i < F.precision; i++) *--s = '0';
3848 if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
3849 break;
3850
3851 case 'c': *--s = (char)va_arg(arg, int); i = 1; break;
3852
3853 case 's': s = va_arg(arg, char *);
3854 if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
3855 else switch (F.altForm)
3856 {
3857 case 0: i=0;
3858 if (!F.havePrecision) // C string
3859 while (s[i]) i++;
3860 else
3861 {
3862 while ((i < F.precision) && s[i]) i++;
3863 // Make sure we don't truncate in the middle of a UTF-8 character
3864 // If last character we got was any kind of UTF-8 multi-byte character,
3865 // then see if we have to back up.
3866 // This is not as easy as the similar checks below, because
3867 // here we can't assume it's safe to examine the *next* byte, so we
3868 // have to confine ourselves to working only backwards in the string.
3869 j = i; // Record where we got to
3870 // Now, back up until we find first non-continuation-char
3871 while (i>0 && (s[i-1] & 0xC0) == 0x80) i--;
3872 // Now s[i-1] is the first non-continuation-char
3873 // and (j-i) is the number of continuation-chars we found
3874 if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char
3875 {
3876 i--; // Tentatively eliminate this start-char as well
3877 // Now (j-i) is the number of characters we're considering eliminating.
3878 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
3879 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
3880 // (with sign extension) then the result has to be 0xFE.
3881 // If this is right, then we reinstate the tentatively eliminated bytes.
3882 if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j;
3883 }
3884 }
3885 break;
3886 case 1: i = (unsigned char) *s++; break; // Pascal string
3887 case 2: { // DNS label-sequence name
3888 unsigned char *a = (unsigned char *)s;
3889 s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
3890 if (*a == 0) *s++ = '.'; // Special case for root DNS name
3891 while (*a)
3892 {
3893 char buf[63*4+1];
3894 if (*a > 63)
3895 { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
3896 if (s + *a >= &mDNS_VACB[254])
3897 { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
3898 // Need to use ConvertDomainLabelToCString to do proper escaping here,
3899 // so it's clear what's a literal dot and what's a label separator
3900 ConvertDomainLabelToCString((domainlabel*)a, buf);
3901 s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf);
3902 a += 1 + *a;
3903 }
3904 i = (mDNSu32)(s - mDNS_VACB);
3905 s = mDNS_VACB; // Reset s back to the start of the buffer
3906 break;
3907 }
3908 }
3909 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
3910 if (F.havePrecision && i > F.precision)
3911 { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
3912 break;
3913
3914 case 'n': s = va_arg(arg, char *);
3915 if (F.hSize) *(short *) s = (short)nwritten;
3916 else if (F.lSize) *(long *) s = (long)nwritten;
3917 else *(int *) s = (int)nwritten;
3918 continue;
3919
3920 default: s = mDNS_VACB;
3921 i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
3922
3923 case '%': *sbuffer++ = (char)c;
3924 if (++nwritten >= buflen) goto exit;
3925 break;
3926 }
3927
3928 if (i < F.fieldWidth && !F.leftJustify) // Pad on the left
3929 do {
3930 *sbuffer++ = ' ';
3931 if (++nwritten >= buflen) goto exit;
3932 } while (i < --F.fieldWidth);
3933
3934 // Make sure we don't truncate in the middle of a UTF-8 character.
3935 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
3936 // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
3937 // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
3938 // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
3939 if (i > buflen - nwritten)
3940 { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
3941 for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
3942 nwritten += i;
3943 if (nwritten >= buflen) goto exit;
3944
3945 for (; i < F.fieldWidth; i++) // Pad on the right
3946 {
3947 *sbuffer++ = ' ';
3948 if (++nwritten >= buflen) goto exit;
3949 }
3950 }
3951 }
3952 exit:
3953 *sbuffer++ = 0;
3954 return(nwritten);
3955 }
3956
3957 mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
3958 {
3959 mDNSu32 length;
3960
3961 va_list ptr;
3962 va_start(ptr,fmt);
3963 length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
3964 va_end(ptr);
3965
3966 return(length);
3967 }