]>
Commit | Line | Data |
---|---|---|
cc340f17 A |
1 | /* -*- Mode: C; tab-width: 4 -*- |
2 | * | |
8e92c31c A |
3 | * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. |
4 | * | |
67c8f8a1 A |
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 | |
8e92c31c | 8 | * |
67c8f8a1 | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
8e92c31c | 10 | * |
67c8f8a1 A |
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 | |
8e92c31c | 15 | * limitations under the License. |
263eeeab | 16 | */ |
8e92c31c | 17 | |
7f0064bd | 18 | // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary |
8e92c31c A |
19 | #define mDNS_InstantiateInlines 1 |
20 | #include "DNSCommon.h" | |
21 | ||
22 | // Disable certain benign warnings with Microsoft compilers | |
23 | #if (defined(_MSC_VER)) | |
24 | // Disable "conditional expression is constant" warning for debug macros. | |
25 | // Otherwise, this generates warnings for the perfectly natural construct "while(1)" | |
26 | // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know | |
27 | #pragma warning(disable:4127) | |
7f0064bd A |
28 | // Disable "array is too small to include a terminating null character" warning |
29 | // -- domain labels have an initial length byte, not a terminating null character | |
30 | #pragma warning(disable:4295) | |
8e92c31c A |
31 | #endif |
32 | ||
33 | // *************************************************************************** | |
34 | #if COMPILER_LIKES_PRAGMA_MARK | |
67c8f8a1 | 35 | #pragma mark - Program Constants |
8e92c31c A |
36 | #endif |
37 | ||
67c8f8a1 | 38 | mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0; |
263eeeab | 39 | mDNSexport const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)-1; |
ca3eca6b A |
40 | mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2; |
41 | mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)-3; | |
263eeeab | 42 | mDNSexport const mDNSInterfaceID mDNSInterface_P2P = (mDNSInterfaceID)-4; |
67c8f8a1 A |
43 | |
44 | // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of | |
45 | // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP | |
46 | // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders. | |
47 | // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355. | |
48 | // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability | |
49 | // with Microsoft's LLMNR client code. | |
50 | ||
32bb7e43 A |
51 | #define DiscardPortAsNumber 9 |
52 | #define SSHPortAsNumber 22 | |
53 | #define UnicastDNSPortAsNumber 53 | |
67c8f8a1 | 54 | #define SSDPPortAsNumber 1900 |
1a175162 | 55 | #define IPSECPortAsNumber 4500 |
32bb7e43 | 56 | #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback |
67c8f8a1 A |
57 | #define NATPMPAnnouncementPortAsNumber 5350 |
58 | #define NATPMPPortAsNumber 5351 | |
59 | #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc. | |
60 | #define MulticastDNSPortAsNumber 5353 | |
61 | #define LoopbackIPCPortAsNumber 5354 | |
62 | //#define MulticastDNSPortAsNumber 5355 // LLMNR | |
67c8f8a1 A |
63 | #define PrivateDNSPortAsNumber 5533 |
64 | ||
32bb7e43 A |
65 | mDNSexport const mDNSIPPort DiscardPort = { { DiscardPortAsNumber >> 8, DiscardPortAsNumber & 0xFF } }; |
66 | mDNSexport const mDNSIPPort SSHPort = { { SSHPortAsNumber >> 8, SSHPortAsNumber & 0xFF } }; | |
67c8f8a1 | 67 | mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } }; |
32bb7e43 | 68 | mDNSexport const mDNSIPPort SSDPPort = { { SSDPPortAsNumber >> 8, SSDPPortAsNumber & 0xFF } }; |
1a175162 | 69 | mDNSexport const mDNSIPPort IPSECPort = { { IPSECPortAsNumber >> 8, IPSECPortAsNumber & 0xFF } }; |
32bb7e43 | 70 | mDNSexport const mDNSIPPort NSIPCPort = { { NSIPCPortAsNumber >> 8, NSIPCPortAsNumber & 0xFF } }; |
67c8f8a1 A |
71 | mDNSexport const mDNSIPPort NATPMPAnnouncementPort = { { NATPMPAnnouncementPortAsNumber >> 8, NATPMPAnnouncementPortAsNumber & 0xFF } }; |
72 | mDNSexport const mDNSIPPort NATPMPPort = { { NATPMPPortAsNumber >> 8, NATPMPPortAsNumber & 0xFF } }; | |
73 | mDNSexport const mDNSIPPort DNSEXTPort = { { DNSEXTPortAsNumber >> 8, DNSEXTPortAsNumber & 0xFF } }; | |
74 | mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } }; | |
75 | mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } }; | |
67c8f8a1 A |
76 | mDNSexport const mDNSIPPort PrivateDNSPort = { { PrivateDNSPortAsNumber >> 8, PrivateDNSPortAsNumber & 0xFF } }; |
77 | ||
263eeeab A |
78 | mDNSexport const OwnerOptData zeroOwner = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } }; |
79 | ||
80 | mDNSexport const mDNSIPPort zeroIPPort = { { 0 } }; | |
81 | mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } }; | |
82 | mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } }; | |
83 | mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } }; | |
84 | mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } }; | |
85 | mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } }; | |
86 | mDNSexport const mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255, 255 } }; | |
87 | mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; | |
88 | ||
89 | mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; | |
90 | mDNSexport const mDNSv4Addr AllHosts_v4 = { { 224, 0, 0, 1 } }; // For NAT-PMP Annoucements | |
91 | mDNSexport const mDNSv6Addr AllHosts_v6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } }; | |
92 | 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 | |
93 | mDNSexport const mDNSEthAddr AllHosts_v6_Eth = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } }; | |
94 | mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } }; | |
95 | //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR | |
96 | mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } }; | |
97 | //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR | |
67c8f8a1 A |
98 | |
99 | mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } }; | |
32bb7e43 | 100 | mDNSexport const mDNSOpaque16 onesID = { { 255, 255 } }; |
67c8f8a1 A |
101 | mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } }; |
102 | mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } }; | |
103 | mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; | |
104 | mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } }; | |
105 | mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } }; | |
106 | ||
107 | mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } }; | |
8e92c31c A |
108 | |
109 | // *************************************************************************** | |
110 | #if COMPILER_LIKES_PRAGMA_MARK | |
111 | #pragma mark - | |
112 | #pragma mark - General Utility Functions | |
113 | #endif | |
114 | ||
7f0064bd | 115 | // return true for RFC1918 private addresses |
67c8f8a1 | 116 | mDNSexport mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr) |
7f0064bd | 117 | { |
67c8f8a1 A |
118 | return ((addr->b[0] == 10) || // 10/8 prefix |
119 | (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) || // 172.16/12 | |
120 | (addr->b[0] == 192 && addr->b[1] == 168)); // 192.168/16 | |
7f0064bd A |
121 | } |
122 | ||
32bb7e43 | 123 | mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf) |
8e92c31c A |
124 | { |
125 | while (intf && !intf->InterfaceActive) intf = intf->next; | |
126 | return(intf); | |
127 | } | |
128 | ||
129 | mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf) | |
130 | { | |
131 | const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next); | |
132 | if (next) return(next->InterfaceID); else return(mDNSNULL); | |
133 | } | |
134 | ||
135 | mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id) | |
136 | { | |
137 | mDNSu32 slot, used = 0; | |
283ee3ff | 138 | CacheGroup *cg; |
32bb7e43 | 139 | const CacheRecord *rr; |
283ee3ff A |
140 | FORALL_CACHERECORDS(slot, cg, rr) |
141 | if (rr->resrec.InterfaceID == id) used++; | |
8e92c31c A |
142 | return(used); |
143 | } | |
144 | ||
145 | mDNSexport char *DNSTypeName(mDNSu16 rrtype) | |
146 | { | |
147 | switch (rrtype) | |
148 | { | |
149 | case kDNSType_A: return("Addr"); | |
283ee3ff | 150 | case kDNSType_NS: return("NS"); |
8e92c31c | 151 | case kDNSType_CNAME:return("CNAME"); |
283ee3ff | 152 | case kDNSType_SOA: return("SOA"); |
8e92c31c A |
153 | case kDNSType_NULL: return("NULL"); |
154 | case kDNSType_PTR: return("PTR"); | |
155 | case kDNSType_HINFO:return("HINFO"); | |
156 | case kDNSType_TXT: return("TXT"); | |
157 | case kDNSType_AAAA: return("AAAA"); | |
158 | case kDNSType_SRV: return("SRV"); | |
67c8f8a1 | 159 | case kDNSType_OPT: return("OPT"); |
32bb7e43 | 160 | case kDNSType_NSEC: return("NSEC"); |
67c8f8a1 | 161 | case kDNSType_TSIG: return("TSIG"); |
8e92c31c A |
162 | case kDNSQType_ANY: return("ANY"); |
163 | default: { | |
164 | static char buffer[16]; | |
165 | mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype); | |
166 | return(buffer); | |
167 | } | |
168 | } | |
169 | } | |
170 | ||
7cb34e5c A |
171 | // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display |
172 | // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as | |
173 | // long as this routine is only used for debugging messages, it probably isn't a big problem. | |
32bb7e43 | 174 | mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer) |
8e92c31c | 175 | { |
32bb7e43 A |
176 | const RDataBody2 *const rd = (RDataBody2 *)rd1; |
177 | #define RemSpc (MaxMsg-1-length) | |
7f0064bd | 178 | char *ptr = buffer; |
32bb7e43 | 179 | mDNSu32 length = mDNS_snprintf(buffer, MaxMsg-1, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype)); |
67c8f8a1 | 180 | if (rr->RecordType == kDNSRecordTypePacketNegative) return(buffer); |
32bb7e43 | 181 | if (!rr->rdlength) { mDNS_snprintf(buffer+length, RemSpc, "<< ZERO RDATA LENGTH >>"); return(buffer); } |
67c8f8a1 | 182 | |
8e92c31c A |
183 | switch (rr->rrtype) |
184 | { | |
32bb7e43 | 185 | case kDNSType_A: mDNS_snprintf(buffer+length, RemSpc, "%.4a", &rd->ipv4); break; |
7f0064bd A |
186 | |
187 | case kDNSType_NS: // Same as PTR | |
8e92c31c | 188 | case kDNSType_CNAME:// Same as PTR |
32bb7e43 | 189 | case kDNSType_PTR: mDNS_snprintf(buffer+length, RemSpc, "%##s", rd->name.c); break; |
7f0064bd | 190 | |
32bb7e43 | 191 | case kDNSType_SOA: mDNS_snprintf(buffer+length, RemSpc, "%##s %##s %d %d %d %d %d", |
67c8f8a1 A |
192 | rd->soa.mname.c, rd->soa.rname.c, |
193 | rd->soa.serial, rd->soa.refresh, rd->soa.retry, rd->soa.expire, rd->soa.min); | |
194 | break; | |
195 | ||
5e65c77f | 196 | case kDNSType_HINFO:// Display this the same as TXT (show all constituent strings) |
67c8f8a1 | 197 | case kDNSType_TXT: { |
32bb7e43 | 198 | const mDNSu8 *t = rd->txt.c; |
67c8f8a1 A |
199 | while (t < rd->txt.c + rr->rdlength) |
200 | { | |
32bb7e43 | 201 | length += mDNS_snprintf(buffer+length, RemSpc, "%s%#s", t > rd->txt.c ? "¦" : "", t); |
67c8f8a1 A |
202 | t += 1 + t[0]; |
203 | } | |
204 | } break; | |
7f0064bd | 205 | |
32bb7e43 A |
206 | case kDNSType_AAAA: mDNS_snprintf(buffer+length, RemSpc, "%.16a", &rd->ipv6); break; |
207 | case kDNSType_SRV: mDNS_snprintf(buffer+length, RemSpc, "%u %u %u %##s", | |
cc340f17 | 208 | rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break; |
32bb7e43 A |
209 | |
210 | case kDNSType_OPT: { | |
211 | const rdataOPT *opt; | |
212 | const rdataOPT *const end = (const rdataOPT *)&rd->data[rr->rdlength]; | |
213 | length += mDNS_snprintf(buffer+length, RemSpc, "Max %d", rr->rrclass); | |
214 | for (opt = &rd->opt[0]; opt < end; opt++) | |
67c8f8a1 | 215 | { |
32bb7e43 A |
216 | switch(opt->opt) |
217 | { | |
218 | case kDNSOpt_LLQ: | |
219 | length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.llq.vers); | |
220 | length += mDNS_snprintf(buffer+length, RemSpc, " Op %d", opt->u.llq.llqOp); | |
221 | length += mDNS_snprintf(buffer+length, RemSpc, " Err/Port %d", opt->u.llq.err); | |
222 | length += mDNS_snprintf(buffer+length, RemSpc, " ID %08X%08X", opt->u.llq.id.l[0], opt->u.llq.id.l[1]); | |
223 | length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.llq.llqlease); | |
224 | break; | |
225 | case kDNSOpt_Lease: | |
226 | length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.updatelease); | |
227 | break; | |
228 | case kDNSOpt_Owner: | |
229 | length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.owner.vers); | |
230 | length += mDNS_snprintf(buffer+length, RemSpc, " Seq %3d", (mDNSu8)opt->u.owner.seq); // Display as unsigned | |
231 | length += mDNS_snprintf(buffer+length, RemSpc, " MAC %.6a", opt->u.owner.HMAC.b); | |
232 | if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4) | |
233 | { | |
234 | length += mDNS_snprintf(buffer+length, RemSpc, " I-MAC %.6a", opt->u.owner.IMAC.b); | |
235 | if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4) | |
236 | length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b); | |
237 | } | |
238 | break; | |
239 | default: | |
240 | length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d", opt->opt); | |
241 | break; | |
242 | } | |
67c8f8a1 | 243 | } |
32bb7e43 | 244 | } |
67c8f8a1 | 245 | break; |
32bb7e43 A |
246 | |
247 | case kDNSType_NSEC: { | |
263eeeab | 248 | mDNSu16 i; |
32bb7e43 A |
249 | for (i=0; i<255; i++) |
250 | if (rd->nsec.bitmap[i>>3] & (128 >> (i&7))) | |
251 | length += mDNS_snprintf(buffer+length, RemSpc, "%s ", DNSTypeName(i)); | |
252 | } | |
253 | break; | |
254 | ||
255 | default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %s", rr->rdlength, rd->data); | |
67c8f8a1 A |
256 | // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not |
257 | for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.'; | |
258 | break; | |
8e92c31c | 259 | } |
7f0064bd | 260 | return(buffer); |
8e92c31c A |
261 | } |
262 | ||
32bb7e43 A |
263 | // See comments in mDNSEmbeddedAPI.h |
264 | #if _PLATFORM_HAS_STRONG_PRNG_ | |
265 | #define mDNSRandomNumber mDNSPlatformRandomNumber | |
266 | #else | |
267 | mDNSlocal mDNSu32 mDNSRandomFromSeed(mDNSu32 seed) | |
268 | { | |
269 | return seed * 21 + 1; | |
270 | } | |
9f29194f | 271 | |
32bb7e43 A |
272 | mDNSlocal mDNSu32 mDNSMixRandomSeed(mDNSu32 seed, mDNSu8 iteration) |
273 | { | |
274 | return iteration ? mDNSMixRandomSeed(mDNSRandomFromSeed(seed), --iteration) : seed; | |
275 | } | |
9f29194f | 276 | |
32bb7e43 | 277 | mDNSlocal mDNSu32 mDNSRandomNumber() |
8e92c31c | 278 | { |
32bb7e43 | 279 | static mDNSBool seeded = mDNSfalse; |
8e92c31c | 280 | static mDNSu32 seed = 0; |
32bb7e43 | 281 | if (!seeded) |
7f0064bd | 282 | { |
32bb7e43 A |
283 | seed = mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100); |
284 | seeded = mDNStrue; | |
7f0064bd | 285 | } |
32bb7e43 | 286 | return (seed = mDNSRandomFromSeed(seed)); |
8e92c31c | 287 | } |
32bb7e43 A |
288 | #endif // ! _PLATFORM_HAS_STRONG_PRNG_ |
289 | ||
290 | mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) // Returns pseudo-random result from zero to max inclusive | |
67c8f8a1 | 291 | { |
32bb7e43 | 292 | mDNSu32 ret = 0; |
67c8f8a1 | 293 | mDNSu32 mask = 1; |
32bb7e43 | 294 | |
67c8f8a1 | 295 | while (mask < max) mask = (mask << 1) | 1; |
32bb7e43 A |
296 | |
297 | do ret = mDNSRandomNumber() & mask; | |
298 | while (ret > max); | |
299 | ||
300 | return ret; | |
67c8f8a1 A |
301 | } |
302 | ||
8e92c31c A |
303 | mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2) |
304 | { | |
305 | if (ip1->type == ip2->type) | |
306 | { | |
307 | switch (ip1->type) | |
308 | { | |
7f0064bd | 309 | case mDNSAddrType_None : return(mDNStrue); // Empty addresses have no data and are therefore always equal |
8e92c31c A |
310 | case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4)); |
311 | case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6)); | |
312 | } | |
313 | } | |
314 | return(mDNSfalse); | |
315 | } | |
316 | ||
317 | mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip) | |
318 | { | |
319 | switch(ip->type) | |
320 | { | |
67c8f8a1 A |
321 | case mDNSAddrType_IPv4: return(mDNSBool)(mDNSSameIPv4Address(ip->ip.v4, AllDNSLinkGroup_v4.ip.v4)); |
322 | case mDNSAddrType_IPv6: return(mDNSBool)(mDNSSameIPv6Address(ip->ip.v6, AllDNSLinkGroup_v6.ip.v6)); | |
8e92c31c A |
323 | default: return(mDNSfalse); |
324 | } | |
325 | } | |
326 | ||
327 | // *************************************************************************** | |
328 | #if COMPILER_LIKES_PRAGMA_MARK | |
329 | #pragma mark - | |
330 | #pragma mark - Domain Name Utility Functions | |
331 | #endif | |
332 | ||
333 | mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b) | |
334 | { | |
335 | int i; | |
336 | const int len = *a++; | |
337 | ||
338 | if (len > MAX_DOMAIN_LABEL) | |
339 | { debugf("Malformed label (too long)"); return(mDNSfalse); } | |
340 | ||
341 | if (len != *b++) return(mDNSfalse); | |
342 | for (i=0; i<len; i++) | |
343 | { | |
344 | mDNSu8 ac = *a++; | |
345 | mDNSu8 bc = *b++; | |
346 | if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; | |
347 | if (mDNSIsUpperCase(bc)) bc += 'a' - 'A'; | |
348 | if (ac != bc) return(mDNSfalse); | |
349 | } | |
350 | return(mDNStrue); | |
351 | } | |
352 | ||
353 | mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2) | |
354 | { | |
355 | const mDNSu8 * a = d1->c; | |
356 | const mDNSu8 * b = d2->c; | |
357 | const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid | |
358 | ||
359 | while (*a || *b) | |
360 | { | |
361 | if (a + 1 + *a >= max) | |
32bb7e43 | 362 | { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse); } |
8e92c31c A |
363 | if (!SameDomainLabel(a, b)) return(mDNSfalse); |
364 | a += 1 + *a; | |
365 | b += 1 + *b; | |
366 | } | |
367 | ||
368 | return(mDNStrue); | |
369 | } | |
370 | ||
67c8f8a1 A |
371 | mDNSexport mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2) |
372 | { | |
373 | mDNSu16 l1 = DomainNameLength(d1); | |
374 | mDNSu16 l2 = DomainNameLength(d2); | |
375 | return(l1 <= MAX_DOMAIN_NAME && l1 == l2 && mDNSPlatformMemSame(d1, d2, l1)); | |
376 | } | |
377 | ||
8e92c31c A |
378 | mDNSexport mDNSBool IsLocalDomain(const domainname *d) |
379 | { | |
380 | // Domains that are defined to be resolved via link-local multicast are: | |
67c8f8a1 A |
381 | // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa. |
382 | static const domainname *nL = (const domainname*)"\x5" "local"; | |
383 | static const domainname *nR = (const domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa"; | |
384 | static const domainname *n8 = (const domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; | |
385 | static const domainname *n9 = (const domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; | |
386 | static const domainname *nA = (const domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; | |
387 | static const domainname *nB = (const domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa"; | |
388 | ||
389 | const domainname *d1, *d2, *d3, *d4, *d5; // Top-level domain, second-level domain, etc. | |
390 | d1 = d2 = d3 = d4 = d5 = mDNSNULL; | |
8e92c31c A |
391 | while (d->c[0]) |
392 | { | |
67c8f8a1 A |
393 | d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d; |
394 | d = (const domainname*)(d->c + 1 + d->c[0]); | |
8e92c31c A |
395 | } |
396 | ||
67c8f8a1 A |
397 | if (d1 && SameDomainName(d1, nL)) return(mDNStrue); |
398 | if (d4 && SameDomainName(d4, nR)) return(mDNStrue); | |
399 | if (d5 && SameDomainName(d5, n8)) return(mDNStrue); | |
400 | if (d5 && SameDomainName(d5, n9)) return(mDNStrue); | |
401 | if (d5 && SameDomainName(d5, nA)) return(mDNStrue); | |
402 | if (d5 && SameDomainName(d5, nB)) return(mDNStrue); | |
8e92c31c A |
403 | return(mDNSfalse); |
404 | } | |
405 | ||
32bb7e43 A |
406 | mDNSexport const mDNSu8 *LastLabel(const domainname *d) |
407 | { | |
408 | const mDNSu8 *p = d->c; | |
409 | while (d->c[0]) | |
410 | { | |
411 | p = d->c; | |
412 | d = (const domainname*)(d->c + 1 + d->c[0]); | |
413 | } | |
414 | return(p); | |
415 | } | |
416 | ||
8e92c31c | 417 | // Returns length of a domain name INCLUDING the byte for the final null label |
67c8f8a1 | 418 | // e.g. for the root label "." it returns one |
8e92c31c | 419 | // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero) |
32bb7e43 A |
420 | // Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME) |
421 | // If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1) | |
67c8f8a1 | 422 | mDNSexport mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit) |
8e92c31c A |
423 | { |
424 | const mDNSu8 *src = name->c; | |
67c8f8a1 | 425 | while (src < limit && *src <= MAX_DOMAIN_LABEL) |
8e92c31c | 426 | { |
67c8f8a1 | 427 | if (*src == 0) return((mDNSu16)(src - name->c + 1)); |
8e92c31c | 428 | src += 1 + *src; |
8e92c31c | 429 | } |
67c8f8a1 | 430 | return(MAX_DOMAIN_NAME+1); |
8e92c31c A |
431 | } |
432 | ||
433 | // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte | |
67c8f8a1 | 434 | // for the final null label, e.g. for the root label "." it returns one. |
8e92c31c A |
435 | // E.g. for the FQDN "foo.com." it returns 9 |
436 | // (length, three data bytes, length, three more data bytes, final zero). | |
437 | // In the case where a parent domain name is provided, and the given name is a child | |
438 | // of that parent, CompressedDomainNameLength returns the length of the prefix portion | |
439 | // of the child name, plus TWO bytes for the compression pointer. | |
440 | // E.g. for the name "foo.com." with parent "com.", it returns 6 | |
441 | // (length, three data bytes, two-byte compression pointer). | |
442 | mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent) | |
443 | { | |
444 | const mDNSu8 *src = name->c; | |
445 | if (parent && parent->c[0] == 0) parent = mDNSNULL; | |
446 | while (*src) | |
447 | { | |
448 | if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1); | |
67c8f8a1 | 449 | if (parent && SameDomainName((const domainname *)src, parent)) return((mDNSu16)(src - name->c + 2)); |
8e92c31c A |
450 | src += 1 + *src; |
451 | if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1); | |
452 | } | |
453 | return((mDNSu16)(src - name->c + 1)); | |
454 | } | |
455 | ||
67c8f8a1 A |
456 | // CountLabels() returns number of labels in name, excluding final root label |
457 | // (e.g. for "apple.com." CountLabels returns 2.) | |
458 | mDNSexport int CountLabels(const domainname *d) | |
459 | { | |
460 | int count = 0; | |
461 | const mDNSu8 *ptr; | |
462 | for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++; | |
463 | return count; | |
464 | } | |
465 | ||
466 | // SkipLeadingLabels skips over the first 'skip' labels in the domainname, | |
467 | // returning a pointer to the suffix with 'skip' labels removed. | |
468 | mDNSexport const domainname *SkipLeadingLabels(const domainname *d, int skip) | |
469 | { | |
470 | while (skip > 0 && d->c[0]) { d = (const domainname *)(d->c + 1 + d->c[0]); skip--; } | |
471 | return(d); | |
472 | } | |
473 | ||
8e92c31c A |
474 | // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname. |
475 | // The C string contains the label as-is, with no escaping, etc. | |
476 | // Any dots in the name are literal dots, not label separators | |
477 | // If successful, AppendLiteralLabelString returns a pointer to the next unused byte | |
67c8f8a1 | 478 | // in the domainname bufer (i.e. the next byte after the terminating zero). |
32bb7e43 | 479 | // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) |
8e92c31c A |
480 | // AppendLiteralLabelString returns mDNSNULL. |
481 | mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr) | |
482 | { | |
483 | mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name | |
484 | const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) | |
485 | const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL; | |
486 | const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2; | |
487 | mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go | |
488 | ||
489 | while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data | |
490 | *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte | |
491 | *ptr++ = 0; // Put the null root label on the end | |
492 | if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input | |
493 | else return(ptr); // Success: return new value of ptr | |
494 | } | |
495 | ||
496 | // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname. | |
497 | // The C string is in conventional DNS syntax: | |
498 | // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots. | |
499 | // If successful, AppendDNSNameString returns a pointer to the next unused byte | |
67c8f8a1 | 500 | // in the domainname bufer (i.e. the next byte after the terminating zero). |
32bb7e43 | 501 | // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) |
8e92c31c | 502 | // AppendDNSNameString returns mDNSNULL. |
7f0064bd | 503 | mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring) |
8e92c31c | 504 | { |
7f0064bd | 505 | const char *cstr = cstring; |
8e92c31c A |
506 | mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name |
507 | const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) | |
508 | while (*cstr && ptr < lim) // While more characters, and space to put them... | |
509 | { | |
510 | mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go | |
7f0064bd | 511 | if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); } |
8e92c31c A |
512 | while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label... |
513 | { | |
514 | mDNSu8 c = (mDNSu8)*cstr++; // Read the character | |
515 | if (c == '\\') // If escape character, check next character | |
516 | { | |
517 | c = (mDNSu8)*cstr++; // Assume we'll just take the next character | |
32bb7e43 | 518 | if (mDNSIsDigit(cstr[-1]) && mDNSIsDigit(cstr[0]) && mDNSIsDigit(cstr[1])) |
8e92c31c A |
519 | { // If three decimal digits, |
520 | int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal | |
521 | int v1 = cstr[ 0] - '0'; | |
522 | int v2 = cstr[ 1] - '0'; | |
523 | int val = v0 * 100 + v1 * 10 + v2; | |
524 | if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it | |
525 | } | |
526 | } | |
527 | *ptr++ = c; // Write the character | |
528 | } | |
529 | if (*cstr) cstr++; // Skip over the trailing dot (if present) | |
530 | if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort | |
531 | return(mDNSNULL); | |
532 | *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte | |
533 | } | |
534 | ||
535 | *ptr++ = 0; // Put the null root label on the end | |
536 | if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input | |
537 | else return(ptr); // Success: return new value of ptr | |
538 | } | |
539 | ||
540 | // AppendDomainLabel appends a single label to a name. | |
541 | // If successful, AppendDomainLabel returns a pointer to the next unused byte | |
67c8f8a1 | 542 | // in the domainname bufer (i.e. the next byte after the terminating zero). |
32bb7e43 | 543 | // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) |
8e92c31c A |
544 | // AppendDomainLabel returns mDNSNULL. |
545 | mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label) | |
546 | { | |
547 | int i; | |
548 | mDNSu8 *ptr = name->c + DomainNameLength(name) - 1; | |
549 | ||
550 | // Check label is legal | |
551 | if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL); | |
552 | ||
553 | // Check that ptr + length byte + data bytes + final zero does not exceed our limit | |
554 | if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL); | |
555 | ||
556 | for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data | |
557 | *ptr++ = 0; // Put the null root label on the end | |
558 | return(ptr); | |
559 | } | |
560 | ||
561 | mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append) | |
562 | { | |
563 | mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name | |
564 | const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero) | |
565 | const mDNSu8 * src = append->c; | |
67c8f8a1 | 566 | while (src[0]) |
8e92c31c A |
567 | { |
568 | int i; | |
569 | if (ptr + 1 + src[0] > lim) return(mDNSNULL); | |
570 | for (i=0; i<=src[0]; i++) *ptr++ = src[i]; | |
571 | *ptr = 0; // Put the null root label on the end | |
572 | src += i; | |
573 | } | |
574 | return(ptr); | |
575 | } | |
576 | ||
577 | // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping). | |
578 | // If successful, MakeDomainLabelFromLiteralString returns mDNStrue. | |
579 | // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then | |
580 | // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse. | |
581 | // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored. | |
582 | // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result. | |
583 | mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr) | |
584 | { | |
585 | mDNSu8 * ptr = label->c + 1; // Where we're putting it | |
586 | const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put | |
587 | while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label | |
588 | label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte | |
589 | return(*cstr == 0); // Return mDNStrue if we successfully consumed all input | |
590 | } | |
591 | ||
592 | // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string. | |
593 | // The C string is in conventional DNS syntax: | |
594 | // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots. | |
595 | // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte | |
67c8f8a1 | 596 | // in the domainname bufer (i.e. the next byte after the terminating zero). |
32bb7e43 | 597 | // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes) |
8e92c31c A |
598 | // MakeDomainNameFromDNSNameString returns mDNSNULL. |
599 | mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr) | |
600 | { | |
601 | name->c[0] = 0; // Make an empty domain name | |
602 | return(AppendDNSNameString(name, cstr)); // And then add this string to it | |
603 | } | |
604 | ||
605 | mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc) | |
606 | { | |
607 | const mDNSu8 * src = label->c; // Domain label we're reading | |
608 | const mDNSu8 len = *src++; // Read length of this (non-null) label | |
609 | const mDNSu8 *const end = src + len; // Work out where the label ends | |
610 | if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort | |
611 | while (src < end) // While we have characters in the label | |
612 | { | |
613 | mDNSu8 c = *src++; | |
614 | if (esc) | |
615 | { | |
616 | if (c == '.' || c == esc) // If character is a dot or the escape character | |
617 | *ptr++ = esc; // Output escape character | |
618 | else if (c <= ' ') // If non-printing ascii, | |
619 | { // Output decimal escape sequence | |
620 | *ptr++ = esc; | |
621 | *ptr++ = (char) ('0' + (c / 100) ); | |
622 | *ptr++ = (char) ('0' + (c / 10) % 10); | |
623 | c = (mDNSu8)('0' + (c ) % 10); | |
624 | } | |
625 | } | |
626 | *ptr++ = (char)c; // Copy the character | |
627 | } | |
628 | *ptr = 0; // Null-terminate the string | |
629 | return(ptr); // and return | |
630 | } | |
631 | ||
32bb7e43 | 632 | // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1009 bytes) |
8e92c31c A |
633 | mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc) |
634 | { | |
32bb7e43 | 635 | const mDNSu8 *src = name->c; // Domain name we're reading |
8e92c31c A |
636 | const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid |
637 | ||
638 | if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot | |
639 | ||
640 | while (*src) // While more characters in the domain name | |
641 | { | |
642 | if (src + 1 + *src >= max) return(mDNSNULL); | |
643 | ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc); | |
644 | if (!ptr) return(mDNSNULL); | |
645 | src += 1 + *src; | |
646 | *ptr++ = '.'; // Write the dot after the label | |
647 | } | |
648 | ||
649 | *ptr++ = 0; // Null-terminate the string | |
650 | return(ptr); // and return | |
651 | } | |
652 | ||
653 | // RFC 1034 rules: | |
654 | // Host names must start with a letter, end with a letter or digit, | |
655 | // and have as interior characters only letters, digits, and hyphen. | |
656 | // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit | |
657 | ||
658 | mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel) | |
659 | { | |
660 | const mDNSu8 * src = &UTF8Name[1]; | |
661 | const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0]; | |
662 | mDNSu8 * ptr = &hostlabel->c[1]; | |
663 | const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL; | |
664 | while (src < end) | |
665 | { | |
666 | // Delete apostrophes from source name | |
667 | if (src[0] == '\'') { src++; continue; } // Standard straight single quote | |
668 | if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99) | |
669 | { src += 3; continue; } // Unicode curly apostrophe | |
670 | if (ptr < lim) | |
671 | { | |
32bb7e43 | 672 | if (mDNSValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src; |
8e92c31c A |
673 | else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-'; |
674 | } | |
675 | src++; | |
676 | } | |
677 | while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks | |
678 | hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]); | |
679 | } | |
680 | ||
67c8f8a1 A |
681 | #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \ |
682 | ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \ | |
683 | ((X)[4] | 0x20) == 'p') | |
684 | ||
8e92c31c A |
685 | mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, |
686 | const domainlabel *name, const domainname *type, const domainname *const domain) | |
687 | { | |
688 | int i, len; | |
689 | mDNSu8 *dst = fqdn->c; | |
690 | const mDNSu8 *src; | |
691 | const char *errormsg; | |
32bb7e43 A |
692 | #if APPLE_OSX_mDNSResponder |
693 | mDNSBool loggedUnderscore = mDNSfalse; | |
694 | static char typeBuf[MAX_ESCAPED_DOMAIN_NAME]; | |
695 | #endif | |
8e92c31c A |
696 | |
697 | // In the case where there is no name (and ONLY in that case), | |
698 | // a single-label subtype is allowed as the first label of a three-part "type" | |
7f0064bd | 699 | if (!name && type) |
8e92c31c | 700 | { |
7f0064bd A |
701 | const mDNSu8 *s0 = type->c; |
702 | if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63) | |
8e92c31c | 703 | { |
7f0064bd A |
704 | const mDNSu8 * s1 = s0 + 1 + s0[0]; |
705 | if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63) | |
706 | { | |
707 | const mDNSu8 *s2 = s1 + 1 + s1[0]; | |
708 | if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels | |
709 | { | |
710 | static const mDNSu8 SubTypeLabel[5] = "\x04_sub"; | |
711 | src = s0; // Copy the first label | |
712 | len = *src; | |
713 | for (i=0; i <= len; i++) *dst++ = *src++; | |
714 | for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i]; | |
67c8f8a1 | 715 | type = (const domainname *)s1; |
7f0064bd | 716 | |
67c8f8a1 | 717 | // Special support to enable the DNSServiceBrowse call made by Bonjour Browser |
7f0064bd | 718 | // For these queries, we retract the "._sub" we just added between the subtype and the main type |
67c8f8a1 A |
719 | // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse |
720 | if (SameDomainName((domainname*)s0, (const domainname*)"\x09_services\x07_dns-sd\x04_udp")) | |
7f0064bd A |
721 | dst -= sizeof(SubTypeLabel); |
722 | } | |
723 | } | |
8e92c31c A |
724 | } |
725 | } | |
726 | ||
727 | if (name && name->c[0]) | |
728 | { | |
729 | src = name->c; // Put the service name into the domain name | |
730 | len = *src; | |
67c8f8a1 | 731 | if (len >= 0x40) { errormsg = "Service instance name too long"; goto fail; } |
8e92c31c A |
732 | for (i=0; i<=len; i++) *dst++ = *src++; |
733 | } | |
734 | else | |
735 | name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below | |
736 | ||
737 | src = type->c; // Put the service type into the domain name | |
738 | len = *src; | |
263eeeab | 739 | if (len < 2 || len > 16) |
7f0064bd | 740 | { |
263eeeab | 741 | LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-15 characters. " |
32bb7e43 A |
742 | "See <http://www.dns-sd.org/ServiceTypes.html>", name->c, type->c, domain->c); |
743 | #if APPLE_OSX_mDNSResponder | |
744 | ConvertDomainNameToCString(type, typeBuf); | |
745 | mDNSASLLog(mDNSNULL, "serviceType.nameTooLong", "noop", typeBuf, ""); | |
746 | #endif | |
7f0064bd | 747 | } |
263eeeab | 748 | if (len < 2 || len >= 0x40 || (len > 16 && !SameDomainName(domain, &localdomain))) return(mDNSNULL); |
67c8f8a1 | 749 | if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; } |
8e92c31c | 750 | for (i=2; i<=len; i++) |
67c8f8a1 A |
751 | { |
752 | // Letters and digits are allowed anywhere | |
32bb7e43 | 753 | if (mDNSIsLetter(src[i]) || mDNSIsDigit(src[i])) continue; |
67c8f8a1 A |
754 | // Hyphens are only allowed as interior characters |
755 | // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them, | |
756 | // with the same rule as hyphens | |
32bb7e43 A |
757 | if ((src[i] == '-' || src[i] == '_') && i > 2 && i < len) |
758 | { | |
759 | #if APPLE_OSX_mDNSResponder | |
760 | if (src[i] == '_' && loggedUnderscore == mDNSfalse) | |
761 | { | |
762 | ConvertDomainNameToCString(type, typeBuf); | |
763 | mDNSASLLog(mDNSNULL, "serviceType.nameWithUnderscore", "noop", typeBuf, ""); | |
764 | loggedUnderscore = mDNStrue; | |
765 | } | |
766 | #endif | |
767 | continue; | |
768 | } | |
769 | errormsg = "Application protocol name must contain only letters, digits, and hyphens"; | |
770 | #if APPLE_OSX_mDNSResponder | |
771 | { | |
772 | ConvertDomainNameToCString(type, typeBuf); | |
773 | mDNSASLLog(mDNSNULL, "serviceType.nameWithIllegalCharacters", "noop", typeBuf, ""); | |
774 | } | |
775 | #endif | |
776 | goto fail; | |
67c8f8a1 | 777 | } |
8e92c31c A |
778 | for (i=0; i<=len; i++) *dst++ = *src++; |
779 | ||
780 | len = *src; | |
67c8f8a1 | 781 | if (!ValidTransportProtocol(src)) { errormsg = "Transport protocol name must be _udp or _tcp"; goto fail; } |
8e92c31c A |
782 | for (i=0; i<=len; i++) *dst++ = *src++; |
783 | ||
67c8f8a1 | 784 | if (*src) { errormsg = "Service type must have only two labels"; goto fail; } |
8e92c31c A |
785 | |
786 | *dst = 0; | |
67c8f8a1 A |
787 | if (!domain->c[0]) { errormsg = "Service domain must be non-empty"; goto fail; } |
788 | if (SameDomainName(domain, (const domainname*)"\x05" "local" "\x04" "arpa")) | |
789 | { errormsg = "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; } | |
8e92c31c | 790 | dst = AppendDomainName(fqdn, domain); |
67c8f8a1 | 791 | if (!dst) { errormsg = "Service domain too long"; goto fail; } |
8e92c31c A |
792 | return(dst); |
793 | ||
794 | fail: | |
795 | LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c); | |
796 | return(mDNSNULL); | |
797 | } | |
798 | ||
cc340f17 A |
799 | // A service name has the form: instance.application-protocol.transport-protocol.domain |
800 | // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character | |
801 | // set or length limits for the protocol names, and the final domain is allowed to be empty. | |
802 | // However, if the given FQDN doesn't contain at least three labels, | |
803 | // DeconstructServiceName will reject it and return mDNSfalse. | |
8e92c31c A |
804 | mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn, |
805 | domainlabel *const name, domainname *const type, domainname *const domain) | |
806 | { | |
807 | int i, len; | |
808 | const mDNSu8 *src = fqdn->c; | |
809 | const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME; | |
810 | mDNSu8 *dst; | |
811 | ||
cc340f17 | 812 | dst = name->c; // Extract the service name |
8e92c31c | 813 | len = *src; |
67c8f8a1 A |
814 | if (!len) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse); } |
815 | if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); } | |
8e92c31c A |
816 | for (i=0; i<=len; i++) *dst++ = *src++; |
817 | ||
cc340f17 | 818 | dst = type->c; // Extract the service type |
8e92c31c | 819 | len = *src; |
67c8f8a1 A |
820 | if (!len) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse); } |
821 | if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); } | |
822 | if (src[1] != '_'){ debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse); } | |
8e92c31c A |
823 | for (i=0; i<=len; i++) *dst++ = *src++; |
824 | ||
825 | len = *src; | |
67c8f8a1 | 826 | if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); } |
32bb7e43 A |
827 | if (!ValidTransportProtocol(src)) |
828 | { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); } | |
8e92c31c | 829 | for (i=0; i<=len; i++) *dst++ = *src++; |
cc340f17 | 830 | *dst++ = 0; // Put terminator on the end of service type |
8e92c31c | 831 | |
cc340f17 | 832 | dst = domain->c; // Extract the service domain |
8e92c31c A |
833 | while (*src) |
834 | { | |
835 | len = *src; | |
836 | if (len >= 0x40) | |
cc340f17 | 837 | { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); } |
8e92c31c | 838 | if (src + 1 + len + 1 >= max) |
cc340f17 | 839 | { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); } |
8e92c31c A |
840 | for (i=0; i<=len; i++) *dst++ = *src++; |
841 | } | |
842 | *dst++ = 0; // Put the null root label on the end | |
843 | ||
844 | return(mDNStrue); | |
845 | } | |
846 | ||
7cb34e5c A |
847 | // Notes on UTF-8: |
848 | // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F | |
849 | // 10xxxxxx is a continuation byte of a multi-byte character | |
850 | // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1) | |
851 | // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1) | |
852 | // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1) | |
853 | // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1) | |
854 | // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1) | |
855 | // | |
856 | // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF. | |
857 | // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive | |
858 | // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?") | |
859 | // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8), | |
860 | // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8). | |
861 | ||
862 | mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max) | |
863 | { | |
864 | if (length > max) | |
865 | { | |
67c8f8a1 A |
866 | mDNSu8 c1 = string[max]; // First byte after cut point |
867 | mDNSu8 c2 = (max+1 < length) ? string[max+1] : (mDNSu8)0xB0; // Second byte after cut point | |
7cb34e5c A |
868 | length = max; // Trim length down |
869 | while (length > 0) | |
870 | { | |
871 | // Check if the byte right after the chop point is a UTF-8 continuation byte, | |
872 | // or if the character right after the chop point is the second of a UTF-16 surrogate pair. | |
873 | // If so, then we continue to chop more bytes until we get to a legal chop point. | |
874 | mDNSBool continuation = ((c1 & 0xC0) == 0x80); | |
875 | mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0); | |
876 | if (!continuation && !secondsurrogate) break; | |
877 | c2 = c1; | |
878 | c1 = string[--length]; | |
879 | } | |
880 | // Having truncated characters off the end of our string, also cut off any residual white space | |
881 | while (length > 0 && string[length-1] <= ' ') length--; | |
882 | } | |
883 | return(length); | |
884 | } | |
885 | ||
8e92c31c A |
886 | // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034 |
887 | // name ends in "-nnn", where n is some decimal number. | |
888 | mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText) | |
889 | { | |
890 | mDNSu16 l = name->c[0]; | |
891 | ||
892 | if (RichText) | |
893 | { | |
894 | if (l < 4) return mDNSfalse; // Need at least " (2)" | |
895 | if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')' | |
32bb7e43 | 896 | if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit |
8e92c31c | 897 | l--; |
32bb7e43 | 898 | while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits |
8e92c31c A |
899 | return (name->c[l] == '(' && name->c[l - 1] == ' '); |
900 | } | |
901 | else | |
902 | { | |
903 | if (l < 2) return mDNSfalse; // Need at least "-2" | |
32bb7e43 | 904 | if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit |
8e92c31c | 905 | l--; |
32bb7e43 | 906 | while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits |
8e92c31c A |
907 | return (name->c[l] == '-'); |
908 | } | |
909 | } | |
910 | ||
911 | // removes an auto-generated suffix (appended on a name collision) from a label. caller is | |
912 | // responsible for ensuring that the label does indeed contain a suffix. returns the number | |
913 | // from the suffix that was removed. | |
914 | mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText) | |
915 | { | |
916 | mDNSu32 val = 0, multiplier = 1; | |
917 | ||
918 | // Chop closing parentheses from RichText suffix | |
919 | if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--; | |
920 | ||
921 | // Get any existing numerical suffix off the name | |
32bb7e43 | 922 | while (mDNSIsDigit(name->c[name->c[0]])) |
8e92c31c A |
923 | { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; } |
924 | ||
925 | // Chop opening parentheses or dash from suffix | |
926 | if (RichText) | |
927 | { | |
928 | if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2; | |
929 | } | |
930 | else | |
931 | { | |
932 | if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1; | |
933 | } | |
934 | ||
935 | return(val); | |
936 | } | |
937 | ||
938 | // appends a numerical suffix to a label, with the number following a whitespace and enclosed | |
939 | // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label). | |
67c8f8a1 | 940 | mDNSexport void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText) |
8e92c31c A |
941 | { |
942 | mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2") | |
943 | if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)") | |
944 | ||
945 | // Truncate trailing spaces from RichText names | |
946 | if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--; | |
947 | ||
67c8f8a1 | 948 | while (divisor < 0xFFFFFFFFUL/10 && val >= divisor * 10) { divisor *= 10; chars++; } |
8e92c31c | 949 | |
f5e6e86c | 950 | name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars); |
8e92c31c A |
951 | |
952 | if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; } | |
953 | else { name->c[++name->c[0]] = '-'; } | |
954 | ||
955 | while (divisor) | |
956 | { | |
957 | name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor); | |
958 | val %= divisor; | |
959 | divisor /= 10; | |
960 | } | |
961 | ||
962 | if (RichText) name->c[++name->c[0]] = ')'; | |
963 | } | |
964 | ||
965 | mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText) | |
966 | { | |
967 | mDNSu32 val = 0; | |
968 | ||
969 | if (LabelContainsSuffix(name, RichText)) | |
970 | val = RemoveLabelSuffix(name, RichText); | |
971 | ||
972 | // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate. | |
973 | // If existing suffix in the range 2-9, increment it. | |
974 | // If we've had ten conflicts already, there are probably too many hosts trying to use the same name, | |
975 | // so add a random increment to improve the chances of finding an available name next time. | |
976 | if (val == 0) val = 2; | |
977 | else if (val < 10) val++; | |
978 | else val += 1 + mDNSRandom(99); | |
979 | ||
980 | AppendLabelSuffix(name, val, RichText); | |
981 | } | |
982 | ||
983 | // *************************************************************************** | |
984 | #if COMPILER_LIKES_PRAGMA_MARK | |
985 | #pragma mark - | |
986 | #pragma mark - Resource Record Utility Functions | |
987 | #endif | |
988 | ||
67c8f8a1 A |
989 | // Set up a AuthRecord with sensible default values. |
990 | // These defaults may be overwritten with new values before mDNS_Register is called | |
991 | mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID, | |
992 | mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, mDNSRecordCallback Callback, void *Context) | |
993 | { | |
994 | // Don't try to store a TTL bigger than we can represent in platform time units | |
995 | if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond) | |
996 | ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond; | |
997 | else if (ttl == 0) // And Zero TTL is illegal | |
998 | ttl = DefaultTTLforRRType(rrtype); | |
999 | ||
1000 | // Field Group 1: The actual information pertaining to this resource record | |
1001 | rr->resrec.RecordType = RecordType; | |
1002 | rr->resrec.InterfaceID = InterfaceID; | |
1003 | rr->resrec.name = &rr->namestorage; | |
1004 | rr->resrec.rrtype = rrtype; | |
1005 | rr->resrec.rrclass = kDNSClass_IN; | |
1006 | rr->resrec.rroriginalttl = ttl; | |
263eeeab | 1007 | rr->resrec.rDNSServer = mDNSNULL; |
67c8f8a1 A |
1008 | // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal |
1009 | // rr->resrec.rdestimate = set in mDNS_Register_internal | |
1010 | // rr->resrec.rdata = MUST be set by client | |
1011 | ||
1012 | if (RDataStorage) | |
1013 | rr->resrec.rdata = RDataStorage; | |
1014 | else | |
1015 | { | |
1016 | rr->resrec.rdata = &rr->rdatastorage; | |
1017 | rr->resrec.rdata->MaxRDLength = sizeof(RDataBody); | |
1018 | } | |
1019 | ||
1020 | // Field Group 2: Persistent metadata for Authoritative Records | |
1021 | rr->Additional1 = mDNSNULL; | |
1022 | rr->Additional2 = mDNSNULL; | |
1023 | rr->DependentOn = mDNSNULL; | |
1024 | rr->RRSet = mDNSNULL; | |
1025 | rr->RecordCallback = Callback; | |
1026 | rr->RecordContext = Context; | |
1027 | ||
1028 | rr->AutoTarget = Target_Manual; | |
1029 | rr->AllowRemoteQuery = mDNSfalse; | |
1030 | rr->ForceMCast = mDNSfalse; | |
1031 | ||
32bb7e43 A |
1032 | rr->WakeUp = zeroOwner; |
1033 | rr->AddressProxy = zeroAddr; | |
1034 | rr->TimeRcvd = 0; | |
1035 | rr->TimeExpire = 0; | |
1036 | ||
67c8f8a1 A |
1037 | // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal) |
1038 | // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal) | |
1039 | ||
1040 | // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case | |
1041 | // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch | |
1042 | // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.) | |
1043 | rr->state = regState_Zero; | |
1044 | rr->uselease = 0; | |
1045 | rr->expire = 0; | |
1046 | rr->Private = 0; | |
32bb7e43 A |
1047 | rr->updateid = zeroID; |
1048 | rr->zone = rr->resrec.name; | |
67c8f8a1 A |
1049 | rr->nta = mDNSNULL; |
1050 | rr->tcp = mDNSNULL; | |
1051 | rr->OrigRData = 0; | |
1052 | rr->OrigRDLen = 0; | |
1053 | rr->InFlightRData = 0; | |
1054 | rr->InFlightRDLen = 0; | |
1055 | rr->QueuedRData = 0; | |
1056 | rr->QueuedRDLen = 0; | |
263eeeab A |
1057 | mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo)); |
1058 | rr->SRVChanged = mDNSfalse; | |
1059 | rr->mState = mergeState_Zero; | |
67c8f8a1 A |
1060 | |
1061 | rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register() | |
1062 | } | |
1063 | ||
32bb7e43 A |
1064 | mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name, |
1065 | const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context) | |
1066 | { | |
1067 | q->InterfaceID = InterfaceID; | |
1068 | q->Target = zeroAddr; | |
1069 | AssignDomainName(&q->qname, name); | |
1070 | q->qtype = qtype; | |
1071 | q->qclass = kDNSClass_IN; | |
1072 | q->LongLived = (qtype == kDNSType_PTR); | |
1073 | q->ExpectUnique = (qtype != kDNSType_PTR); | |
1074 | q->ForceMCast = mDNSfalse; | |
1075 | q->ReturnIntermed = mDNSfalse; | |
263eeeab | 1076 | q->SuppressUnusable = mDNSfalse; |
32bb7e43 A |
1077 | q->QuestionCallback = callback; |
1078 | q->QuestionContext = context; | |
1079 | } | |
1080 | ||
030b743d | 1081 | mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr) |
8e92c31c | 1082 | { |
32bb7e43 A |
1083 | int len = rr->rdlength; |
1084 | const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; | |
030b743d | 1085 | switch(rr->rrtype) |
8e92c31c | 1086 | { |
030b743d A |
1087 | case kDNSType_NS: |
1088 | case kDNSType_CNAME: | |
1089 | case kDNSType_PTR: | |
32bb7e43 | 1090 | case kDNSType_DNAME: return DomainNameHashValue(&rdb->name); |
030b743d | 1091 | |
32bb7e43 A |
1092 | case kDNSType_SOA: return rdb->soa.serial + |
1093 | rdb->soa.refresh + | |
1094 | rdb->soa.retry + | |
1095 | rdb->soa.expire + | |
1096 | rdb->soa.min + | |
1097 | DomainNameHashValue(&rdb->soa.mname) + | |
1098 | DomainNameHashValue(&rdb->soa.rname); | |
030b743d A |
1099 | |
1100 | case kDNSType_MX: | |
1101 | case kDNSType_AFSDB: | |
1102 | case kDNSType_RT: | |
32bb7e43 | 1103 | case kDNSType_KX: return DomainNameHashValue(&rdb->mx.exchange); |
030b743d | 1104 | |
32bb7e43 | 1105 | case kDNSType_RP: return DomainNameHashValue(&rdb->rp.mbox) + DomainNameHashValue(&rdb->rp.txt); |
030b743d | 1106 | |
32bb7e43 | 1107 | case kDNSType_PX: return DomainNameHashValue(&rdb->px.map822) + DomainNameHashValue(&rdb->px.mapx400); |
030b743d | 1108 | |
32bb7e43 | 1109 | case kDNSType_SRV: return DomainNameHashValue(&rdb->srv.target); |
030b743d | 1110 | |
32bb7e43 A |
1111 | case kDNSType_OPT: return 0; // OPT is a pseudo-RR container structure; makes no sense to compare |
1112 | ||
1113 | case kDNSType_NSEC: len = sizeof(rdataNSEC); // Use in-memory length of 32, and fall through default checksum computation below | |
030b743d A |
1114 | |
1115 | default: | |
1116 | { | |
1117 | mDNSu32 sum = 0; | |
1118 | int i; | |
32bb7e43 | 1119 | for (i=0; i+1 < len; i+=2) |
030b743d | 1120 | { |
32bb7e43 | 1121 | sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1]; |
030b743d A |
1122 | sum = (sum<<3) | (sum>>29); |
1123 | } | |
32bb7e43 | 1124 | if (i < len) |
030b743d | 1125 | { |
32bb7e43 | 1126 | sum += ((mDNSu32)(rdb->data[i])) << 8; |
030b743d A |
1127 | } |
1128 | return(sum); | |
1129 | } | |
8e92c31c | 1130 | } |
8e92c31c A |
1131 | } |
1132 | ||
cc340f17 A |
1133 | // r1 has to be a full ResourceRecord including rrtype and rdlength |
1134 | // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1 | |
263eeeab | 1135 | mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename) |
8e92c31c | 1136 | { |
32bb7e43 A |
1137 | const RDataBody2 *const b1 = (RDataBody2 *)r1->rdata->u.data; |
1138 | const RDataBody2 *const b2 = (RDataBody2 *)r2; | |
8e92c31c A |
1139 | switch(r1->rrtype) |
1140 | { | |
67c8f8a1 A |
1141 | case kDNSType_NS: |
1142 | case kDNSType_CNAME: | |
1143 | case kDNSType_PTR: | |
32bb7e43 | 1144 | case kDNSType_DNAME:return(SameDomainName(&b1->name, &b2->name)); |
67c8f8a1 | 1145 | |
32bb7e43 A |
1146 | case kDNSType_SOA: return(mDNSBool)( b1->soa.serial == b2->soa.serial && |
1147 | b1->soa.refresh == b2->soa.refresh && | |
1148 | b1->soa.retry == b2->soa.retry && | |
1149 | b1->soa.expire == b2->soa.expire && | |
1150 | b1->soa.min == b2->soa.min && | |
263eeeab A |
1151 | samename(&b1->soa.mname, &b2->soa.mname) && |
1152 | samename(&b1->soa.rname, &b2->soa.rname)); | |
67c8f8a1 A |
1153 | |
1154 | case kDNSType_MX: | |
1155 | case kDNSType_AFSDB: | |
1156 | case kDNSType_RT: | |
32bb7e43 | 1157 | case kDNSType_KX: return(mDNSBool)( b1->mx.preference == b2->mx.preference && |
263eeeab | 1158 | samename(&b1->mx.exchange, &b2->mx.exchange)); |
32bb7e43 | 1159 | |
263eeeab A |
1160 | case kDNSType_RP: return(mDNSBool)( samename(&b1->rp.mbox, &b2->rp.mbox) && |
1161 | samename(&b1->rp.txt, &b2->rp.txt)); | |
67c8f8a1 | 1162 | |
32bb7e43 | 1163 | case kDNSType_PX: return(mDNSBool)( b1->px.preference == b2->px.preference && |
263eeeab A |
1164 | samename(&b1->px.map822, &b2->px.map822) && |
1165 | samename(&b1->px.mapx400, &b2->px.mapx400)); | |
67c8f8a1 | 1166 | |
32bb7e43 A |
1167 | case kDNSType_SRV: return(mDNSBool)( b1->srv.priority == b2->srv.priority && |
1168 | b1->srv.weight == b2->srv.weight && | |
1169 | mDNSSameIPPort(b1->srv.port, b2->srv.port) && | |
263eeeab | 1170 | samename(&b1->srv.target, &b2->srv.target)); |
67c8f8a1 | 1171 | |
32bb7e43 | 1172 | case kDNSType_OPT: return mDNSfalse; // OPT is a pseudo-RR container structure; makes no sense to compare |
67c8f8a1 | 1173 | |
32bb7e43 | 1174 | case kDNSType_NSEC: return(mDNSPlatformMemSame(b1->data, b2->data, sizeof(rdataNSEC))); |
8e92c31c | 1175 | |
32bb7e43 | 1176 | default: return(mDNSPlatformMemSame(b1->data, b2->data, r1->rdlength)); |
8e92c31c A |
1177 | } |
1178 | } | |
1179 | ||
67c8f8a1 A |
1180 | // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question. |
1181 | // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call. | |
1182 | // SameDomainName() is generally cheap when the names don't match, but expensive when they do match, | |
1183 | // because it has to check all the way to the end of the names to be sure. | |
1184 | // In cases where we know in advance that the names match it's especially advantageous to skip the | |
1185 | // SameDomainName() call because that's precisely the time when it's most expensive and least useful. | |
1186 | ||
1187 | mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) | |
1188 | { | |
1189 | if (rr->InterfaceID && | |
1190 | q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && | |
1191 | rr->InterfaceID != q->InterfaceID) return(mDNSfalse); | |
1192 | ||
263eeeab A |
1193 | // Resource record received via unicast, the DNSServer entries should match ? |
1194 | if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse); | |
1195 | ||
67c8f8a1 A |
1196 | // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question |
1197 | if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); | |
1198 | ||
1199 | // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. | |
32bb7e43 A |
1200 | if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); |
1201 | if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); | |
67c8f8a1 | 1202 | |
67c8f8a1 A |
1203 | return(mDNStrue); |
1204 | } | |
1205 | ||
8e92c31c A |
1206 | mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) |
1207 | { | |
1208 | if (rr->InterfaceID && | |
283ee3ff | 1209 | q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && |
8e92c31c A |
1210 | rr->InterfaceID != q->InterfaceID) return(mDNSfalse); |
1211 | ||
263eeeab A |
1212 | // Resource record received via unicast, the DNSServer entries should match ? |
1213 | if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse); | |
1214 | ||
1215 | // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question. | |
1216 | // This also covers the case where the ResourceRecord is mDNSInterface_LocalOnly and the question is expecting a unicast | |
1217 | // DNS response. We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com" | |
1218 | // which would then cause other applications (e.g. Safari) to connect to the wrong address. If we decide to support this later, | |
1219 | // the restrictions need to be at least as strict as the restrictions on who can edit /etc/hosts and put fake addresses there. | |
67c8f8a1 A |
1220 | if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); |
1221 | ||
8e92c31c | 1222 | // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. |
32bb7e43 A |
1223 | if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); |
1224 | if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); | |
1225 | ||
1226 | return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); | |
1227 | } | |
1228 | ||
1229 | mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) | |
1230 | { | |
1231 | if (rr->InterfaceID && | |
1232 | q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && | |
1233 | rr->InterfaceID != q->InterfaceID) return(mDNSfalse); | |
1234 | ||
263eeeab A |
1235 | // Resource record received via unicast, the DNSServer entries should match ? |
1236 | // Note that Auth Records are normally setup with NULL InterfaceID and | |
1237 | // both the DNSServers are assumed to be NULL in that case | |
1238 | if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse); | |
1239 | ||
32bb7e43 A |
1240 | // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question |
1241 | if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); | |
1242 | ||
1243 | if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); | |
1244 | ||
283ee3ff | 1245 | return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); |
8e92c31c A |
1246 | } |
1247 | ||
263eeeab A |
1248 | // This is called only when the caller knows that it is a Unicast Resource Record and it is a Unicast Question |
1249 | // and hence we don't need InterfaceID checks like above. Though this may not be a big optimization, the main | |
1250 | // reason we need this is that we can't compare DNSServers between the question and the resource record because | |
1251 | // the resource record may not be completely initialized e.g., mDNSCoreReceiveResponse | |
1252 | mDNSexport mDNSBool UnicastResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) | |
1253 | { | |
1254 | // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. | |
1255 | if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); | |
1256 | ||
1257 | if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); | |
1258 | ||
1259 | return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); | |
1260 | } | |
1261 | ||
8e92c31c A |
1262 | mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate) |
1263 | { | |
32bb7e43 | 1264 | const RDataBody2 *const rd = (RDataBody2 *)rr->rdata->u.data; |
283ee3ff | 1265 | const domainname *const name = estimate ? rr->name : mDNSNULL; |
67c8f8a1 A |
1266 | if (rr->rrclass == kDNSQClass_ANY) return(rr->rdlength); // Used in update packets to mean "Delete An RRset" (RFC 2136) |
1267 | else switch (rr->rrtype) | |
8e92c31c | 1268 | { |
7f0064bd | 1269 | case kDNSType_A: return(sizeof(rd->ipv4)); |
67c8f8a1 A |
1270 | |
1271 | case kDNSType_NS: | |
1272 | case kDNSType_CNAME: | |
1273 | case kDNSType_PTR: | |
1274 | case kDNSType_DNAME:return(CompressedDomainNameLength(&rd->name, name)); | |
1275 | ||
1276 | case kDNSType_SOA: return(mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) + | |
1277 | CompressedDomainNameLength(&rd->soa.rname, name) + | |
1278 | 5 * sizeof(mDNSOpaque32)); | |
1279 | ||
1280 | case kDNSType_NULL: | |
1281 | case kDNSType_TSIG: | |
1282 | case kDNSType_TXT: | |
1283 | case kDNSType_X25: | |
1284 | case kDNSType_ISDN: | |
1285 | case kDNSType_LOC: | |
1286 | case kDNSType_DHCID:return(rr->rdlength); // Not self-describing, so have to just trust rdlength | |
1287 | ||
8e92c31c | 1288 | case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]); |
67c8f8a1 A |
1289 | |
1290 | case kDNSType_MX: | |
1291 | case kDNSType_AFSDB: | |
1292 | case kDNSType_RT: | |
1293 | case kDNSType_KX: return(mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name)); | |
1294 | ||
1295 | case kDNSType_RP: return(mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) + | |
1296 | CompressedDomainNameLength(&rd->rp.txt, name)); | |
1297 | ||
1298 | case kDNSType_PX: return(mDNSu16)(2 + CompressedDomainNameLength(&rd->px.map822, name) + | |
1299 | CompressedDomainNameLength(&rd->px.mapx400, name)); | |
1300 | ||
8e92c31c | 1301 | case kDNSType_AAAA: return(sizeof(rd->ipv6)); |
67c8f8a1 | 1302 | |
8e92c31c | 1303 | case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name)); |
67c8f8a1 | 1304 | |
8e92c31c | 1305 | case kDNSType_OPT: return(rr->rdlength); |
67c8f8a1 | 1306 | |
32bb7e43 A |
1307 | case kDNSType_NSEC: { |
1308 | int i; | |
1309 | for (i=sizeof(rdataNSEC); i>0; i--) if (rd->nsec.bitmap[i-1]) break; | |
1310 | // For our simplified use of NSEC synthetic records: | |
1311 | // nextname is always the record's own name, | |
263eeeab A |
1312 | // and if we have at least one record type that exists, |
1313 | // - the block number is always 0, | |
1314 | // - the count byte is a value in the range 1-32, | |
1315 | // - followed by the 1-32 data bytes | |
1316 | return(mDNSu16)((estimate ? 2 : DomainNameLength(rr->name)) + (i ? (2 + i) : 0)); | |
32bb7e43 A |
1317 | } |
1318 | ||
8e92c31c A |
1319 | default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype); |
1320 | return(rr->rdlength); | |
1321 | } | |
1322 | } | |
1323 | ||
32bb7e43 A |
1324 | // When a local client registers (or updates) a record, we use this routine to do some simple validation checks |
1325 | // to help reduce the risk of bogus malformed data on the network | |
8e92c31c A |
1326 | mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd) |
1327 | { | |
1328 | mDNSu16 len; | |
7f0064bd | 1329 | |
8e92c31c A |
1330 | switch(rrtype) |
1331 | { | |
1332 | case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr)); | |
1333 | ||
1334 | case kDNSType_NS: // Same as PTR | |
1335 | case kDNSType_MD: // Same as PTR | |
1336 | case kDNSType_MF: // Same as PTR | |
1337 | case kDNSType_CNAME:// Same as PTR | |
1338 | //case kDNSType_SOA not checked | |
1339 | case kDNSType_MB: // Same as PTR | |
1340 | case kDNSType_MG: // Same as PTR | |
1341 | case kDNSType_MR: // Same as PTR | |
1342 | //case kDNSType_NULL not checked (no specified format, so always valid) | |
1343 | //case kDNSType_WKS not checked | |
67c8f8a1 | 1344 | case kDNSType_PTR: len = DomainNameLengthLimit(&rd->u.name, rd->u.data + rdlength); |
8e92c31c A |
1345 | return(len <= MAX_DOMAIN_NAME && rdlength == len); |
1346 | ||
1347 | case kDNSType_HINFO:// Same as TXT (roughly) | |
1348 | case kDNSType_MINFO:// Same as TXT (roughly) | |
7cb34e5c A |
1349 | case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035) |
1350 | { | |
8e92c31c A |
1351 | const mDNSu8 *ptr = rd->u.txt.c; |
1352 | const mDNSu8 *end = rd->u.txt.c + rdlength; | |
1353 | while (ptr < end) ptr += 1 + ptr[0]; | |
1354 | return (ptr == end); | |
1355 | } | |
1356 | ||
1357 | case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr)); | |
1358 | ||
67c8f8a1 A |
1359 | case kDNSType_MX: // Must be at least two-byte preference, plus domainname |
1360 | // Call to DomainNameLengthLimit() implicitly enforces both requirements for us | |
1361 | len = DomainNameLengthLimit(&rd->u.mx.exchange, rd->u.data + rdlength); | |
8e92c31c A |
1362 | return(len <= MAX_DOMAIN_NAME && rdlength == 2+len); |
1363 | ||
67c8f8a1 A |
1364 | case kDNSType_SRV: // Must be at least priority+weight+port, plus domainname |
1365 | // Call to DomainNameLengthLimit() implicitly enforces both requirements for us | |
1366 | len = DomainNameLengthLimit(&rd->u.srv.target, rd->u.data + rdlength); | |
8e92c31c A |
1367 | return(len <= MAX_DOMAIN_NAME && rdlength == 6+len); |
1368 | ||
32bb7e43 A |
1369 | //case kDNSType_NSEC not checked |
1370 | ||
8e92c31c A |
1371 | default: return(mDNStrue); // Allow all other types without checking |
1372 | } | |
1373 | } | |
1374 | ||
1375 | // *************************************************************************** | |
1376 | #if COMPILER_LIKES_PRAGMA_MARK | |
1377 | #pragma mark - | |
8e92c31c A |
1378 | #pragma mark - DNS Message Creation Functions |
1379 | #endif | |
1380 | ||
1381 | mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags) | |
1382 | { | |
1383 | h->id = id; | |
1384 | h->flags = flags; | |
1385 | h->numQuestions = 0; | |
1386 | h->numAnswers = 0; | |
1387 | h->numAuthorities = 0; | |
1388 | h->numAdditionals = 0; | |
1389 | } | |
1390 | ||
1391 | mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname) | |
1392 | { | |
1393 | const mDNSu8 *result = end - *domname - 1; | |
1394 | ||
1395 | if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label | |
1396 | ||
1397 | // This loop examines each possible starting position in packet, starting end of the packet and working backwards | |
1398 | while (result >= base) | |
1399 | { | |
1400 | // If the length byte and first character of the label match, then check further to see | |
1401 | // if this location in the packet will yield a useful name compression pointer. | |
1402 | if (result[0] == domname[0] && result[1] == domname[1]) | |
1403 | { | |
1404 | const mDNSu8 *name = domname; | |
1405 | const mDNSu8 *targ = result; | |
1406 | while (targ + *name < end) | |
1407 | { | |
1408 | // First see if this label matches | |
1409 | int i; | |
1410 | const mDNSu8 *pointertarget; | |
1411 | for (i=0; i <= *name; i++) if (targ[i] != name[i]) break; | |
1412 | if (i <= *name) break; // If label did not match, bail out | |
1413 | targ += 1 + *name; // Else, did match, so advance target pointer | |
1414 | name += 1 + *name; // and proceed to check next label | |
1415 | if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match! | |
1416 | if (*name == 0) break; // If no more labels to match, we failed, so bail out | |
1417 | ||
1418 | // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches | |
1419 | if (targ[0] < 0x40) continue; // If length value, continue to check next label | |
1420 | if (targ[0] < 0xC0) break; // If 40-BF, not valid | |
1421 | if (targ+1 >= end) break; // Second byte not present! | |
1422 | pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1]; | |
1423 | if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet | |
1424 | if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte | |
1425 | targ = pointertarget; | |
1426 | } | |
1427 | } | |
1428 | result--; // We failed to match at this search position, so back up the tentative result pointer and try again | |
1429 | } | |
1430 | return(mDNSNULL); | |
1431 | } | |
1432 | ||
1433 | // Put a string of dot-separated labels as length-prefixed labels | |
1434 | // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't) | |
1435 | // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers) | |
1436 | // end points to the end of the message so far | |
1437 | // ptr points to where we want to put the name | |
1438 | // limit points to one byte past the end of the buffer that we must not overrun | |
1439 | // domainname is the name to put | |
1440 | mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, | |
1441 | mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name) | |
1442 | { | |
1443 | const mDNSu8 *const base = (const mDNSu8 *)msg; | |
1444 | const mDNSu8 * np = name->c; | |
1445 | const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid | |
1446 | const mDNSu8 * pointer = mDNSNULL; | |
1447 | const mDNSu8 *const searchlimit = ptr; | |
1448 | ||
32bb7e43 | 1449 | if (!ptr) { LogMsg("putDomainNameAsLabels %##s ptr is null", name->c); return(mDNSNULL); } |
67c8f8a1 | 1450 | |
32bb7e43 | 1451 | if (!*np) // If just writing one-byte root label, make sure we have space for that |
8e92c31c | 1452 | { |
32bb7e43 | 1453 | if (ptr >= limit) return(mDNSNULL); |
8e92c31c | 1454 | } |
32bb7e43 | 1455 | else // else, loop through writing labels and/or a compression offset |
8e92c31c | 1456 | { |
32bb7e43 A |
1457 | do { |
1458 | if (*np > MAX_DOMAIN_LABEL) | |
1459 | { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); } | |
1460 | ||
1461 | // This check correctly allows for the final trailing root label: | |
1462 | // e.g. | |
1463 | // Suppose our domain name is exactly 256 bytes long, including the final trailing root label. | |
1464 | // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local"). | |
1465 | // We know that max will be at name->c[256] | |
1466 | // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our | |
1467 | // six bytes, then exit the loop, write the final terminating root label, and the domain | |
1468 | // name we've written is exactly 256 bytes long, exactly at the correct legal limit. | |
1469 | // If the name is one byte longer, then we fail the "if" test below, and correctly bail out. | |
1470 | if (np + 1 + *np >= max) | |
1471 | { LogMsg("Malformed domain name %##s (more than 256 bytes)", name->c); return(mDNSNULL); } | |
1472 | ||
1473 | if (base) pointer = FindCompressionPointer(base, searchlimit, np); | |
1474 | if (pointer) // Use a compression pointer if we can | |
1475 | { | |
1476 | const mDNSu16 offset = (mDNSu16)(pointer - base); | |
1477 | if (ptr+2 > limit) return(mDNSNULL); // If we don't have two bytes of space left, give up | |
1478 | *ptr++ = (mDNSu8)(0xC0 | (offset >> 8)); | |
1479 | *ptr++ = (mDNSu8)( offset & 0xFF); | |
1480 | return(ptr); | |
1481 | } | |
1482 | else // Else copy one label and try again | |
1483 | { | |
1484 | int i; | |
1485 | mDNSu8 len = *np++; | |
1486 | // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up | |
1487 | if (ptr + 1 + len >= limit) return(mDNSNULL); | |
1488 | *ptr++ = len; | |
1489 | for (i=0; i<len; i++) *ptr++ = *np++; | |
1490 | } | |
1491 | } while (*np); // While we've got characters remaining in the name, continue | |
8e92c31c A |
1492 | } |
1493 | ||
32bb7e43 A |
1494 | *ptr++ = 0; // Put the final root label |
1495 | return(ptr); | |
8e92c31c A |
1496 | } |
1497 | ||
1498 | mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val) | |
1499 | { | |
1500 | ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF); | |
1501 | ptr[1] = (mDNSu8)((val ) & 0xFF); | |
1502 | return ptr + sizeof(mDNSOpaque16); | |
1503 | } | |
1504 | ||
7f0064bd A |
1505 | mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val) |
1506 | { | |
1507 | ptr[0] = (mDNSu8)((val >> 24) & 0xFF); | |
1508 | ptr[1] = (mDNSu8)((val >> 16) & 0xFF); | |
1509 | ptr[2] = (mDNSu8)((val >> 8) & 0xFF); | |
1510 | ptr[3] = (mDNSu8)((val ) & 0xFF); | |
1511 | return ptr + sizeof(mDNSu32); | |
1512 | } | |
1513 | ||
67c8f8a1 A |
1514 | // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers) |
1515 | mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr) | |
8e92c31c | 1516 | { |
32bb7e43 | 1517 | const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; |
8e92c31c A |
1518 | switch (rr->rrtype) |
1519 | { | |
1520 | case kDNSType_A: if (rr->rdlength != 4) | |
32bb7e43 | 1521 | { debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); return(mDNSNULL); } |
8e92c31c | 1522 | if (ptr + 4 > limit) return(mDNSNULL); |
32bb7e43 A |
1523 | *ptr++ = rdb->ipv4.b[0]; |
1524 | *ptr++ = rdb->ipv4.b[1]; | |
1525 | *ptr++ = rdb->ipv4.b[2]; | |
1526 | *ptr++ = rdb->ipv4.b[3]; | |
8e92c31c A |
1527 | return(ptr); |
1528 | ||
67c8f8a1 A |
1529 | case kDNSType_NS: |
1530 | case kDNSType_CNAME: | |
1531 | case kDNSType_PTR: | |
32bb7e43 | 1532 | case kDNSType_DNAME:return(putDomainNameAsLabels(msg, ptr, limit, &rdb->name)); |
67c8f8a1 | 1533 | |
32bb7e43 | 1534 | case kDNSType_SOA: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.mname); |
67c8f8a1 | 1535 | if (!ptr) return(mDNSNULL); |
32bb7e43 | 1536 | ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.rname); |
67c8f8a1 | 1537 | if (!ptr || ptr + 20 > limit) return(mDNSNULL); |
32bb7e43 A |
1538 | ptr = putVal32(ptr, rdb->soa.serial); |
1539 | ptr = putVal32(ptr, rdb->soa.refresh); | |
1540 | ptr = putVal32(ptr, rdb->soa.retry); | |
1541 | ptr = putVal32(ptr, rdb->soa.expire); | |
1542 | ptr = putVal32(ptr, rdb->soa.min); | |
67c8f8a1 A |
1543 | return(ptr); |
1544 | ||
1545 | case kDNSType_NULL: | |
1546 | case kDNSType_HINFO: | |
1547 | case kDNSType_TSIG: | |
1548 | case kDNSType_TXT: | |
1549 | case kDNSType_X25: | |
1550 | case kDNSType_ISDN: | |
1551 | case kDNSType_LOC: | |
1552 | case kDNSType_DHCID:if (ptr + rr->rdlength > limit) return(mDNSNULL); | |
32bb7e43 | 1553 | mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength); |
67c8f8a1 A |
1554 | return(ptr + rr->rdlength); |
1555 | ||
1556 | case kDNSType_MX: | |
1557 | case kDNSType_AFSDB: | |
1558 | case kDNSType_RT: | |
1559 | case kDNSType_KX: if (ptr + 3 > limit) return(mDNSNULL); | |
32bb7e43 A |
1560 | ptr = putVal16(ptr, rdb->mx.preference); |
1561 | return(putDomainNameAsLabels(msg, ptr, limit, &rdb->mx.exchange)); | |
67c8f8a1 | 1562 | |
32bb7e43 | 1563 | case kDNSType_RP: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.mbox); |
67c8f8a1 | 1564 | if (!ptr) return(mDNSNULL); |
32bb7e43 | 1565 | ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.txt); |
67c8f8a1 A |
1566 | return(ptr); |
1567 | ||
1568 | case kDNSType_PX: if (ptr + 5 > limit) return(mDNSNULL); | |
32bb7e43 A |
1569 | ptr = putVal16(ptr, rdb->px.preference); |
1570 | ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.map822); | |
67c8f8a1 | 1571 | if (!ptr) return(mDNSNULL); |
32bb7e43 | 1572 | ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.mapx400); |
67c8f8a1 | 1573 | return(ptr); |
8e92c31c | 1574 | |
32bb7e43 A |
1575 | case kDNSType_AAAA: if (rr->rdlength != sizeof(rdb->ipv6)) |
1576 | { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); return(mDNSNULL); } | |
1577 | if (ptr + sizeof(rdb->ipv6) > limit) return(mDNSNULL); | |
1578 | mDNSPlatformMemCopy(ptr, &rdb->ipv6, sizeof(rdb->ipv6)); | |
1579 | return(ptr + sizeof(rdb->ipv6)); | |
1580 | ||
1581 | case kDNSType_SRV: if (ptr + 7 > limit) return(mDNSNULL); | |
1582 | *ptr++ = (mDNSu8)(rdb->srv.priority >> 8); | |
1583 | *ptr++ = (mDNSu8)(rdb->srv.priority & 0xFF); | |
1584 | *ptr++ = (mDNSu8)(rdb->srv.weight >> 8); | |
1585 | *ptr++ = (mDNSu8)(rdb->srv.weight & 0xFF); | |
1586 | *ptr++ = rdb->srv.port.b[0]; | |
1587 | *ptr++ = rdb->srv.port.b[1]; | |
1588 | return(putDomainNameAsLabels(msg, ptr, limit, &rdb->srv.target)); | |
1589 | ||
1590 | case kDNSType_OPT: { | |
1591 | int len = 0; | |
1592 | const rdataOPT *opt; | |
1593 | const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength]; | |
1594 | for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) len += DNSOpt_Data_Space(opt); | |
1595 | if (ptr + len > limit) { LogMsg("ERROR: putOptRData - out of space"); return mDNSNULL; } | |
1596 | ||
1597 | for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) | |
8e92c31c | 1598 | { |
32bb7e43 A |
1599 | const int space = DNSOpt_Data_Space(opt); |
1600 | ptr = putVal16(ptr, opt->opt); | |
263eeeab | 1601 | ptr = putVal16(ptr, (mDNSu16)space - 4); |
32bb7e43 A |
1602 | switch (opt->opt) |
1603 | { | |
1604 | case kDNSOpt_LLQ: | |
1605 | ptr = putVal16(ptr, opt->u.llq.vers); | |
1606 | ptr = putVal16(ptr, opt->u.llq.llqOp); | |
1607 | ptr = putVal16(ptr, opt->u.llq.err); | |
1608 | mDNSPlatformMemCopy(ptr, opt->u.llq.id.b, 8); // 8-byte id | |
1609 | ptr += 8; | |
1610 | ptr = putVal32(ptr, opt->u.llq.llqlease); | |
1611 | break; | |
1612 | case kDNSOpt_Lease: | |
1613 | ptr = putVal32(ptr, opt->u.updatelease); | |
1614 | break; | |
1615 | case kDNSOpt_Owner: | |
1616 | *ptr++ = opt->u.owner.vers; | |
1617 | *ptr++ = opt->u.owner.seq; | |
1618 | mDNSPlatformMemCopy(ptr, opt->u.owner.HMAC.b, 6); // 6-byte Host identifier | |
1619 | ptr += 6; | |
1620 | if (space >= DNSOpt_OwnerData_ID_Wake_Space) | |
1621 | { | |
1622 | mDNSPlatformMemCopy(ptr, opt->u.owner.IMAC.b, 6); // 6-byte interface MAC | |
1623 | ptr += 6; | |
1624 | if (space > DNSOpt_OwnerData_ID_Wake_Space) | |
1625 | { | |
1626 | mDNSPlatformMemCopy(ptr, opt->u.owner.password.b, space - DNSOpt_OwnerData_ID_Wake_Space); | |
1627 | ptr += space - DNSOpt_OwnerData_ID_Wake_Space; | |
1628 | } | |
1629 | } | |
1630 | break; | |
1631 | } | |
8e92c31c | 1632 | } |
32bb7e43 A |
1633 | return ptr; |
1634 | } | |
1635 | ||
1636 | case kDNSType_NSEC: { | |
1637 | // For our simplified use of NSEC synthetic records: | |
1638 | // nextname is always the record's own name, | |
1639 | // the block number is always 0, | |
1640 | // the count byte is a value in the range 1-32, | |
1641 | // followed by the 1-32 data bytes | |
1642 | int i, j; | |
1643 | for (i=sizeof(rdataNSEC); i>0; i--) if (rdb->nsec.bitmap[i-1]) break; | |
1644 | ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name); | |
1645 | if (!ptr) return(mDNSNULL); | |
263eeeab A |
1646 | if (i) // Only put a block if at least one type exists for this name |
1647 | { | |
1648 | if (ptr + 2 + i > limit) return(mDNSNULL); | |
1649 | *ptr++ = 0; | |
1650 | *ptr++ = (mDNSu8)i; | |
1651 | for (j=0; j<i; j++) *ptr++ = rdb->nsec.bitmap[j]; | |
1652 | } | |
32bb7e43 A |
1653 | return ptr; |
1654 | } | |
8e92c31c | 1655 | |
8e92c31c | 1656 | default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype); |
67c8f8a1 | 1657 | if (ptr + rr->rdlength > limit) return(mDNSNULL); |
32bb7e43 | 1658 | mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength); |
8e92c31c A |
1659 | return(ptr + rr->rdlength); |
1660 | } | |
1661 | } | |
1662 | ||
32bb7e43 A |
1663 | #define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update) |
1664 | ||
7f0064bd | 1665 | mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit) |
8e92c31c A |
1666 | { |
1667 | mDNSu8 *endofrdata; | |
1668 | mDNSu16 actualLength; | |
32bb7e43 A |
1669 | // When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782) |
1670 | const DNSMessage *const rdatacompressionbase = (IsUnicastUpdate(msg) && rr->rrtype == kDNSType_SRV) ? mDNSNULL : msg; | |
8e92c31c A |
1671 | |
1672 | if (rr->RecordType == kDNSRecordTypeUnregistered) | |
1673 | { | |
283ee3ff | 1674 | LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); |
8e92c31c A |
1675 | return(ptr); |
1676 | } | |
1677 | ||
67c8f8a1 A |
1678 | if (!ptr) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL); } |
1679 | ||
283ee3ff | 1680 | ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name); |
8e92c31c A |
1681 | if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL |
1682 | ptr[0] = (mDNSu8)(rr->rrtype >> 8); | |
1683 | ptr[1] = (mDNSu8)(rr->rrtype & 0xFF); | |
32bb7e43 A |
1684 | ptr[2] = (mDNSu8)(rr->rrclass >> 8); |
1685 | ptr[3] = (mDNSu8)(rr->rrclass & 0xFF); | |
8e92c31c A |
1686 | ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF); |
1687 | ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF); | |
1688 | ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF); | |
1689 | ptr[7] = (mDNSu8)( ttl & 0xFF); | |
263eeeab A |
1690 | // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes |
1691 | ||
32bb7e43 | 1692 | endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr); |
283ee3ff | 1693 | if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); } |
8e92c31c A |
1694 | |
1695 | // Go back and fill in the actual number of data bytes we wrote | |
1696 | // (actualLength can be less than rdlength when domain name compression is used) | |
1697 | actualLength = (mDNSu16)(endofrdata - ptr - 10); | |
1698 | ptr[8] = (mDNSu8)(actualLength >> 8); | |
1699 | ptr[9] = (mDNSu8)(actualLength & 0xFF); | |
1700 | ||
1701 | if (count) (*count)++; | |
283ee3ff | 1702 | else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); |
8e92c31c A |
1703 | return(endofrdata); |
1704 | } | |
1705 | ||
32bb7e43 | 1706 | mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr) |
8e92c31c | 1707 | { |
283ee3ff | 1708 | ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name); |
8e92c31c A |
1709 | if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL |
1710 | ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type | |
1711 | ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF); | |
1712 | ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class | |
1713 | ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF); | |
1714 | ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero | |
1715 | ptr[8] = ptr[9] = 0; // RDATA length is zero | |
1716 | (*count)++; | |
1717 | return(ptr + 10); | |
1718 | } | |
1719 | ||
1720 | mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass) | |
1721 | { | |
1722 | ptr = putDomainNameAsLabels(msg, ptr, limit, name); | |
1723 | if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL | |
1724 | ptr[0] = (mDNSu8)(rrtype >> 8); | |
1725 | ptr[1] = (mDNSu8)(rrtype & 0xFF); | |
1726 | ptr[2] = (mDNSu8)(rrclass >> 8); | |
1727 | ptr[3] = (mDNSu8)(rrclass & 0xFF); | |
1728 | msg->h.numQuestions++; | |
1729 | return(ptr+4); | |
1730 | } | |
1731 | ||
7f0064bd A |
1732 | // for dynamic updates |
1733 | mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass) | |
1734 | { | |
1735 | ptr = putDomainNameAsLabels(msg, ptr, limit, zone); | |
1736 | if (!ptr || ptr + 4 > limit) return mDNSNULL; // If we're out-of-space, return NULL | |
1737 | *ptr++ = (mDNSu8)(kDNSType_SOA >> 8); | |
1738 | *ptr++ = (mDNSu8)(kDNSType_SOA & 0xFF); | |
1739 | *ptr++ = zoneClass.b[0]; | |
1740 | *ptr++ = zoneClass.b[1]; | |
1741 | msg->h.mDNS_numZones++; | |
1742 | return ptr; | |
1743 | } | |
1744 | ||
1745 | // for dynamic updates | |
67c8f8a1 | 1746 | mDNSexport mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end) |
7f0064bd A |
1747 | { |
1748 | AuthRecord prereq; | |
283ee3ff | 1749 | mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL); |
67c8f8a1 | 1750 | AssignDomainName(&prereq.namestorage, name); |
7f0064bd A |
1751 | prereq.resrec.rrtype = kDNSQType_ANY; |
1752 | prereq.resrec.rrclass = kDNSClass_NONE; | |
67c8f8a1 | 1753 | return putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq); |
7f0064bd A |
1754 | } |
1755 | ||
1756 | // for dynamic updates | |
1757 | mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr) | |
1758 | { | |
7f0064bd | 1759 | // deletion: specify record w/ TTL 0, class NONE |
67c8f8a1 | 1760 | const mDNSu16 origclass = rr->rrclass; |
7f0064bd A |
1761 | rr->rrclass = kDNSClass_NONE; |
1762 | ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0); | |
1763 | rr->rrclass = origclass; | |
1764 | return ptr; | |
1765 | } | |
1766 | ||
263eeeab A |
1767 | // for dynamic updates |
1768 | mDNSexport mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit) | |
1769 | { | |
1770 | // deletion: specify record w/ TTL 0, class NONE | |
1771 | const mDNSu16 origclass = rr->rrclass; | |
1772 | rr->rrclass = kDNSClass_NONE; | |
1773 | ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0, limit); | |
1774 | rr->rrclass = origclass; | |
1775 | return ptr; | |
1776 | } | |
1777 | ||
1778 | mDNSexport mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit) | |
283ee3ff | 1779 | { |
283ee3ff A |
1780 | mDNSu16 class = kDNSQClass_ANY; |
1781 | ||
1782 | ptr = putDomainNameAsLabels(msg, ptr, limit, name); | |
1783 | if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL | |
1784 | ptr[0] = (mDNSu8)(rrtype >> 8); | |
1785 | ptr[1] = (mDNSu8)(rrtype & 0xFF); | |
1786 | ptr[2] = (mDNSu8)(class >> 8); | |
1787 | ptr[3] = (mDNSu8)(class & 0xFF); | |
1788 | ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl | |
1789 | ptr[8] = ptr[9] = 0; // zero rdlength/rdata | |
1790 | ||
1791 | msg->h.mDNS_numUpdates++; | |
1792 | return ptr + 10; | |
1793 | } | |
1794 | ||
7f0064bd A |
1795 | // for dynamic updates |
1796 | mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name) | |
1797 | { | |
283ee3ff | 1798 | const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; |
7f0064bd A |
1799 | mDNSu16 class = kDNSQClass_ANY; |
1800 | mDNSu16 rrtype = kDNSQType_ANY; | |
1801 | ||
1802 | ptr = putDomainNameAsLabels(msg, ptr, limit, name); | |
1803 | if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL | |
32bb7e43 A |
1804 | ptr[0] = (mDNSu8)(rrtype >> 8); |
1805 | ptr[1] = (mDNSu8)(rrtype & 0xFF); | |
1806 | ptr[2] = (mDNSu8)(class >> 8); | |
1807 | ptr[3] = (mDNSu8)(class & 0xFF); | |
7f0064bd A |
1808 | ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl |
1809 | ptr[8] = ptr[9] = 0; // zero rdlength/rdata | |
1810 | ||
1811 | msg->h.mDNS_numUpdates++; | |
1812 | return ptr + 10; | |
1813 | } | |
1814 | ||
1815 | // for dynamic updates | |
1816 | mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease) | |
1817 | { | |
1818 | AuthRecord rr; | |
67c8f8a1 A |
1819 | mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); |
1820 | rr.resrec.rrclass = NormalMaxDNSMessageData; | |
32bb7e43 A |
1821 | rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record |
1822 | rr.resrec.rdestimate = sizeof(rdataOPT); | |
1823 | rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; | |
1824 | rr.resrec.rdata->u.opt[0].u.updatelease = lease; | |
67c8f8a1 | 1825 | end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, &rr.resrec, 0); |
7f0064bd | 1826 | if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; } |
7f0064bd A |
1827 | return end; |
1828 | } | |
1829 | ||
263eeeab A |
1830 | // for dynamic updates |
1831 | mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease, mDNSu8 *limit) | |
1832 | { | |
1833 | AuthRecord rr; | |
1834 | mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL); | |
1835 | rr.resrec.rrclass = NormalMaxDNSMessageData; | |
1836 | rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record | |
1837 | rr.resrec.rdestimate = sizeof(rdataOPT); | |
1838 | rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; | |
1839 | rr.resrec.rdata->u.opt[0].u.updatelease = lease; | |
1840 | end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, 0, limit); | |
1841 | if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; } | |
1842 | return end; | |
1843 | } | |
1844 | ||
1845 | mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, DomainAuthInfo *authInfo, mDNSu8 *limit) | |
5e65c77f A |
1846 | { |
1847 | if (authInfo && authInfo->AutoTunnel) | |
1848 | { | |
1849 | AuthRecord hinfo; | |
1850 | mDNSu8 *h = hinfo.rdatastorage.u.data; | |
1851 | mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0]; | |
1852 | mDNSu8 *newptr; | |
1853 | mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, mDNSNULL, mDNSNULL); | |
1854 | AppendDomainLabel(&hinfo.namestorage, &m->hostlabel); | |
1855 | AppendDomainName (&hinfo.namestorage, &authInfo->domain); | |
1856 | hinfo.resrec.rroriginalttl = 0; | |
1857 | mDNSPlatformMemCopy(h, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]); | |
1858 | h += 1 + (int)h[0]; | |
1859 | mDNSPlatformMemCopy(h, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]); | |
1860 | hinfo.resrec.rdlength = len; | |
1861 | hinfo.resrec.rdestimate = len; | |
263eeeab | 1862 | newptr = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &hinfo.resrec, 0, limit); |
5e65c77f A |
1863 | return newptr; |
1864 | } | |
1865 | else | |
1866 | return end; | |
1867 | } | |
1868 | ||
8e92c31c A |
1869 | // *************************************************************************** |
1870 | #if COMPILER_LIKES_PRAGMA_MARK | |
1871 | #pragma mark - | |
1872 | #pragma mark - DNS Message Parsing Functions | |
1873 | #endif | |
1874 | ||
1875 | mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name) | |
1876 | { | |
1877 | mDNSu32 sum = 0; | |
1878 | const mDNSu8 *c; | |
1879 | ||
1880 | for (c = name->c; c[0] != 0 && c[1] != 0; c += 2) | |
1881 | { | |
1882 | sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) | | |
1883 | (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]); | |
1884 | sum = (sum<<3) | (sum>>29); | |
1885 | } | |
1886 | if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8); | |
1887 | return(sum); | |
1888 | } | |
1889 | ||
1890 | mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength) | |
1891 | { | |
1892 | domainname *target; | |
1893 | if (NewRData) | |
1894 | { | |
1895 | rr->rdata = NewRData; | |
1896 | rr->rdlength = rdlength; | |
1897 | } | |
1898 | // Must not try to get target pointer until after updating rr->rdata | |
1899 | target = GetRRDomainNameTarget(rr); | |
1900 | rr->rdlength = GetRDLength(rr, mDNSfalse); | |
1901 | rr->rdestimate = GetRDLength(rr, mDNStrue); | |
030b743d | 1902 | rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr); |
8e92c31c A |
1903 | } |
1904 | ||
1905 | mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end) | |
1906 | { | |
1907 | mDNSu16 total = 0; | |
1908 | ||
1909 | if (ptr < (mDNSu8*)msg || ptr >= end) | |
1910 | { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } | |
1911 | ||
1912 | while (1) // Read sequence of labels | |
1913 | { | |
1914 | const mDNSu8 len = *ptr++; // Read length of this label | |
1915 | if (len == 0) return(ptr); // If length is zero, that means this name is complete | |
1916 | switch (len & 0xC0) | |
1917 | { | |
1918 | case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label | |
1919 | { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } | |
1920 | if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label | |
32bb7e43 | 1921 | { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); } |
8e92c31c A |
1922 | ptr += len; |
1923 | total += 1 + len; | |
1924 | break; | |
1925 | ||
1926 | case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL); | |
1927 | case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL); | |
1928 | case 0xC0: return(ptr+1); | |
1929 | } | |
1930 | } | |
1931 | } | |
1932 | ||
1933 | // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary. | |
1934 | mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, | |
1935 | domainname *const name) | |
1936 | { | |
1937 | const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers | |
1938 | mDNSu8 *np = name->c; // Name pointer | |
1939 | const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer | |
1940 | ||
1941 | if (ptr < (mDNSu8*)msg || ptr >= end) | |
1942 | { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); } | |
1943 | ||
1944 | *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels) | |
1945 | ||
1946 | while (1) // Read sequence of labels | |
1947 | { | |
1948 | const mDNSu8 len = *ptr++; // Read length of this label | |
1949 | if (len == 0) break; // If length is zero, that means this name is complete | |
1950 | switch (len & 0xC0) | |
1951 | { | |
1952 | int i; | |
1953 | mDNSu16 offset; | |
1954 | ||
1955 | case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label | |
1956 | { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); } | |
1957 | if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label | |
32bb7e43 | 1958 | { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); } |
8e92c31c A |
1959 | *np++ = len; |
1960 | for (i=0; i<len; i++) *np++ = *ptr++; | |
1961 | *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels) | |
1962 | break; | |
1963 | ||
1964 | case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c); | |
1965 | return(mDNSNULL); | |
1966 | ||
1967 | case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL); | |
1968 | ||
1969 | case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++); | |
1970 | if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers | |
1971 | ptr = (mDNSu8 *)msg + offset; | |
1972 | if (ptr < (mDNSu8*)msg || ptr >= end) | |
1973 | { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); } | |
1974 | if (*ptr & 0xC0) | |
1975 | { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); } | |
1976 | break; | |
1977 | } | |
1978 | } | |
1979 | ||
1980 | if (nextbyte) return(nextbyte); | |
1981 | else return(ptr); | |
1982 | } | |
1983 | ||
1984 | mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) | |
1985 | { | |
1986 | mDNSu16 pktrdlength; | |
1987 | ||
1988 | ptr = skipDomainName(msg, ptr, end); | |
1989 | if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); } | |
1990 | ||
1991 | if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } | |
1992 | pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); | |
1993 | ptr += 10; | |
1994 | if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } | |
1995 | ||
1996 | return(ptr + pktrdlength); | |
1997 | } | |
1998 | ||
67c8f8a1 | 1999 | mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, |
32bb7e43 | 2000 | const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr) |
8e92c31c | 2001 | { |
32bb7e43 A |
2002 | CacheRecord *const rr = &largecr->r; |
2003 | RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data; | |
8e92c31c | 2004 | mDNSu16 pktrdlength; |
7f0064bd | 2005 | |
32bb7e43 A |
2006 | if (largecr == &m->rec && m->rec.r.resrec.RecordType) |
2007 | { | |
2008 | LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); | |
2009 | #if ForceAlerts | |
2010 | *(long*)0 = 0; | |
2011 | #endif | |
2012 | } | |
8e92c31c A |
2013 | |
2014 | rr->next = mDNSNULL; | |
283ee3ff | 2015 | rr->resrec.name = &largecr->namestorage; |
8e92c31c A |
2016 | |
2017 | rr->NextInKAList = mDNSNULL; | |
7f0064bd A |
2018 | rr->TimeRcvd = m ? m->timenow : 0; |
2019 | rr->DelayDelivery = 0; | |
263eeeab | 2020 | rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord() |
7f0064bd | 2021 | rr->LastUsed = m ? m->timenow : 0; |
8e92c31c A |
2022 | rr->CRActiveQuestion = mDNSNULL; |
2023 | rr->UnansweredQueries = 0; | |
2024 | rr->LastUnansweredTime= 0; | |
32bb7e43 | 2025 | #if ENABLE_MULTI_PACKET_QUERY_SNOOPING |
8e92c31c A |
2026 | rr->MPUnansweredQ = 0; |
2027 | rr->MPLastUnansweredQT= 0; | |
2028 | rr->MPUnansweredKA = 0; | |
2029 | rr->MPExpectingKA = mDNSfalse; | |
32bb7e43 | 2030 | #endif |
8e92c31c A |
2031 | rr->NextInCFList = mDNSNULL; |
2032 | ||
2033 | rr->resrec.InterfaceID = InterfaceID; | |
263eeeab A |
2034 | rr->resrec.rDNSServer = mDNSNULL; |
2035 | ||
2036 | ptr = getDomainName(msg, ptr, end, &largecr->namestorage); // Will bail out correctly if ptr is NULL | |
67c8f8a1 | 2037 | if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); } |
263eeeab | 2038 | rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); |
8e92c31c | 2039 | |
67c8f8a1 | 2040 | if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); } |
8e92c31c A |
2041 | |
2042 | rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); | |
2043 | rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask); | |
2044 | rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]); | |
2045 | if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1) | |
2046 | rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond; | |
2047 | // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for | |
2048 | // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly. | |
2049 | pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]); | |
67c8f8a1 A |
2050 | |
2051 | // If mDNS record has cache-flush bit set, we mark it unique | |
2052 | // For uDNS records, all are implicitly deemed unique (a single DNS server is always | |
2053 | // authoritative for the entire RRSet), unless this is a truncated response | |
2054 | if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || (!InterfaceID && !(msg->h.flags.b[0] & kDNSFlag0_TC))) | |
cc340f17 | 2055 | RecordType |= kDNSRecordTypePacketUniqueMask; |
8e92c31c | 2056 | ptr += 10; |
67c8f8a1 | 2057 | if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); } |
7f0064bd | 2058 | end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record |
8e92c31c | 2059 | |
32bb7e43 | 2060 | rr->resrec.rdata = (RData*)&rr->smallrdatastorage; |
7f0064bd A |
2061 | rr->resrec.rdata->MaxRDLength = MaximumRDSize; |
2062 | ||
283ee3ff | 2063 | if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c); |
8e92c31c | 2064 | |
030b743d A |
2065 | // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding |
2066 | // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind | |
2067 | // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data. | |
67c8f8a1 A |
2068 | // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that |
2069 | // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ. | |
2070 | if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0) // Used in update packets to mean "Delete An RRset" (RFC 2136) | |
2071 | rr->resrec.rdlength = 0; | |
2072 | else switch (rr->resrec.rrtype) | |
8e92c31c | 2073 | { |
263eeeab | 2074 | case kDNSType_A: if (pktrdlength != sizeof(mDNSv4Addr)) goto fail; |
32bb7e43 A |
2075 | rdb->ipv4.b[0] = ptr[0]; |
2076 | rdb->ipv4.b[1] = ptr[1]; | |
2077 | rdb->ipv4.b[2] = ptr[2]; | |
2078 | rdb->ipv4.b[3] = ptr[3]; | |
8e92c31c A |
2079 | break; |
2080 | ||
8e92c31c | 2081 | case kDNSType_NS: |
67c8f8a1 A |
2082 | case kDNSType_CNAME: |
2083 | case kDNSType_PTR: | |
32bb7e43 | 2084 | case kDNSType_DNAME:ptr = getDomainName(msg, ptr, end, &rdb->name); |
263eeeab | 2085 | if (ptr != end) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); goto fail; } |
32bb7e43 | 2086 | //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rdb->name.c, pktrdlength); |
8e92c31c A |
2087 | break; |
2088 | ||
32bb7e43 | 2089 | case kDNSType_SOA: ptr = getDomainName(msg, ptr, end, &rdb->soa.mname); |
263eeeab | 2090 | if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); goto fail; } |
32bb7e43 | 2091 | ptr = getDomainName(msg, ptr, end, &rdb->soa.rname); |
263eeeab A |
2092 | if (!ptr) { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); goto fail; } |
2093 | if (ptr + 0x14 != end) { debugf("GetLargeResourceRecord: Malformed SOA RDATA"); goto fail; } | |
2094 | rdb->soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]); | |
2095 | rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]); | |
2096 | rdb->soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]); | |
2097 | rdb->soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]); | |
2098 | rdb->soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]); | |
2099 | break; | |
67c8f8a1 A |
2100 | |
2101 | case kDNSType_NULL: | |
2102 | case kDNSType_HINFO: | |
2103 | case kDNSType_TSIG: | |
2104 | case kDNSType_TXT: | |
2105 | case kDNSType_X25: | |
2106 | case kDNSType_ISDN: | |
2107 | case kDNSType_LOC: | |
2108 | case kDNSType_DHCID:if (pktrdlength > rr->resrec.rdata->MaxRDLength) | |
8e92c31c | 2109 | { |
67c8f8a1 | 2110 | debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)", |
8e92c31c | 2111 | DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); |
263eeeab | 2112 | goto fail; |
8e92c31c A |
2113 | } |
2114 | rr->resrec.rdlength = pktrdlength; | |
32bb7e43 | 2115 | mDNSPlatformMemCopy(rdb->data, ptr, pktrdlength); |
8e92c31c A |
2116 | break; |
2117 | ||
67c8f8a1 A |
2118 | case kDNSType_MX: |
2119 | case kDNSType_AFSDB: | |
2120 | case kDNSType_RT: | |
263eeeab | 2121 | case kDNSType_KX: if (pktrdlength < 3) goto fail; // Preference + domainname |
32bb7e43 A |
2122 | rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); |
2123 | ptr = getDomainName(msg, ptr+2, end, &rdb->mx.exchange); | |
263eeeab | 2124 | if (ptr != end) { debugf("GetLargeResourceRecord: Malformed MX name"); goto fail; } |
32bb7e43 | 2125 | //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength); |
67c8f8a1 A |
2126 | break; |
2127 | ||
32bb7e43 | 2128 | case kDNSType_RP: ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox); // Domainname + domainname |
263eeeab | 2129 | if (!ptr) { debugf("GetLargeResourceRecord: Malformed RP mbox"); goto fail; } |
32bb7e43 | 2130 | ptr = getDomainName(msg, ptr, end, &rdb->rp.txt); |
263eeeab | 2131 | if (ptr != end) { debugf("GetLargeResourceRecord: Malformed RP txt"); goto fail; } |
67c8f8a1 A |
2132 | break; |
2133 | ||
263eeeab | 2134 | case kDNSType_PX: if (pktrdlength < 4) goto fail; // Preference + domainname + domainname |
32bb7e43 A |
2135 | rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); |
2136 | ptr = getDomainName(msg, ptr, end, &rdb->px.map822); | |
263eeeab | 2137 | if (!ptr) { debugf("GetLargeResourceRecord: Malformed PX map822"); goto fail; } |
32bb7e43 | 2138 | ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400); |
263eeeab | 2139 | if (ptr != end) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); goto fail; } |
8e92c31c A |
2140 | break; |
2141 | ||
263eeeab | 2142 | case kDNSType_AAAA: if (pktrdlength != sizeof(mDNSv6Addr)) goto fail; |
32bb7e43 | 2143 | mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6)); |
67c8f8a1 A |
2144 | break; |
2145 | ||
263eeeab | 2146 | case kDNSType_SRV: if (pktrdlength < 7) goto fail; // Priority + weight + port + domainname |
32bb7e43 A |
2147 | rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); |
2148 | rdb->srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); | |
2149 | rdb->srv.port.b[0] = ptr[4]; | |
2150 | rdb->srv.port.b[1] = ptr[5]; | |
2151 | ptr = getDomainName(msg, ptr+6, end, &rdb->srv.target); | |
263eeeab | 2152 | if (ptr != end) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); goto fail; } |
32bb7e43 | 2153 | //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength); |
8e92c31c A |
2154 | break; |
2155 | ||
32bb7e43 A |
2156 | case kDNSType_OPT: { |
2157 | rdataOPT *opt = rr->resrec.rdata->u.opt; | |
2158 | rr->resrec.rdlength = 0; | |
2159 | while (ptr < end && (mDNSu8 *)(opt+1) < &rr->resrec.rdata->u.data[MaximumRDSize]) | |
2160 | { | |
263eeeab A |
2161 | const rdataOPT *const currentopt = opt; |
2162 | if (ptr + 4 > end) { LogInfo("GetLargeResourceRecord: OPT RDATA ptr + 4 > end"); goto fail; } | |
2163 | opt->opt = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); | |
2164 | opt->optlen = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); | |
2165 | ptr += 4; | |
2166 | if (ptr + opt->optlen > end) { LogInfo("GetLargeResourceRecord: ptr + opt->optlen > end"); goto fail; } | |
2167 | switch (opt->opt) | |
32bb7e43 A |
2168 | { |
2169 | case kDNSOpt_LLQ: | |
263eeeab A |
2170 | if (opt->optlen == DNSOpt_LLQData_Space - 4) |
2171 | { | |
2172 | opt->u.llq.vers = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); | |
2173 | opt->u.llq.llqOp = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); | |
2174 | opt->u.llq.err = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); | |
2175 | mDNSPlatformMemCopy(opt->u.llq.id.b, ptr+6, 8); | |
2176 | opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[14] << 24 | (mDNSu32)ptr[15] << 16 | (mDNSu32)ptr[16] << 8 | ptr[17]); | |
2177 | if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond) | |
2178 | opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond; | |
2179 | opt++; | |
2180 | } | |
32bb7e43 A |
2181 | break; |
2182 | case kDNSOpt_Lease: | |
263eeeab A |
2183 | if (opt->optlen == DNSOpt_LeaseData_Space - 4) |
2184 | { | |
2185 | opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]); | |
2186 | if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond) | |
2187 | opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond; | |
2188 | opt++; | |
2189 | } | |
32bb7e43 A |
2190 | break; |
2191 | case kDNSOpt_Owner: | |
263eeeab | 2192 | if (ValidOwnerLength(opt->optlen)) |
32bb7e43 | 2193 | { |
263eeeab A |
2194 | opt->u.owner.vers = ptr[0]; |
2195 | opt->u.owner.seq = ptr[1]; | |
2196 | mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6); // 6-byte MAC address | |
2197 | mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6); // 6-byte MAC address | |
2198 | opt->u.owner.password = zeroEthAddr; | |
2199 | if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4) | |
2200 | { | |
2201 | mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6); // 6-byte MAC address | |
2202 | // This mDNSPlatformMemCopy is safe because the ValidOwnerLength(opt->optlen) check above | |
2203 | // ensures that opt->optlen is no more than DNSOpt_OwnerData_ID_Wake_PW6_Space - 4 | |
2204 | if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4) | |
2205 | mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4)); | |
2206 | } | |
2207 | opt++; | |
32bb7e43 | 2208 | } |
32bb7e43 A |
2209 | break; |
2210 | } | |
263eeeab | 2211 | ptr += currentopt->optlen; |
32bb7e43 | 2212 | } |
263eeeab A |
2213 | rr->resrec.rdlength = (mDNSu16)((mDNSu8*)opt - rr->resrec.rdata->u.data); |
2214 | if (ptr != end) { LogInfo("GetLargeResourceRecord: Malformed OptRdata"); goto fail; } | |
32bb7e43 A |
2215 | break; |
2216 | } | |
2217 | ||
2218 | case kDNSType_NSEC: { | |
2219 | unsigned int i, j; | |
2220 | domainname d; | |
2221 | ptr = getDomainName(msg, ptr, end, &d); // Ignored for our simplified use of NSEC synthetic records | |
263eeeab | 2222 | if (!ptr) { LogInfo("GetLargeResourceRecord: Malformed NSEC nextname"); goto fail; } |
32bb7e43 | 2223 | mDNSPlatformMemZero(rdb->nsec.bitmap, sizeof(rdb->nsec.bitmap)); |
263eeeab A |
2224 | if (ptr < end) |
2225 | { | |
2226 | if (*ptr++ != 0) { debugf("GetLargeResourceRecord: We only handle block zero NSECs"); goto fail; } | |
2227 | i = *ptr++; | |
2228 | if (i > sizeof(rdataNSEC)) { debugf("GetLargeResourceRecord: invalid block length %d", i); goto fail; } | |
2229 | for (j=0; j<i; j++) rdb->nsec.bitmap[j] = *ptr++; | |
2230 | } | |
2231 | if (ptr != end) { debugf("GetLargeResourceRecord: Malformed NSEC"); goto fail; } | |
32bb7e43 A |
2232 | break; |
2233 | } | |
8e92c31c A |
2234 | |
2235 | default: if (pktrdlength > rr->resrec.rdata->MaxRDLength) | |
2236 | { | |
67c8f8a1 | 2237 | debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)", |
8e92c31c | 2238 | rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength); |
263eeeab | 2239 | goto fail; |
8e92c31c | 2240 | } |
67c8f8a1 | 2241 | debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data", |
8e92c31c A |
2242 | rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype)); |
2243 | // Note: Just because we don't understand the record type, that doesn't | |
2244 | // mean we fail. The DNS protocol specifies rdlength, so we can | |
2245 | // safely skip over unknown records and ignore them. | |
2246 | // We also grab a binary copy of the rdata anyway, since the caller | |
2247 | // might know how to interpret it even if we don't. | |
2248 | rr->resrec.rdlength = pktrdlength; | |
32bb7e43 | 2249 | mDNSPlatformMemCopy(rdb->data, ptr, pktrdlength); |
8e92c31c A |
2250 | break; |
2251 | } | |
2252 | ||
67c8f8a1 | 2253 | SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rdlength, rdestimate, rdatahash for us |
8e92c31c | 2254 | |
cc340f17 A |
2255 | // Success! Now fill in RecordType to show this record contains valid data |
2256 | rr->resrec.RecordType = RecordType; | |
67c8f8a1 | 2257 | return(end); |
263eeeab A |
2258 | |
2259 | fail: | |
2260 | // If we were unable to parse the rdata in this record, we indicate that by | |
2261 | // returing a 'kDNSRecordTypePacketNegative' record with rdlength set to zero | |
2262 | rr->resrec.RecordType = kDNSRecordTypePacketNegative; | |
2263 | rr->resrec.rdlength = 0; | |
2264 | rr->resrec.rdestimate = 0; | |
2265 | rr->resrec.rdatahash = 0; | |
2266 | return(end); | |
8e92c31c A |
2267 | } |
2268 | ||
2269 | mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end) | |
2270 | { | |
2271 | ptr = skipDomainName(msg, ptr, end); | |
2272 | if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); } | |
2273 | if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } | |
2274 | return(ptr+4); | |
2275 | } | |
2276 | ||
2277 | mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, | |
2278 | DNSQuestion *question) | |
2279 | { | |
67c8f8a1 | 2280 | mDNSPlatformMemZero(question, sizeof(*question)); |
8e92c31c | 2281 | question->InterfaceID = InterfaceID; |
32bb7e43 | 2282 | if (!InterfaceID) question->TargetQID = onesID; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast |
8e92c31c A |
2283 | ptr = getDomainName(msg, ptr, end, &question->qname); |
2284 | if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); } | |
2285 | if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); } | |
2286 | ||
2287 | question->qnamehash = DomainNameHashValue(&question->qname); | |
2288 | question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type | |
2289 | question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class | |
2290 | return(ptr+4); | |
2291 | } | |
2292 | ||
2293 | mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end) | |
2294 | { | |
2295 | int i; | |
2296 | const mDNSu8 *ptr = msg->data; | |
2297 | for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end); | |
2298 | return(ptr); | |
2299 | } | |
2300 | ||
2301 | mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end) | |
2302 | { | |
2303 | int i; | |
2304 | const mDNSu8 *ptr = LocateAnswers(msg, end); | |
2305 | for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end); | |
2306 | return(ptr); | |
2307 | } | |
2308 | ||
2309 | mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end) | |
2310 | { | |
2311 | int i; | |
2312 | const mDNSu8 *ptr = LocateAuthorities(msg, end); | |
2313 | for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end); | |
2314 | return (ptr); | |
2315 | } | |
2316 | ||
32bb7e43 | 2317 | mDNSexport const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize) |
67c8f8a1 A |
2318 | { |
2319 | int i; | |
2320 | const mDNSu8 *ptr = LocateAdditionals(msg, end); | |
2321 | ||
2322 | // Locate the OPT record. | |
2323 | // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response." | |
2324 | // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section, | |
2325 | // but not necessarily the *last* entry in the Additional Section. | |
2326 | for (i = 0; ptr && i < msg->h.numAdditionals; i++) | |
2327 | { | |
32bb7e43 A |
2328 | if (ptr + DNSOpt_Header_Space + minsize <= end && // Make sure we have 11+minsize bytes of data |
2329 | ptr[0] == 0 && // Name must be root label | |
2330 | ptr[1] == (kDNSType_OPT >> 8 ) && // rrtype OPT | |
2331 | ptr[2] == (kDNSType_OPT & 0xFF) && | |
2332 | ((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)minsize) | |
67c8f8a1 A |
2333 | return(ptr); |
2334 | else | |
2335 | ptr = skipResourceRecord(msg, ptr, end); | |
2336 | } | |
2337 | return(mDNSNULL); | |
2338 | } | |
2339 | ||
2340 | // On success, GetLLQOptData returns pointer to storage within shared "m->rec"; | |
32bb7e43 | 2341 | // it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use |
67c8f8a1 A |
2342 | // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together |
2343 | // The code that currently calls this assumes there's only one, instead of iterating through the set | |
2344 | mDNSexport const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end) | |
2345 | { | |
32bb7e43 | 2346 | const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LLQData_Space); |
67c8f8a1 A |
2347 | if (ptr) |
2348 | { | |
2349 | ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); | |
263eeeab | 2350 | if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) return(&m->rec.r.resrec.rdata->u.opt[0]); |
67c8f8a1 A |
2351 | } |
2352 | return(mDNSNULL); | |
2353 | } | |
2354 | ||
2355 | // Get the lease life of records in a dynamic update | |
2356 | // returns 0 on error or if no lease present | |
2357 | mDNSexport mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end) | |
2358 | { | |
2359 | mDNSu32 result = 0; | |
32bb7e43 | 2360 | const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space); |
67c8f8a1 | 2361 | if (ptr) ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); |
32bb7e43 A |
2362 | if (ptr && m->rec.r.resrec.rdlength >= DNSOpt_LeaseData_Space && m->rec.r.resrec.rdata->u.opt[0].opt == kDNSOpt_Lease) |
2363 | result = m->rec.r.resrec.rdata->u.opt[0].u.updatelease; | |
67c8f8a1 A |
2364 | m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it |
2365 | return(result); | |
2366 | } | |
2367 | ||
2368 | mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label) | |
2369 | { | |
2370 | int i; | |
2371 | LogMsg("%2d %s", count, label); | |
2372 | for (i = 0; i < count && ptr; i++) | |
2373 | { | |
2374 | // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage, | |
2375 | // but since it's only used for debugging (and probably only on OS X, not on | |
2376 | // embedded systems) putting a 9kB object on the stack isn't a big problem. | |
2377 | LargeCacheRecord largecr; | |
2378 | ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr); | |
1a175162 | 2379 | if (ptr) LogMsg("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); |
67c8f8a1 A |
2380 | } |
2381 | if (!ptr) LogMsg("ERROR: Premature end of packet data"); | |
2382 | return(ptr); | |
2383 | } | |
2384 | ||
2385 | #define DNS_OP_Name(X) ( \ | |
2386 | (X) == kDNSFlag0_OP_StdQuery ? "" : \ | |
2387 | (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \ | |
2388 | (X) == kDNSFlag0_OP_Status ? "Status " : \ | |
2389 | (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \ | |
2390 | (X) == kDNSFlag0_OP_Notify ? "Notify " : \ | |
2391 | (X) == kDNSFlag0_OP_Update ? "Update " : "?? " ) | |
2392 | ||
2393 | #define DNS_RC_Name(X) ( \ | |
2394 | (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \ | |
32bb7e43 A |
2395 | (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \ |
2396 | (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \ | |
67c8f8a1 A |
2397 | (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \ |
2398 | (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \ | |
2399 | (X) == kDNSFlag1_RC_Refused ? "Refused" : \ | |
2400 | (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \ | |
2401 | (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \ | |
2402 | (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \ | |
2403 | (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \ | |
2404 | (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" ) | |
2405 | ||
2406 | // Note: DumpPacket expects the packet header fields in host byte order, not network byte order | |
32bb7e43 | 2407 | mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport, |
9f29194f A |
2408 | const mDNSAddr *srcaddr, mDNSIPPort srcport, |
2409 | const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end) | |
67c8f8a1 A |
2410 | { |
2411 | mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update); | |
2412 | const mDNSu8 *ptr = msg->data; | |
2413 | int i; | |
2414 | DNSQuestion q; | |
32bb7e43 A |
2415 | char tbuffer[64], sbuffer[64], dbuffer[64] = ""; |
2416 | if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received" )] = 0; | |
2417 | else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receiv")] = 0; | |
9f29194f A |
2418 | if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port " )] = 0; |
2419 | else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0; | |
2420 | if (dstaddr || !mDNSIPPortIsZero(dstport)) | |
2421 | dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0; | |
67c8f8a1 | 2422 | |
9f29194f | 2423 | 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 --", |
32bb7e43 | 2424 | tbuffer, transport, |
67c8f8a1 A |
2425 | DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask), |
2426 | msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query", | |
2427 | msg->h.flags.b[0], msg->h.flags.b[1], | |
2428 | DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask), | |
2429 | msg->h.flags.b[1] & kDNSFlag1_RC_Mask, | |
2430 | msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "", | |
2431 | msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "", | |
2432 | msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "", | |
2433 | msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "", | |
2434 | msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "", | |
2435 | msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "", | |
2436 | mDNSVal16(msg->h.id), | |
2437 | end - msg->data, | |
9f29194f | 2438 | sbuffer, mDNSVal16(srcport), dbuffer, |
67c8f8a1 A |
2439 | (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : "" |
2440 | ); | |
2441 | ||
2442 | LogMsg("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions"); | |
2443 | for (i = 0; i < msg->h.numQuestions && ptr; i++) | |
2444 | { | |
2445 | ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q); | |
2446 | if (ptr) LogMsg("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype)); | |
2447 | } | |
2448 | ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers"); | |
2449 | ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities"); | |
2450 | ptr = DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals"); | |
2451 | LogMsg("--------------"); | |
2452 | } | |
2453 | ||
8e92c31c A |
2454 | // *************************************************************************** |
2455 | #if COMPILER_LIKES_PRAGMA_MARK | |
2456 | #pragma mark - | |
8e92c31c A |
2457 | #pragma mark - Packet Sending Functions |
2458 | #endif | |
2459 | ||
67c8f8a1 A |
2460 | // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.) |
2461 | struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ }; | |
2462 | ||
9f29194f A |
2463 | struct UDPSocket_struct |
2464 | { | |
2465 | mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port | |
2466 | }; | |
2467 | ||
030b743d A |
2468 | // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which |
2469 | // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible. | |
67c8f8a1 | 2470 | mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end, |
9f29194f | 2471 | mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo) |
8e92c31c | 2472 | { |
030b743d | 2473 | mStatus status = mStatus_NoError; |
5e65c77f | 2474 | const mDNSu16 numAdditionals = msg->h.numAdditionals; |
32bb7e43 | 2475 | mDNSu8 *newend; |
263eeeab | 2476 | mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; |
8e92c31c | 2477 | |
32bb7e43 A |
2478 | // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code |
2479 | if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData) | |
96f69b28 A |
2480 | { |
2481 | LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data); | |
2482 | return mStatus_BadParamErr; | |
2483 | } | |
2484 | ||
263eeeab A |
2485 | newend = putHINFO(m, msg, end, authInfo, limit); |
2486 | if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed msg %p end %p, limit %p", msg->data, end, limit); // Not fatal | |
32bb7e43 | 2487 | else end = newend; |
5e65c77f | 2488 | |
32bb7e43 A |
2489 | // Put all the integer values in IETF byte-order (MSB first, LSB second) |
2490 | SwapDNSHeaderBytes(msg); | |
5e65c77f | 2491 | |
32bb7e43 A |
2492 | if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0); // DNSDigest_SignMessage operates on message in network byte order |
2493 | if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; } | |
2494 | else | |
2495 | { | |
2496 | // Send the packet on the wire | |
2497 | if (!sock) | |
2498 | status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, src, dst, dstport); | |
030b743d A |
2499 | else |
2500 | { | |
32bb7e43 A |
2501 | mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg); |
2502 | mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) }; | |
2503 | long nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2); // Should do scatter/gather here -- this is probably going out as two packets | |
2504 | if (nsent != 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); status = mStatus_ConnFailed; } | |
030b743d A |
2505 | else |
2506 | { | |
32bb7e43 A |
2507 | nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen); |
2508 | if (nsent != msglen) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); status = mStatus_ConnFailed; } | |
030b743d A |
2509 | } |
2510 | } | |
8e92c31c A |
2511 | } |
2512 | ||
32bb7e43 A |
2513 | // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage) |
2514 | SwapDNSHeaderBytes(msg); | |
67c8f8a1 | 2515 | |
5e65c77f | 2516 | // Dump the packet with the HINFO and TSIG |
32bb7e43 A |
2517 | if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id)) |
2518 | DumpPacket(m, status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end); | |
8e92c31c | 2519 | |
32bb7e43 | 2520 | // put the number of additionals back the way it was |
5e65c77f A |
2521 | msg->h.numAdditionals = numAdditionals; |
2522 | ||
8e92c31c | 2523 | return(status); |
8e92c31c A |
2524 | } |
2525 | ||
7f0064bd A |
2526 | // *************************************************************************** |
2527 | #if COMPILER_LIKES_PRAGMA_MARK | |
2528 | #pragma mark - | |
2529 | #pragma mark - RR List Management & Task Management | |
2530 | #endif | |
8e92c31c | 2531 | |
263eeeab | 2532 | mDNSexport void mDNS_Lock_(mDNS *const m, const char * const functionname) |
8e92c31c | 2533 | { |
7f0064bd A |
2534 | // MUST grab the platform lock FIRST! |
2535 | mDNSPlatformLock(m); | |
2536 | ||
2537 | // Normally, mDNS_reentrancy is zero and so is mDNS_busy | |
2538 | // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too | |
2539 | // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one | |
2540 | // If mDNS_busy != mDNS_reentrancy that's a bad sign | |
2541 | if (m->mDNS_busy != m->mDNS_reentrancy) | |
67c8f8a1 | 2542 | { |
263eeeab A |
2543 | LogMsg("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); |
2544 | #if ForceAlerts | |
67c8f8a1 | 2545 | *(long*)0 = 0; |
67c8f8a1 | 2546 | #endif |
263eeeab | 2547 | } |
7f0064bd A |
2548 | |
2549 | // If this is an initial entry into the mDNSCore code, set m->timenow | |
2550 | // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set | |
2551 | if (m->mDNS_busy == 0) | |
2552 | { | |
2553 | if (m->timenow) | |
263eeeab | 2554 | LogMsg("%s: mDNS_Lock: m->timenow already set (%ld/%ld)", functionname, m->timenow, mDNS_TimeNow_NoLock(m)); |
cc340f17 | 2555 | m->timenow = mDNS_TimeNow_NoLock(m); |
7f0064bd A |
2556 | if (m->timenow == 0) m->timenow = 1; |
2557 | } | |
2558 | else if (m->timenow == 0) | |
2559 | { | |
263eeeab | 2560 | LogMsg("%s: mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", functionname, m->mDNS_busy); |
cc340f17 | 2561 | m->timenow = mDNS_TimeNow_NoLock(m); |
7f0064bd A |
2562 | if (m->timenow == 0) m->timenow = 1; |
2563 | } | |
2564 | ||
2565 | if (m->timenow_last - m->timenow > 0) | |
2566 | { | |
2567 | m->timenow_adjust += m->timenow_last - m->timenow; | |
263eeeab | 2568 | LogMsg("%s: mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", functionname, m->timenow_last - m->timenow, m->timenow_adjust); |
7f0064bd A |
2569 | m->timenow = m->timenow_last; |
2570 | } | |
2571 | m->timenow_last = m->timenow; | |
2572 | ||
2573 | // Increment mDNS_busy so we'll recognise re-entrant calls | |
2574 | m->mDNS_busy++; | |
8e92c31c A |
2575 | } |
2576 | ||
263eeeab A |
2577 | mDNSlocal AuthRecord *AnyLocalRecordReady(const mDNS *const m) |
2578 | { | |
2579 | AuthRecord *rr; | |
2580 | for (rr = m->NewLocalRecords; rr; rr = rr->next) | |
2581 | if (LocalRecordReady(rr)) return rr; | |
2582 | return mDNSNULL; | |
2583 | } | |
2584 | ||
7f0064bd | 2585 | mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) |
8e92c31c | 2586 | { |
7f0064bd | 2587 | mDNSs32 e = m->timenow + 0x78000000; |
030b743d | 2588 | if (m->mDNSPlatformStatus != mStatus_NoError) return(e); |
7f0064bd A |
2589 | if (m->NewQuestions) |
2590 | { | |
2591 | if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering; | |
2592 | else return(m->timenow); | |
2593 | } | |
263eeeab A |
2594 | if (m->NewLocalOnlyQuestions) return(m->timenow); |
2595 | if (m->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow); | |
2596 | if (m->SPSProxyListChanged) return(m->timenow); | |
2597 | if (m->LocalRemoveEvents) return(m->timenow); | |
2598 | ||
7f0064bd | 2599 | #ifndef UNICAST_DISABLED |
67c8f8a1 | 2600 | if (e - m->NextuDNSEvent > 0) e = m->NextuDNSEvent; |
030b743d | 2601 | if (e - m->NextScheduledNATOp > 0) e = m->NextScheduledNATOp; |
263eeeab | 2602 | if (m->NextSRVUpdate && e - m->NextSRVUpdate > 0) e = m->NextSRVUpdate; |
7f0064bd | 2603 | #endif |
263eeeab | 2604 | |
7f0064bd | 2605 | if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; |
32bb7e43 | 2606 | if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS; |
1a175162 A |
2607 | // NextScheduledSPRetry only valid when DelaySleep not set |
2608 | if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry; | |
263eeeab | 2609 | if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep; |
030b743d | 2610 | |
32bb7e43 | 2611 | if (m->SuppressSending) |
030b743d | 2612 | { |
32bb7e43 | 2613 | if (e - m->SuppressSending > 0) e = m->SuppressSending; |
030b743d | 2614 | } |
32bb7e43 A |
2615 | else |
2616 | { | |
2617 | if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery; | |
2618 | if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe; | |
2619 | if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; | |
2620 | } | |
2621 | ||
7f0064bd | 2622 | return(e); |
8e92c31c A |
2623 | } |
2624 | ||
32bb7e43 A |
2625 | mDNSexport void ShowTaskSchedulingError(mDNS *const m) |
2626 | { | |
2627 | mDNS_Lock(m); | |
2628 | ||
2629 | LogMsg("Task Scheduling Error: Continuously busy for more than a second"); | |
2630 | ||
2631 | // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above | |
2632 | ||
2633 | if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0)) | |
2634 | LogMsg("Task Scheduling Error: NewQuestion %##s (%s)", | |
2635 | m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype)); | |
2636 | ||
2637 | if (m->NewLocalOnlyQuestions) | |
2638 | LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)", | |
2639 | m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); | |
2640 | ||
263eeeab A |
2641 | if (m->NewLocalRecords) |
2642 | { | |
2643 | AuthRecord *rr = AnyLocalRecordReady(m); | |
2644 | if (rr) LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr)); | |
2645 | } | |
2646 | ||
2647 | if (m->SPSProxyListChanged) LogMsg("Task Scheduling Error: SPSProxyListChanged"); | |
2648 | if (m->LocalRemoveEvents) LogMsg("Task Scheduling Error: LocalRemoveEvents"); | |
32bb7e43 A |
2649 | |
2650 | if (m->timenow - m->NextScheduledEvent >= 0) | |
2651 | LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m->timenow - m->NextScheduledEvent); | |
263eeeab A |
2652 | |
2653 | #ifndef UNICAST_DISABLED | |
2654 | if (m->timenow - m->NextuDNSEvent >= 0) | |
2655 | LogMsg("Task Scheduling Error: m->NextuDNSEvent %d", m->timenow - m->NextuDNSEvent); | |
32bb7e43 A |
2656 | if (m->timenow - m->NextScheduledNATOp >= 0) |
2657 | LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp); | |
263eeeab A |
2658 | if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) |
2659 | LogMsg("Task Scheduling Error: m->NextSRVUpdate %d", m->timenow - m->NextSRVUpdate); | |
2660 | #endif | |
2661 | ||
2662 | if (m->timenow - m->NextCacheCheck >= 0) | |
2663 | LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck); | |
32bb7e43 A |
2664 | if (m->timenow - m->NextScheduledSPS >= 0) |
2665 | LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS); | |
1a175162 | 2666 | if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0) |
32bb7e43 A |
2667 | LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry); |
2668 | if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) | |
2669 | LogMsg("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep); | |
263eeeab A |
2670 | |
2671 | if (m->SuppressSending && m->timenow - m->SuppressSending >= 0) | |
2672 | LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending); | |
2673 | if (m->timenow - m->NextScheduledQuery >= 0) | |
2674 | LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery); | |
2675 | if (m->timenow - m->NextScheduledProbe >= 0) | |
2676 | LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe); | |
2677 | if (m->timenow - m->NextScheduledResponse >= 0) | |
2678 | LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse); | |
32bb7e43 A |
2679 | |
2680 | mDNS_Unlock(m); | |
2681 | } | |
2682 | ||
263eeeab | 2683 | mDNSexport void mDNS_Unlock_(mDNS *const m, const char * const functionname) |
8e92c31c | 2684 | { |
7f0064bd A |
2685 | // Decrement mDNS_busy |
2686 | m->mDNS_busy--; | |
2687 | ||
2688 | // Check for locking failures | |
2689 | if (m->mDNS_busy != m->mDNS_reentrancy) | |
67c8f8a1 | 2690 | { |
263eeeab A |
2691 | LogMsg("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); |
2692 | #if ForceAlerts | |
67c8f8a1 | 2693 | *(long*)0 = 0; |
67c8f8a1 | 2694 | #endif |
263eeeab | 2695 | } |
7f0064bd A |
2696 | |
2697 | // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow | |
2698 | if (m->mDNS_busy == 0) | |
2699 | { | |
2700 | m->NextScheduledEvent = GetNextScheduledEvent(m); | |
263eeeab | 2701 | if (m->timenow == 0) LogMsg("%s: mDNS_Unlock: ERROR! m->timenow aready zero", functionname); |
7f0064bd A |
2702 | m->timenow = 0; |
2703 | } | |
2704 | ||
2705 | // MUST release the platform lock LAST! | |
2706 | mDNSPlatformUnlock(m); | |
8e92c31c | 2707 | } |
67c8f8a1 A |
2708 | |
2709 | // *************************************************************************** | |
2710 | #if COMPILER_LIKES_PRAGMA_MARK | |
2711 | #pragma mark - | |
2712 | #pragma mark - Specialized mDNS version of vsnprintf | |
2713 | #endif | |
2714 | ||
2715 | static const struct mDNSprintf_format | |
2716 | { | |
2717 | unsigned leftJustify : 1; | |
2718 | unsigned forceSign : 1; | |
2719 | unsigned zeroPad : 1; | |
2720 | unsigned havePrecision : 1; | |
2721 | unsigned hSize : 1; | |
2722 | unsigned lSize : 1; | |
2723 | char altForm; | |
2724 | char sign; // +, - or space | |
2725 | unsigned int fieldWidth; | |
2726 | unsigned int precision; | |
2727 | } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; | |
2728 | ||
2729 | mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) | |
2730 | { | |
2731 | mDNSu32 nwritten = 0; | |
2732 | int c; | |
2733 | if (buflen == 0) return(0); | |
2734 | buflen--; // Pre-reserve one space in the buffer for the terminating null | |
2735 | if (buflen == 0) goto exit; | |
2736 | ||
2737 | for (c = *fmt; c != 0; c = *++fmt) | |
2738 | { | |
2739 | if (c != '%') | |
2740 | { | |
2741 | *sbuffer++ = (char)c; | |
2742 | if (++nwritten >= buflen) goto exit; | |
2743 | } | |
2744 | else | |
2745 | { | |
2746 | unsigned int i=0, j; | |
2747 | // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for | |
2748 | // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc. | |
2749 | // The size needs to be enough for a 256-byte domain name plus some error text. | |
2750 | #define mDNS_VACB_Size 300 | |
2751 | char mDNS_VACB[mDNS_VACB_Size]; | |
2752 | #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size]) | |
2753 | #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s)) | |
2754 | char *s = mDNS_VACB_Lim, *digits; | |
2755 | struct mDNSprintf_format F = mDNSprintf_format_default; | |
2756 | ||
2757 | while (1) // decode flags | |
2758 | { | |
2759 | c = *++fmt; | |
2760 | if (c == '-') F.leftJustify = 1; | |
2761 | else if (c == '+') F.forceSign = 1; | |
2762 | else if (c == ' ') F.sign = ' '; | |
2763 | else if (c == '#') F.altForm++; | |
2764 | else if (c == '0') F.zeroPad = 1; | |
2765 | else break; | |
2766 | } | |
2767 | ||
2768 | if (c == '*') // decode field width | |
2769 | { | |
2770 | int f = va_arg(arg, int); | |
2771 | if (f < 0) { f = -f; F.leftJustify = 1; } | |
2772 | F.fieldWidth = (unsigned int)f; | |
2773 | c = *++fmt; | |
2774 | } | |
2775 | else | |
2776 | { | |
2777 | for (; c >= '0' && c <= '9'; c = *++fmt) | |
2778 | F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); | |
2779 | } | |
2780 | ||
2781 | if (c == '.') // decode precision | |
2782 | { | |
2783 | if ((c = *++fmt) == '*') | |
2784 | { F.precision = va_arg(arg, unsigned int); c = *++fmt; } | |
2785 | else for (; c >= '0' && c <= '9'; c = *++fmt) | |
2786 | F.precision = (10 * F.precision) + (c - '0'); | |
2787 | F.havePrecision = 1; | |
2788 | } | |
2789 | ||
2790 | if (F.leftJustify) F.zeroPad = 0; | |
2791 | ||
2792 | conv: | |
2793 | switch (c) // perform appropriate conversion | |
2794 | { | |
2795 | unsigned long n; | |
2796 | case 'h' : F.hSize = 1; c = *++fmt; goto conv; | |
2797 | case 'l' : // fall through | |
2798 | case 'L' : F.lSize = 1; c = *++fmt; goto conv; | |
2799 | case 'd' : | |
2800 | case 'i' : if (F.lSize) n = (unsigned long)va_arg(arg, long); | |
2801 | else n = (unsigned long)va_arg(arg, int); | |
2802 | if (F.hSize) n = (short) n; | |
2803 | if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; } | |
2804 | else if (F.forceSign) F.sign = '+'; | |
2805 | goto decimal; | |
2806 | case 'u' : if (F.lSize) n = va_arg(arg, unsigned long); | |
2807 | else n = va_arg(arg, unsigned int); | |
2808 | if (F.hSize) n = (unsigned short) n; | |
2809 | F.sign = 0; | |
2810 | goto decimal; | |
2811 | decimal: if (!F.havePrecision) | |
2812 | { | |
2813 | if (F.zeroPad) | |
2814 | { | |
2815 | F.precision = F.fieldWidth; | |
2816 | if (F.sign) --F.precision; | |
2817 | } | |
2818 | if (F.precision < 1) F.precision = 1; | |
2819 | } | |
2820 | if (F.precision > mDNS_VACB_Size - 1) | |
2821 | F.precision = mDNS_VACB_Size - 1; | |
2822 | for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0'); | |
2823 | for (; i < F.precision; i++) *--s = '0'; | |
2824 | if (F.sign) { *--s = F.sign; i++; } | |
2825 | break; | |
2826 | ||
2827 | case 'o' : if (F.lSize) n = va_arg(arg, unsigned long); | |
2828 | else n = va_arg(arg, unsigned int); | |
2829 | if (F.hSize) n = (unsigned short) n; | |
2830 | if (!F.havePrecision) | |
2831 | { | |
2832 | if (F.zeroPad) F.precision = F.fieldWidth; | |
2833 | if (F.precision < 1) F.precision = 1; | |
2834 | } | |
2835 | if (F.precision > mDNS_VACB_Size - 1) | |
2836 | F.precision = mDNS_VACB_Size - 1; | |
2837 | for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0'); | |
2838 | if (F.altForm && i && *s != '0') { *--s = '0'; i++; } | |
2839 | for (; i < F.precision; i++) *--s = '0'; | |
2840 | break; | |
2841 | ||
2842 | case 'a' : { | |
2843 | unsigned char *a = va_arg(arg, unsigned char *); | |
2844 | if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; } | |
2845 | else | |
2846 | { | |
2847 | s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end | |
2848 | if (F.altForm) | |
2849 | { | |
2850 | mDNSAddr *ip = (mDNSAddr*)a; | |
2851 | switch (ip->type) | |
2852 | { | |
2853 | case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break; | |
2854 | case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break; | |
2855 | default: F.precision = 0; break; | |
2856 | } | |
2857 | } | |
2858 | if (F.altForm && !F.precision) | |
2859 | i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "«ZERO ADDRESS»"); | |
2860 | else switch (F.precision) | |
2861 | { | |
2862 | case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d", | |
2863 | a[0], a[1], a[2], a[3]); break; | |
2864 | case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", | |
2865 | a[0], a[1], a[2], a[3], a[4], a[5]); break; | |
2866 | case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), | |
2867 | "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", | |
2868 | a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7], | |
2869 | a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break; | |
2870 | default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify" | |
2871 | " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break; | |
2872 | } | |
2873 | } | |
2874 | } | |
2875 | break; | |
2876 | ||
2877 | case 'p' : F.havePrecision = F.lSize = 1; | |
263eeeab | 2878 | F.precision = sizeof(void*) * 2; // 8 characters on 32-bit; 16 characters on 64-bit |
67c8f8a1 A |
2879 | case 'X' : digits = "0123456789ABCDEF"; |
2880 | goto hexadecimal; | |
2881 | case 'x' : digits = "0123456789abcdef"; | |
2882 | hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long); | |
2883 | else n = va_arg(arg, unsigned int); | |
2884 | if (F.hSize) n = (unsigned short) n; | |
2885 | if (!F.havePrecision) | |
2886 | { | |
2887 | if (F.zeroPad) | |
2888 | { | |
2889 | F.precision = F.fieldWidth; | |
2890 | if (F.altForm) F.precision -= 2; | |
2891 | } | |
2892 | if (F.precision < 1) F.precision = 1; | |
2893 | } | |
2894 | if (F.precision > mDNS_VACB_Size - 1) | |
2895 | F.precision = mDNS_VACB_Size - 1; | |
2896 | for (i = 0; n; n /= 16, i++) *--s = digits[n % 16]; | |
2897 | for (; i < F.precision; i++) *--s = '0'; | |
2898 | if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; } | |
2899 | break; | |
2900 | ||
2901 | case 'c' : *--s = (char)va_arg(arg, int); i = 1; break; | |
2902 | ||
2903 | case 's' : s = va_arg(arg, char *); | |
2904 | if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; } | |
2905 | else switch (F.altForm) | |
2906 | { | |
2907 | case 0: i=0; | |
2908 | if (!F.havePrecision) // C string | |
2909 | while (s[i]) i++; | |
2910 | else | |
2911 | { | |
2912 | while ((i < F.precision) && s[i]) i++; | |
2913 | // Make sure we don't truncate in the middle of a UTF-8 character | |
2914 | // If last character we got was any kind of UTF-8 multi-byte character, | |
2915 | // then see if we have to back up. | |
2916 | // This is not as easy as the similar checks below, because | |
2917 | // here we can't assume it's safe to examine the *next* byte, so we | |
2918 | // have to confine ourselves to working only backwards in the string. | |
2919 | j = i; // Record where we got to | |
2920 | // Now, back up until we find first non-continuation-char | |
2921 | while (i>0 && (s[i-1] & 0xC0) == 0x80) i--; | |
2922 | // Now s[i-1] is the first non-continuation-char | |
2923 | // and (j-i) is the number of continuation-chars we found | |
2924 | if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char | |
2925 | { | |
2926 | i--; // Tentatively eliminate this start-char as well | |
2927 | // Now (j-i) is the number of characters we're considering eliminating. | |
2928 | // To be legal UTF-8, the start-char must contain (j-i) one-bits, | |
2929 | // followed by a zero bit. If we shift it right by (7-(j-i)) bits | |
2930 | // (with sign extension) then the result has to be 0xFE. | |
2931 | // If this is right, then we reinstate the tentatively eliminated bytes. | |
2932 | if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j; | |
2933 | } | |
2934 | } | |
2935 | break; | |
2936 | case 1: i = (unsigned char) *s++; break; // Pascal string | |
2937 | case 2: { // DNS label-sequence name | |
2938 | unsigned char *a = (unsigned char *)s; | |
2939 | s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end | |
2940 | if (*a == 0) *s++ = '.'; // Special case for root DNS name | |
2941 | while (*a) | |
2942 | { | |
2943 | char buf[63*4+1]; | |
2944 | if (*a > 63) | |
2945 | { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; } | |
2946 | if (s + *a >= &mDNS_VACB[254]) | |
2947 | { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; } | |
2948 | // Need to use ConvertDomainLabelToCString to do proper escaping here, | |
2949 | // so it's clear what's a literal dot and what's a label separator | |
2950 | ConvertDomainLabelToCString((domainlabel*)a, buf); | |
2951 | s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf); | |
2952 | a += 1 + *a; | |
2953 | } | |
2954 | i = (mDNSu32)(s - mDNS_VACB); | |
2955 | s = mDNS_VACB; // Reset s back to the start of the buffer | |
2956 | break; | |
2957 | } | |
2958 | } | |
2959 | // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below) | |
2960 | if (F.havePrecision && i > F.precision) | |
2961 | { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } | |
2962 | break; | |
2963 | ||
2964 | case 'n' : s = va_arg(arg, char *); | |
2965 | if (F.hSize) * (short *) s = (short)nwritten; | |
2966 | else if (F.lSize) * (long *) s = (long)nwritten; | |
2967 | else * (int *) s = (int)nwritten; | |
2968 | continue; | |
2969 | ||
2970 | default: s = mDNS_VACB; | |
2971 | i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c); | |
2972 | ||
2973 | case '%' : *sbuffer++ = (char)c; | |
2974 | if (++nwritten >= buflen) goto exit; | |
2975 | break; | |
2976 | } | |
2977 | ||
2978 | if (i < F.fieldWidth && !F.leftJustify) // Pad on the left | |
2979 | do { | |
2980 | *sbuffer++ = ' '; | |
2981 | if (++nwritten >= buflen) goto exit; | |
2982 | } while (i < --F.fieldWidth); | |
2983 | ||
2984 | // Make sure we don't truncate in the middle of a UTF-8 character. | |
2985 | // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the | |
2986 | // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half, | |
2987 | // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly | |
2988 | // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated). | |
2989 | if (i > buflen - nwritten) | |
2990 | { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; } | |
2991 | for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result | |
2992 | nwritten += i; | |
2993 | if (nwritten >= buflen) goto exit; | |
2994 | ||
2995 | for (; i < F.fieldWidth; i++) // Pad on the right | |
2996 | { | |
2997 | *sbuffer++ = ' '; | |
2998 | if (++nwritten >= buflen) goto exit; | |
2999 | } | |
3000 | } | |
3001 | } | |
3002 | exit: | |
3003 | *sbuffer++ = 0; | |
3004 | return(nwritten); | |
3005 | } | |
3006 | ||
3007 | mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) | |
3008 | { | |
3009 | mDNSu32 length; | |
3010 | ||
3011 | va_list ptr; | |
3012 | va_start(ptr,fmt); | |
3013 | length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr); | |
3014 | va_end(ptr); | |
3015 | ||
3016 | return(length); | |
3017 | } |