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