14b22b933Srs200217 /* -*- Mode: C; tab-width: 4 -*-
24b22b933Srs200217 *
3*5ffb0c9bSToomas Soome * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved.
44b22b933Srs200217 *
54b22b933Srs200217 * Licensed under the Apache License, Version 2.0 (the "License");
64b22b933Srs200217 * you may not use this file except in compliance with the License.
74b22b933Srs200217 * You may obtain a copy of the License at
84b22b933Srs200217 *
94b22b933Srs200217 * http://www.apache.org/licenses/LICENSE-2.0
104b22b933Srs200217 *
114b22b933Srs200217 * Unless required by applicable law or agreed to in writing, software
124b22b933Srs200217 * distributed under the License is distributed on an "AS IS" BASIS,
134b22b933Srs200217 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
144b22b933Srs200217 * See the License for the specific language governing permissions and
154b22b933Srs200217 * limitations under the License.
164b22b933Srs200217 */
174b22b933Srs200217
184b22b933Srs200217 // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
194b22b933Srs200217 #define mDNS_InstantiateInlines 1
204b22b933Srs200217 #include "DNSCommon.h"
21*5ffb0c9bSToomas Soome #include "CryptoAlg.h"
22*5ffb0c9bSToomas Soome #include "anonymous.h"
234b22b933Srs200217
244b22b933Srs200217 // Disable certain benign warnings with Microsoft compilers
254b22b933Srs200217 #if (defined(_MSC_VER))
264b22b933Srs200217 // Disable "conditional expression is constant" warning for debug macros.
274b22b933Srs200217 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
284b22b933Srs200217 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
294b22b933Srs200217 #pragma warning(disable:4127)
304b22b933Srs200217 // Disable "array is too small to include a terminating null character" warning
314b22b933Srs200217 // -- domain labels have an initial length byte, not a terminating null character
324b22b933Srs200217 #pragma warning(disable:4295)
334b22b933Srs200217 #endif
344b22b933Srs200217
354b22b933Srs200217 // ***************************************************************************
364b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
37*5ffb0c9bSToomas Soome #pragma mark - Program Constants
384b22b933Srs200217 #endif
394b22b933Srs200217
40*5ffb0c9bSToomas Soome mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0;
41*5ffb0c9bSToomas Soome mDNSexport const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)-1;
42*5ffb0c9bSToomas Soome mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2;
43*5ffb0c9bSToomas Soome mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)-3;
44*5ffb0c9bSToomas Soome mDNSexport const mDNSInterfaceID mDNSInterface_P2P = (mDNSInterfaceID)-4;
45*5ffb0c9bSToomas Soome mDNSexport const mDNSInterfaceID uDNSInterfaceMark = (mDNSInterfaceID)-5;
464b22b933Srs200217
47*5ffb0c9bSToomas Soome // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
48*5ffb0c9bSToomas Soome // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
49*5ffb0c9bSToomas Soome // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
50*5ffb0c9bSToomas Soome // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
51*5ffb0c9bSToomas Soome // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
52*5ffb0c9bSToomas Soome // with Microsoft's LLMNR client code.
534b22b933Srs200217
54*5ffb0c9bSToomas Soome #define DiscardPortAsNumber 9
55*5ffb0c9bSToomas Soome #define SSHPortAsNumber 22
56*5ffb0c9bSToomas Soome #define UnicastDNSPortAsNumber 53
57*5ffb0c9bSToomas Soome #define SSDPPortAsNumber 1900
58*5ffb0c9bSToomas Soome #define IPSECPortAsNumber 4500
59*5ffb0c9bSToomas Soome #define NSIPCPortAsNumber 5030 // Port used for dnsextd to talk to local nameserver bound to loopback
60*5ffb0c9bSToomas Soome #define NATPMPAnnouncementPortAsNumber 5350
61*5ffb0c9bSToomas Soome #define NATPMPPortAsNumber 5351
62*5ffb0c9bSToomas Soome #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
63*5ffb0c9bSToomas Soome #define MulticastDNSPortAsNumber 5353
64*5ffb0c9bSToomas Soome #define LoopbackIPCPortAsNumber 5354
65*5ffb0c9bSToomas Soome //#define MulticastDNSPortAsNumber 5355 // LLMNR
66*5ffb0c9bSToomas Soome #define PrivateDNSPortAsNumber 5533
674b22b933Srs200217
68*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort DiscardPort = { { DiscardPortAsNumber >> 8, DiscardPortAsNumber & 0xFF } };
69*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort SSHPort = { { SSHPortAsNumber >> 8, SSHPortAsNumber & 0xFF } };
70*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 0xFF } };
71*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort SSDPPort = { { SSDPPortAsNumber >> 8, SSDPPortAsNumber & 0xFF } };
72*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort IPSECPort = { { IPSECPortAsNumber >> 8, IPSECPortAsNumber & 0xFF } };
73*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort NSIPCPort = { { NSIPCPortAsNumber >> 8, NSIPCPortAsNumber & 0xFF } };
74*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort NATPMPAnnouncementPort = { { NATPMPAnnouncementPortAsNumber >> 8, NATPMPAnnouncementPortAsNumber & 0xFF } };
75*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort NATPMPPort = { { NATPMPPortAsNumber >> 8, NATPMPPortAsNumber & 0xFF } };
76*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort DNSEXTPort = { { DNSEXTPortAsNumber >> 8, DNSEXTPortAsNumber & 0xFF } };
77*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort MulticastDNSPort = { { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF } };
78*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort LoopbackIPCPort = { { LoopbackIPCPortAsNumber >> 8, LoopbackIPCPortAsNumber & 0xFF } };
79*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort PrivateDNSPort = { { PrivateDNSPortAsNumber >> 8, PrivateDNSPortAsNumber & 0xFF } };
80*5ffb0c9bSToomas Soome
81*5ffb0c9bSToomas Soome mDNSexport const OwnerOptData zeroOwner = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } };
82*5ffb0c9bSToomas Soome
83*5ffb0c9bSToomas Soome mDNSexport const mDNSIPPort zeroIPPort = { { 0 } };
84*5ffb0c9bSToomas Soome mDNSexport const mDNSv4Addr zerov4Addr = { { 0 } };
85*5ffb0c9bSToomas Soome mDNSexport const mDNSv6Addr zerov6Addr = { { 0 } };
86*5ffb0c9bSToomas Soome mDNSexport const mDNSEthAddr zeroEthAddr = { { 0 } };
87*5ffb0c9bSToomas Soome mDNSexport const mDNSv4Addr onesIPv4Addr = { { 255, 255, 255, 255 } };
88*5ffb0c9bSToomas Soome mDNSexport const mDNSv6Addr onesIPv6Addr = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
89*5ffb0c9bSToomas Soome mDNSexport const mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255, 255 } };
90*5ffb0c9bSToomas Soome mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} };
91*5ffb0c9bSToomas Soome
92*5ffb0c9bSToomas Soome mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
93*5ffb0c9bSToomas Soome mDNSexport const mDNSv4Addr AllHosts_v4 = { { 224, 0, 0, 1 } }; // For NAT-PMP & PCP Annoucements
94*5ffb0c9bSToomas Soome mDNSexport const mDNSv6Addr AllHosts_v6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } };
95*5ffb0c9bSToomas Soome mDNSexport const mDNSv6Addr NDP_prefix = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01, 0xFF,0x00,0x00,0xFB } }; // FF02:0:0:0:0:1:FF00::/104
96*5ffb0c9bSToomas Soome mDNSexport const mDNSEthAddr AllHosts_v6_Eth = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } };
97*5ffb0c9bSToomas Soome mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } };
98*5ffb0c9bSToomas Soome //mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 252 } } } }; // LLMNR
99*5ffb0c9bSToomas Soome mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
100*5ffb0c9bSToomas Soome //mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
101*5ffb0c9bSToomas Soome
102*5ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 zeroID = { { 0, 0 } };
103*5ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 onesID = { { 255, 255 } };
104*5ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 QueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery, 0 } };
105*5ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 uQueryFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } };
106*5ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 DNSSecQFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, kDNSFlag1_CD } };
107*5ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
108*5ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } };
109*5ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } };
110*5ffb0c9bSToomas Soome
111*5ffb0c9bSToomas Soome mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } };
1124b22b933Srs200217
1134b22b933Srs200217 // ***************************************************************************
1144b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
1154b22b933Srs200217 #pragma mark -
1164b22b933Srs200217 #pragma mark - General Utility Functions
1174b22b933Srs200217 #endif
1184b22b933Srs200217
1194b22b933Srs200217 // return true for RFC1918 private addresses
mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr)120*5ffb0c9bSToomas Soome mDNSexport mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr)
1214b22b933Srs200217 {
122*5ffb0c9bSToomas Soome return ((addr->b[0] == 10) || // 10/8 prefix
123*5ffb0c9bSToomas Soome (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) || // 172.16/12
124*5ffb0c9bSToomas Soome (addr->b[0] == 192 && addr->b[1] == 168)); // 192.168/16
1254b22b933Srs200217 }
1264b22b933Srs200217
mDNSAddrMapIPv4toIPv6(mDNSv4Addr * in,mDNSv6Addr * out)127*5ffb0c9bSToomas Soome mDNSexport void mDNSAddrMapIPv4toIPv6(mDNSv4Addr* in, mDNSv6Addr* out)
128*5ffb0c9bSToomas Soome {
129*5ffb0c9bSToomas Soome out->l[0] = 0;
130*5ffb0c9bSToomas Soome out->l[1] = 0;
131*5ffb0c9bSToomas Soome out->w[4] = 0;
132*5ffb0c9bSToomas Soome out->w[5] = 0xffff;
133*5ffb0c9bSToomas Soome out->b[12] = in->b[0];
134*5ffb0c9bSToomas Soome out->b[13] = in->b[1];
135*5ffb0c9bSToomas Soome out->b[14] = in->b[2];
136*5ffb0c9bSToomas Soome out->b[15] = in->b[3];
137*5ffb0c9bSToomas Soome }
138*5ffb0c9bSToomas Soome
mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr * in,mDNSv4Addr * out)139*5ffb0c9bSToomas Soome mDNSexport mDNSBool mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr *in, mDNSv4Addr* out)
140*5ffb0c9bSToomas Soome {
141*5ffb0c9bSToomas Soome if (in->l[0] != 0 || in->l[1] != 0 || in->w[4] != 0 || in->w[5] != 0xffff)
142*5ffb0c9bSToomas Soome return mDNSfalse;
143*5ffb0c9bSToomas Soome
144*5ffb0c9bSToomas Soome out->NotAnInteger = in->l[3];
145*5ffb0c9bSToomas Soome return mDNStrue;
146*5ffb0c9bSToomas Soome }
147*5ffb0c9bSToomas Soome
GetFirstActiveInterface(NetworkInterfaceInfo * intf)148*5ffb0c9bSToomas Soome mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf)
1494b22b933Srs200217 {
1504b22b933Srs200217 while (intf && !intf->InterfaceActive) intf = intf->next;
1514b22b933Srs200217 return(intf);
1524b22b933Srs200217 }
1534b22b933Srs200217
GetNextActiveInterfaceID(const NetworkInterfaceInfo * intf)1544b22b933Srs200217 mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
1554b22b933Srs200217 {
1564b22b933Srs200217 const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
1574b22b933Srs200217 if (next) return(next->InterfaceID);else return(mDNSNULL);
1584b22b933Srs200217 }
1594b22b933Srs200217
NumCacheRecordsForInterfaceID(const mDNS * const m,mDNSInterfaceID id)1604b22b933Srs200217 mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id)
1614b22b933Srs200217 {
1624b22b933Srs200217 mDNSu32 slot, used = 0;
1634b22b933Srs200217 CacheGroup *cg;
164*5ffb0c9bSToomas Soome const CacheRecord *rr;
1654b22b933Srs200217 FORALL_CACHERECORDS(slot, cg, rr)
166*5ffb0c9bSToomas Soome {
167*5ffb0c9bSToomas Soome if (rr->resrec.InterfaceID == id)
168*5ffb0c9bSToomas Soome used++;
169*5ffb0c9bSToomas Soome }
1704b22b933Srs200217 return(used);
1714b22b933Srs200217 }
1724b22b933Srs200217
DNSTypeName(mDNSu16 rrtype)1734b22b933Srs200217 mDNSexport char *DNSTypeName(mDNSu16 rrtype)
1744b22b933Srs200217 {
1754b22b933Srs200217 switch (rrtype)
1764b22b933Srs200217 {
1774b22b933Srs200217 case kDNSType_A: return("Addr");
1784b22b933Srs200217 case kDNSType_NS: return("NS");
1794b22b933Srs200217 case kDNSType_CNAME: return("CNAME");
1804b22b933Srs200217 case kDNSType_SOA: return("SOA");
1814b22b933Srs200217 case kDNSType_NULL: return("NULL");
1824b22b933Srs200217 case kDNSType_PTR: return("PTR");
1834b22b933Srs200217 case kDNSType_HINFO: return("HINFO");
1844b22b933Srs200217 case kDNSType_TXT: return("TXT");
1854b22b933Srs200217 case kDNSType_AAAA: return("AAAA");
1864b22b933Srs200217 case kDNSType_SRV: return("SRV");
187*5ffb0c9bSToomas Soome case kDNSType_OPT: return("OPT");
188*5ffb0c9bSToomas Soome case kDNSType_NSEC: return("NSEC");
189*5ffb0c9bSToomas Soome case kDNSType_NSEC3: return("NSEC3");
190*5ffb0c9bSToomas Soome case kDNSType_NSEC3PARAM: return("NSEC3PARAM");
191*5ffb0c9bSToomas Soome case kDNSType_TSIG: return("TSIG");
192*5ffb0c9bSToomas Soome case kDNSType_RRSIG: return("RRSIG");
193*5ffb0c9bSToomas Soome case kDNSType_DNSKEY: return("DNSKEY");
194*5ffb0c9bSToomas Soome case kDNSType_DS: return("DS");
1954b22b933Srs200217 case kDNSQType_ANY: return("ANY");
1964b22b933Srs200217 default: {
1974b22b933Srs200217 static char buffer[16];
198*5ffb0c9bSToomas Soome mDNS_snprintf(buffer, sizeof(buffer), "TYPE%d", rrtype);
1994b22b933Srs200217 return(buffer);
2004b22b933Srs200217 }
2014b22b933Srs200217 }
2024b22b933Srs200217 }
2034b22b933Srs200217
DNSSECAlgName(mDNSu8 alg)204*5ffb0c9bSToomas Soome mDNSlocal char *DNSSECAlgName(mDNSu8 alg)
205*5ffb0c9bSToomas Soome {
206*5ffb0c9bSToomas Soome switch (alg)
207*5ffb0c9bSToomas Soome {
208*5ffb0c9bSToomas Soome case CRYPTO_RSA_SHA1: return "RSA_SHA1";
209*5ffb0c9bSToomas Soome case CRYPTO_DSA_NSEC3_SHA1: return "DSA_NSEC3_SHA1";
210*5ffb0c9bSToomas Soome case CRYPTO_RSA_NSEC3_SHA1: return "RSA_NSEC3_SHA1";
211*5ffb0c9bSToomas Soome case CRYPTO_RSA_SHA256: return "RSA_SHA256";
212*5ffb0c9bSToomas Soome case CRYPTO_RSA_SHA512: return "RSA_SHA512";
213*5ffb0c9bSToomas Soome default: {
214*5ffb0c9bSToomas Soome static char algbuffer[16];
215*5ffb0c9bSToomas Soome mDNS_snprintf(algbuffer, sizeof(algbuffer), "ALG%d", alg);
216*5ffb0c9bSToomas Soome return(algbuffer);
217*5ffb0c9bSToomas Soome }
218*5ffb0c9bSToomas Soome }
219*5ffb0c9bSToomas Soome }
220*5ffb0c9bSToomas Soome
DNSSECDigestName(mDNSu8 digest)221*5ffb0c9bSToomas Soome mDNSlocal char *DNSSECDigestName(mDNSu8 digest)
222*5ffb0c9bSToomas Soome {
223*5ffb0c9bSToomas Soome switch (digest)
224*5ffb0c9bSToomas Soome {
225*5ffb0c9bSToomas Soome case SHA1_DIGEST_TYPE: return "SHA1";
226*5ffb0c9bSToomas Soome case SHA256_DIGEST_TYPE: return "SHA256";
227*5ffb0c9bSToomas Soome default:
228*5ffb0c9bSToomas Soome {
229*5ffb0c9bSToomas Soome static char digbuffer[16];
230*5ffb0c9bSToomas Soome mDNS_snprintf(digbuffer, sizeof(digbuffer), "DIG%d", digest);
231*5ffb0c9bSToomas Soome return(digbuffer);
232*5ffb0c9bSToomas Soome }
233*5ffb0c9bSToomas Soome }
234*5ffb0c9bSToomas Soome }
235*5ffb0c9bSToomas Soome
swap32(mDNSu32 x)236*5ffb0c9bSToomas Soome mDNSexport mDNSu32 swap32(mDNSu32 x)
237*5ffb0c9bSToomas Soome {
238*5ffb0c9bSToomas Soome mDNSu8 *ptr = (mDNSu8 *)&x;
239*5ffb0c9bSToomas Soome return (mDNSu32)((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
240*5ffb0c9bSToomas Soome }
241*5ffb0c9bSToomas Soome
swap16(mDNSu16 x)242*5ffb0c9bSToomas Soome mDNSexport mDNSu16 swap16(mDNSu16 x)
243*5ffb0c9bSToomas Soome {
244*5ffb0c9bSToomas Soome mDNSu8 *ptr = (mDNSu8 *)&x;
245*5ffb0c9bSToomas Soome return (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
246*5ffb0c9bSToomas Soome }
247*5ffb0c9bSToomas Soome
248*5ffb0c9bSToomas Soome // RFC 4034 Appendix B: Get the keyid of a DNS KEY. It is not transmitted
249*5ffb0c9bSToomas Soome // explicitly on the wire.
250*5ffb0c9bSToomas Soome //
251*5ffb0c9bSToomas Soome // Note: This just helps narrow down the list of keys to look at. It is possible
252*5ffb0c9bSToomas Soome // for two DNS keys to have the same ID i.e., key ID is not a unqiue tag. We ignore
253*5ffb0c9bSToomas Soome // MD5 keys.
254*5ffb0c9bSToomas Soome //
255*5ffb0c9bSToomas Soome // 1st argument - the RDATA part of the DNSKEY RR
256*5ffb0c9bSToomas Soome // 2nd argument - the RDLENGTH
257*5ffb0c9bSToomas Soome //
keytag(mDNSu8 * key,mDNSu32 keysize)258*5ffb0c9bSToomas Soome mDNSlocal mDNSu32 keytag(mDNSu8 *key, mDNSu32 keysize)
259*5ffb0c9bSToomas Soome {
260*5ffb0c9bSToomas Soome unsigned long ac;
261*5ffb0c9bSToomas Soome unsigned int i;
262*5ffb0c9bSToomas Soome
263*5ffb0c9bSToomas Soome for (ac = 0, i = 0; i < keysize; ++i)
264*5ffb0c9bSToomas Soome ac += (i & 1) ? key[i] : key[i] << 8;
265*5ffb0c9bSToomas Soome ac += (ac >> 16) & 0xFFFF;
266*5ffb0c9bSToomas Soome return ac & 0xFFFF;
267*5ffb0c9bSToomas Soome }
268*5ffb0c9bSToomas Soome
baseEncode(char * buffer,int blen,const mDNSu8 * data,int len,int encAlg)269*5ffb0c9bSToomas Soome mDNSexport int baseEncode(char *buffer, int blen, const mDNSu8 *data, int len, int encAlg)
270*5ffb0c9bSToomas Soome {
271*5ffb0c9bSToomas Soome AlgContext *ctx;
272*5ffb0c9bSToomas Soome mDNSu8 *outputBuffer;
273*5ffb0c9bSToomas Soome int length;
274*5ffb0c9bSToomas Soome
275*5ffb0c9bSToomas Soome ctx = AlgCreate(ENC_ALG, encAlg);
276*5ffb0c9bSToomas Soome if (!ctx)
277*5ffb0c9bSToomas Soome {
278*5ffb0c9bSToomas Soome LogMsg("baseEncode: AlgCreate failed\n");
279*5ffb0c9bSToomas Soome return 0;
280*5ffb0c9bSToomas Soome }
281*5ffb0c9bSToomas Soome AlgAdd(ctx, data, len);
282*5ffb0c9bSToomas Soome outputBuffer = AlgEncode(ctx);
283*5ffb0c9bSToomas Soome length = 0;
284*5ffb0c9bSToomas Soome if (outputBuffer)
285*5ffb0c9bSToomas Soome {
286*5ffb0c9bSToomas Soome // Note: don't include any spaces in the format string below. This
287*5ffb0c9bSToomas Soome // is also used by NSEC3 code for proving non-existence where it
288*5ffb0c9bSToomas Soome // needs the base32 encoding without any spaces etc.
289*5ffb0c9bSToomas Soome length = mDNS_snprintf(buffer, blen, "%s", outputBuffer);
290*5ffb0c9bSToomas Soome }
291*5ffb0c9bSToomas Soome AlgDestroy(ctx);
292*5ffb0c9bSToomas Soome return length;
293*5ffb0c9bSToomas Soome }
294*5ffb0c9bSToomas Soome
PrintTypeBitmap(const mDNSu8 * bmap,int bitmaplen,char * const buffer,mDNSu32 length)295*5ffb0c9bSToomas Soome mDNSlocal void PrintTypeBitmap(const mDNSu8 *bmap, int bitmaplen, char *const buffer, mDNSu32 length)
296*5ffb0c9bSToomas Soome {
297*5ffb0c9bSToomas Soome int win, wlen, type;
298*5ffb0c9bSToomas Soome
299*5ffb0c9bSToomas Soome while (bitmaplen > 0)
300*5ffb0c9bSToomas Soome {
301*5ffb0c9bSToomas Soome int i;
302*5ffb0c9bSToomas Soome
303*5ffb0c9bSToomas Soome if (bitmaplen < 3)
304*5ffb0c9bSToomas Soome {
305*5ffb0c9bSToomas Soome LogMsg("PrintTypeBitmap: malformed bitmap, bitmaplen %d short", bitmaplen);
306*5ffb0c9bSToomas Soome break;
307*5ffb0c9bSToomas Soome }
308*5ffb0c9bSToomas Soome
309*5ffb0c9bSToomas Soome win = *bmap++;
310*5ffb0c9bSToomas Soome wlen = *bmap++;
311*5ffb0c9bSToomas Soome bitmaplen -= 2;
312*5ffb0c9bSToomas Soome if (bitmaplen < wlen || wlen < 1 || wlen > 32)
313*5ffb0c9bSToomas Soome {
314*5ffb0c9bSToomas Soome LogInfo("PrintTypeBitmap: malformed nsec, bitmaplen %d wlen %d", bitmaplen, wlen);
315*5ffb0c9bSToomas Soome break;
316*5ffb0c9bSToomas Soome }
317*5ffb0c9bSToomas Soome if (win < 0 || win >= 256)
318*5ffb0c9bSToomas Soome {
319*5ffb0c9bSToomas Soome LogInfo("PrintTypeBitmap: malformed nsec, bad window win %d", win);
320*5ffb0c9bSToomas Soome break;
321*5ffb0c9bSToomas Soome }
322*5ffb0c9bSToomas Soome type = win * 256;
323*5ffb0c9bSToomas Soome for (i = 0; i < wlen * 8; i++)
324*5ffb0c9bSToomas Soome {
325*5ffb0c9bSToomas Soome if (bmap[i>>3] & (128 >> (i&7)))
326*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, (MaxMsg - 1) - length, "%s ", DNSTypeName(type + i));
327*5ffb0c9bSToomas Soome }
328*5ffb0c9bSToomas Soome bmap += wlen;
329*5ffb0c9bSToomas Soome bitmaplen -= wlen;
330*5ffb0c9bSToomas Soome }
331*5ffb0c9bSToomas Soome }
332*5ffb0c9bSToomas Soome
333*5ffb0c9bSToomas Soome // Parse the fields beyond the base header. NSEC3 should have been validated.
NSEC3Parse(const ResourceRecord * const rr,mDNSu8 ** salt,int * hashLength,mDNSu8 ** nxtName,int * bitmaplen,mDNSu8 ** bitmap)334*5ffb0c9bSToomas Soome mDNSexport void NSEC3Parse(const ResourceRecord *const rr, mDNSu8 **salt, int *hashLength, mDNSu8 **nxtName, int *bitmaplen, mDNSu8 **bitmap)
335*5ffb0c9bSToomas Soome {
336*5ffb0c9bSToomas Soome const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
337*5ffb0c9bSToomas Soome rdataNSEC3 *nsec3 = (rdataNSEC3 *)rdb->data;
338*5ffb0c9bSToomas Soome mDNSu8 *p = (mDNSu8 *)&nsec3->salt;
339*5ffb0c9bSToomas Soome int hlen;
340*5ffb0c9bSToomas Soome
341*5ffb0c9bSToomas Soome if (salt)
342*5ffb0c9bSToomas Soome {
343*5ffb0c9bSToomas Soome if (nsec3->saltLength)
344*5ffb0c9bSToomas Soome *salt = p;
345*5ffb0c9bSToomas Soome else
346*5ffb0c9bSToomas Soome *salt = mDNSNULL;
347*5ffb0c9bSToomas Soome }
348*5ffb0c9bSToomas Soome p += nsec3->saltLength;
349*5ffb0c9bSToomas Soome // p is pointing at hashLength
350*5ffb0c9bSToomas Soome hlen = (int)*p;
351*5ffb0c9bSToomas Soome if (hashLength)
352*5ffb0c9bSToomas Soome *hashLength = hlen;
353*5ffb0c9bSToomas Soome p++;
354*5ffb0c9bSToomas Soome if (nxtName)
355*5ffb0c9bSToomas Soome *nxtName = p;
356*5ffb0c9bSToomas Soome p += hlen;
357*5ffb0c9bSToomas Soome if (bitmaplen)
358*5ffb0c9bSToomas Soome *bitmaplen = rr->rdlength - (int)(p - rdb->data);
359*5ffb0c9bSToomas Soome if (bitmap)
360*5ffb0c9bSToomas Soome *bitmap = p;
361*5ffb0c9bSToomas Soome }
362*5ffb0c9bSToomas Soome
3634b22b933Srs200217 // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
3644b22b933Srs200217 // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
3654b22b933Srs200217 // long as this routine is only used for debugging messages, it probably isn't a big problem.
GetRRDisplayString_rdb(const ResourceRecord * const rr,const RDataBody * const rd1,char * const buffer)366*5ffb0c9bSToomas Soome mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer)
3674b22b933Srs200217 {
368*5ffb0c9bSToomas Soome const RDataBody2 *const rd = (RDataBody2 *)rd1;
369*5ffb0c9bSToomas Soome #define RemSpc (MaxMsg-1-length)
3704b22b933Srs200217 char *ptr = buffer;
371*5ffb0c9bSToomas Soome mDNSu32 length = mDNS_snprintf(buffer, MaxMsg-1, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype));
372*5ffb0c9bSToomas Soome if (rr->RecordType == kDNSRecordTypePacketNegative) return(buffer);
373*5ffb0c9bSToomas Soome if (!rr->rdlength && rr->rrtype != kDNSType_OPT) { mDNS_snprintf(buffer+length, RemSpc, "<< ZERO RDATA LENGTH >>"); return(buffer); }
374*5ffb0c9bSToomas Soome
3754b22b933Srs200217 switch (rr->rrtype)
3764b22b933Srs200217 {
377*5ffb0c9bSToomas Soome case kDNSType_A: mDNS_snprintf(buffer+length, RemSpc, "%.4a", &rd->ipv4); break;
3784b22b933Srs200217
3794b22b933Srs200217 case kDNSType_NS: // Same as PTR
3804b22b933Srs200217 case kDNSType_CNAME: // Same as PTR
381*5ffb0c9bSToomas Soome case kDNSType_PTR: mDNS_snprintf(buffer+length, RemSpc, "%##s", rd->name.c); break;
3824b22b933Srs200217
383*5ffb0c9bSToomas Soome case kDNSType_SOA: mDNS_snprintf(buffer+length, RemSpc, "%##s %##s %d %d %d %d %d",
384*5ffb0c9bSToomas Soome rd->soa.mname.c, rd->soa.rname.c,
385*5ffb0c9bSToomas Soome rd->soa.serial, rd->soa.refresh, rd->soa.retry, rd->soa.expire, rd->soa.min);
386*5ffb0c9bSToomas Soome break;
3874b22b933Srs200217
388*5ffb0c9bSToomas Soome case kDNSType_HINFO: // Display this the same as TXT (show all constituent strings)
389*5ffb0c9bSToomas Soome case kDNSType_TXT: {
390*5ffb0c9bSToomas Soome const mDNSu8 *t = rd->txt.c;
391*5ffb0c9bSToomas Soome while (t < rd->txt.c + rr->rdlength)
392*5ffb0c9bSToomas Soome {
393*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, "%s%#s", t > rd->txt.c ? "¦" : "", t);
394*5ffb0c9bSToomas Soome t += 1 + t[0];
3954b22b933Srs200217 }
396*5ffb0c9bSToomas Soome } break;
397*5ffb0c9bSToomas Soome
398*5ffb0c9bSToomas Soome case kDNSType_AAAA: mDNS_snprintf(buffer+length, RemSpc, "%.16a", &rd->ipv6); break;
399*5ffb0c9bSToomas Soome case kDNSType_SRV: mDNS_snprintf(buffer+length, RemSpc, "%u %u %u %##s",
400*5ffb0c9bSToomas Soome rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break;
401*5ffb0c9bSToomas Soome
402*5ffb0c9bSToomas Soome case kDNSType_OPT: {
403*5ffb0c9bSToomas Soome const rdataOPT *opt;
404*5ffb0c9bSToomas Soome const rdataOPT *const end = (const rdataOPT *)&rd->data[rr->rdlength];
405*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, "Max %d", rr->rrclass);
406*5ffb0c9bSToomas Soome for (opt = &rd->opt[0]; opt < end; opt++)
407*5ffb0c9bSToomas Soome {
408*5ffb0c9bSToomas Soome switch(opt->opt)
409*5ffb0c9bSToomas Soome {
410*5ffb0c9bSToomas Soome case kDNSOpt_LLQ:
411*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " LLQ");
412*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.llq.vers);
413*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Op %d", opt->u.llq.llqOp);
414*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Err/Port %d", opt->u.llq.err);
415*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " ID %08X%08X", opt->u.llq.id.l[0], opt->u.llq.id.l[1]);
416*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.llq.llqlease);
417*5ffb0c9bSToomas Soome break;
418*5ffb0c9bSToomas Soome case kDNSOpt_Lease:
419*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d", opt->u.updatelease);
420*5ffb0c9bSToomas Soome break;
421*5ffb0c9bSToomas Soome case kDNSOpt_Owner:
422*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Owner");
423*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d", opt->u.owner.vers);
424*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Seq %3d", (mDNSu8)opt->u.owner.seq); // Display as unsigned
425*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " MAC %.6a", opt->u.owner.HMAC.b);
426*5ffb0c9bSToomas Soome if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
427*5ffb0c9bSToomas Soome {
428*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " I-MAC %.6a", opt->u.owner.IMAC.b);
429*5ffb0c9bSToomas Soome if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
430*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b);
431*5ffb0c9bSToomas Soome }
432*5ffb0c9bSToomas Soome break;
433*5ffb0c9bSToomas Soome case kDNSOpt_Trace:
434*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Trace");
435*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Platform %d", opt->u.tracer.platf);
436*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " mDNSVers %d", opt->u.tracer.mDNSv);
437*5ffb0c9bSToomas Soome break;
438*5ffb0c9bSToomas Soome default:
439*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d", opt->opt);
440*5ffb0c9bSToomas Soome break;
441*5ffb0c9bSToomas Soome }
442*5ffb0c9bSToomas Soome }
443*5ffb0c9bSToomas Soome }
444*5ffb0c9bSToomas Soome break;
445*5ffb0c9bSToomas Soome
446*5ffb0c9bSToomas Soome case kDNSType_NSEC: {
447*5ffb0c9bSToomas Soome domainname *next = (domainname *)rd->data;
448*5ffb0c9bSToomas Soome int len, bitmaplen;
449*5ffb0c9bSToomas Soome mDNSu8 *bmap;
450*5ffb0c9bSToomas Soome len = DomainNameLength(next);
451*5ffb0c9bSToomas Soome bitmaplen = rr->rdlength - len;
452*5ffb0c9bSToomas Soome bmap = (mDNSu8 *)((mDNSu8 *)next + len);
453*5ffb0c9bSToomas Soome
454*5ffb0c9bSToomas Soome if (UNICAST_NSEC(rr))
455*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, "%##s ", next->c);
456*5ffb0c9bSToomas Soome PrintTypeBitmap(bmap, bitmaplen, buffer, length);
457*5ffb0c9bSToomas Soome
458*5ffb0c9bSToomas Soome }
459*5ffb0c9bSToomas Soome break;
460*5ffb0c9bSToomas Soome case kDNSType_NSEC3: {
461*5ffb0c9bSToomas Soome rdataNSEC3 *nsec3 = (rdataNSEC3 *)rd->data;
462*5ffb0c9bSToomas Soome const mDNSu8 *p = (mDNSu8 *)&nsec3->salt;
463*5ffb0c9bSToomas Soome int hashLength, bitmaplen, i;
464*5ffb0c9bSToomas Soome
465*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %d %d ",
466*5ffb0c9bSToomas Soome DNSSECDigestName(nsec3->alg), nsec3->flags, swap16(nsec3->iterations));
467*5ffb0c9bSToomas Soome
468*5ffb0c9bSToomas Soome if (!nsec3->saltLength)
469*5ffb0c9bSToomas Soome {
470*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, "-");
471*5ffb0c9bSToomas Soome }
472*5ffb0c9bSToomas Soome else
473*5ffb0c9bSToomas Soome {
474*5ffb0c9bSToomas Soome for (i = 0; i < nsec3->saltLength; i++)
475*5ffb0c9bSToomas Soome {
476*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, "%x", p[i]);
477*5ffb0c9bSToomas Soome }
478*5ffb0c9bSToomas Soome }
479*5ffb0c9bSToomas Soome
480*5ffb0c9bSToomas Soome // put a space at the end
481*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " ");
482*5ffb0c9bSToomas Soome
483*5ffb0c9bSToomas Soome p += nsec3->saltLength;
484*5ffb0c9bSToomas Soome // p is pointing at hashLength
485*5ffb0c9bSToomas Soome hashLength = (int)*p++;
486*5ffb0c9bSToomas Soome
487*5ffb0c9bSToomas Soome length += baseEncode(buffer + length, RemSpc, p, hashLength, ENC_BASE32);
488*5ffb0c9bSToomas Soome
489*5ffb0c9bSToomas Soome // put a space at the end
490*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, " ");
491*5ffb0c9bSToomas Soome
492*5ffb0c9bSToomas Soome p += hashLength;
493*5ffb0c9bSToomas Soome bitmaplen = rr->rdlength - (int)(p - rd->data);
494*5ffb0c9bSToomas Soome PrintTypeBitmap(p, bitmaplen, buffer, length);
495*5ffb0c9bSToomas Soome }
496*5ffb0c9bSToomas Soome break;
497*5ffb0c9bSToomas Soome case kDNSType_RRSIG: {
498*5ffb0c9bSToomas Soome rdataRRSig *rrsig = (rdataRRSig *)rd->data;
499*5ffb0c9bSToomas Soome mDNSu8 expTimeBuf[64];
500*5ffb0c9bSToomas Soome mDNSu8 inceptTimeBuf[64];
501*5ffb0c9bSToomas Soome unsigned long inceptClock;
502*5ffb0c9bSToomas Soome unsigned long expClock;
503*5ffb0c9bSToomas Soome int len;
504*5ffb0c9bSToomas Soome
505*5ffb0c9bSToomas Soome expClock = (unsigned long)swap32(rrsig->sigExpireTime);
506*5ffb0c9bSToomas Soome mDNSPlatformFormatTime(expClock, expTimeBuf, sizeof(expTimeBuf));
507*5ffb0c9bSToomas Soome
508*5ffb0c9bSToomas Soome inceptClock = (unsigned long)swap32(rrsig->sigInceptTime);
509*5ffb0c9bSToomas Soome mDNSPlatformFormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf));
510*5ffb0c9bSToomas Soome
511*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %s %d %d %s %s %d %##s ",
512*5ffb0c9bSToomas Soome DNSTypeName(swap16(rrsig->typeCovered)), DNSSECAlgName(rrsig->alg), rrsig->labels, swap32(rrsig->origTTL),
513*5ffb0c9bSToomas Soome expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag), ((domainname *)(&rrsig->signerName))->c);
514*5ffb0c9bSToomas Soome
515*5ffb0c9bSToomas Soome len = DomainNameLength((domainname *)&rrsig->signerName);
516*5ffb0c9bSToomas Soome length += baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + len + RRSIG_FIXED_SIZE),
517*5ffb0c9bSToomas Soome rr->rdlength - (len + RRSIG_FIXED_SIZE), ENC_BASE64);
518*5ffb0c9bSToomas Soome }
519*5ffb0c9bSToomas Soome break;
520*5ffb0c9bSToomas Soome case kDNSType_DNSKEY: {
521*5ffb0c9bSToomas Soome rdataDNSKey *rrkey = (rdataDNSKey *)rd->data;
522*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, "\t%d %d %s %u ", swap16(rrkey->flags), rrkey->proto,
523*5ffb0c9bSToomas Soome DNSSECAlgName(rrkey->alg), (unsigned int)keytag((mDNSu8 *)rrkey, rr->rdlength));
524*5ffb0c9bSToomas Soome length += baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + DNSKEY_FIXED_SIZE),
525*5ffb0c9bSToomas Soome rr->rdlength - DNSKEY_FIXED_SIZE, ENC_BASE64);
526*5ffb0c9bSToomas Soome }
527*5ffb0c9bSToomas Soome break;
528*5ffb0c9bSToomas Soome case kDNSType_DS: {
529*5ffb0c9bSToomas Soome mDNSu8 *p;
530*5ffb0c9bSToomas Soome int i;
531*5ffb0c9bSToomas Soome rdataDS *rrds = (rdataDS *)rd->data;
532*5ffb0c9bSToomas Soome
533*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, "\t%s\t%d\t%s ", DNSSECAlgName(rrds->alg), swap16(rrds->keyTag),
534*5ffb0c9bSToomas Soome DNSSECDigestName(rrds->digestType));
535*5ffb0c9bSToomas Soome
536*5ffb0c9bSToomas Soome p = (mDNSu8 *)(rd->data + DS_FIXED_SIZE);
537*5ffb0c9bSToomas Soome for (i = 0; i < (rr->rdlength - DS_FIXED_SIZE); i++)
538*5ffb0c9bSToomas Soome {
539*5ffb0c9bSToomas Soome length += mDNS_snprintf(buffer+length, RemSpc, "%x", p[i]);
540*5ffb0c9bSToomas Soome }
541*5ffb0c9bSToomas Soome }
542*5ffb0c9bSToomas Soome break;
543*5ffb0c9bSToomas Soome
544*5ffb0c9bSToomas Soome default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %s", rr->rdlength, rd->data);
545*5ffb0c9bSToomas Soome // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
5464b22b933Srs200217 for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.';
547*5ffb0c9bSToomas Soome break;
548*5ffb0c9bSToomas Soome }
5494b22b933Srs200217 return(buffer);
5504b22b933Srs200217 }
5514b22b933Srs200217
552*5ffb0c9bSToomas Soome // See comments in mDNSEmbeddedAPI.h
553*5ffb0c9bSToomas Soome #if _PLATFORM_HAS_STRONG_PRNG_
554*5ffb0c9bSToomas Soome #define mDNSRandomNumber mDNSPlatformRandomNumber
555*5ffb0c9bSToomas Soome #else
mDNSRandomFromSeed(mDNSu32 seed)556*5ffb0c9bSToomas Soome mDNSlocal mDNSu32 mDNSRandomFromSeed(mDNSu32 seed)
5574b22b933Srs200217 {
558*5ffb0c9bSToomas Soome return seed * 21 + 1;
559*5ffb0c9bSToomas Soome }
560*5ffb0c9bSToomas Soome
mDNSMixRandomSeed(mDNSu32 seed,mDNSu8 iteration)561*5ffb0c9bSToomas Soome mDNSlocal mDNSu32 mDNSMixRandomSeed(mDNSu32 seed, mDNSu8 iteration)
562*5ffb0c9bSToomas Soome {
563*5ffb0c9bSToomas Soome return iteration ? mDNSMixRandomSeed(mDNSRandomFromSeed(seed), --iteration) : seed;
564*5ffb0c9bSToomas Soome }
565*5ffb0c9bSToomas Soome
mDNSRandomNumber()566*5ffb0c9bSToomas Soome mDNSlocal mDNSu32 mDNSRandomNumber()
567*5ffb0c9bSToomas Soome {
568*5ffb0c9bSToomas Soome static mDNSBool seeded = mDNSfalse;
5694b22b933Srs200217 static mDNSu32 seed = 0;
570*5ffb0c9bSToomas Soome if (!seeded)
571*5ffb0c9bSToomas Soome {
572*5ffb0c9bSToomas Soome seed = mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100);
573*5ffb0c9bSToomas Soome seeded = mDNStrue;
574*5ffb0c9bSToomas Soome }
575*5ffb0c9bSToomas Soome return (seed = mDNSRandomFromSeed(seed));
576*5ffb0c9bSToomas Soome }
577*5ffb0c9bSToomas Soome #endif // ! _PLATFORM_HAS_STRONG_PRNG_
578*5ffb0c9bSToomas Soome
mDNSRandom(mDNSu32 max)579*5ffb0c9bSToomas Soome mDNSexport mDNSu32 mDNSRandom(mDNSu32 max) // Returns pseudo-random result from zero to max inclusive
580*5ffb0c9bSToomas Soome {
581*5ffb0c9bSToomas Soome mDNSu32 ret = 0;
5824b22b933Srs200217 mDNSu32 mask = 1;
5834b22b933Srs200217
5844b22b933Srs200217 while (mask < max) mask = (mask << 1) | 1;
5854b22b933Srs200217
586*5ffb0c9bSToomas Soome do ret = mDNSRandomNumber() & mask;
587*5ffb0c9bSToomas Soome while (ret > max);
588*5ffb0c9bSToomas Soome
589*5ffb0c9bSToomas Soome return ret;
5904b22b933Srs200217 }
5914b22b933Srs200217
mDNSSameAddress(const mDNSAddr * ip1,const mDNSAddr * ip2)5924b22b933Srs200217 mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
5934b22b933Srs200217 {
5944b22b933Srs200217 if (ip1->type == ip2->type)
5954b22b933Srs200217 {
5964b22b933Srs200217 switch (ip1->type)
5974b22b933Srs200217 {
5984b22b933Srs200217 case mDNSAddrType_None: return(mDNStrue); // Empty addresses have no data and are therefore always equal
5994b22b933Srs200217 case mDNSAddrType_IPv4: return (mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4));
6004b22b933Srs200217 case mDNSAddrType_IPv6: return (mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6));
6014b22b933Srs200217 }
6024b22b933Srs200217 }
6034b22b933Srs200217 return(mDNSfalse);
6044b22b933Srs200217 }
6054b22b933Srs200217
mDNSAddrIsDNSMulticast(const mDNSAddr * ip)6064b22b933Srs200217 mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
6074b22b933Srs200217 {
6084b22b933Srs200217 switch(ip->type)
6094b22b933Srs200217 {
610*5ffb0c9bSToomas Soome case mDNSAddrType_IPv4: return (mDNSBool)(mDNSSameIPv4Address(ip->ip.v4, AllDNSLinkGroup_v4.ip.v4));
611*5ffb0c9bSToomas Soome case mDNSAddrType_IPv6: return (mDNSBool)(mDNSSameIPv6Address(ip->ip.v6, AllDNSLinkGroup_v6.ip.v6));
6124b22b933Srs200217 default: return(mDNSfalse);
6134b22b933Srs200217 }
6144b22b933Srs200217 }
6154b22b933Srs200217
6164b22b933Srs200217 // ***************************************************************************
6174b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
6184b22b933Srs200217 #pragma mark -
6194b22b933Srs200217 #pragma mark - Domain Name Utility Functions
6204b22b933Srs200217 #endif
6214b22b933Srs200217
SameDomainLabel(const mDNSu8 * a,const mDNSu8 * b)6224b22b933Srs200217 mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
6234b22b933Srs200217 {
6244b22b933Srs200217 int i;
6254b22b933Srs200217 const int len = *a++;
6264b22b933Srs200217
6274b22b933Srs200217 if (len > MAX_DOMAIN_LABEL)
6284b22b933Srs200217 { debugf("Malformed label (too long)"); return(mDNSfalse); }
6294b22b933Srs200217
6304b22b933Srs200217 if (len != *b++) return(mDNSfalse);
6314b22b933Srs200217 for (i=0; i<len; i++)
6324b22b933Srs200217 {
6334b22b933Srs200217 mDNSu8 ac = *a++;
6344b22b933Srs200217 mDNSu8 bc = *b++;
6354b22b933Srs200217 if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
6364b22b933Srs200217 if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
6374b22b933Srs200217 if (ac != bc) return(mDNSfalse);
6384b22b933Srs200217 }
6394b22b933Srs200217 return(mDNStrue);
6404b22b933Srs200217 }
6414b22b933Srs200217
SameDomainName(const domainname * const d1,const domainname * const d2)6424b22b933Srs200217 mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
6434b22b933Srs200217 {
6444b22b933Srs200217 const mDNSu8 * a = d1->c;
6454b22b933Srs200217 const mDNSu8 * b = d2->c;
6464b22b933Srs200217 const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME; // Maximum that's valid
6474b22b933Srs200217
6484b22b933Srs200217 while (*a || *b)
6494b22b933Srs200217 {
6504b22b933Srs200217 if (a + 1 + *a >= max)
651*5ffb0c9bSToomas Soome { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse); }
6524b22b933Srs200217 if (!SameDomainLabel(a, b)) return(mDNSfalse);
6534b22b933Srs200217 a += 1 + *a;
6544b22b933Srs200217 b += 1 + *b;
6554b22b933Srs200217 }
6564b22b933Srs200217
6574b22b933Srs200217 return(mDNStrue);
6584b22b933Srs200217 }
6594b22b933Srs200217
SameDomainNameCS(const domainname * const d1,const domainname * const d2)660*5ffb0c9bSToomas Soome mDNSexport mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2)
661*5ffb0c9bSToomas Soome {
662*5ffb0c9bSToomas Soome mDNSu16 l1 = DomainNameLength(d1);
663*5ffb0c9bSToomas Soome mDNSu16 l2 = DomainNameLength(d2);
664*5ffb0c9bSToomas Soome return(l1 <= MAX_DOMAIN_NAME && l1 == l2 && mDNSPlatformMemSame(d1, d2, l1));
665*5ffb0c9bSToomas Soome }
666*5ffb0c9bSToomas Soome
IsLocalDomain(const domainname * d)6674b22b933Srs200217 mDNSexport mDNSBool IsLocalDomain(const domainname *d)
6684b22b933Srs200217 {
6694b22b933Srs200217 // Domains that are defined to be resolved via link-local multicast are:
6704b22b933Srs200217 // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
671*5ffb0c9bSToomas Soome static const domainname *nL = (const domainname*)"\x5" "local";
672*5ffb0c9bSToomas Soome static const domainname *nR = (const domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
673*5ffb0c9bSToomas Soome static const domainname *n8 = (const domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
674*5ffb0c9bSToomas Soome static const domainname *n9 = (const domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
675*5ffb0c9bSToomas Soome static const domainname *nA = (const domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
676*5ffb0c9bSToomas Soome static const domainname *nB = (const domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
6774b22b933Srs200217
678*5ffb0c9bSToomas Soome const domainname *d1, *d2, *d3, *d4, *d5; // Top-level domain, second-level domain, etc.
679*5ffb0c9bSToomas Soome d1 = d2 = d3 = d4 = d5 = mDNSNULL;
6804b22b933Srs200217 while (d->c[0])
6814b22b933Srs200217 {
682*5ffb0c9bSToomas Soome d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
683*5ffb0c9bSToomas Soome d = (const domainname*)(d->c + 1 + d->c[0]);
6844b22b933Srs200217 }
6854b22b933Srs200217
6864b22b933Srs200217 if (d1 && SameDomainName(d1, nL)) return(mDNStrue);
6874b22b933Srs200217 if (d4 && SameDomainName(d4, nR)) return(mDNStrue);
688*5ffb0c9bSToomas Soome if (d5 && SameDomainName(d5, n8)) return(mDNStrue);
689*5ffb0c9bSToomas Soome if (d5 && SameDomainName(d5, n9)) return(mDNStrue);
690*5ffb0c9bSToomas Soome if (d5 && SameDomainName(d5, nA)) return(mDNStrue);
691*5ffb0c9bSToomas Soome if (d5 && SameDomainName(d5, nB)) return(mDNStrue);
6924b22b933Srs200217 return(mDNSfalse);
6934b22b933Srs200217 }
6944b22b933Srs200217
LastLabel(const domainname * d)695*5ffb0c9bSToomas Soome mDNSexport const mDNSu8 *LastLabel(const domainname *d)
696*5ffb0c9bSToomas Soome {
697*5ffb0c9bSToomas Soome const mDNSu8 *p = d->c;
698*5ffb0c9bSToomas Soome while (d->c[0])
699*5ffb0c9bSToomas Soome {
700*5ffb0c9bSToomas Soome p = d->c;
701*5ffb0c9bSToomas Soome d = (const domainname*)(d->c + 1 + d->c[0]);
702*5ffb0c9bSToomas Soome }
703*5ffb0c9bSToomas Soome return(p);
704*5ffb0c9bSToomas Soome }
705*5ffb0c9bSToomas Soome
7064b22b933Srs200217 // Returns length of a domain name INCLUDING the byte for the final null label
707*5ffb0c9bSToomas Soome // e.g. for the root label "." it returns one
7084b22b933Srs200217 // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
709*5ffb0c9bSToomas Soome // Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME)
710*5ffb0c9bSToomas Soome // If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1)
DomainNameLengthLimit(const domainname * const name,const mDNSu8 * limit)711*5ffb0c9bSToomas Soome mDNSexport mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit)
7124b22b933Srs200217 {
7134b22b933Srs200217 const mDNSu8 *src = name->c;
714*5ffb0c9bSToomas Soome while (src < limit && *src <= MAX_DOMAIN_LABEL)
7154b22b933Srs200217 {
716*5ffb0c9bSToomas Soome if (*src == 0) return((mDNSu16)(src - name->c + 1));
7174b22b933Srs200217 src += 1 + *src;
7184b22b933Srs200217 }
719*5ffb0c9bSToomas Soome return(MAX_DOMAIN_NAME+1);
7204b22b933Srs200217 }
7214b22b933Srs200217
7224b22b933Srs200217 // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
723*5ffb0c9bSToomas Soome // for the final null label, e.g. for the root label "." it returns one.
7244b22b933Srs200217 // E.g. for the FQDN "foo.com." it returns 9
7254b22b933Srs200217 // (length, three data bytes, length, three more data bytes, final zero).
7264b22b933Srs200217 // In the case where a parent domain name is provided, and the given name is a child
7274b22b933Srs200217 // of that parent, CompressedDomainNameLength returns the length of the prefix portion
7284b22b933Srs200217 // of the child name, plus TWO bytes for the compression pointer.
7294b22b933Srs200217 // E.g. for the name "foo.com." with parent "com.", it returns 6
7304b22b933Srs200217 // (length, three data bytes, two-byte compression pointer).
CompressedDomainNameLength(const domainname * const name,const domainname * parent)7314b22b933Srs200217 mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
7324b22b933Srs200217 {
7334b22b933Srs200217 const mDNSu8 *src = name->c;
7344b22b933Srs200217 if (parent && parent->c[0] == 0) parent = mDNSNULL;
7354b22b933Srs200217 while (*src)
7364b22b933Srs200217 {
7374b22b933Srs200217 if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
738*5ffb0c9bSToomas Soome if (parent && SameDomainName((const domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
7394b22b933Srs200217 src += 1 + *src;
7404b22b933Srs200217 if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
7414b22b933Srs200217 }
7424b22b933Srs200217 return((mDNSu16)(src - name->c + 1));
7434b22b933Srs200217 }
7444b22b933Srs200217
745*5ffb0c9bSToomas Soome // CountLabels() returns number of labels in name, excluding final root label
746*5ffb0c9bSToomas Soome // (e.g. for "apple.com." CountLabels returns 2.)
CountLabels(const domainname * d)747*5ffb0c9bSToomas Soome mDNSexport int CountLabels(const domainname *d)
748*5ffb0c9bSToomas Soome {
749*5ffb0c9bSToomas Soome int count = 0;
750*5ffb0c9bSToomas Soome const mDNSu8 *ptr;
751*5ffb0c9bSToomas Soome for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++;
752*5ffb0c9bSToomas Soome return count;
753*5ffb0c9bSToomas Soome }
754*5ffb0c9bSToomas Soome
755*5ffb0c9bSToomas Soome // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
756*5ffb0c9bSToomas Soome // returning a pointer to the suffix with 'skip' labels removed.
SkipLeadingLabels(const domainname * d,int skip)757*5ffb0c9bSToomas Soome mDNSexport const domainname *SkipLeadingLabels(const domainname *d, int skip)
758*5ffb0c9bSToomas Soome {
759*5ffb0c9bSToomas Soome while (skip > 0 && d->c[0]) { d = (const domainname *)(d->c + 1 + d->c[0]); skip--; }
760*5ffb0c9bSToomas Soome return(d);
761*5ffb0c9bSToomas Soome }
762*5ffb0c9bSToomas Soome
7634b22b933Srs200217 // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
7644b22b933Srs200217 // The C string contains the label as-is, with no escaping, etc.
7654b22b933Srs200217 // Any dots in the name are literal dots, not label separators
7664b22b933Srs200217 // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
767*5ffb0c9bSToomas Soome // in the domainname bufer (i.e. the next byte after the terminating zero).
768*5ffb0c9bSToomas Soome // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
7694b22b933Srs200217 // AppendLiteralLabelString returns mDNSNULL.
AppendLiteralLabelString(domainname * const name,const char * cstr)7704b22b933Srs200217 mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr)
7714b22b933Srs200217 {
7724b22b933Srs200217 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
7734b22b933Srs200217 const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
7744b22b933Srs200217 const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL;
7754b22b933Srs200217 const mDNSu8 *const lim = (lim1 < lim2) ? lim1 : lim2;
7764b22b933Srs200217 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
7774b22b933Srs200217
7784b22b933Srs200217 while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++; // Copy the data
7794b22b933Srs200217 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
7804b22b933Srs200217 *ptr++ = 0; // Put the null root label on the end
7814b22b933Srs200217 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
7824b22b933Srs200217 else return(ptr); // Success: return new value of ptr
7834b22b933Srs200217 }
7844b22b933Srs200217
7854b22b933Srs200217 // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
7864b22b933Srs200217 // The C string is in conventional DNS syntax:
7874b22b933Srs200217 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
7884b22b933Srs200217 // If successful, AppendDNSNameString returns a pointer to the next unused byte
789*5ffb0c9bSToomas Soome // in the domainname bufer (i.e. the next byte after the terminating zero).
790*5ffb0c9bSToomas Soome // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
7914b22b933Srs200217 // AppendDNSNameString returns mDNSNULL.
AppendDNSNameString(domainname * const name,const char * cstring)7924b22b933Srs200217 mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring)
7934b22b933Srs200217 {
7944b22b933Srs200217 const char *cstr = cstring;
7954b22b933Srs200217 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
7964b22b933Srs200217 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
7974b22b933Srs200217 while (*cstr && ptr < lim) // While more characters, and space to put them...
7984b22b933Srs200217 {
7994b22b933Srs200217 mDNSu8 *lengthbyte = ptr++; // Record where the length is going to go
8004b22b933Srs200217 if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); }
8014b22b933Srs200217 while (*cstr && *cstr != '.' && ptr < lim) // While we have characters in the label...
8024b22b933Srs200217 {
8034b22b933Srs200217 mDNSu8 c = (mDNSu8)*cstr++; // Read the character
8044b22b933Srs200217 if (c == '\\') // If escape character, check next character
8054b22b933Srs200217 {
8064b22b933Srs200217 c = (mDNSu8)*cstr++; // Assume we'll just take the next character
807*5ffb0c9bSToomas Soome if (mDNSIsDigit(cstr[-1]) && mDNSIsDigit(cstr[0]) && mDNSIsDigit(cstr[1]))
8084b22b933Srs200217 { // If three decimal digits,
8094b22b933Srs200217 int v0 = cstr[-1] - '0'; // then interpret as three-digit decimal
8104b22b933Srs200217 int v1 = cstr[ 0] - '0';
8114b22b933Srs200217 int v2 = cstr[ 1] - '0';
8124b22b933Srs200217 int val = v0 * 100 + v1 * 10 + v2;
8134b22b933Srs200217 if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it
8144b22b933Srs200217 }
8154b22b933Srs200217 }
8164b22b933Srs200217 *ptr++ = c; // Write the character
8174b22b933Srs200217 }
8184b22b933Srs200217 if (*cstr) cstr++; // Skip over the trailing dot (if present)
8194b22b933Srs200217 if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort
8204b22b933Srs200217 return(mDNSNULL);
8214b22b933Srs200217 *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte
8224b22b933Srs200217 }
8234b22b933Srs200217
8244b22b933Srs200217 *ptr++ = 0; // Put the null root label on the end
8254b22b933Srs200217 if (*cstr) return(mDNSNULL); // Failure: We didn't successfully consume all input
8264b22b933Srs200217 else return(ptr); // Success: return new value of ptr
8274b22b933Srs200217 }
8284b22b933Srs200217
8294b22b933Srs200217 // AppendDomainLabel appends a single label to a name.
8304b22b933Srs200217 // If successful, AppendDomainLabel returns a pointer to the next unused byte
831*5ffb0c9bSToomas Soome // in the domainname bufer (i.e. the next byte after the terminating zero).
832*5ffb0c9bSToomas Soome // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
8334b22b933Srs200217 // AppendDomainLabel returns mDNSNULL.
AppendDomainLabel(domainname * const name,const domainlabel * const label)8344b22b933Srs200217 mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label)
8354b22b933Srs200217 {
8364b22b933Srs200217 int i;
8374b22b933Srs200217 mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
8384b22b933Srs200217
8394b22b933Srs200217 // Check label is legal
8404b22b933Srs200217 if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
8414b22b933Srs200217
8424b22b933Srs200217 // Check that ptr + length byte + data bytes + final zero does not exceed our limit
8434b22b933Srs200217 if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL);
8444b22b933Srs200217
8454b22b933Srs200217 for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i]; // Copy the label data
8464b22b933Srs200217 *ptr++ = 0; // Put the null root label on the end
8474b22b933Srs200217 return(ptr);
8484b22b933Srs200217 }
8494b22b933Srs200217
AppendDomainName(domainname * const name,const domainname * const append)8504b22b933Srs200217 mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
8514b22b933Srs200217 {
8524b22b933Srs200217 mDNSu8 * ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
8534b22b933Srs200217 const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1; // Limit of how much we can add (not counting final zero)
8544b22b933Srs200217 const mDNSu8 * src = append->c;
8554b22b933Srs200217 while (src[0])
8564b22b933Srs200217 {
8574b22b933Srs200217 int i;
8584b22b933Srs200217 if (ptr + 1 + src[0] > lim) return(mDNSNULL);
8594b22b933Srs200217 for (i=0; i<=src[0]; i++) *ptr++ = src[i];
8604b22b933Srs200217 *ptr = 0; // Put the null root label on the end
8614b22b933Srs200217 src += i;
8624b22b933Srs200217 }
8634b22b933Srs200217 return(ptr);
8644b22b933Srs200217 }
8654b22b933Srs200217
8664b22b933Srs200217 // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
8674b22b933Srs200217 // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
8684b22b933Srs200217 // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
8694b22b933Srs200217 // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
8704b22b933Srs200217 // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
8714b22b933Srs200217 // In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
MakeDomainLabelFromLiteralString(domainlabel * const label,const char * cstr)8724b22b933Srs200217 mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr)
8734b22b933Srs200217 {
8744b22b933Srs200217 mDNSu8 * ptr = label->c + 1; // Where we're putting it
8754b22b933Srs200217 const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL; // The maximum we can put
8764b22b933Srs200217 while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++; // Copy the label
8774b22b933Srs200217 label->c[0] = (mDNSu8)(ptr - label->c - 1); // Set the length byte
8784b22b933Srs200217 return(*cstr == 0); // Return mDNStrue if we successfully consumed all input
8794b22b933Srs200217 }
8804b22b933Srs200217
8814b22b933Srs200217 // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
8824b22b933Srs200217 // The C string is in conventional DNS syntax:
8834b22b933Srs200217 // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
8844b22b933Srs200217 // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
885*5ffb0c9bSToomas Soome // in the domainname bufer (i.e. the next byte after the terminating zero).
886*5ffb0c9bSToomas Soome // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
8874b22b933Srs200217 // MakeDomainNameFromDNSNameString returns mDNSNULL.
MakeDomainNameFromDNSNameString(domainname * const name,const char * cstr)8884b22b933Srs200217 mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr)
8894b22b933Srs200217 {
8904b22b933Srs200217 name->c[0] = 0; // Make an empty domain name
8914b22b933Srs200217 return(AppendDNSNameString(name, cstr)); // And then add this string to it
8924b22b933Srs200217 }
8934b22b933Srs200217
ConvertDomainLabelToCString_withescape(const domainlabel * const label,char * ptr,char esc)8944b22b933Srs200217 mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
8954b22b933Srs200217 {
8964b22b933Srs200217 const mDNSu8 * src = label->c; // Domain label we're reading
8974b22b933Srs200217 const mDNSu8 len = *src++; // Read length of this (non-null) label
8984b22b933Srs200217 const mDNSu8 *const end = src + len; // Work out where the label ends
8994b22b933Srs200217 if (len > MAX_DOMAIN_LABEL) return(mDNSNULL); // If illegal label, abort
9004b22b933Srs200217 while (src < end) // While we have characters in the label
9014b22b933Srs200217 {
9024b22b933Srs200217 mDNSu8 c = *src++;
9034b22b933Srs200217 if (esc)
9044b22b933Srs200217 {
9054b22b933Srs200217 if (c == '.' || c == esc) // If character is a dot or the escape character
9064b22b933Srs200217 *ptr++ = esc; // Output escape character
9074b22b933Srs200217 else if (c <= ' ') // If non-printing ascii,
9084b22b933Srs200217 { // Output decimal escape sequence
9094b22b933Srs200217 *ptr++ = esc;
9104b22b933Srs200217 *ptr++ = (char) ('0' + (c / 100) );
9114b22b933Srs200217 *ptr++ = (char) ('0' + (c / 10) % 10);
9124b22b933Srs200217 c = (mDNSu8)('0' + (c ) % 10);
9134b22b933Srs200217 }
9144b22b933Srs200217 }
9154b22b933Srs200217 *ptr++ = (char)c; // Copy the character
9164b22b933Srs200217 }
9174b22b933Srs200217 *ptr = 0; // Null-terminate the string
9184b22b933Srs200217 return(ptr); // and return
9194b22b933Srs200217 }
9204b22b933Srs200217
921*5ffb0c9bSToomas Soome // Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1009 bytes)
ConvertDomainNameToCString_withescape(const domainname * const name,char * ptr,char esc)9224b22b933Srs200217 mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
9234b22b933Srs200217 {
9244b22b933Srs200217 const mDNSu8 *src = name->c; // Domain name we're reading
9254b22b933Srs200217 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
9264b22b933Srs200217
9274b22b933Srs200217 if (*src == 0) *ptr++ = '.'; // Special case: For root, just write a dot
9284b22b933Srs200217
9294b22b933Srs200217 while (*src) // While more characters in the domain name
9304b22b933Srs200217 {
9314b22b933Srs200217 if (src + 1 + *src >= max) return(mDNSNULL);
9324b22b933Srs200217 ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
9334b22b933Srs200217 if (!ptr) return(mDNSNULL);
9344b22b933Srs200217 src += 1 + *src;
9354b22b933Srs200217 *ptr++ = '.'; // Write the dot after the label
9364b22b933Srs200217 }
9374b22b933Srs200217
9384b22b933Srs200217 *ptr++ = 0; // Null-terminate the string
9394b22b933Srs200217 return(ptr); // and return
9404b22b933Srs200217 }
9414b22b933Srs200217
9424b22b933Srs200217 // RFC 1034 rules:
9434b22b933Srs200217 // Host names must start with a letter, end with a letter or digit,
9444b22b933Srs200217 // and have as interior characters only letters, digits, and hyphen.
9454b22b933Srs200217 // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
9464b22b933Srs200217
ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[],domainlabel * const hostlabel)9474b22b933Srs200217 mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
9484b22b933Srs200217 {
9494b22b933Srs200217 const mDNSu8 * src = &UTF8Name[1];
9504b22b933Srs200217 const mDNSu8 *const end = &UTF8Name[1] + UTF8Name[0];
9514b22b933Srs200217 mDNSu8 * ptr = &hostlabel->c[1];
9524b22b933Srs200217 const mDNSu8 *const lim = &hostlabel->c[1] + MAX_DOMAIN_LABEL;
9534b22b933Srs200217 while (src < end)
9544b22b933Srs200217 {
9554b22b933Srs200217 // Delete apostrophes from source name
9564b22b933Srs200217 if (src[0] == '\'') { src++; continue; } // Standard straight single quote
9574b22b933Srs200217 if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99)
9584b22b933Srs200217 { src += 3; continue; } // Unicode curly apostrophe
9594b22b933Srs200217 if (ptr < lim)
9604b22b933Srs200217 {
961*5ffb0c9bSToomas Soome if (mDNSValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
9624b22b933Srs200217 else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
9634b22b933Srs200217 }
9644b22b933Srs200217 src++;
9654b22b933Srs200217 }
9664b22b933Srs200217 while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks
9674b22b933Srs200217 hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
9684b22b933Srs200217 }
9694b22b933Srs200217
970*5ffb0c9bSToomas Soome #define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
971*5ffb0c9bSToomas Soome ((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
972*5ffb0c9bSToomas Soome ((X)[4] | 0x20) == 'p')
973*5ffb0c9bSToomas Soome
ConstructServiceName(domainname * const fqdn,const domainlabel * name,const domainname * type,const domainname * const domain)9744b22b933Srs200217 mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
9754b22b933Srs200217 const domainlabel *name, const domainname *type, const domainname *const domain)
9764b22b933Srs200217 {
9774b22b933Srs200217 int i, len;
9784b22b933Srs200217 mDNSu8 *dst = fqdn->c;
9794b22b933Srs200217 const mDNSu8 *src;
9804b22b933Srs200217 const char *errormsg;
981*5ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder
982*5ffb0c9bSToomas Soome mDNSBool loggedUnderscore = mDNSfalse;
983*5ffb0c9bSToomas Soome static char typeBuf[MAX_ESCAPED_DOMAIN_NAME];
984*5ffb0c9bSToomas Soome #endif
9854b22b933Srs200217
9864b22b933Srs200217 // In the case where there is no name (and ONLY in that case),
9874b22b933Srs200217 // a single-label subtype is allowed as the first label of a three-part "type"
9884b22b933Srs200217 if (!name && type)
9894b22b933Srs200217 {
9904b22b933Srs200217 const mDNSu8 *s0 = type->c;
9914b22b933Srs200217 if (s0[0] && s0[0] < 0x40) // If legal first label (at least one character, and no more than 63)
9924b22b933Srs200217 {
9934b22b933Srs200217 const mDNSu8 * s1 = s0 + 1 + s0[0];
9944b22b933Srs200217 if (s1[0] && s1[0] < 0x40) // and legal second label (at least one character, and no more than 63)
9954b22b933Srs200217 {
9964b22b933Srs200217 const mDNSu8 *s2 = s1 + 1 + s1[0];
9974b22b933Srs200217 if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels
9984b22b933Srs200217 {
999*5ffb0c9bSToomas Soome static const mDNSu8 SubTypeLabel[5] = mDNSSubTypeLabel;
10004b22b933Srs200217 src = s0; // Copy the first label
10014b22b933Srs200217 len = *src;
10024b22b933Srs200217 for (i=0; i <= len; i++) *dst++ = *src++;
10034b22b933Srs200217 for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i];
1004*5ffb0c9bSToomas Soome type = (const domainname *)s1;
10054b22b933Srs200217
1006*5ffb0c9bSToomas Soome // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
10074b22b933Srs200217 // For these queries, we retract the "._sub" we just added between the subtype and the main type
1008*5ffb0c9bSToomas Soome // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
1009*5ffb0c9bSToomas Soome if (SameDomainName((domainname*)s0, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
10104b22b933Srs200217 dst -= sizeof(SubTypeLabel);
10114b22b933Srs200217 }
10124b22b933Srs200217 }
10134b22b933Srs200217 }
10144b22b933Srs200217 }
10154b22b933Srs200217
10164b22b933Srs200217 if (name && name->c[0])
10174b22b933Srs200217 {
10184b22b933Srs200217 src = name->c; // Put the service name into the domain name
10194b22b933Srs200217 len = *src;
10204b22b933Srs200217 if (len >= 0x40) { errormsg = "Service instance name too long"; goto fail; }
10214b22b933Srs200217 for (i=0; i<=len; i++) *dst++ = *src++;
10224b22b933Srs200217 }
10234b22b933Srs200217 else
10244b22b933Srs200217 name = (domainlabel*)""; // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
10254b22b933Srs200217
10264b22b933Srs200217 src = type->c; // Put the service type into the domain name
10274b22b933Srs200217 len = *src;
1028*5ffb0c9bSToomas Soome if (len < 2 || len > 16)
10294b22b933Srs200217 {
1030*5ffb0c9bSToomas Soome LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-15 characters. "
1031*5ffb0c9bSToomas Soome "See <http://www.dns-sd.org/ServiceTypes.html>", name->c, type->c, domain->c);
1032*5ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder
1033*5ffb0c9bSToomas Soome ConvertDomainNameToCString(type, typeBuf);
1034*5ffb0c9bSToomas Soome mDNSASLLog(mDNSNULL, "serviceType.nameTooLong", "noop", typeBuf, "");
1035*5ffb0c9bSToomas Soome #endif
10364b22b933Srs200217 }
1037*5ffb0c9bSToomas Soome if (len < 2 || len >= 0x40 || (len > 16 && !SameDomainName(domain, &localdomain))) return(mDNSNULL);
10384b22b933Srs200217 if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; }
10394b22b933Srs200217 for (i=2; i<=len; i++)
1040*5ffb0c9bSToomas Soome {
1041*5ffb0c9bSToomas Soome // Letters and digits are allowed anywhere
1042*5ffb0c9bSToomas Soome if (mDNSIsLetter(src[i]) || mDNSIsDigit(src[i])) continue;
1043*5ffb0c9bSToomas Soome // Hyphens are only allowed as interior characters
1044*5ffb0c9bSToomas Soome // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
1045*5ffb0c9bSToomas Soome // with the same rule as hyphens
1046*5ffb0c9bSToomas Soome if ((src[i] == '-' || src[i] == '_') && i > 2 && i < len)
1047*5ffb0c9bSToomas Soome {
1048*5ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder
1049*5ffb0c9bSToomas Soome if (src[i] == '_' && loggedUnderscore == mDNSfalse)
1050*5ffb0c9bSToomas Soome {
1051*5ffb0c9bSToomas Soome ConvertDomainNameToCString(type, typeBuf);
1052*5ffb0c9bSToomas Soome mDNSASLLog(mDNSNULL, "serviceType.nameWithUnderscore", "noop", typeBuf, "");
1053*5ffb0c9bSToomas Soome loggedUnderscore = mDNStrue;
1054*5ffb0c9bSToomas Soome }
1055*5ffb0c9bSToomas Soome #endif
1056*5ffb0c9bSToomas Soome continue;
1057*5ffb0c9bSToomas Soome }
1058*5ffb0c9bSToomas Soome errormsg = "Application protocol name must contain only letters, digits, and hyphens";
1059*5ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder
1060*5ffb0c9bSToomas Soome {
1061*5ffb0c9bSToomas Soome ConvertDomainNameToCString(type, typeBuf);
1062*5ffb0c9bSToomas Soome mDNSASLLog(mDNSNULL, "serviceType.nameWithIllegalCharacters", "noop", typeBuf, "");
1063*5ffb0c9bSToomas Soome }
1064*5ffb0c9bSToomas Soome #endif
1065*5ffb0c9bSToomas Soome goto fail;
1066*5ffb0c9bSToomas Soome }
10674b22b933Srs200217 for (i=0; i<=len; i++) *dst++ = *src++;
10684b22b933Srs200217
10694b22b933Srs200217 len = *src;
1070*5ffb0c9bSToomas Soome if (!ValidTransportProtocol(src)) { errormsg = "Transport protocol name must be _udp or _tcp"; goto fail; }
10714b22b933Srs200217 for (i=0; i<=len; i++) *dst++ = *src++;
10724b22b933Srs200217
10734b22b933Srs200217 if (*src) { errormsg = "Service type must have only two labels"; goto fail; }
10744b22b933Srs200217
10754b22b933Srs200217 *dst = 0;
10764b22b933Srs200217 if (!domain->c[0]) { errormsg = "Service domain must be non-empty"; goto fail; }
1077*5ffb0c9bSToomas Soome if (SameDomainName(domain, (const domainname*)"\x05" "local" "\x04" "arpa"))
10784b22b933Srs200217 { errormsg = "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; }
10794b22b933Srs200217 dst = AppendDomainName(fqdn, domain);
10804b22b933Srs200217 if (!dst) { errormsg = "Service domain too long"; goto fail; }
10814b22b933Srs200217 return(dst);
10824b22b933Srs200217
10834b22b933Srs200217 fail:
10844b22b933Srs200217 LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
10854b22b933Srs200217 return(mDNSNULL);
10864b22b933Srs200217 }
10874b22b933Srs200217
10884b22b933Srs200217 // A service name has the form: instance.application-protocol.transport-protocol.domain
10894b22b933Srs200217 // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
10904b22b933Srs200217 // set or length limits for the protocol names, and the final domain is allowed to be empty.
10914b22b933Srs200217 // However, if the given FQDN doesn't contain at least three labels,
10924b22b933Srs200217 // DeconstructServiceName will reject it and return mDNSfalse.
DeconstructServiceName(const domainname * const fqdn,domainlabel * const name,domainname * const type,domainname * const domain)10934b22b933Srs200217 mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
10944b22b933Srs200217 domainlabel *const name, domainname *const type, domainname *const domain)
10954b22b933Srs200217 {
10964b22b933Srs200217 int i, len;
10974b22b933Srs200217 const mDNSu8 *src = fqdn->c;
10984b22b933Srs200217 const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
10994b22b933Srs200217 mDNSu8 *dst;
11004b22b933Srs200217
11014b22b933Srs200217 dst = name->c; // Extract the service name
11024b22b933Srs200217 len = *src;
11034b22b933Srs200217 if (!len) { debugf("DeconstructServiceName: FQDN empty!"); return(mDNSfalse); }
11044b22b933Srs200217 if (len >= 0x40) { debugf("DeconstructServiceName: Instance name too long"); return(mDNSfalse); }
11054b22b933Srs200217 for (i=0; i<=len; i++) *dst++ = *src++;
11064b22b933Srs200217
11074b22b933Srs200217 dst = type->c; // Extract the service type
11084b22b933Srs200217 len = *src;
11094b22b933Srs200217 if (!len) { debugf("DeconstructServiceName: FQDN contains only one label!"); return(mDNSfalse); }
11104b22b933Srs200217 if (len >= 0x40) { debugf("DeconstructServiceName: Application protocol name too long"); return(mDNSfalse); }
1111*5ffb0c9bSToomas Soome if (src[1] != '_') { debugf("DeconstructServiceName: No _ at start of application protocol"); return(mDNSfalse); }
11124b22b933Srs200217 for (i=0; i<=len; i++) *dst++ = *src++;
11134b22b933Srs200217
11144b22b933Srs200217 len = *src;
11154b22b933Srs200217 if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); }
1116*5ffb0c9bSToomas Soome if (!ValidTransportProtocol(src))
1117*5ffb0c9bSToomas Soome { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); }
11184b22b933Srs200217 for (i=0; i<=len; i++) *dst++ = *src++;
11194b22b933Srs200217 *dst++ = 0; // Put terminator on the end of service type
11204b22b933Srs200217
11214b22b933Srs200217 dst = domain->c; // Extract the service domain
11224b22b933Srs200217 while (*src)
11234b22b933Srs200217 {
11244b22b933Srs200217 len = *src;
11254b22b933Srs200217 if (len >= 0x40)
11264b22b933Srs200217 { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); }
11274b22b933Srs200217 if (src + 1 + len + 1 >= max)
11284b22b933Srs200217 { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); }
11294b22b933Srs200217 for (i=0; i<=len; i++) *dst++ = *src++;
11304b22b933Srs200217 }
11314b22b933Srs200217 *dst++ = 0; // Put the null root label on the end
11324b22b933Srs200217
11334b22b933Srs200217 return(mDNStrue);
11344b22b933Srs200217 }
11354b22b933Srs200217
DNSNameToLowerCase(domainname * d,domainname * result)1136*5ffb0c9bSToomas Soome mDNSexport mStatus DNSNameToLowerCase(domainname *d, domainname *result)
1137*5ffb0c9bSToomas Soome {
1138*5ffb0c9bSToomas Soome const mDNSu8 *a = d->c;
1139*5ffb0c9bSToomas Soome mDNSu8 *b = result->c;
1140*5ffb0c9bSToomas Soome const mDNSu8 *const max = d->c + MAX_DOMAIN_NAME;
1141*5ffb0c9bSToomas Soome int i, len;
1142*5ffb0c9bSToomas Soome
1143*5ffb0c9bSToomas Soome while (*a)
1144*5ffb0c9bSToomas Soome {
1145*5ffb0c9bSToomas Soome if (a + 1 + *a >= max)
1146*5ffb0c9bSToomas Soome {
1147*5ffb0c9bSToomas Soome LogMsg("DNSNameToLowerCase: ERROR!! Malformed Domain name");
1148*5ffb0c9bSToomas Soome return mStatus_BadParamErr;
1149*5ffb0c9bSToomas Soome }
1150*5ffb0c9bSToomas Soome len = *a++;
1151*5ffb0c9bSToomas Soome *b++ = len;
1152*5ffb0c9bSToomas Soome for (i = 0; i < len; i++)
1153*5ffb0c9bSToomas Soome {
1154*5ffb0c9bSToomas Soome mDNSu8 ac = *a++;
1155*5ffb0c9bSToomas Soome if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
1156*5ffb0c9bSToomas Soome *b++ = ac;
1157*5ffb0c9bSToomas Soome }
1158*5ffb0c9bSToomas Soome }
1159*5ffb0c9bSToomas Soome *b = 0;
1160*5ffb0c9bSToomas Soome
1161*5ffb0c9bSToomas Soome return mStatus_NoError;
1162*5ffb0c9bSToomas Soome }
1163*5ffb0c9bSToomas Soome
NSEC3HashName(const domainname * name,rdataNSEC3 * nsec3,const mDNSu8 * AnonData,int AnonDataLen,const mDNSu8 hash[NSEC3_MAX_HASH_LEN],int * dlen)1164*5ffb0c9bSToomas Soome mDNSexport const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3, const mDNSu8 *AnonData, int AnonDataLen,
1165*5ffb0c9bSToomas Soome const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen)
1166*5ffb0c9bSToomas Soome {
1167*5ffb0c9bSToomas Soome AlgContext *ctx;
1168*5ffb0c9bSToomas Soome int i;
1169*5ffb0c9bSToomas Soome domainname lname;
1170*5ffb0c9bSToomas Soome mDNSu8 *p = (mDNSu8 *)&nsec3->salt;
1171*5ffb0c9bSToomas Soome const mDNSu8 *digest;
1172*5ffb0c9bSToomas Soome int digestlen;
1173*5ffb0c9bSToomas Soome mDNSBool first = mDNStrue;
1174*5ffb0c9bSToomas Soome
1175*5ffb0c9bSToomas Soome if (DNSNameToLowerCase((domainname *)name, &lname) != mStatus_NoError)
1176*5ffb0c9bSToomas Soome {
1177*5ffb0c9bSToomas Soome LogMsg("NSEC3HashName: ERROR!! DNSNameToLowerCase failed");
1178*5ffb0c9bSToomas Soome return mDNSNULL;
1179*5ffb0c9bSToomas Soome }
1180*5ffb0c9bSToomas Soome
1181*5ffb0c9bSToomas Soome digest = lname.c;
1182*5ffb0c9bSToomas Soome digestlen = DomainNameLength(&lname);
1183*5ffb0c9bSToomas Soome
1184*5ffb0c9bSToomas Soome // Note that it is "i <=". The first iteration is for digesting the name and salt.
1185*5ffb0c9bSToomas Soome // The iteration count does not include that.
1186*5ffb0c9bSToomas Soome for (i = 0; i <= swap16(nsec3->iterations); i++)
1187*5ffb0c9bSToomas Soome {
1188*5ffb0c9bSToomas Soome ctx = AlgCreate(DIGEST_ALG, nsec3->alg);
1189*5ffb0c9bSToomas Soome if (!ctx)
1190*5ffb0c9bSToomas Soome {
1191*5ffb0c9bSToomas Soome LogMsg("NSEC3HashName: ERROR!! Cannot allocate context");
1192*5ffb0c9bSToomas Soome return mDNSNULL;
1193*5ffb0c9bSToomas Soome }
1194*5ffb0c9bSToomas Soome
1195*5ffb0c9bSToomas Soome AlgAdd(ctx, digest, digestlen);
1196*5ffb0c9bSToomas Soome if (nsec3->saltLength)
1197*5ffb0c9bSToomas Soome AlgAdd(ctx, p, nsec3->saltLength);
1198*5ffb0c9bSToomas Soome if (AnonDataLen)
1199*5ffb0c9bSToomas Soome AlgAdd(ctx, AnonData, AnonDataLen);
1200*5ffb0c9bSToomas Soome if (first)
1201*5ffb0c9bSToomas Soome {
1202*5ffb0c9bSToomas Soome first = mDNSfalse;
1203*5ffb0c9bSToomas Soome digest = hash;
1204*5ffb0c9bSToomas Soome digestlen = AlgLength(ctx);
1205*5ffb0c9bSToomas Soome }
1206*5ffb0c9bSToomas Soome AlgFinal(ctx, (void *)digest, digestlen);
1207*5ffb0c9bSToomas Soome AlgDestroy(ctx);
1208*5ffb0c9bSToomas Soome }
1209*5ffb0c9bSToomas Soome *dlen = digestlen;
1210*5ffb0c9bSToomas Soome return digest;
1211*5ffb0c9bSToomas Soome }
1212*5ffb0c9bSToomas Soome
12134b22b933Srs200217 // Notes on UTF-8:
12144b22b933Srs200217 // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
12154b22b933Srs200217 // 10xxxxxx is a continuation byte of a multi-byte character
12164b22b933Srs200217 // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x 80 - 0x 800-1)
12174b22b933Srs200217 // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x 800 - 0x 10000-1)
12184b22b933Srs200217 // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x 10000 - 0x 200000-1)
12194b22b933Srs200217 // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
12204b22b933Srs200217 // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
12214b22b933Srs200217 //
12224b22b933Srs200217 // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
12234b22b933Srs200217 // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
12244b22b933Srs200217 // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
12254b22b933Srs200217 // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
12264b22b933Srs200217 // and the second is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
12274b22b933Srs200217
TruncateUTF8ToLength(mDNSu8 * string,mDNSu32 length,mDNSu32 max)12284b22b933Srs200217 mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
12294b22b933Srs200217 {
12304b22b933Srs200217 if (length > max)
12314b22b933Srs200217 {
12324b22b933Srs200217 mDNSu8 c1 = string[max]; // First byte after cut point
1233*5ffb0c9bSToomas Soome mDNSu8 c2 = (max+1 < length) ? string[max+1] : (mDNSu8)0xB0; // Second byte after cut point
12344b22b933Srs200217 length = max; // Trim length down
12354b22b933Srs200217 while (length > 0)
12364b22b933Srs200217 {
12374b22b933Srs200217 // Check if the byte right after the chop point is a UTF-8 continuation byte,
12384b22b933Srs200217 // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
12394b22b933Srs200217 // If so, then we continue to chop more bytes until we get to a legal chop point.
12404b22b933Srs200217 mDNSBool continuation = ((c1 & 0xC0) == 0x80);
12414b22b933Srs200217 mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0);
12424b22b933Srs200217 if (!continuation && !secondsurrogate) break;
12434b22b933Srs200217 c2 = c1;
12444b22b933Srs200217 c1 = string[--length];
12454b22b933Srs200217 }
12464b22b933Srs200217 // Having truncated characters off the end of our string, also cut off any residual white space
12474b22b933Srs200217 while (length > 0 && string[length-1] <= ' ') length--;
12484b22b933Srs200217 }
12494b22b933Srs200217 return(length);
12504b22b933Srs200217 }
12514b22b933Srs200217
12524b22b933Srs200217 // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
12534b22b933Srs200217 // name ends in "-nnn", where n is some decimal number.
LabelContainsSuffix(const domainlabel * const name,const mDNSBool RichText)12544b22b933Srs200217 mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
12554b22b933Srs200217 {
12564b22b933Srs200217 mDNSu16 l = name->c[0];
12574b22b933Srs200217
12584b22b933Srs200217 if (RichText)
12594b22b933Srs200217 {
12604b22b933Srs200217 if (l < 4) return mDNSfalse; // Need at least " (2)"
12614b22b933Srs200217 if (name->c[l--] != ')') return mDNSfalse; // Last char must be ')'
1262*5ffb0c9bSToomas Soome if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Preceeded by a digit
12634b22b933Srs200217 l--;
1264*5ffb0c9bSToomas Soome while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits
12654b22b933Srs200217 return (name->c[l] == '(' && name->c[l - 1] == ' ');
12664b22b933Srs200217 }
12674b22b933Srs200217 else
12684b22b933Srs200217 {
12694b22b933Srs200217 if (l < 2) return mDNSfalse; // Need at least "-2"
1270*5ffb0c9bSToomas Soome if (!mDNSIsDigit(name->c[l])) return mDNSfalse; // Last char must be a digit
12714b22b933Srs200217 l--;
1272*5ffb0c9bSToomas Soome while (l > 2 && mDNSIsDigit(name->c[l])) l--; // Strip off digits
12734b22b933Srs200217 return (name->c[l] == '-');
12744b22b933Srs200217 }
12754b22b933Srs200217 }
12764b22b933Srs200217
12774b22b933Srs200217 // removes an auto-generated suffix (appended on a name collision) from a label. caller is
12784b22b933Srs200217 // responsible for ensuring that the label does indeed contain a suffix. returns the number
12794b22b933Srs200217 // from the suffix that was removed.
RemoveLabelSuffix(domainlabel * name,mDNSBool RichText)12804b22b933Srs200217 mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText)
12814b22b933Srs200217 {
12824b22b933Srs200217 mDNSu32 val = 0, multiplier = 1;
12834b22b933Srs200217
12844b22b933Srs200217 // Chop closing parentheses from RichText suffix
12854b22b933Srs200217 if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
12864b22b933Srs200217
12874b22b933Srs200217 // Get any existing numerical suffix off the name
1288*5ffb0c9bSToomas Soome while (mDNSIsDigit(name->c[name->c[0]]))
12894b22b933Srs200217 { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
12904b22b933Srs200217
12914b22b933Srs200217 // Chop opening parentheses or dash from suffix
12924b22b933Srs200217 if (RichText)
12934b22b933Srs200217 {
12944b22b933Srs200217 if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
12954b22b933Srs200217 }
12964b22b933Srs200217 else
12974b22b933Srs200217 {
12984b22b933Srs200217 if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
12994b22b933Srs200217 }
13004b22b933Srs200217
13014b22b933Srs200217 return(val);
13024b22b933Srs200217 }
13034b22b933Srs200217
13044b22b933Srs200217 // appends a numerical suffix to a label, with the number following a whitespace and enclosed
13054b22b933Srs200217 // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
AppendLabelSuffix(domainlabel * const name,mDNSu32 val,const mDNSBool RichText)1306*5ffb0c9bSToomas Soome mDNSexport void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText)
13074b22b933Srs200217 {
13084b22b933Srs200217 mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
13094b22b933Srs200217 if (RichText) chars = 4; // Shortest possible RichText suffix is 4 characters (" (2)")
13104b22b933Srs200217
13114b22b933Srs200217 // Truncate trailing spaces from RichText names
13124b22b933Srs200217 if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
13134b22b933Srs200217
1314*5ffb0c9bSToomas Soome while (divisor < 0xFFFFFFFFUL/10 && val >= divisor * 10) { divisor *= 10; chars++; }
13154b22b933Srs200217
13164b22b933Srs200217 name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
13174b22b933Srs200217
13184b22b933Srs200217 if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
13194b22b933Srs200217 else { name->c[++name->c[0]] = '-'; }
13204b22b933Srs200217
13214b22b933Srs200217 while (divisor)
13224b22b933Srs200217 {
13234b22b933Srs200217 name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
13244b22b933Srs200217 val %= divisor;
13254b22b933Srs200217 divisor /= 10;
13264b22b933Srs200217 }
13274b22b933Srs200217
13284b22b933Srs200217 if (RichText) name->c[++name->c[0]] = ')';
13294b22b933Srs200217 }
13304b22b933Srs200217
IncrementLabelSuffix(domainlabel * name,mDNSBool RichText)13314b22b933Srs200217 mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
13324b22b933Srs200217 {
13334b22b933Srs200217 mDNSu32 val = 0;
13344b22b933Srs200217
13354b22b933Srs200217 if (LabelContainsSuffix(name, RichText))
13364b22b933Srs200217 val = RemoveLabelSuffix(name, RichText);
13374b22b933Srs200217
13384b22b933Srs200217 // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
13394b22b933Srs200217 // If existing suffix in the range 2-9, increment it.
13404b22b933Srs200217 // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
13414b22b933Srs200217 // so add a random increment to improve the chances of finding an available name next time.
13424b22b933Srs200217 if (val == 0) val = 2;
13434b22b933Srs200217 else if (val < 10) val++;
13444b22b933Srs200217 else val += 1 + mDNSRandom(99);
13454b22b933Srs200217
13464b22b933Srs200217 AppendLabelSuffix(name, val, RichText);
13474b22b933Srs200217 }
13484b22b933Srs200217
13494b22b933Srs200217 // ***************************************************************************
13504b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
13514b22b933Srs200217 #pragma mark -
13524b22b933Srs200217 #pragma mark - Resource Record Utility Functions
13534b22b933Srs200217 #endif
13544b22b933Srs200217
1355*5ffb0c9bSToomas Soome // Set up a AuthRecord with sensible default values.
1356*5ffb0c9bSToomas Soome // These defaults may be overwritten with new values before mDNS_Register is called
mDNS_SetupResourceRecord(AuthRecord * rr,RData * RDataStorage,mDNSInterfaceID InterfaceID,mDNSu16 rrtype,mDNSu32 ttl,mDNSu8 RecordType,AuthRecType artype,mDNSRecordCallback Callback,void * Context)1357*5ffb0c9bSToomas Soome mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
1358*5ffb0c9bSToomas Soome mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context)
13594b22b933Srs200217 {
1360*5ffb0c9bSToomas Soome //
1361*5ffb0c9bSToomas Soome // LocalOnly auth record can be created with LocalOnly InterfaceID or a valid InterfaceID.
1362*5ffb0c9bSToomas Soome // Most of the applications normally create with LocalOnly InterfaceID and we store them as
1363*5ffb0c9bSToomas Soome // such, so that we can deliver the response to questions that specify LocalOnly InterfaceID.
1364*5ffb0c9bSToomas Soome // LocalOnly resource records can also be created with valid InterfaceID which happens today
1365*5ffb0c9bSToomas Soome // when we create LocalOnly records for /etc/hosts.
1366*5ffb0c9bSToomas Soome
1367*5ffb0c9bSToomas Soome if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly)
1368*5ffb0c9bSToomas Soome {
1369*5ffb0c9bSToomas Soome LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype);
1370*5ffb0c9bSToomas Soome return;
1371*5ffb0c9bSToomas Soome }
1372*5ffb0c9bSToomas Soome else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P)
1373*5ffb0c9bSToomas Soome {
1374*5ffb0c9bSToomas Soome LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype);
1375*5ffb0c9bSToomas Soome return;
1376*5ffb0c9bSToomas Soome }
1377*5ffb0c9bSToomas Soome else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly))
1378*5ffb0c9bSToomas Soome {
1379*5ffb0c9bSToomas Soome LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype);
1380*5ffb0c9bSToomas Soome return;
1381*5ffb0c9bSToomas Soome }
1382*5ffb0c9bSToomas Soome
1383*5ffb0c9bSToomas Soome // Don't try to store a TTL bigger than we can represent in platform time units
1384*5ffb0c9bSToomas Soome if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond)
1385*5ffb0c9bSToomas Soome ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond;
1386*5ffb0c9bSToomas Soome else if (ttl == 0) // And Zero TTL is illegal
1387*5ffb0c9bSToomas Soome ttl = DefaultTTLforRRType(rrtype);
1388*5ffb0c9bSToomas Soome
1389*5ffb0c9bSToomas Soome // Field Group 1: The actual information pertaining to this resource record
1390*5ffb0c9bSToomas Soome rr->resrec.RecordType = RecordType;
1391*5ffb0c9bSToomas Soome rr->resrec.InterfaceID = InterfaceID;
1392*5ffb0c9bSToomas Soome rr->resrec.name = &rr->namestorage;
1393*5ffb0c9bSToomas Soome rr->resrec.rrtype = rrtype;
1394*5ffb0c9bSToomas Soome rr->resrec.rrclass = kDNSClass_IN;
1395*5ffb0c9bSToomas Soome rr->resrec.rroriginalttl = ttl;
1396*5ffb0c9bSToomas Soome rr->resrec.rDNSServer = mDNSNULL;
1397*5ffb0c9bSToomas Soome rr->resrec.AnonInfo = mDNSNULL;
1398*5ffb0c9bSToomas Soome // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
1399*5ffb0c9bSToomas Soome // rr->resrec.rdestimate = set in mDNS_Register_internal
1400*5ffb0c9bSToomas Soome // rr->resrec.rdata = MUST be set by client
1401*5ffb0c9bSToomas Soome
1402*5ffb0c9bSToomas Soome if (RDataStorage)
1403*5ffb0c9bSToomas Soome rr->resrec.rdata = RDataStorage;
1404*5ffb0c9bSToomas Soome else
1405*5ffb0c9bSToomas Soome {
1406*5ffb0c9bSToomas Soome rr->resrec.rdata = &rr->rdatastorage;
1407*5ffb0c9bSToomas Soome rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
1408*5ffb0c9bSToomas Soome }
1409*5ffb0c9bSToomas Soome
1410*5ffb0c9bSToomas Soome // Field Group 2: Persistent metadata for Authoritative Records
1411*5ffb0c9bSToomas Soome rr->Additional1 = mDNSNULL;
1412*5ffb0c9bSToomas Soome rr->Additional2 = mDNSNULL;
1413*5ffb0c9bSToomas Soome rr->DependentOn = mDNSNULL;
1414*5ffb0c9bSToomas Soome rr->RRSet = mDNSNULL;
1415*5ffb0c9bSToomas Soome rr->RecordCallback = Callback;
1416*5ffb0c9bSToomas Soome rr->RecordContext = Context;
1417*5ffb0c9bSToomas Soome
1418*5ffb0c9bSToomas Soome rr->AutoTarget = Target_Manual;
1419*5ffb0c9bSToomas Soome rr->AllowRemoteQuery = mDNSfalse;
1420*5ffb0c9bSToomas Soome rr->ForceMCast = mDNSfalse;
1421*5ffb0c9bSToomas Soome
1422*5ffb0c9bSToomas Soome rr->WakeUp = zeroOwner;
1423*5ffb0c9bSToomas Soome rr->AddressProxy = zeroAddr;
1424*5ffb0c9bSToomas Soome rr->TimeRcvd = 0;
1425*5ffb0c9bSToomas Soome rr->TimeExpire = 0;
1426*5ffb0c9bSToomas Soome rr->ARType = artype;
1427*5ffb0c9bSToomas Soome rr->AuthFlags = 0;
1428*5ffb0c9bSToomas Soome
1429*5ffb0c9bSToomas Soome // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
1430*5ffb0c9bSToomas Soome // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
1431*5ffb0c9bSToomas Soome
1432*5ffb0c9bSToomas Soome // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
1433*5ffb0c9bSToomas Soome // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
1434*5ffb0c9bSToomas Soome // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
1435*5ffb0c9bSToomas Soome rr->state = regState_Zero;
1436*5ffb0c9bSToomas Soome rr->uselease = 0;
1437*5ffb0c9bSToomas Soome rr->expire = 0;
1438*5ffb0c9bSToomas Soome rr->Private = 0;
1439*5ffb0c9bSToomas Soome rr->updateid = zeroID;
1440*5ffb0c9bSToomas Soome rr->zone = rr->resrec.name;
1441*5ffb0c9bSToomas Soome rr->nta = mDNSNULL;
1442*5ffb0c9bSToomas Soome rr->tcp = mDNSNULL;
1443*5ffb0c9bSToomas Soome rr->OrigRData = 0;
1444*5ffb0c9bSToomas Soome rr->OrigRDLen = 0;
1445*5ffb0c9bSToomas Soome rr->InFlightRData = 0;
1446*5ffb0c9bSToomas Soome rr->InFlightRDLen = 0;
1447*5ffb0c9bSToomas Soome rr->QueuedRData = 0;
1448*5ffb0c9bSToomas Soome rr->QueuedRDLen = 0;
1449*5ffb0c9bSToomas Soome mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo));
1450*5ffb0c9bSToomas Soome rr->SRVChanged = mDNSfalse;
1451*5ffb0c9bSToomas Soome rr->mState = mergeState_Zero;
1452*5ffb0c9bSToomas Soome
1453*5ffb0c9bSToomas Soome rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register()
1454*5ffb0c9bSToomas Soome }
1455*5ffb0c9bSToomas Soome
mDNS_SetupQuestion(DNSQuestion * const q,const mDNSInterfaceID InterfaceID,const domainname * const name,const mDNSu16 qtype,mDNSQuestionCallback * const callback,void * const context)1456*5ffb0c9bSToomas Soome mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name,
1457*5ffb0c9bSToomas Soome const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context)
1458*5ffb0c9bSToomas Soome {
1459*5ffb0c9bSToomas Soome q->InterfaceID = InterfaceID;
1460*5ffb0c9bSToomas Soome q->flags = 0;
1461*5ffb0c9bSToomas Soome q->Target = zeroAddr;
1462*5ffb0c9bSToomas Soome AssignDomainName(&q->qname, name);
1463*5ffb0c9bSToomas Soome q->qtype = qtype;
1464*5ffb0c9bSToomas Soome q->qclass = kDNSClass_IN;
1465*5ffb0c9bSToomas Soome q->LongLived = (qtype == kDNSType_PTR);
1466*5ffb0c9bSToomas Soome q->ExpectUnique = (qtype != kDNSType_PTR);
1467*5ffb0c9bSToomas Soome q->ForceMCast = mDNSfalse;
1468*5ffb0c9bSToomas Soome q->ReturnIntermed = mDNSfalse;
1469*5ffb0c9bSToomas Soome q->SuppressUnusable = mDNSfalse;
1470*5ffb0c9bSToomas Soome q->DenyOnCellInterface = mDNSfalse;
1471*5ffb0c9bSToomas Soome q->DenyOnExpInterface = mDNSfalse;
1472*5ffb0c9bSToomas Soome q->SearchListIndex = 0;
1473*5ffb0c9bSToomas Soome q->AppendSearchDomains = 0;
1474*5ffb0c9bSToomas Soome q->RetryWithSearchDomains = mDNSfalse;
1475*5ffb0c9bSToomas Soome q->TimeoutQuestion = 0;
1476*5ffb0c9bSToomas Soome q->WakeOnResolve = 0;
1477*5ffb0c9bSToomas Soome q->UseBackgroundTrafficClass = mDNSfalse;
1478*5ffb0c9bSToomas Soome q->ValidationRequired = 0;
1479*5ffb0c9bSToomas Soome q->ValidatingResponse = 0;
1480*5ffb0c9bSToomas Soome q->ProxyQuestion = 0;
1481*5ffb0c9bSToomas Soome q->qnameOrig = mDNSNULL;
1482*5ffb0c9bSToomas Soome q->AnonInfo = mDNSNULL;
1483*5ffb0c9bSToomas Soome q->pid = mDNSPlatformGetPID();
1484*5ffb0c9bSToomas Soome q->DisallowPID = mDNSfalse;
1485*5ffb0c9bSToomas Soome q->ServiceID = -1;
1486*5ffb0c9bSToomas Soome q->QuestionCallback = callback;
1487*5ffb0c9bSToomas Soome q->QuestionContext = context;
1488*5ffb0c9bSToomas Soome }
1489*5ffb0c9bSToomas Soome
RDataHashValue(const ResourceRecord * const rr)1490*5ffb0c9bSToomas Soome mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr)
1491*5ffb0c9bSToomas Soome {
1492*5ffb0c9bSToomas Soome int len = rr->rdlength;
1493*5ffb0c9bSToomas Soome const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
1494*5ffb0c9bSToomas Soome const mDNSu8 *ptr = rdb->data;
14954b22b933Srs200217 mDNSu32 sum = 0;
1496*5ffb0c9bSToomas Soome
1497*5ffb0c9bSToomas Soome switch(rr->rrtype)
14984b22b933Srs200217 {
1499*5ffb0c9bSToomas Soome case kDNSType_NS:
1500*5ffb0c9bSToomas Soome case kDNSType_MD:
1501*5ffb0c9bSToomas Soome case kDNSType_MF:
1502*5ffb0c9bSToomas Soome case kDNSType_CNAME:
1503*5ffb0c9bSToomas Soome case kDNSType_MB:
1504*5ffb0c9bSToomas Soome case kDNSType_MG:
1505*5ffb0c9bSToomas Soome case kDNSType_MR:
1506*5ffb0c9bSToomas Soome case kDNSType_PTR:
1507*5ffb0c9bSToomas Soome case kDNSType_NSAP_PTR:
1508*5ffb0c9bSToomas Soome case kDNSType_DNAME: return DomainNameHashValue(&rdb->name);
1509*5ffb0c9bSToomas Soome
1510*5ffb0c9bSToomas Soome case kDNSType_SOA: return rdb->soa.serial +
1511*5ffb0c9bSToomas Soome rdb->soa.refresh +
1512*5ffb0c9bSToomas Soome rdb->soa.retry +
1513*5ffb0c9bSToomas Soome rdb->soa.expire +
1514*5ffb0c9bSToomas Soome rdb->soa.min +
1515*5ffb0c9bSToomas Soome DomainNameHashValue(&rdb->soa.mname) +
1516*5ffb0c9bSToomas Soome DomainNameHashValue(&rdb->soa.rname);
1517*5ffb0c9bSToomas Soome
1518*5ffb0c9bSToomas Soome case kDNSType_MX:
1519*5ffb0c9bSToomas Soome case kDNSType_AFSDB:
1520*5ffb0c9bSToomas Soome case kDNSType_RT:
1521*5ffb0c9bSToomas Soome case kDNSType_KX: return DomainNameHashValue(&rdb->mx.exchange);
1522*5ffb0c9bSToomas Soome
1523*5ffb0c9bSToomas Soome case kDNSType_MINFO:
1524*5ffb0c9bSToomas Soome case kDNSType_RP: return DomainNameHashValue(&rdb->rp.mbox) + DomainNameHashValue(&rdb->rp.txt);
1525*5ffb0c9bSToomas Soome
1526*5ffb0c9bSToomas Soome case kDNSType_PX: return DomainNameHashValue(&rdb->px.map822) + DomainNameHashValue(&rdb->px.mapx400);
1527*5ffb0c9bSToomas Soome
1528*5ffb0c9bSToomas Soome case kDNSType_SRV: return DomainNameHashValue(&rdb->srv.target);
1529*5ffb0c9bSToomas Soome
1530*5ffb0c9bSToomas Soome case kDNSType_OPT: return 0; // OPT is a pseudo-RR container structure; makes no sense to compare
1531*5ffb0c9bSToomas Soome
1532*5ffb0c9bSToomas Soome case kDNSType_NSEC: {
1533*5ffb0c9bSToomas Soome int dlen;
1534*5ffb0c9bSToomas Soome dlen = DomainNameLength((domainname *)rdb->data);
1535*5ffb0c9bSToomas Soome sum = DomainNameHashValue((domainname *)rdb->data);
1536*5ffb0c9bSToomas Soome ptr += dlen;
1537*5ffb0c9bSToomas Soome len -= dlen;
1538*5ffb0c9bSToomas Soome /* FALLTHROUGH */
1539*5ffb0c9bSToomas Soome }
1540*5ffb0c9bSToomas Soome
1541*5ffb0c9bSToomas Soome default:
1542*5ffb0c9bSToomas Soome {
1543*5ffb0c9bSToomas Soome int i;
1544*5ffb0c9bSToomas Soome for (i=0; i+1 < len; i+=2)
1545*5ffb0c9bSToomas Soome {
1546*5ffb0c9bSToomas Soome sum += (((mDNSu32)(ptr[i])) << 8) | ptr[i+1];
15474b22b933Srs200217 sum = (sum<<3) | (sum>>29);
15484b22b933Srs200217 }
1549*5ffb0c9bSToomas Soome if (i < len)
15504b22b933Srs200217 {
1551*5ffb0c9bSToomas Soome sum += ((mDNSu32)(ptr[i])) << 8;
15524b22b933Srs200217 }
15534b22b933Srs200217 return(sum);
15544b22b933Srs200217 }
1555*5ffb0c9bSToomas Soome }
1556*5ffb0c9bSToomas Soome }
15574b22b933Srs200217
15584b22b933Srs200217 // r1 has to be a full ResourceRecord including rrtype and rdlength
15594b22b933Srs200217 // r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
SameRDataBody(const ResourceRecord * const r1,const RDataBody * const r2,DomainNameComparisonFn * samename)1560*5ffb0c9bSToomas Soome mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename)
15614b22b933Srs200217 {
1562*5ffb0c9bSToomas Soome const RDataBody2 *const b1 = (RDataBody2 *)r1->rdata->u.data;
1563*5ffb0c9bSToomas Soome const RDataBody2 *const b2 = (RDataBody2 *)r2;
15644b22b933Srs200217 switch(r1->rrtype)
15654b22b933Srs200217 {
1566*5ffb0c9bSToomas Soome case kDNSType_NS:
1567*5ffb0c9bSToomas Soome case kDNSType_MD:
1568*5ffb0c9bSToomas Soome case kDNSType_MF:
1569*5ffb0c9bSToomas Soome case kDNSType_CNAME:
1570*5ffb0c9bSToomas Soome case kDNSType_MB:
1571*5ffb0c9bSToomas Soome case kDNSType_MG:
1572*5ffb0c9bSToomas Soome case kDNSType_MR:
1573*5ffb0c9bSToomas Soome case kDNSType_PTR:
1574*5ffb0c9bSToomas Soome case kDNSType_NSAP_PTR:
1575*5ffb0c9bSToomas Soome case kDNSType_DNAME: return(SameDomainName(&b1->name, &b2->name));
15764b22b933Srs200217
1577*5ffb0c9bSToomas Soome case kDNSType_SOA: return (mDNSBool)( b1->soa.serial == b2->soa.serial &&
1578*5ffb0c9bSToomas Soome b1->soa.refresh == b2->soa.refresh &&
1579*5ffb0c9bSToomas Soome b1->soa.retry == b2->soa.retry &&
1580*5ffb0c9bSToomas Soome b1->soa.expire == b2->soa.expire &&
1581*5ffb0c9bSToomas Soome b1->soa.min == b2->soa.min &&
1582*5ffb0c9bSToomas Soome samename(&b1->soa.mname, &b2->soa.mname) &&
1583*5ffb0c9bSToomas Soome samename(&b1->soa.rname, &b2->soa.rname));
15844b22b933Srs200217
1585*5ffb0c9bSToomas Soome case kDNSType_MX:
1586*5ffb0c9bSToomas Soome case kDNSType_AFSDB:
1587*5ffb0c9bSToomas Soome case kDNSType_RT:
1588*5ffb0c9bSToomas Soome case kDNSType_KX: return (mDNSBool)( b1->mx.preference == b2->mx.preference &&
1589*5ffb0c9bSToomas Soome samename(&b1->mx.exchange, &b2->mx.exchange));
1590*5ffb0c9bSToomas Soome
1591*5ffb0c9bSToomas Soome case kDNSType_MINFO:
1592*5ffb0c9bSToomas Soome case kDNSType_RP: return (mDNSBool)( samename(&b1->rp.mbox, &b2->rp.mbox) &&
1593*5ffb0c9bSToomas Soome samename(&b1->rp.txt, &b2->rp.txt));
1594*5ffb0c9bSToomas Soome
1595*5ffb0c9bSToomas Soome case kDNSType_PX: return (mDNSBool)( b1->px.preference == b2->px.preference &&
1596*5ffb0c9bSToomas Soome samename(&b1->px.map822, &b2->px.map822) &&
1597*5ffb0c9bSToomas Soome samename(&b1->px.mapx400, &b2->px.mapx400));
1598*5ffb0c9bSToomas Soome
1599*5ffb0c9bSToomas Soome case kDNSType_SRV: return (mDNSBool)( b1->srv.priority == b2->srv.priority &&
1600*5ffb0c9bSToomas Soome b1->srv.weight == b2->srv.weight &&
1601*5ffb0c9bSToomas Soome mDNSSameIPPort(b1->srv.port, b2->srv.port) &&
1602*5ffb0c9bSToomas Soome samename(&b1->srv.target, &b2->srv.target));
1603*5ffb0c9bSToomas Soome
1604*5ffb0c9bSToomas Soome case kDNSType_OPT: return mDNSfalse; // OPT is a pseudo-RR container structure; makes no sense to compare
1605*5ffb0c9bSToomas Soome case kDNSType_NSEC: {
1606*5ffb0c9bSToomas Soome // If the "nxt" name changes in case, we want to delete the old
1607*5ffb0c9bSToomas Soome // and store just the new one. If the caller passes in SameDomainCS for "samename",
1608*5ffb0c9bSToomas Soome // we would return "false" when the only change between the two rdata is the case
1609*5ffb0c9bSToomas Soome // change in "nxt".
1610*5ffb0c9bSToomas Soome //
1611*5ffb0c9bSToomas Soome // Note: rdlength of both the RData are same (ensured by the caller) and hence we can
1612*5ffb0c9bSToomas Soome // use just r1->rdlength below
1613*5ffb0c9bSToomas Soome
1614*5ffb0c9bSToomas Soome int dlen1 = DomainNameLength((domainname *)b1->data);
1615*5ffb0c9bSToomas Soome int dlen2 = DomainNameLength((domainname *)b2->data);
1616*5ffb0c9bSToomas Soome return (mDNSBool)(dlen1 == dlen2 &&
1617*5ffb0c9bSToomas Soome samename((domainname *)b1->data, (domainname *)b2->data) &&
1618*5ffb0c9bSToomas Soome mDNSPlatformMemSame(b1->data + dlen1, b2->data + dlen2, r1->rdlength - dlen1));
1619*5ffb0c9bSToomas Soome }
1620*5ffb0c9bSToomas Soome
1621*5ffb0c9bSToomas Soome default: return(mDNSPlatformMemSame(b1->data, b2->data, r1->rdlength));
16224b22b933Srs200217 }
16234b22b933Srs200217 }
16244b22b933Srs200217
BitmapTypeCheck(mDNSu8 * bmap,int bitmaplen,mDNSu16 type)1625*5ffb0c9bSToomas Soome mDNSexport mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type)
16264b22b933Srs200217 {
1627*5ffb0c9bSToomas Soome int win, wlen;
1628*5ffb0c9bSToomas Soome int wintype;
1629*5ffb0c9bSToomas Soome
1630*5ffb0c9bSToomas Soome // The window that this type belongs to. NSEC has 256 windows that
1631*5ffb0c9bSToomas Soome // comprises of 256 types.
1632*5ffb0c9bSToomas Soome wintype = type >> 8;
1633*5ffb0c9bSToomas Soome
1634*5ffb0c9bSToomas Soome while (bitmaplen > 0)
1635*5ffb0c9bSToomas Soome {
1636*5ffb0c9bSToomas Soome if (bitmaplen < 3)
1637*5ffb0c9bSToomas Soome {
1638*5ffb0c9bSToomas Soome LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d short", bitmaplen);
1639*5ffb0c9bSToomas Soome return mDNSfalse;
16404b22b933Srs200217 }
16414b22b933Srs200217
1642*5ffb0c9bSToomas Soome win = *bmap++;
1643*5ffb0c9bSToomas Soome wlen = *bmap++;
1644*5ffb0c9bSToomas Soome bitmaplen -= 2;
1645*5ffb0c9bSToomas Soome if (bitmaplen < wlen || wlen < 1 || wlen > 32)
16464b22b933Srs200217 {
1647*5ffb0c9bSToomas Soome LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d wlen %d, win %d", bitmaplen, wlen, win);
1648*5ffb0c9bSToomas Soome return mDNSfalse;
1649*5ffb0c9bSToomas Soome }
1650*5ffb0c9bSToomas Soome if (win < 0 || win >= 256)
1651*5ffb0c9bSToomas Soome {
1652*5ffb0c9bSToomas Soome LogInfo("BitmapTypeCheck: malformed nsec, wlen %d", wlen);
1653*5ffb0c9bSToomas Soome return mDNSfalse;
1654*5ffb0c9bSToomas Soome }
1655*5ffb0c9bSToomas Soome if (win == wintype)
1656*5ffb0c9bSToomas Soome {
1657*5ffb0c9bSToomas Soome // First byte in the window serves 0 to 7, the next one serves 8 to 15 and so on.
1658*5ffb0c9bSToomas Soome // Calculate the right byte offset first.
1659*5ffb0c9bSToomas Soome int boff = (type & 0xff ) >> 3;
1660*5ffb0c9bSToomas Soome if (wlen <= boff)
1661*5ffb0c9bSToomas Soome return mDNSfalse;
1662*5ffb0c9bSToomas Soome // The last three bits values 0 to 7 corresponds to bit positions
1663*5ffb0c9bSToomas Soome // within the byte.
1664*5ffb0c9bSToomas Soome return (bmap[boff] & (0x80 >> (type & 7)));
1665*5ffb0c9bSToomas Soome }
1666*5ffb0c9bSToomas Soome else
1667*5ffb0c9bSToomas Soome {
1668*5ffb0c9bSToomas Soome // If the windows are ordered, then we could check to see
1669*5ffb0c9bSToomas Soome // if wintype > win and then return early.
1670*5ffb0c9bSToomas Soome bmap += wlen;
1671*5ffb0c9bSToomas Soome bitmaplen -= wlen;
1672*5ffb0c9bSToomas Soome }
1673*5ffb0c9bSToomas Soome }
1674*5ffb0c9bSToomas Soome return mDNSfalse;
16754b22b933Srs200217 }
16764b22b933Srs200217
1677*5ffb0c9bSToomas Soome // Don't call this function if the resource record is not NSEC. It will return false
1678*5ffb0c9bSToomas Soome // which means that the type does not exist.
RRAssertsExistence(const ResourceRecord * const rr,mDNSu16 type)1679*5ffb0c9bSToomas Soome mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type)
16804b22b933Srs200217 {
1681*5ffb0c9bSToomas Soome const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
1682*5ffb0c9bSToomas Soome mDNSu8 *nsec = (mDNSu8 *)rdb->data;
1683*5ffb0c9bSToomas Soome int len, bitmaplen;
1684*5ffb0c9bSToomas Soome mDNSu8 *bmap;
1685*5ffb0c9bSToomas Soome
1686*5ffb0c9bSToomas Soome if (rr->rrtype != kDNSType_NSEC) return mDNSfalse;
1687*5ffb0c9bSToomas Soome
1688*5ffb0c9bSToomas Soome len = DomainNameLength((domainname *)nsec);
1689*5ffb0c9bSToomas Soome
1690*5ffb0c9bSToomas Soome bitmaplen = rr->rdlength - len;
1691*5ffb0c9bSToomas Soome bmap = nsec + len;
1692*5ffb0c9bSToomas Soome return (BitmapTypeCheck(bmap, bitmaplen, type));
1693*5ffb0c9bSToomas Soome }
1694*5ffb0c9bSToomas Soome
1695*5ffb0c9bSToomas Soome // Don't call this function if the resource record is not NSEC. It will return false
1696*5ffb0c9bSToomas Soome // which means that the type exists.
RRAssertsNonexistence(const ResourceRecord * const rr,mDNSu16 type)1697*5ffb0c9bSToomas Soome mDNSexport mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type)
1698*5ffb0c9bSToomas Soome {
1699*5ffb0c9bSToomas Soome if (rr->rrtype != kDNSType_NSEC) return mDNSfalse;
1700*5ffb0c9bSToomas Soome
1701*5ffb0c9bSToomas Soome return !RRAssertsExistence(rr, type);
1702*5ffb0c9bSToomas Soome }
1703*5ffb0c9bSToomas Soome
1704*5ffb0c9bSToomas Soome // Checks whether the RRSIG or NSEC record answers the question "q".
DNSSECRecordAnswersQuestion(const ResourceRecord * const rr,const DNSQuestion * const q,mDNSBool * checkType)1705*5ffb0c9bSToomas Soome mDNSlocal mDNSBool DNSSECRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q, mDNSBool *checkType)
1706*5ffb0c9bSToomas Soome {
1707*5ffb0c9bSToomas Soome *checkType = mDNStrue;
1708*5ffb0c9bSToomas Soome
1709*5ffb0c9bSToomas Soome // This function is called for all questions and as long as the type matches,
1710*5ffb0c9bSToomas Soome // return true. For the types (RRSIG and NSEC) that are specifically checked in
1711*5ffb0c9bSToomas Soome // this function, returning true still holds good.
1712*5ffb0c9bSToomas Soome if (q->qtype == rr->rrtype)
1713*5ffb0c9bSToomas Soome return mDNStrue;
1714*5ffb0c9bSToomas Soome
1715*5ffb0c9bSToomas Soome // For DS and DNSKEY questions, the types should match i.e., don't answer using CNAME
1716*5ffb0c9bSToomas Soome // records as it answers any question type.
1717*5ffb0c9bSToomas Soome //
1718*5ffb0c9bSToomas Soome // - DS record comes from the parent zone where CNAME record cannot coexist and hence
1719*5ffb0c9bSToomas Soome // cannot possibly answer it.
1720*5ffb0c9bSToomas Soome //
1721*5ffb0c9bSToomas Soome // - For DNSKEY, one could potentially follow CNAME but there could be a DNSKEY at
1722*5ffb0c9bSToomas Soome // the "qname" itself. To keep it simple, we don't follow CNAME.
1723*5ffb0c9bSToomas Soome
1724*5ffb0c9bSToomas Soome if ((q->qtype == kDNSType_DS || q->qtype == kDNSType_DNSKEY) && (q->qtype != rr->rrtype))
1725*5ffb0c9bSToomas Soome {
1726*5ffb0c9bSToomas Soome debugf("DNSSECRecordAnswersQuestion: %d type resource record matched question %##s (%s), ignoring", rr->rrtype,
1727*5ffb0c9bSToomas Soome q->qname.c, DNSTypeName(q->qtype));
1728*5ffb0c9bSToomas Soome return mDNSfalse;
1729*5ffb0c9bSToomas Soome }
1730*5ffb0c9bSToomas Soome
1731*5ffb0c9bSToomas Soome // If we are validating a response using DNSSEC, we might already have the records
1732*5ffb0c9bSToomas Soome // for the "q->qtype" in the cache but we issued a query with DO bit set
1733*5ffb0c9bSToomas Soome // to get the RRSIGs e.g., if you have two questions one of which does not require
1734*5ffb0c9bSToomas Soome // DNSSEC validation. When the RRSIG is added to the cache, we need to deliver
1735*5ffb0c9bSToomas Soome // the response to the question. The RRSIG type won't match the q->qtype and hence
1736*5ffb0c9bSToomas Soome // we need to bypass the check in that case.
1737*5ffb0c9bSToomas Soome if (rr->rrtype == kDNSType_RRSIG && q->ValidatingResponse)
1738*5ffb0c9bSToomas Soome {
1739*5ffb0c9bSToomas Soome const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
1740*5ffb0c9bSToomas Soome rdataRRSig *rrsig = (rdataRRSig *)rdb->data;
1741*5ffb0c9bSToomas Soome mDNSu16 typeCovered = swap16(rrsig->typeCovered);
1742*5ffb0c9bSToomas Soome debugf("DNSSECRecordAnswersQuestion: Matching RRSIG typeCovered %s", DNSTypeName(typeCovered));
1743*5ffb0c9bSToomas Soome if (typeCovered != kDNSType_CNAME && typeCovered != q->qtype)
1744*5ffb0c9bSToomas Soome {
1745*5ffb0c9bSToomas Soome debugf("DNSSECRecordAnswersQuestion: RRSIG did not match question %##s (%s)", q->qname.c,
1746*5ffb0c9bSToomas Soome DNSTypeName(q->qtype));
1747*5ffb0c9bSToomas Soome return mDNSfalse;
1748*5ffb0c9bSToomas Soome }
1749*5ffb0c9bSToomas Soome LogInfo("DNSSECRecordAnswersQuestion: RRSIG matched question %##s (%s)", q->qname.c,
1750*5ffb0c9bSToomas Soome DNSTypeName(q->qtype));
1751*5ffb0c9bSToomas Soome *checkType = mDNSfalse;
1752*5ffb0c9bSToomas Soome return mDNStrue;
1753*5ffb0c9bSToomas Soome }
1754*5ffb0c9bSToomas Soome // If the NSEC record asserts the non-existence of a name looked up by the question, we would
1755*5ffb0c9bSToomas Soome // typically answer that e.g., the bitmap asserts that q->qtype does not exist. If we have
1756*5ffb0c9bSToomas Soome // to prove the non-existence as required by ValidatingResponse and ValidationRequired question,
1757*5ffb0c9bSToomas Soome // then we should not answer that as it may not be the right one always. We may need more than
1758*5ffb0c9bSToomas Soome // one NSEC to prove the non-existence.
1759*5ffb0c9bSToomas Soome if (rr->rrtype == kDNSType_NSEC && DNSSECQuestion(q))
1760*5ffb0c9bSToomas Soome {
1761*5ffb0c9bSToomas Soome debugf("DNSSECRecordAnswersQuestion: Question %##s (%s) matched record %##s (NSEC)", q->qname.c,
1762*5ffb0c9bSToomas Soome DNSTypeName(q->qtype), rr->name->c);
1763*5ffb0c9bSToomas Soome return mDNSfalse;
1764*5ffb0c9bSToomas Soome }
1765*5ffb0c9bSToomas Soome return mDNStrue;
1766*5ffb0c9bSToomas Soome }
1767*5ffb0c9bSToomas Soome
1768*5ffb0c9bSToomas Soome // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
1769*5ffb0c9bSToomas Soome // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
1770*5ffb0c9bSToomas Soome // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
1771*5ffb0c9bSToomas Soome // because it has to check all the way to the end of the names to be sure.
1772*5ffb0c9bSToomas Soome // In cases where we know in advance that the names match it's especially advantageous to skip the
1773*5ffb0c9bSToomas Soome // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
1774*5ffb0c9bSToomas Soome
SameNameRecordAnswersQuestion(const ResourceRecord * const rr,const DNSQuestion * const q)1775*5ffb0c9bSToomas Soome mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1776*5ffb0c9bSToomas Soome {
1777*5ffb0c9bSToomas Soome mDNSBool checkType = mDNStrue;
1778*5ffb0c9bSToomas Soome
1779*5ffb0c9bSToomas Soome // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
1780*5ffb0c9bSToomas Soome // are handled in LocalOnlyRecordAnswersQuestion
1781*5ffb0c9bSToomas Soome if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
1782*5ffb0c9bSToomas Soome {
1783*5ffb0c9bSToomas Soome LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
1784*5ffb0c9bSToomas Soome return mDNSfalse;
1785*5ffb0c9bSToomas Soome }
1786*5ffb0c9bSToomas Soome if (QuerySuppressed(q))
1787*5ffb0c9bSToomas Soome return mDNSfalse;
1788*5ffb0c9bSToomas Soome
17894b22b933Srs200217 if (rr->InterfaceID &&
17904b22b933Srs200217 q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
17914b22b933Srs200217 rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
17924b22b933Srs200217
1793*5ffb0c9bSToomas Soome // Resource record received via unicast, the resolver group ID should match ?
1794*5ffb0c9bSToomas Soome if (!rr->InterfaceID)
1795*5ffb0c9bSToomas Soome {
1796*5ffb0c9bSToomas Soome mDNSu16 idr = (rr->rDNSServer ? rr->rDNSServer->resGroupID : 0);
1797*5ffb0c9bSToomas Soome mDNSu16 idq = (q->qDNSServer ? q->qDNSServer->resGroupID : 0);
1798*5ffb0c9bSToomas Soome if (idr != idq) return(mDNSfalse);
1799*5ffb0c9bSToomas Soome if (!DNSSECRecordAnswersQuestion(rr, q, &checkType)) return mDNSfalse;
1800*5ffb0c9bSToomas Soome }
1801*5ffb0c9bSToomas Soome
1802*5ffb0c9bSToomas Soome // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1803*5ffb0c9bSToomas Soome if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1804*5ffb0c9bSToomas Soome
1805*5ffb0c9bSToomas Soome // CNAME answers question of any type and a negative cache record should not prevent us from querying other
1806*5ffb0c9bSToomas Soome // valid types at the same name.
1807*5ffb0c9bSToomas Soome if (rr->rrtype == kDNSType_CNAME && rr->RecordType == kDNSRecordTypePacketNegative && rr->rrtype != q->qtype)
1808*5ffb0c9bSToomas Soome return mDNSfalse;
1809*5ffb0c9bSToomas Soome
18104b22b933Srs200217 // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1811*5ffb0c9bSToomas Soome if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
18124b22b933Srs200217 if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1813*5ffb0c9bSToomas Soome
1814*5ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder
1815*5ffb0c9bSToomas Soome if (!mDNSPlatformValidRecordForQuestion(rr, q))
1816*5ffb0c9bSToomas Soome return mDNSfalse;
1817*5ffb0c9bSToomas Soome #endif // APPLE_OSX_mDNSResponder
1818*5ffb0c9bSToomas Soome
1819*5ffb0c9bSToomas Soome if (!AnonInfoAnswersQuestion(rr, q))
1820*5ffb0c9bSToomas Soome return mDNSfalse;
1821*5ffb0c9bSToomas Soome
1822*5ffb0c9bSToomas Soome return(mDNStrue);
1823*5ffb0c9bSToomas Soome }
1824*5ffb0c9bSToomas Soome
ResourceRecordAnswersQuestion(const ResourceRecord * const rr,const DNSQuestion * const q)1825*5ffb0c9bSToomas Soome mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1826*5ffb0c9bSToomas Soome {
1827*5ffb0c9bSToomas Soome if (!SameNameRecordAnswersQuestion(rr, q))
1828*5ffb0c9bSToomas Soome return mDNSfalse;
1829*5ffb0c9bSToomas Soome
1830*5ffb0c9bSToomas Soome return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1831*5ffb0c9bSToomas Soome }
1832*5ffb0c9bSToomas Soome
1833*5ffb0c9bSToomas Soome // We have a separate function to handle LocalOnly AuthRecords because they can be created with
1834*5ffb0c9bSToomas Soome // a valid InterfaceID (e.g., scoped /etc/hosts) and can be used to answer unicast questions unlike
1835*5ffb0c9bSToomas Soome // multicast resource records (which has a valid InterfaceID) which can't be used to answer
1836*5ffb0c9bSToomas Soome // unicast questions. ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion can't tell whether
1837*5ffb0c9bSToomas Soome // a resource record is multicast or LocalOnly by just looking at the ResourceRecord because
1838*5ffb0c9bSToomas Soome // LocalOnly records are truly identified by ARType in the AuthRecord. As P2P and LocalOnly record
1839*5ffb0c9bSToomas Soome // are kept in the same hash table, we use the same function to make it easy for the callers when
1840*5ffb0c9bSToomas Soome // they walk the hash table to answer LocalOnly/P2P questions
1841*5ffb0c9bSToomas Soome //
LocalOnlyRecordAnswersQuestion(AuthRecord * const ar,const DNSQuestion * const q)1842*5ffb0c9bSToomas Soome mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const DNSQuestion *const q)
1843*5ffb0c9bSToomas Soome {
1844*5ffb0c9bSToomas Soome ResourceRecord *rr = &ar->resrec;
1845*5ffb0c9bSToomas Soome
1846*5ffb0c9bSToomas Soome // mDNSInterface_Any questions can be answered with LocalOnly/P2P records in this function. AuthRecord_Any
1847*5ffb0c9bSToomas Soome // records are handled in ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion
1848*5ffb0c9bSToomas Soome if (RRAny(ar))
1849*5ffb0c9bSToomas Soome {
1850*5ffb0c9bSToomas Soome LogMsg("LocalOnlyRecordAnswersQuestion: ERROR!! called with regular AuthRecordAny %##s", rr->name->c);
1851*5ffb0c9bSToomas Soome return mDNSfalse;
1852*5ffb0c9bSToomas Soome }
1853*5ffb0c9bSToomas Soome
1854*5ffb0c9bSToomas Soome // Questions with mDNSInterface_LocalOnly InterfaceID should be answered with all resource records that are
1855*5ffb0c9bSToomas Soome // *local* to the machine. These include resource records that have InterfaceID set to mDNSInterface_LocalOnly,
1856*5ffb0c9bSToomas Soome // mDNSInterface_Any and any other real InterfaceID. Hence, LocalOnly questions should not be checked against
1857*5ffb0c9bSToomas Soome // the InterfaceID in the resource record.
1858*5ffb0c9bSToomas Soome //
1859*5ffb0c9bSToomas Soome // mDNSInterface_Unicast does not indicate any scope and hence treat them like mDNSInterface_Any.
1860*5ffb0c9bSToomas Soome
1861*5ffb0c9bSToomas Soome if (rr->InterfaceID &&
1862*5ffb0c9bSToomas Soome q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && q->InterfaceID != mDNSInterface_Unicast &&
1863*5ffb0c9bSToomas Soome rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1864*5ffb0c9bSToomas Soome
1865*5ffb0c9bSToomas Soome // Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records
1866*5ffb0c9bSToomas Soome // may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set
1867*5ffb0c9bSToomas Soome // to mDNSInterface_Any, mDNSInterface_LocalOnly or a real InterfaceID (scoped).
1868*5ffb0c9bSToomas Soome //
1869*5ffb0c9bSToomas Soome // 1) Question: Any, LocalOnly Record: no scope. This question should be answered with this record.
1870*5ffb0c9bSToomas Soome //
1871*5ffb0c9bSToomas Soome // 2) Question: Any, LocalOnly Record: scoped. This question should be answered with the record because
1872*5ffb0c9bSToomas Soome // traditionally applications never specify scope e.g., getaddrinfo, but need to be able
1873*5ffb0c9bSToomas Soome // to get to /etc/hosts entries.
1874*5ffb0c9bSToomas Soome //
1875*5ffb0c9bSToomas Soome // 3) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: no scope. This is the inverse of (2).
1876*5ffb0c9bSToomas Soome // If we register a LocalOnly record, we need to answer a LocalOnly question. If the /etc/hosts has a
1877*5ffb0c9bSToomas Soome // non scoped entry, it may not make sense to answer a scoped question. But we can't tell these two
1878*5ffb0c9bSToomas Soome // cases apart. As we currently answer LocalOnly question with LocalOnly record, we continue to do so.
1879*5ffb0c9bSToomas Soome //
1880*5ffb0c9bSToomas Soome // 4) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: scoped. LocalOnly questions should be
1881*5ffb0c9bSToomas Soome // answered with any resource record where as if it has a valid InterfaceID, the scope should match.
1882*5ffb0c9bSToomas Soome //
1883*5ffb0c9bSToomas Soome // (1) and (2) is bypassed because we check for a non-NULL InterfaceID above. For (3), the InterfaceID is NULL
1884*5ffb0c9bSToomas Soome // and hence bypassed above. For (4) we bypassed LocalOnly questions and checked the scope of the record
1885*5ffb0c9bSToomas Soome // against the question.
1886*5ffb0c9bSToomas Soome //
1887*5ffb0c9bSToomas Soome // For P2P, InterfaceIDs of the question and the record should match.
1888*5ffb0c9bSToomas Soome
1889*5ffb0c9bSToomas Soome // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
1890*5ffb0c9bSToomas Soome // LocalOnly authoritative answers are exempt. LocalOnly authoritative answers are used for /etc/host entries.
1891*5ffb0c9bSToomas Soome // We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com" which would then
1892*5ffb0c9bSToomas Soome // cause other applications (e.g. Safari) to connect to the wrong address. The rpc to register records filters out records
1893*5ffb0c9bSToomas Soome // with names that don't end in local and have mDNSInterface_LocalOnly set.
1894*5ffb0c9bSToomas Soome //
1895*5ffb0c9bSToomas Soome // Note: The check is bypassed for LocalOnly and for P2P it is not needed as only .local records are registered and for
1896*5ffb0c9bSToomas Soome // a question to match its names, it also has to end in .local and that question can't be a unicast question (See
1897*5ffb0c9bSToomas Soome // Question_uDNS macro and its usage). As P2P does not enforce .local only registrations we still make this check
1898*5ffb0c9bSToomas Soome // and also makes it future proof.
1899*5ffb0c9bSToomas Soome
1900*5ffb0c9bSToomas Soome if (ar->ARType != AuthRecordLocalOnly && rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1901*5ffb0c9bSToomas Soome
1902*5ffb0c9bSToomas Soome // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1903*5ffb0c9bSToomas Soome if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
1904*5ffb0c9bSToomas Soome if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1905*5ffb0c9bSToomas Soome
1906*5ffb0c9bSToomas Soome if (!AnonInfoAnswersQuestion(rr, q))
1907*5ffb0c9bSToomas Soome return mDNSfalse;
1908*5ffb0c9bSToomas Soome
1909*5ffb0c9bSToomas Soome return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1910*5ffb0c9bSToomas Soome }
1911*5ffb0c9bSToomas Soome
AnyTypeRecordAnswersQuestion(const ResourceRecord * const rr,const DNSQuestion * const q)1912*5ffb0c9bSToomas Soome mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1913*5ffb0c9bSToomas Soome {
1914*5ffb0c9bSToomas Soome // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
1915*5ffb0c9bSToomas Soome // are handled in LocalOnlyRecordAnswersQuestion
1916*5ffb0c9bSToomas Soome if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
1917*5ffb0c9bSToomas Soome {
1918*5ffb0c9bSToomas Soome LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
1919*5ffb0c9bSToomas Soome return mDNSfalse;
1920*5ffb0c9bSToomas Soome }
1921*5ffb0c9bSToomas Soome if (rr->InterfaceID &&
1922*5ffb0c9bSToomas Soome q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
1923*5ffb0c9bSToomas Soome rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1924*5ffb0c9bSToomas Soome
1925*5ffb0c9bSToomas Soome // Resource record received via unicast, the resolver group ID should match ?
1926*5ffb0c9bSToomas Soome // Note that Auth Records are normally setup with NULL InterfaceID and
1927*5ffb0c9bSToomas Soome // both the DNSServers are assumed to be NULL in that case
1928*5ffb0c9bSToomas Soome if (!rr->InterfaceID)
1929*5ffb0c9bSToomas Soome {
1930*5ffb0c9bSToomas Soome mDNSu16 idr = (rr->rDNSServer ? rr->rDNSServer->resGroupID : 0);
1931*5ffb0c9bSToomas Soome mDNSu16 idq = (q->qDNSServer ? q->qDNSServer->resGroupID : 0);
1932*5ffb0c9bSToomas Soome if (idr != idq) return(mDNSfalse);
1933*5ffb0c9bSToomas Soome }
1934*5ffb0c9bSToomas Soome
1935*5ffb0c9bSToomas Soome // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
1936*5ffb0c9bSToomas Soome if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1937*5ffb0c9bSToomas Soome
1938*5ffb0c9bSToomas Soome if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1939*5ffb0c9bSToomas Soome
1940*5ffb0c9bSToomas Soome if (!AnonInfoAnswersQuestion(rr, q))
1941*5ffb0c9bSToomas Soome return mDNSfalse;
1942*5ffb0c9bSToomas Soome
1943*5ffb0c9bSToomas Soome return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
1944*5ffb0c9bSToomas Soome }
1945*5ffb0c9bSToomas Soome
1946*5ffb0c9bSToomas Soome // This is called with both unicast resource record and multicast resource record. The question that
1947*5ffb0c9bSToomas Soome // received the unicast response could be the regular unicast response from a DNS server or a response
1948*5ffb0c9bSToomas Soome // to a mDNS QU query. The main reason we need this function is that we can't compare DNSServers between the
1949*5ffb0c9bSToomas Soome // question and the resource record because the resource record is not completely initialized in
1950*5ffb0c9bSToomas Soome // mDNSCoreReceiveResponse when this function is called.
ResourceRecordAnswersUnicastResponse(const ResourceRecord * const rr,const DNSQuestion * const q)1951*5ffb0c9bSToomas Soome mDNSexport mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q)
1952*5ffb0c9bSToomas Soome {
1953*5ffb0c9bSToomas Soome mDNSBool checkType = mDNStrue;
1954*5ffb0c9bSToomas Soome
1955*5ffb0c9bSToomas Soome if (QuerySuppressed(q))
1956*5ffb0c9bSToomas Soome return mDNSfalse;
1957*5ffb0c9bSToomas Soome
1958*5ffb0c9bSToomas Soome // For resource records created using multicast, the InterfaceIDs have to match
1959*5ffb0c9bSToomas Soome if (rr->InterfaceID &&
1960*5ffb0c9bSToomas Soome q->InterfaceID && rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
1961*5ffb0c9bSToomas Soome
1962*5ffb0c9bSToomas Soome // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
1963*5ffb0c9bSToomas Soome if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1964*5ffb0c9bSToomas Soome
1965*5ffb0c9bSToomas Soome if (!DNSSECRecordAnswersQuestion(rr, q, &checkType)) return mDNSfalse;
1966*5ffb0c9bSToomas Soome
1967*5ffb0c9bSToomas Soome // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
1968*5ffb0c9bSToomas Soome if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
1969*5ffb0c9bSToomas Soome
1970*5ffb0c9bSToomas Soome if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
1971*5ffb0c9bSToomas Soome
19724b22b933Srs200217 return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
19734b22b933Srs200217 }
19744b22b933Srs200217
GetRDLength(const ResourceRecord * const rr,mDNSBool estimate)19754b22b933Srs200217 mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
19764b22b933Srs200217 {
1977*5ffb0c9bSToomas Soome const RDataBody2 *const rd = (RDataBody2 *)rr->rdata->u.data;
19784b22b933Srs200217 const domainname *const name = estimate ? rr->name : mDNSNULL;
1979*5ffb0c9bSToomas Soome if (rr->rrclass == kDNSQClass_ANY) return(rr->rdlength); // Used in update packets to mean "Delete An RRset" (RFC 2136)
1980*5ffb0c9bSToomas Soome else switch (rr->rrtype)
19814b22b933Srs200217 {
19824b22b933Srs200217 case kDNSType_A: return(sizeof(rd->ipv4));
1983*5ffb0c9bSToomas Soome
1984*5ffb0c9bSToomas Soome case kDNSType_NS:
1985*5ffb0c9bSToomas Soome case kDNSType_CNAME:
1986*5ffb0c9bSToomas Soome case kDNSType_PTR:
1987*5ffb0c9bSToomas Soome case kDNSType_DNAME: return(CompressedDomainNameLength(&rd->name, name));
1988*5ffb0c9bSToomas Soome
19894b22b933Srs200217 case kDNSType_SOA: return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) +
19904b22b933Srs200217 CompressedDomainNameLength(&rd->soa.rname, name) +
19914b22b933Srs200217 5 * sizeof(mDNSOpaque32));
1992*5ffb0c9bSToomas Soome
1993*5ffb0c9bSToomas Soome case kDNSType_NULL:
1994*5ffb0c9bSToomas Soome case kDNSType_TSIG:
1995*5ffb0c9bSToomas Soome case kDNSType_TXT:
1996*5ffb0c9bSToomas Soome case kDNSType_X25:
1997*5ffb0c9bSToomas Soome case kDNSType_ISDN:
1998*5ffb0c9bSToomas Soome case kDNSType_LOC:
1999*5ffb0c9bSToomas Soome case kDNSType_DHCID: return(rr->rdlength); // Not self-describing, so have to just trust rdlength
2000*5ffb0c9bSToomas Soome
2001*5ffb0c9bSToomas Soome case kDNSType_HINFO: return (mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
2002*5ffb0c9bSToomas Soome
2003*5ffb0c9bSToomas Soome case kDNSType_MX:
2004*5ffb0c9bSToomas Soome case kDNSType_AFSDB:
2005*5ffb0c9bSToomas Soome case kDNSType_RT:
2006*5ffb0c9bSToomas Soome case kDNSType_KX: return (mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name));
2007*5ffb0c9bSToomas Soome
2008*5ffb0c9bSToomas Soome case kDNSType_RP: return (mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) +
2009*5ffb0c9bSToomas Soome CompressedDomainNameLength(&rd->rp.txt, name));
2010*5ffb0c9bSToomas Soome
2011*5ffb0c9bSToomas Soome case kDNSType_PX: return (mDNSu16)(2 + CompressedDomainNameLength(&rd->px.map822, name) +
2012*5ffb0c9bSToomas Soome CompressedDomainNameLength(&rd->px.mapx400, name));
2013*5ffb0c9bSToomas Soome
2014*5ffb0c9bSToomas Soome case kDNSType_AAAA: return(sizeof(rd->ipv6));
2015*5ffb0c9bSToomas Soome
2016*5ffb0c9bSToomas Soome case kDNSType_SRV: return (mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
2017*5ffb0c9bSToomas Soome
20184b22b933Srs200217 case kDNSType_OPT: return(rr->rdlength);
2019*5ffb0c9bSToomas Soome
2020*5ffb0c9bSToomas Soome case kDNSType_NSEC: {
2021*5ffb0c9bSToomas Soome domainname *next = (domainname *)rd->data;
2022*5ffb0c9bSToomas Soome int dlen = DomainNameLength(next);
2023*5ffb0c9bSToomas Soome //
2024*5ffb0c9bSToomas Soome if (UNICAST_NSEC(rr))
2025*5ffb0c9bSToomas Soome return (mDNSu16)(CompressedDomainNameLength(next, name) + rr->rdlength - dlen);
2026*5ffb0c9bSToomas Soome else
2027*5ffb0c9bSToomas Soome return (mDNSu16)((estimate ? 2 : dlen) + rr->rdlength - dlen);
2028*5ffb0c9bSToomas Soome }
2029*5ffb0c9bSToomas Soome
20304b22b933Srs200217 default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
20314b22b933Srs200217 return(rr->rdlength);
20324b22b933Srs200217 }
20334b22b933Srs200217 }
20344b22b933Srs200217
2035*5ffb0c9bSToomas Soome // When a local client registers (or updates) a record, we use this routine to do some simple validation checks
2036*5ffb0c9bSToomas Soome // to help reduce the risk of bogus malformed data on the network
ValidateRData(const mDNSu16 rrtype,const mDNSu16 rdlength,const RData * const rd)20374b22b933Srs200217 mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
20384b22b933Srs200217 {
20394b22b933Srs200217 mDNSu16 len;
20404b22b933Srs200217
20414b22b933Srs200217 switch(rrtype)
20424b22b933Srs200217 {
20434b22b933Srs200217 case kDNSType_A: return(rdlength == sizeof(mDNSv4Addr));
20444b22b933Srs200217
20454b22b933Srs200217 case kDNSType_NS: // Same as PTR
20464b22b933Srs200217 case kDNSType_MD: // Same as PTR
20474b22b933Srs200217 case kDNSType_MF: // Same as PTR
20484b22b933Srs200217 case kDNSType_CNAME: // Same as PTR
20494b22b933Srs200217 //case kDNSType_SOA not checked
20504b22b933Srs200217 case kDNSType_MB: // Same as PTR
20514b22b933Srs200217 case kDNSType_MG: // Same as PTR
20524b22b933Srs200217 case kDNSType_MR: // Same as PTR
20534b22b933Srs200217 //case kDNSType_NULL not checked (no specified format, so always valid)
20544b22b933Srs200217 //case kDNSType_WKS not checked
2055*5ffb0c9bSToomas Soome case kDNSType_PTR: len = DomainNameLengthLimit(&rd->u.name, rd->u.data + rdlength);
20564b22b933Srs200217 return(len <= MAX_DOMAIN_NAME && rdlength == len);
20574b22b933Srs200217
20584b22b933Srs200217 case kDNSType_HINFO: // Same as TXT (roughly)
20594b22b933Srs200217 case kDNSType_MINFO: // Same as TXT (roughly)
20604b22b933Srs200217 case kDNSType_TXT: if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035)
20614b22b933Srs200217 {
20624b22b933Srs200217 const mDNSu8 *ptr = rd->u.txt.c;
20634b22b933Srs200217 const mDNSu8 *end = rd->u.txt.c + rdlength;
20644b22b933Srs200217 while (ptr < end) ptr += 1 + ptr[0];
20654b22b933Srs200217 return (ptr == end);
20664b22b933Srs200217 }
20674b22b933Srs200217
20684b22b933Srs200217 case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
20694b22b933Srs200217
2070*5ffb0c9bSToomas Soome case kDNSType_MX: // Must be at least two-byte preference, plus domainname
2071*5ffb0c9bSToomas Soome // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
2072*5ffb0c9bSToomas Soome len = DomainNameLengthLimit(&rd->u.mx.exchange, rd->u.data + rdlength);
20734b22b933Srs200217 return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
20744b22b933Srs200217
2075*5ffb0c9bSToomas Soome case kDNSType_SRV: // Must be at least priority+weight+port, plus domainname
2076*5ffb0c9bSToomas Soome // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
2077*5ffb0c9bSToomas Soome len = DomainNameLengthLimit(&rd->u.srv.target, rd->u.data + rdlength);
20784b22b933Srs200217 return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
20794b22b933Srs200217
2080*5ffb0c9bSToomas Soome //case kDNSType_NSEC not checked
2081*5ffb0c9bSToomas Soome
20824b22b933Srs200217 default: return(mDNStrue); // Allow all other types without checking
20834b22b933Srs200217 }
20844b22b933Srs200217 }
20854b22b933Srs200217
20864b22b933Srs200217 // ***************************************************************************
20874b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
20884b22b933Srs200217 #pragma mark -
20894b22b933Srs200217 #pragma mark - DNS Message Creation Functions
20904b22b933Srs200217 #endif
20914b22b933Srs200217
InitializeDNSMessage(DNSMessageHeader * h,mDNSOpaque16 id,mDNSOpaque16 flags)20924b22b933Srs200217 mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
20934b22b933Srs200217 {
20944b22b933Srs200217 h->id = id;
20954b22b933Srs200217 h->flags = flags;
20964b22b933Srs200217 h->numQuestions = 0;
20974b22b933Srs200217 h->numAnswers = 0;
20984b22b933Srs200217 h->numAuthorities = 0;
20994b22b933Srs200217 h->numAdditionals = 0;
21004b22b933Srs200217 }
21014b22b933Srs200217
FindCompressionPointer(const mDNSu8 * const base,const mDNSu8 * const end,const mDNSu8 * const domname)21024b22b933Srs200217 mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
21034b22b933Srs200217 {
21044b22b933Srs200217 const mDNSu8 *result = end - *domname - 1;
21054b22b933Srs200217
21064b22b933Srs200217 if (*domname == 0) return(mDNSNULL); // There's no point trying to match just the root label
21074b22b933Srs200217
21084b22b933Srs200217 // This loop examines each possible starting position in packet, starting end of the packet and working backwards
21094b22b933Srs200217 while (result >= base)
21104b22b933Srs200217 {
21114b22b933Srs200217 // If the length byte and first character of the label match, then check further to see
21124b22b933Srs200217 // if this location in the packet will yield a useful name compression pointer.
21134b22b933Srs200217 if (result[0] == domname[0] && result[1] == domname[1])
21144b22b933Srs200217 {
21154b22b933Srs200217 const mDNSu8 *name = domname;
21164b22b933Srs200217 const mDNSu8 *targ = result;
21174b22b933Srs200217 while (targ + *name < end)
21184b22b933Srs200217 {
21194b22b933Srs200217 // First see if this label matches
21204b22b933Srs200217 int i;
21214b22b933Srs200217 const mDNSu8 *pointertarget;
21224b22b933Srs200217 for (i=0; i <= *name; i++) if (targ[i] != name[i]) break;
21234b22b933Srs200217 if (i <= *name) break; // If label did not match, bail out
21244b22b933Srs200217 targ += 1 + *name; // Else, did match, so advance target pointer
21254b22b933Srs200217 name += 1 + *name; // and proceed to check next label
21264b22b933Srs200217 if (*name == 0 && *targ == 0) return(result); // If no more labels, we found a match!
21274b22b933Srs200217 if (*name == 0) break; // If no more labels to match, we failed, so bail out
21284b22b933Srs200217
21294b22b933Srs200217 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
21304b22b933Srs200217 if (targ[0] < 0x40) continue; // If length value, continue to check next label
21314b22b933Srs200217 if (targ[0] < 0xC0) break; // If 40-BF, not valid
21324b22b933Srs200217 if (targ+1 >= end) break; // Second byte not present!
21334b22b933Srs200217 pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1];
21344b22b933Srs200217 if (targ < pointertarget) break; // Pointertarget must point *backwards* in the packet
21354b22b933Srs200217 if (pointertarget[0] >= 0x40) break; // Pointertarget must point to a valid length byte
21364b22b933Srs200217 targ = pointertarget;
21374b22b933Srs200217 }
21384b22b933Srs200217 }
21394b22b933Srs200217 result--; // We failed to match at this search position, so back up the tentative result pointer and try again
21404b22b933Srs200217 }
21414b22b933Srs200217 return(mDNSNULL);
21424b22b933Srs200217 }
21434b22b933Srs200217
21444b22b933Srs200217 // Put a string of dot-separated labels as length-prefixed labels
21454b22b933Srs200217 // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
21464b22b933Srs200217 // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
21474b22b933Srs200217 // end points to the end of the message so far
21484b22b933Srs200217 // ptr points to where we want to put the name
21494b22b933Srs200217 // limit points to one byte past the end of the buffer that we must not overrun
21504b22b933Srs200217 // domainname is the name to put
putDomainNameAsLabels(const DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,const domainname * const name)21514b22b933Srs200217 mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg,
21524b22b933Srs200217 mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name)
21534b22b933Srs200217 {
21544b22b933Srs200217 const mDNSu8 *const base = (const mDNSu8 *)msg;
21554b22b933Srs200217 const mDNSu8 * np = name->c;
21564b22b933Srs200217 const mDNSu8 *const max = name->c + MAX_DOMAIN_NAME; // Maximum that's valid
21574b22b933Srs200217 const mDNSu8 * pointer = mDNSNULL;
21584b22b933Srs200217 const mDNSu8 *const searchlimit = ptr;
21594b22b933Srs200217
2160*5ffb0c9bSToomas Soome if (!ptr) { LogMsg("putDomainNameAsLabels %##s ptr is null", name->c); return(mDNSNULL); }
2161*5ffb0c9bSToomas Soome
2162*5ffb0c9bSToomas Soome if (!*np) // If just writing one-byte root label, make sure we have space for that
21634b22b933Srs200217 {
2164*5ffb0c9bSToomas Soome if (ptr >= limit) return(mDNSNULL);
2165*5ffb0c9bSToomas Soome }
2166*5ffb0c9bSToomas Soome else // else, loop through writing labels and/or a compression offset
2167*5ffb0c9bSToomas Soome {
2168*5ffb0c9bSToomas Soome do {
21694b22b933Srs200217 if (*np > MAX_DOMAIN_LABEL)
21704b22b933Srs200217 { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
21714b22b933Srs200217
21724b22b933Srs200217 // This check correctly allows for the final trailing root label:
21734b22b933Srs200217 // e.g.
2174*5ffb0c9bSToomas Soome // Suppose our domain name is exactly 256 bytes long, including the final trailing root label.
2175*5ffb0c9bSToomas Soome // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local").
2176*5ffb0c9bSToomas Soome // We know that max will be at name->c[256]
21774b22b933Srs200217 // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
21784b22b933Srs200217 // six bytes, then exit the loop, write the final terminating root label, and the domain
2179*5ffb0c9bSToomas Soome // name we've written is exactly 256 bytes long, exactly at the correct legal limit.
21804b22b933Srs200217 // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
21814b22b933Srs200217 if (np + 1 + *np >= max)
2182*5ffb0c9bSToomas Soome { LogMsg("Malformed domain name %##s (more than 256 bytes)", name->c); return(mDNSNULL); }
21834b22b933Srs200217
21844b22b933Srs200217 if (base) pointer = FindCompressionPointer(base, searchlimit, np);
21854b22b933Srs200217 if (pointer) // Use a compression pointer if we can
21864b22b933Srs200217 {
2187*5ffb0c9bSToomas Soome const mDNSu16 offset = (mDNSu16)(pointer - base);
2188*5ffb0c9bSToomas Soome if (ptr+2 > limit) return(mDNSNULL); // If we don't have two bytes of space left, give up
21894b22b933Srs200217 *ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
21904b22b933Srs200217 *ptr++ = (mDNSu8)( offset & 0xFF);
21914b22b933Srs200217 return(ptr);
21924b22b933Srs200217 }
21934b22b933Srs200217 else // Else copy one label and try again
21944b22b933Srs200217 {
21954b22b933Srs200217 int i;
21964b22b933Srs200217 mDNSu8 len = *np++;
2197*5ffb0c9bSToomas Soome // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up
21984b22b933Srs200217 if (ptr + 1 + len >= limit) return(mDNSNULL);
21994b22b933Srs200217 *ptr++ = len;
22004b22b933Srs200217 for (i=0; i<len; i++) *ptr++ = *np++;
22014b22b933Srs200217 }
2202*5ffb0c9bSToomas Soome } while (*np); // While we've got characters remaining in the name, continue
22034b22b933Srs200217 }
22044b22b933Srs200217
22054b22b933Srs200217 *ptr++ = 0; // Put the final root label
2206*5ffb0c9bSToomas Soome return(ptr);
22074b22b933Srs200217 }
22084b22b933Srs200217
putVal16(mDNSu8 * ptr,mDNSu16 val)22094b22b933Srs200217 mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
22104b22b933Srs200217 {
22114b22b933Srs200217 ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
22124b22b933Srs200217 ptr[1] = (mDNSu8)((val ) & 0xFF);
22134b22b933Srs200217 return ptr + sizeof(mDNSOpaque16);
22144b22b933Srs200217 }
22154b22b933Srs200217
putVal32(mDNSu8 * ptr,mDNSu32 val)22164b22b933Srs200217 mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
22174b22b933Srs200217 {
22184b22b933Srs200217 ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
22194b22b933Srs200217 ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
22204b22b933Srs200217 ptr[2] = (mDNSu8)((val >> 8) & 0xFF);
22214b22b933Srs200217 ptr[3] = (mDNSu8)((val ) & 0xFF);
22224b22b933Srs200217 return ptr + sizeof(mDNSu32);
22234b22b933Srs200217 }
22244b22b933Srs200217
2225*5ffb0c9bSToomas Soome // Copy the RDATA information. The actual in memory storage for the data might be bigger than what the rdlength
2226*5ffb0c9bSToomas Soome // says. Hence, the only way to copy out the data from a resource record is to use putRData.
2227*5ffb0c9bSToomas Soome // msg points to the message we're building (pass mDNSNULL for "msg" if we don't want to use compression pointers)
putRData(const DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,const ResourceRecord * const rr)2228*5ffb0c9bSToomas Soome mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr)
22294b22b933Srs200217 {
2230*5ffb0c9bSToomas Soome const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
22314b22b933Srs200217 switch (rr->rrtype)
22324b22b933Srs200217 {
22334b22b933Srs200217 case kDNSType_A: if (rr->rdlength != 4)
2234*5ffb0c9bSToomas Soome { debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); return(mDNSNULL); }
22354b22b933Srs200217 if (ptr + 4 > limit) return(mDNSNULL);
2236*5ffb0c9bSToomas Soome *ptr++ = rdb->ipv4.b[0];
2237*5ffb0c9bSToomas Soome *ptr++ = rdb->ipv4.b[1];
2238*5ffb0c9bSToomas Soome *ptr++ = rdb->ipv4.b[2];
2239*5ffb0c9bSToomas Soome *ptr++ = rdb->ipv4.b[3];
22404b22b933Srs200217 return(ptr);
22414b22b933Srs200217
2242*5ffb0c9bSToomas Soome case kDNSType_NS:
2243*5ffb0c9bSToomas Soome case kDNSType_CNAME:
2244*5ffb0c9bSToomas Soome case kDNSType_PTR:
2245*5ffb0c9bSToomas Soome case kDNSType_DNAME: return(putDomainNameAsLabels(msg, ptr, limit, &rdb->name));
22464b22b933Srs200217
2247*5ffb0c9bSToomas Soome case kDNSType_SOA: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.mname);
2248*5ffb0c9bSToomas Soome if (!ptr) return(mDNSNULL);
2249*5ffb0c9bSToomas Soome ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.rname);
2250*5ffb0c9bSToomas Soome if (!ptr || ptr + 20 > limit) return(mDNSNULL);
2251*5ffb0c9bSToomas Soome ptr = putVal32(ptr, rdb->soa.serial);
2252*5ffb0c9bSToomas Soome ptr = putVal32(ptr, rdb->soa.refresh);
2253*5ffb0c9bSToomas Soome ptr = putVal32(ptr, rdb->soa.retry);
2254*5ffb0c9bSToomas Soome ptr = putVal32(ptr, rdb->soa.expire);
2255*5ffb0c9bSToomas Soome ptr = putVal32(ptr, rdb->soa.min);
2256*5ffb0c9bSToomas Soome return(ptr);
22574b22b933Srs200217
2258*5ffb0c9bSToomas Soome case kDNSType_NULL:
22594b22b933Srs200217 case kDNSType_HINFO:
2260*5ffb0c9bSToomas Soome case kDNSType_TSIG:
22614b22b933Srs200217 case kDNSType_TXT:
2262*5ffb0c9bSToomas Soome case kDNSType_X25:
2263*5ffb0c9bSToomas Soome case kDNSType_ISDN:
2264*5ffb0c9bSToomas Soome case kDNSType_LOC:
2265*5ffb0c9bSToomas Soome case kDNSType_DHCID: if (ptr + rr->rdlength > limit) return(mDNSNULL);
2266*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
2267*5ffb0c9bSToomas Soome return(ptr + rr->rdlength);
2268*5ffb0c9bSToomas Soome
2269*5ffb0c9bSToomas Soome case kDNSType_MX:
2270*5ffb0c9bSToomas Soome case kDNSType_AFSDB:
2271*5ffb0c9bSToomas Soome case kDNSType_RT:
2272*5ffb0c9bSToomas Soome case kDNSType_KX: if (ptr + 3 > limit) return(mDNSNULL);
2273*5ffb0c9bSToomas Soome ptr = putVal16(ptr, rdb->mx.preference);
2274*5ffb0c9bSToomas Soome return(putDomainNameAsLabels(msg, ptr, limit, &rdb->mx.exchange));
2275*5ffb0c9bSToomas Soome
2276*5ffb0c9bSToomas Soome case kDNSType_RP: ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.mbox);
2277*5ffb0c9bSToomas Soome if (!ptr) return(mDNSNULL);
2278*5ffb0c9bSToomas Soome ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.txt);
2279*5ffb0c9bSToomas Soome return(ptr);
2280*5ffb0c9bSToomas Soome
2281*5ffb0c9bSToomas Soome case kDNSType_PX: if (ptr + 5 > limit) return(mDNSNULL);
2282*5ffb0c9bSToomas Soome ptr = putVal16(ptr, rdb->px.preference);
2283*5ffb0c9bSToomas Soome ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.map822);
2284*5ffb0c9bSToomas Soome if (!ptr) return(mDNSNULL);
2285*5ffb0c9bSToomas Soome ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.mapx400);
2286*5ffb0c9bSToomas Soome return(ptr);
2287*5ffb0c9bSToomas Soome
2288*5ffb0c9bSToomas Soome case kDNSType_AAAA: if (rr->rdlength != sizeof(rdb->ipv6))
2289*5ffb0c9bSToomas Soome { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); return(mDNSNULL); }
2290*5ffb0c9bSToomas Soome if (ptr + sizeof(rdb->ipv6) > limit) return(mDNSNULL);
2291*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(ptr, &rdb->ipv6, sizeof(rdb->ipv6));
2292*5ffb0c9bSToomas Soome return(ptr + sizeof(rdb->ipv6));
2293*5ffb0c9bSToomas Soome
2294*5ffb0c9bSToomas Soome case kDNSType_SRV: if (ptr + 7 > limit) return(mDNSNULL);
2295*5ffb0c9bSToomas Soome *ptr++ = (mDNSu8)(rdb->srv.priority >> 8);
2296*5ffb0c9bSToomas Soome *ptr++ = (mDNSu8)(rdb->srv.priority & 0xFF);
2297*5ffb0c9bSToomas Soome *ptr++ = (mDNSu8)(rdb->srv.weight >> 8);
2298*5ffb0c9bSToomas Soome *ptr++ = (mDNSu8)(rdb->srv.weight & 0xFF);
2299*5ffb0c9bSToomas Soome *ptr++ = rdb->srv.port.b[0];
2300*5ffb0c9bSToomas Soome *ptr++ = rdb->srv.port.b[1];
2301*5ffb0c9bSToomas Soome return(putDomainNameAsLabels(msg, ptr, limit, &rdb->srv.target));
2302*5ffb0c9bSToomas Soome
2303*5ffb0c9bSToomas Soome case kDNSType_OPT: {
2304*5ffb0c9bSToomas Soome int len = 0;
2305*5ffb0c9bSToomas Soome const rdataOPT *opt;
2306*5ffb0c9bSToomas Soome const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength];
2307*5ffb0c9bSToomas Soome for (opt = &rr->rdata->u.opt[0]; opt < end; opt++)
2308*5ffb0c9bSToomas Soome len += DNSOpt_Data_Space(opt);
2309*5ffb0c9bSToomas Soome if (ptr + len > limit)
2310*5ffb0c9bSToomas Soome {
2311*5ffb0c9bSToomas Soome LogMsg("ERROR: putOptRData - out of space");
2312*5ffb0c9bSToomas Soome return mDNSNULL;
2313*5ffb0c9bSToomas Soome }
2314*5ffb0c9bSToomas Soome for (opt = &rr->rdata->u.opt[0]; opt < end; opt++)
2315*5ffb0c9bSToomas Soome {
2316*5ffb0c9bSToomas Soome const int space = DNSOpt_Data_Space(opt);
2317*5ffb0c9bSToomas Soome ptr = putVal16(ptr, opt->opt);
2318*5ffb0c9bSToomas Soome ptr = putVal16(ptr, (mDNSu16)space - 4);
2319*5ffb0c9bSToomas Soome switch (opt->opt)
2320*5ffb0c9bSToomas Soome {
2321*5ffb0c9bSToomas Soome case kDNSOpt_LLQ:
2322*5ffb0c9bSToomas Soome ptr = putVal16(ptr, opt->u.llq.vers);
2323*5ffb0c9bSToomas Soome ptr = putVal16(ptr, opt->u.llq.llqOp);
2324*5ffb0c9bSToomas Soome ptr = putVal16(ptr, opt->u.llq.err);
2325*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(ptr, opt->u.llq.id.b, 8); // 8-byte id
2326*5ffb0c9bSToomas Soome ptr += 8;
2327*5ffb0c9bSToomas Soome ptr = putVal32(ptr, opt->u.llq.llqlease);
2328*5ffb0c9bSToomas Soome break;
2329*5ffb0c9bSToomas Soome case kDNSOpt_Lease:
2330*5ffb0c9bSToomas Soome ptr = putVal32(ptr, opt->u.updatelease);
2331*5ffb0c9bSToomas Soome break;
2332*5ffb0c9bSToomas Soome case kDNSOpt_Owner:
2333*5ffb0c9bSToomas Soome *ptr++ = opt->u.owner.vers;
2334*5ffb0c9bSToomas Soome *ptr++ = opt->u.owner.seq;
2335*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(ptr, opt->u.owner.HMAC.b, 6); // 6-byte Host identifier
2336*5ffb0c9bSToomas Soome ptr += 6;
2337*5ffb0c9bSToomas Soome if (space >= DNSOpt_OwnerData_ID_Wake_Space)
2338*5ffb0c9bSToomas Soome {
2339*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(ptr, opt->u.owner.IMAC.b, 6); // 6-byte interface MAC
2340*5ffb0c9bSToomas Soome ptr += 6;
2341*5ffb0c9bSToomas Soome if (space > DNSOpt_OwnerData_ID_Wake_Space)
2342*5ffb0c9bSToomas Soome {
2343*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(ptr, opt->u.owner.password.b, space - DNSOpt_OwnerData_ID_Wake_Space);
2344*5ffb0c9bSToomas Soome ptr += space - DNSOpt_OwnerData_ID_Wake_Space;
2345*5ffb0c9bSToomas Soome }
2346*5ffb0c9bSToomas Soome }
2347*5ffb0c9bSToomas Soome break;
2348*5ffb0c9bSToomas Soome case kDNSOpt_Trace:
2349*5ffb0c9bSToomas Soome *ptr++ = opt->u.tracer.platf;
2350*5ffb0c9bSToomas Soome ptr = putVal32(ptr, opt->u.tracer.mDNSv);
2351*5ffb0c9bSToomas Soome break;
2352*5ffb0c9bSToomas Soome }
2353*5ffb0c9bSToomas Soome }
2354*5ffb0c9bSToomas Soome return ptr;
2355*5ffb0c9bSToomas Soome }
2356*5ffb0c9bSToomas Soome
2357*5ffb0c9bSToomas Soome case kDNSType_NSEC: {
2358*5ffb0c9bSToomas Soome // For NSEC records, rdlength represents the exact number of bytes
2359*5ffb0c9bSToomas Soome // of in memory storage.
2360*5ffb0c9bSToomas Soome int len = rr->rdlength;
2361*5ffb0c9bSToomas Soome mDNSu8 *nsec = (mDNSu8 *)rdb->data;
2362*5ffb0c9bSToomas Soome domainname *name = (domainname *)nsec;
2363*5ffb0c9bSToomas Soome int dlen;
2364*5ffb0c9bSToomas Soome
2365*5ffb0c9bSToomas Soome dlen = DomainNameLength(name);
2366*5ffb0c9bSToomas Soome len -= dlen;
2367*5ffb0c9bSToomas Soome nsec += dlen;
2368*5ffb0c9bSToomas Soome // This function is called when we are sending a NSEC record as part of mDNS,
2369*5ffb0c9bSToomas Soome // or to copy the data to any other buffer needed which could be a mDNS or uDNS
2370*5ffb0c9bSToomas Soome // NSEC record. The only time compression is used that when we are sending it
2371*5ffb0c9bSToomas Soome // in mDNS (indicated by non-NULL "msg") and hence we handle mDNS case
2372*5ffb0c9bSToomas Soome // separately.
2373*5ffb0c9bSToomas Soome if (!UNICAST_NSEC(rr))
2374*5ffb0c9bSToomas Soome {
2375*5ffb0c9bSToomas Soome mDNSu8 *save = ptr;
2376*5ffb0c9bSToomas Soome int i, j, wlen;
2377*5ffb0c9bSToomas Soome wlen = *(nsec + 1);
2378*5ffb0c9bSToomas Soome nsec += 2; // Skip the window number and len
2379*5ffb0c9bSToomas Soome len -= 2;
2380*5ffb0c9bSToomas Soome
2381*5ffb0c9bSToomas Soome // For our simplified use of NSEC synthetic records:
2382*5ffb0c9bSToomas Soome //
2383*5ffb0c9bSToomas Soome // nextname is always the record's own name,
2384*5ffb0c9bSToomas Soome // the block number is always 0,
2385*5ffb0c9bSToomas Soome // the count byte is a value in the range 1-32,
2386*5ffb0c9bSToomas Soome // followed by the 1-32 data bytes
2387*5ffb0c9bSToomas Soome //
2388*5ffb0c9bSToomas Soome // Note: When we send the NSEC record in mDNS, the window size is set to 32.
2389*5ffb0c9bSToomas Soome // We need to find out what the last non-NULL byte is. If we are copying out
2390*5ffb0c9bSToomas Soome // from an RDATA, we have the right length. As we need to handle both the case,
2391*5ffb0c9bSToomas Soome // we loop to find the right value instead of blindly using len to copy.
2392*5ffb0c9bSToomas Soome
2393*5ffb0c9bSToomas Soome for (i=wlen; i>0; i--) if (nsec[i-1]) break;
2394*5ffb0c9bSToomas Soome
2395*5ffb0c9bSToomas Soome ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
2396*5ffb0c9bSToomas Soome if (!ptr) { LogInfo("putRData: Can't put name, Length %d, record %##s", limit - save, rr->name->c); return(mDNSNULL); }
2397*5ffb0c9bSToomas Soome if (i) // Only put a block if at least one type exists for this name
2398*5ffb0c9bSToomas Soome {
2399*5ffb0c9bSToomas Soome if (ptr + 2 + i > limit) { LogInfo("putRData: Can't put window, Length %d, i %d, record %##s", limit - ptr, i, rr->name->c); return(mDNSNULL); }
2400*5ffb0c9bSToomas Soome *ptr++ = 0;
2401*5ffb0c9bSToomas Soome *ptr++ = (mDNSu8)i;
2402*5ffb0c9bSToomas Soome for (j=0; j<i; j++) *ptr++ = nsec[j];
2403*5ffb0c9bSToomas Soome }
2404*5ffb0c9bSToomas Soome return ptr;
2405*5ffb0c9bSToomas Soome }
2406*5ffb0c9bSToomas Soome else
2407*5ffb0c9bSToomas Soome {
2408*5ffb0c9bSToomas Soome int win, wlen;
2409*5ffb0c9bSToomas Soome
2410*5ffb0c9bSToomas Soome // Sanity check whether the bitmap is good
2411*5ffb0c9bSToomas Soome while (len)
2412*5ffb0c9bSToomas Soome {
2413*5ffb0c9bSToomas Soome if (len < 3)
2414*5ffb0c9bSToomas Soome { LogMsg("putRData: invalid length %d", len); return mDNSNULL; }
2415*5ffb0c9bSToomas Soome
2416*5ffb0c9bSToomas Soome win = *nsec++;
2417*5ffb0c9bSToomas Soome wlen = *nsec++;
2418*5ffb0c9bSToomas Soome len -= 2;
2419*5ffb0c9bSToomas Soome if (len < wlen || wlen < 1 || wlen > 32)
2420*5ffb0c9bSToomas Soome { LogMsg("putRData: invalid window length %d", wlen); return mDNSNULL; }
2421*5ffb0c9bSToomas Soome if (win < 0 || win >= 256)
2422*5ffb0c9bSToomas Soome { LogMsg("putRData: invalid window %d", win); return mDNSNULL; }
2423*5ffb0c9bSToomas Soome
2424*5ffb0c9bSToomas Soome nsec += wlen;
2425*5ffb0c9bSToomas Soome len -= wlen;
2426*5ffb0c9bSToomas Soome }
2427*5ffb0c9bSToomas Soome if (ptr + rr->rdlength > limit) { LogMsg("putRData: NSEC rdlength beyond limit %##s (%s), ptr %p, rdlength %d, limit %p", rr->name->c, DNSTypeName(rr->rrtype), ptr, rr->rdlength, limit); return(mDNSNULL);}
2428*5ffb0c9bSToomas Soome
2429*5ffb0c9bSToomas Soome // No compression allowed for "nxt", just copy the data.
2430*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
24314b22b933Srs200217 return(ptr + rr->rdlength);
24324b22b933Srs200217 }
24334b22b933Srs200217 }
24344b22b933Srs200217
2435*5ffb0c9bSToomas Soome default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype);
2436*5ffb0c9bSToomas Soome if (ptr + rr->rdlength > limit) return(mDNSNULL);
2437*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
2438*5ffb0c9bSToomas Soome return(ptr + rr->rdlength);
2439*5ffb0c9bSToomas Soome }
2440*5ffb0c9bSToomas Soome }
2441*5ffb0c9bSToomas Soome
2442*5ffb0c9bSToomas Soome #define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update)
2443*5ffb0c9bSToomas Soome
PutResourceRecordTTLWithLimit(DNSMessage * const msg,mDNSu8 * ptr,mDNSu16 * count,ResourceRecord * rr,mDNSu32 ttl,const mDNSu8 * limit)24444b22b933Srs200217 mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit)
24454b22b933Srs200217 {
24464b22b933Srs200217 mDNSu8 *endofrdata;
24474b22b933Srs200217 mDNSu16 actualLength;
2448*5ffb0c9bSToomas Soome // When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782)
2449*5ffb0c9bSToomas Soome const DNSMessage *const rdatacompressionbase = (IsUnicastUpdate(msg) && rr->rrtype == kDNSType_SRV) ? mDNSNULL : msg;
24504b22b933Srs200217
24514b22b933Srs200217 if (rr->RecordType == kDNSRecordTypeUnregistered)
24524b22b933Srs200217 {
2453*5ffb0c9bSToomas Soome LogMsg("PutResourceRecordTTLWithLimit ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
24544b22b933Srs200217 return(ptr);
24554b22b933Srs200217 }
24564b22b933Srs200217
2457*5ffb0c9bSToomas Soome if (!ptr)
2458*5ffb0c9bSToomas Soome {
2459*5ffb0c9bSToomas Soome LogMsg("PutResourceRecordTTLWithLimit ptr is null %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
2460*5ffb0c9bSToomas Soome return(mDNSNULL);
2461*5ffb0c9bSToomas Soome }
2462*5ffb0c9bSToomas Soome
24634b22b933Srs200217 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
2464*5ffb0c9bSToomas Soome // If we're out-of-space, return mDNSNULL
2465*5ffb0c9bSToomas Soome if (!ptr || ptr + 10 >= limit)
2466*5ffb0c9bSToomas Soome {
2467*5ffb0c9bSToomas Soome LogInfo("PutResourceRecordTTLWithLimit: can't put name, out of space %##s (%s), ptr %p, limit %p", rr->name->c,
2468*5ffb0c9bSToomas Soome DNSTypeName(rr->rrtype), ptr, limit);
2469*5ffb0c9bSToomas Soome return(mDNSNULL);
2470*5ffb0c9bSToomas Soome }
24714b22b933Srs200217 ptr[0] = (mDNSu8)(rr->rrtype >> 8);
24724b22b933Srs200217 ptr[1] = (mDNSu8)(rr->rrtype & 0xFF);
24734b22b933Srs200217 ptr[2] = (mDNSu8)(rr->rrclass >> 8);
24744b22b933Srs200217 ptr[3] = (mDNSu8)(rr->rrclass & 0xFF);
24754b22b933Srs200217 ptr[4] = (mDNSu8)((ttl >> 24) & 0xFF);
24764b22b933Srs200217 ptr[5] = (mDNSu8)((ttl >> 16) & 0xFF);
24774b22b933Srs200217 ptr[6] = (mDNSu8)((ttl >> 8) & 0xFF);
24784b22b933Srs200217 ptr[7] = (mDNSu8)( ttl & 0xFF);
2479*5ffb0c9bSToomas Soome // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes
2480*5ffb0c9bSToomas Soome
2481*5ffb0c9bSToomas Soome endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr);
2482*5ffb0c9bSToomas Soome if (!endofrdata)
2483*5ffb0c9bSToomas Soome {
2484*5ffb0c9bSToomas Soome LogInfo("PutResourceRecordTTLWithLimit: Ran out of space in PutResourceRecord for %##s (%s), ptr %p, limit %p", rr->name->c,
2485*5ffb0c9bSToomas Soome DNSTypeName(rr->rrtype), ptr+10, limit);
2486*5ffb0c9bSToomas Soome return(mDNSNULL);
2487*5ffb0c9bSToomas Soome }
24884b22b933Srs200217
24894b22b933Srs200217 // Go back and fill in the actual number of data bytes we wrote
24904b22b933Srs200217 // (actualLength can be less than rdlength when domain name compression is used)
24914b22b933Srs200217 actualLength = (mDNSu16)(endofrdata - ptr - 10);
24924b22b933Srs200217 ptr[8] = (mDNSu8)(actualLength >> 8);
24934b22b933Srs200217 ptr[9] = (mDNSu8)(actualLength & 0xFF);
24944b22b933Srs200217
24954b22b933Srs200217 if (count) (*count)++;
24964b22b933Srs200217 else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
24974b22b933Srs200217 return(endofrdata);
24984b22b933Srs200217 }
24994b22b933Srs200217
putEmptyResourceRecord(DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,mDNSu16 * count,const AuthRecord * rr)2500*5ffb0c9bSToomas Soome mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr)
25014b22b933Srs200217 {
25024b22b933Srs200217 ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name);
25034b22b933Srs200217 if (!ptr || ptr + 10 > limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
25044b22b933Srs200217 ptr[0] = (mDNSu8)(rr->resrec.rrtype >> 8); // Put type
25054b22b933Srs200217 ptr[1] = (mDNSu8)(rr->resrec.rrtype & 0xFF);
25064b22b933Srs200217 ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8); // Put class
25074b22b933Srs200217 ptr[3] = (mDNSu8)(rr->resrec.rrclass & 0xFF);
25084b22b933Srs200217 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // TTL is zero
25094b22b933Srs200217 ptr[8] = ptr[9] = 0; // RDATA length is zero
25104b22b933Srs200217 (*count)++;
25114b22b933Srs200217 return(ptr + 10);
25124b22b933Srs200217 }
25134b22b933Srs200217
putQuestion(DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,const domainname * const name,mDNSu16 rrtype,mDNSu16 rrclass)25144b22b933Srs200217 mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
25154b22b933Srs200217 {
25164b22b933Srs200217 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
25174b22b933Srs200217 if (!ptr || ptr+4 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL
25184b22b933Srs200217 ptr[0] = (mDNSu8)(rrtype >> 8);
25194b22b933Srs200217 ptr[1] = (mDNSu8)(rrtype & 0xFF);
25204b22b933Srs200217 ptr[2] = (mDNSu8)(rrclass >> 8);
25214b22b933Srs200217 ptr[3] = (mDNSu8)(rrclass & 0xFF);
25224b22b933Srs200217 msg->h.numQuestions++;
25234b22b933Srs200217 return(ptr+4);
25244b22b933Srs200217 }
25254b22b933Srs200217
25264b22b933Srs200217 // for dynamic updates
putZone(DNSMessage * const msg,mDNSu8 * ptr,mDNSu8 * limit,const domainname * zone,mDNSOpaque16 zoneClass)25274b22b933Srs200217 mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass)
25284b22b933Srs200217 {
25294b22b933Srs200217 ptr = putDomainNameAsLabels(msg, ptr, limit, zone);
25304b22b933Srs200217 if (!ptr || ptr + 4 > limit) return mDNSNULL; // If we're out-of-space, return NULL
25314b22b933Srs200217 *ptr++ = (mDNSu8)(kDNSType_SOA >> 8);
25324b22b933Srs200217 *ptr++ = (mDNSu8)(kDNSType_SOA & 0xFF);
25334b22b933Srs200217 *ptr++ = zoneClass.b[0];
25344b22b933Srs200217 *ptr++ = zoneClass.b[1];
25354b22b933Srs200217 msg->h.mDNS_numZones++;
25364b22b933Srs200217 return ptr;
25374b22b933Srs200217 }
25384b22b933Srs200217
25394b22b933Srs200217 // for dynamic updates
putPrereqNameNotInUse(const domainname * const name,DNSMessage * const msg,mDNSu8 * const ptr,mDNSu8 * const end)2540*5ffb0c9bSToomas Soome mDNSexport mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end)
25414b22b933Srs200217 {
25424b22b933Srs200217 AuthRecord prereq;
2543*5ffb0c9bSToomas Soome mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
2544*5ffb0c9bSToomas Soome AssignDomainName(&prereq.namestorage, name);
25454b22b933Srs200217 prereq.resrec.rrtype = kDNSQType_ANY;
25464b22b933Srs200217 prereq.resrec.rrclass = kDNSClass_NONE;
2547*5ffb0c9bSToomas Soome return putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq);
25484b22b933Srs200217 }
25494b22b933Srs200217
25504b22b933Srs200217 // for dynamic updates
putDeletionRecord(DNSMessage * msg,mDNSu8 * ptr,ResourceRecord * rr)25514b22b933Srs200217 mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
25524b22b933Srs200217 {
25534b22b933Srs200217 // deletion: specify record w/ TTL 0, class NONE
2554*5ffb0c9bSToomas Soome const mDNSu16 origclass = rr->rrclass;
25554b22b933Srs200217 rr->rrclass = kDNSClass_NONE;
25564b22b933Srs200217 ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
25574b22b933Srs200217 rr->rrclass = origclass;
25584b22b933Srs200217 return ptr;
25594b22b933Srs200217 }
25604b22b933Srs200217
2561*5ffb0c9bSToomas Soome // for dynamic updates
putDeletionRecordWithLimit(DNSMessage * msg,mDNSu8 * ptr,ResourceRecord * rr,mDNSu8 * limit)2562*5ffb0c9bSToomas Soome mDNSexport mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit)
25634b22b933Srs200217 {
2564*5ffb0c9bSToomas Soome // deletion: specify record w/ TTL 0, class NONE
2565*5ffb0c9bSToomas Soome const mDNSu16 origclass = rr->rrclass;
2566*5ffb0c9bSToomas Soome rr->rrclass = kDNSClass_NONE;
2567*5ffb0c9bSToomas Soome ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0, limit);
2568*5ffb0c9bSToomas Soome rr->rrclass = origclass;
2569*5ffb0c9bSToomas Soome return ptr;
2570*5ffb0c9bSToomas Soome }
2571*5ffb0c9bSToomas Soome
putDeleteRRSetWithLimit(DNSMessage * msg,mDNSu8 * ptr,const domainname * name,mDNSu16 rrtype,mDNSu8 * limit)2572*5ffb0c9bSToomas Soome mDNSexport mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit)
2573*5ffb0c9bSToomas Soome {
25744b22b933Srs200217 mDNSu16 class = kDNSQClass_ANY;
25754b22b933Srs200217
25764b22b933Srs200217 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
25774b22b933Srs200217 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
25784b22b933Srs200217 ptr[0] = (mDNSu8)(rrtype >> 8);
25794b22b933Srs200217 ptr[1] = (mDNSu8)(rrtype & 0xFF);
25804b22b933Srs200217 ptr[2] = (mDNSu8)(class >> 8);
25814b22b933Srs200217 ptr[3] = (mDNSu8)(class & 0xFF);
25824b22b933Srs200217 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
25834b22b933Srs200217 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
25844b22b933Srs200217
25854b22b933Srs200217 msg->h.mDNS_numUpdates++;
25864b22b933Srs200217 return ptr + 10;
25874b22b933Srs200217 }
25884b22b933Srs200217
25894b22b933Srs200217 // for dynamic updates
putDeleteAllRRSets(DNSMessage * msg,mDNSu8 * ptr,const domainname * name)25904b22b933Srs200217 mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name)
25914b22b933Srs200217 {
25924b22b933Srs200217 const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
25934b22b933Srs200217 mDNSu16 class = kDNSQClass_ANY;
25944b22b933Srs200217 mDNSu16 rrtype = kDNSQType_ANY;
25954b22b933Srs200217
25964b22b933Srs200217 ptr = putDomainNameAsLabels(msg, ptr, limit, name);
25974b22b933Srs200217 if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
25984b22b933Srs200217 ptr[0] = (mDNSu8)(rrtype >> 8);
25994b22b933Srs200217 ptr[1] = (mDNSu8)(rrtype & 0xFF);
26004b22b933Srs200217 ptr[2] = (mDNSu8)(class >> 8);
26014b22b933Srs200217 ptr[3] = (mDNSu8)(class & 0xFF);
26024b22b933Srs200217 ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
26034b22b933Srs200217 ptr[8] = ptr[9] = 0; // zero rdlength/rdata
26044b22b933Srs200217
26054b22b933Srs200217 msg->h.mDNS_numUpdates++;
26064b22b933Srs200217 return ptr + 10;
26074b22b933Srs200217 }
26084b22b933Srs200217
26094b22b933Srs200217 // for dynamic updates
putUpdateLease(DNSMessage * msg,mDNSu8 * end,mDNSu32 lease)26104b22b933Srs200217 mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
26114b22b933Srs200217 {
26124b22b933Srs200217 AuthRecord rr;
2613*5ffb0c9bSToomas Soome mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
2614*5ffb0c9bSToomas Soome rr.resrec.rrclass = NormalMaxDNSMessageData;
2615*5ffb0c9bSToomas Soome rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
2616*5ffb0c9bSToomas Soome rr.resrec.rdestimate = sizeof(rdataOPT);
2617*5ffb0c9bSToomas Soome rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease;
2618*5ffb0c9bSToomas Soome rr.resrec.rdata->u.opt[0].u.updatelease = lease;
2619*5ffb0c9bSToomas Soome end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, &rr.resrec, 0);
26204b22b933Srs200217 if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; }
2621*5ffb0c9bSToomas Soome return end;
2622*5ffb0c9bSToomas Soome }
26234b22b933Srs200217
2624*5ffb0c9bSToomas Soome // for dynamic updates
putUpdateLeaseWithLimit(DNSMessage * msg,mDNSu8 * end,mDNSu32 lease,mDNSu8 * limit)2625*5ffb0c9bSToomas Soome mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease, mDNSu8 *limit)
2626*5ffb0c9bSToomas Soome {
2627*5ffb0c9bSToomas Soome AuthRecord rr;
2628*5ffb0c9bSToomas Soome mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
2629*5ffb0c9bSToomas Soome rr.resrec.rrclass = NormalMaxDNSMessageData;
2630*5ffb0c9bSToomas Soome rr.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
2631*5ffb0c9bSToomas Soome rr.resrec.rdestimate = sizeof(rdataOPT);
2632*5ffb0c9bSToomas Soome rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease;
2633*5ffb0c9bSToomas Soome rr.resrec.rdata->u.opt[0].u.updatelease = lease;
2634*5ffb0c9bSToomas Soome end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, 0, limit);
2635*5ffb0c9bSToomas Soome if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; }
2636*5ffb0c9bSToomas Soome return end;
2637*5ffb0c9bSToomas Soome }
2638*5ffb0c9bSToomas Soome
putDNSSECOption(DNSMessage * msg,mDNSu8 * end,mDNSu8 * limit)2639*5ffb0c9bSToomas Soome mDNSexport mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit)
2640*5ffb0c9bSToomas Soome {
2641*5ffb0c9bSToomas Soome AuthRecord rr;
2642*5ffb0c9bSToomas Soome mDNSu32 ttl = 0;
2643*5ffb0c9bSToomas Soome
2644*5ffb0c9bSToomas Soome mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
2645*5ffb0c9bSToomas Soome // It is still not clear what the right size is. We will have to fine tune this once we do
2646*5ffb0c9bSToomas Soome // a lot of testing with DNSSEC.
2647*5ffb0c9bSToomas Soome rr.resrec.rrclass = 4096;
2648*5ffb0c9bSToomas Soome rr.resrec.rdlength = 0;
2649*5ffb0c9bSToomas Soome rr.resrec.rdestimate = 0;
2650*5ffb0c9bSToomas Soome // set the DO bit
2651*5ffb0c9bSToomas Soome ttl |= 0x8000;
2652*5ffb0c9bSToomas Soome end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, ttl, limit);
2653*5ffb0c9bSToomas Soome if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; }
2654*5ffb0c9bSToomas Soome return end;
2655*5ffb0c9bSToomas Soome }
2656*5ffb0c9bSToomas Soome
putHINFO(const mDNS * const m,DNSMessage * const msg,mDNSu8 * end,DomainAuthInfo * authInfo,mDNSu8 * limit)2657*5ffb0c9bSToomas Soome mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, DomainAuthInfo *authInfo, mDNSu8 *limit)
2658*5ffb0c9bSToomas Soome {
2659*5ffb0c9bSToomas Soome if (authInfo && authInfo->AutoTunnel)
2660*5ffb0c9bSToomas Soome {
2661*5ffb0c9bSToomas Soome AuthRecord hinfo;
2662*5ffb0c9bSToomas Soome mDNSu8 *h = hinfo.rdatastorage.u.data;
2663*5ffb0c9bSToomas Soome mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0];
2664*5ffb0c9bSToomas Soome mDNSu8 *newptr;
2665*5ffb0c9bSToomas Soome mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
2666*5ffb0c9bSToomas Soome AppendDomainLabel(&hinfo.namestorage, &m->hostlabel);
2667*5ffb0c9bSToomas Soome AppendDomainName (&hinfo.namestorage, &authInfo->domain);
2668*5ffb0c9bSToomas Soome hinfo.resrec.rroriginalttl = 0;
2669*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(h, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]);
2670*5ffb0c9bSToomas Soome h += 1 + (int)h[0];
2671*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(h, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]);
2672*5ffb0c9bSToomas Soome hinfo.resrec.rdlength = len;
2673*5ffb0c9bSToomas Soome hinfo.resrec.rdestimate = len;
2674*5ffb0c9bSToomas Soome newptr = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &hinfo.resrec, 0, limit);
2675*5ffb0c9bSToomas Soome return newptr;
2676*5ffb0c9bSToomas Soome }
2677*5ffb0c9bSToomas Soome else
26784b22b933Srs200217 return end;
26794b22b933Srs200217 }
26804b22b933Srs200217
26814b22b933Srs200217 // ***************************************************************************
26824b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
26834b22b933Srs200217 #pragma mark -
26844b22b933Srs200217 #pragma mark - DNS Message Parsing Functions
26854b22b933Srs200217 #endif
26864b22b933Srs200217
DomainNameHashValue(const domainname * const name)26874b22b933Srs200217 mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name)
26884b22b933Srs200217 {
26894b22b933Srs200217 mDNSu32 sum = 0;
26904b22b933Srs200217 const mDNSu8 *c;
26914b22b933Srs200217
26924b22b933Srs200217 for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
26934b22b933Srs200217 {
26944b22b933Srs200217 sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) |
26954b22b933Srs200217 (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]);
26964b22b933Srs200217 sum = (sum<<3) | (sum>>29);
26974b22b933Srs200217 }
26984b22b933Srs200217 if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
26994b22b933Srs200217 return(sum);
27004b22b933Srs200217 }
27014b22b933Srs200217
SetNewRData(ResourceRecord * const rr,RData * NewRData,mDNSu16 rdlength)27024b22b933Srs200217 mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
27034b22b933Srs200217 {
27044b22b933Srs200217 domainname *target;
27054b22b933Srs200217 if (NewRData)
27064b22b933Srs200217 {
27074b22b933Srs200217 rr->rdata = NewRData;
27084b22b933Srs200217 rr->rdlength = rdlength;
27094b22b933Srs200217 }
27104b22b933Srs200217 // Must not try to get target pointer until after updating rr->rdata
27114b22b933Srs200217 target = GetRRDomainNameTarget(rr);
27124b22b933Srs200217 rr->rdlength = GetRDLength(rr, mDNSfalse);
27134b22b933Srs200217 rr->rdestimate = GetRDLength(rr, mDNStrue);
2714*5ffb0c9bSToomas Soome rr->rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr);
27154b22b933Srs200217 }
27164b22b933Srs200217
skipDomainName(const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * const end)27174b22b933Srs200217 mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
27184b22b933Srs200217 {
27194b22b933Srs200217 mDNSu16 total = 0;
27204b22b933Srs200217
27214b22b933Srs200217 if (ptr < (mDNSu8*)msg || ptr >= end)
27224b22b933Srs200217 { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
27234b22b933Srs200217
27244b22b933Srs200217 while (1) // Read sequence of labels
27254b22b933Srs200217 {
27264b22b933Srs200217 const mDNSu8 len = *ptr++; // Read length of this label
27274b22b933Srs200217 if (len == 0) return(ptr); // If length is zero, that means this name is complete
27284b22b933Srs200217 switch (len & 0xC0)
27294b22b933Srs200217 {
27304b22b933Srs200217 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
27314b22b933Srs200217 { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
27324b22b933Srs200217 if (total + 1 + len >= MAX_DOMAIN_NAME) // Remember: expect at least one more byte for the root label
2733*5ffb0c9bSToomas Soome { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
27344b22b933Srs200217 ptr += len;
27354b22b933Srs200217 total += 1 + len;
27364b22b933Srs200217 break;
27374b22b933Srs200217
27384b22b933Srs200217 case 0x40: debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL);
27394b22b933Srs200217 case 0x80: debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL);
27404b22b933Srs200217 case 0xC0: return(ptr+1);
27414b22b933Srs200217 }
27424b22b933Srs200217 }
27434b22b933Srs200217 }
27444b22b933Srs200217
27454b22b933Srs200217 // Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
getDomainName(const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * const end,domainname * const name)27464b22b933Srs200217 mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
27474b22b933Srs200217 domainname *const name)
27484b22b933Srs200217 {
27494b22b933Srs200217 const mDNSu8 *nextbyte = mDNSNULL; // Record where we got to before we started following pointers
27504b22b933Srs200217 mDNSu8 *np = name->c; // Name pointer
27514b22b933Srs200217 const mDNSu8 *const limit = np + MAX_DOMAIN_NAME; // Limit so we don't overrun buffer
27524b22b933Srs200217
27534b22b933Srs200217 if (ptr < (mDNSu8*)msg || ptr >= end)
27544b22b933Srs200217 { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
27554b22b933Srs200217
27564b22b933Srs200217 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
27574b22b933Srs200217
27584b22b933Srs200217 while (1) // Read sequence of labels
27594b22b933Srs200217 {
27604b22b933Srs200217 const mDNSu8 len = *ptr++; // Read length of this label
27614b22b933Srs200217 if (len == 0) break; // If length is zero, that means this name is complete
27624b22b933Srs200217 switch (len & 0xC0)
27634b22b933Srs200217 {
27644b22b933Srs200217 int i;
27654b22b933Srs200217 mDNSu16 offset;
27664b22b933Srs200217
27674b22b933Srs200217 case 0x00: if (ptr + len >= end) // Remember: expect at least one more byte for the root label
27684b22b933Srs200217 { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
27694b22b933Srs200217 if (np + 1 + len >= limit) // Remember: expect at least one more byte for the root label
2770*5ffb0c9bSToomas Soome { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
27714b22b933Srs200217 *np++ = len;
27724b22b933Srs200217 for (i=0; i<len; i++) *np++ = *ptr++;
27734b22b933Srs200217 *np = 0; // Tentatively place the root label here (may be overwritten if we have more labels)
27744b22b933Srs200217 break;
27754b22b933Srs200217
27764b22b933Srs200217 case 0x40: debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
27774b22b933Srs200217 return(mDNSNULL);
27784b22b933Srs200217
27794b22b933Srs200217 case 0x80: debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
27804b22b933Srs200217
27814b22b933Srs200217 case 0xC0: offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
27824b22b933Srs200217 if (!nextbyte) nextbyte = ptr; // Record where we got to before we started following pointers
27834b22b933Srs200217 ptr = (mDNSu8 *)msg + offset;
27844b22b933Srs200217 if (ptr < (mDNSu8*)msg || ptr >= end)
27854b22b933Srs200217 { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
27864b22b933Srs200217 if (*ptr & 0xC0)
27874b22b933Srs200217 { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
27884b22b933Srs200217 break;
27894b22b933Srs200217 }
27904b22b933Srs200217 }
27914b22b933Srs200217
27924b22b933Srs200217 if (nextbyte) return(nextbyte);
27934b22b933Srs200217 else return(ptr);
27944b22b933Srs200217 }
27954b22b933Srs200217
skipResourceRecord(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end)27964b22b933Srs200217 mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
27974b22b933Srs200217 {
27984b22b933Srs200217 mDNSu16 pktrdlength;
27994b22b933Srs200217
28004b22b933Srs200217 ptr = skipDomainName(msg, ptr, end);
28014b22b933Srs200217 if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
28024b22b933Srs200217
28034b22b933Srs200217 if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
28044b22b933Srs200217 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
28054b22b933Srs200217 ptr += 10;
28064b22b933Srs200217 if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
28074b22b933Srs200217
28084b22b933Srs200217 return(ptr + pktrdlength);
28094b22b933Srs200217 }
28104b22b933Srs200217
2811*5ffb0c9bSToomas Soome // Sanity check whether the NSEC/NSEC3 bitmap is good
SanityCheckBitMap(const mDNSu8 * bmap,const mDNSu8 * end,int len)2812*5ffb0c9bSToomas Soome mDNSlocal mDNSu8 *SanityCheckBitMap(const mDNSu8 *bmap, const mDNSu8 *end, int len)
28134b22b933Srs200217 {
2814*5ffb0c9bSToomas Soome int win, wlen;
2815*5ffb0c9bSToomas Soome
2816*5ffb0c9bSToomas Soome while (bmap < end)
2817*5ffb0c9bSToomas Soome {
2818*5ffb0c9bSToomas Soome if (len < 3)
2819*5ffb0c9bSToomas Soome {
2820*5ffb0c9bSToomas Soome LogInfo("SanityCheckBitMap: invalid length %d", len);
2821*5ffb0c9bSToomas Soome return mDNSNULL;
2822*5ffb0c9bSToomas Soome }
2823*5ffb0c9bSToomas Soome
2824*5ffb0c9bSToomas Soome win = *bmap++;
2825*5ffb0c9bSToomas Soome wlen = *bmap++;
2826*5ffb0c9bSToomas Soome len -= 2;
2827*5ffb0c9bSToomas Soome if (len < wlen || wlen < 1 || wlen > 32)
2828*5ffb0c9bSToomas Soome {
2829*5ffb0c9bSToomas Soome LogInfo("SanityCheckBitMap: invalid window length %d", wlen);
2830*5ffb0c9bSToomas Soome return mDNSNULL;
2831*5ffb0c9bSToomas Soome }
2832*5ffb0c9bSToomas Soome if (win < 0 || win >= 256)
2833*5ffb0c9bSToomas Soome {
2834*5ffb0c9bSToomas Soome LogInfo("SanityCheckBitMap: invalid window %d", win);
2835*5ffb0c9bSToomas Soome return mDNSNULL;
2836*5ffb0c9bSToomas Soome }
2837*5ffb0c9bSToomas Soome
2838*5ffb0c9bSToomas Soome bmap += wlen;
2839*5ffb0c9bSToomas Soome len -= wlen;
2840*5ffb0c9bSToomas Soome }
2841*5ffb0c9bSToomas Soome return (mDNSu8 *)bmap;
2842*5ffb0c9bSToomas Soome }
2843*5ffb0c9bSToomas Soome
2844*5ffb0c9bSToomas Soome // This function is called with "msg" when we receive a DNS message and needs to parse a single resource record
2845*5ffb0c9bSToomas Soome // pointed to by "ptr". Some resource records like SOA, SRV are converted to host order and also expanded
2846*5ffb0c9bSToomas Soome // (domainnames are expanded to 255 bytes) when stored in memory.
2847*5ffb0c9bSToomas Soome //
2848*5ffb0c9bSToomas Soome // This function can also be called with "NULL" msg to parse a single resource record pointed to by ptr.
2849*5ffb0c9bSToomas Soome // The caller can do this only if the names in the resource records are compressed and validity of the
2850*5ffb0c9bSToomas Soome // resource record has already been done before. DNSSEC currently uses it this way.
SetRData(const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * end,LargeCacheRecord * const largecr,mDNSu16 rdlength)2851*5ffb0c9bSToomas Soome mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end,
2852*5ffb0c9bSToomas Soome LargeCacheRecord *const largecr, mDNSu16 rdlength)
2853*5ffb0c9bSToomas Soome {
2854*5ffb0c9bSToomas Soome CacheRecord *const rr = &largecr->r;
2855*5ffb0c9bSToomas Soome RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data;
2856*5ffb0c9bSToomas Soome
2857*5ffb0c9bSToomas Soome switch (rr->resrec.rrtype)
2858*5ffb0c9bSToomas Soome {
2859*5ffb0c9bSToomas Soome case kDNSType_A:
2860*5ffb0c9bSToomas Soome if (rdlength != sizeof(mDNSv4Addr))
2861*5ffb0c9bSToomas Soome goto fail;
2862*5ffb0c9bSToomas Soome rdb->ipv4.b[0] = ptr[0];
2863*5ffb0c9bSToomas Soome rdb->ipv4.b[1] = ptr[1];
2864*5ffb0c9bSToomas Soome rdb->ipv4.b[2] = ptr[2];
2865*5ffb0c9bSToomas Soome rdb->ipv4.b[3] = ptr[3];
2866*5ffb0c9bSToomas Soome break;
2867*5ffb0c9bSToomas Soome
2868*5ffb0c9bSToomas Soome case kDNSType_NS:
2869*5ffb0c9bSToomas Soome case kDNSType_MD:
2870*5ffb0c9bSToomas Soome case kDNSType_MF:
2871*5ffb0c9bSToomas Soome case kDNSType_CNAME:
2872*5ffb0c9bSToomas Soome case kDNSType_MB:
2873*5ffb0c9bSToomas Soome case kDNSType_MG:
2874*5ffb0c9bSToomas Soome case kDNSType_MR:
2875*5ffb0c9bSToomas Soome case kDNSType_PTR:
2876*5ffb0c9bSToomas Soome case kDNSType_NSAP_PTR:
2877*5ffb0c9bSToomas Soome case kDNSType_DNAME:
2878*5ffb0c9bSToomas Soome if (msg)
2879*5ffb0c9bSToomas Soome {
2880*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &rdb->name);
2881*5ffb0c9bSToomas Soome }
2882*5ffb0c9bSToomas Soome else
2883*5ffb0c9bSToomas Soome {
2884*5ffb0c9bSToomas Soome AssignDomainName(&rdb->name, (domainname *)ptr);
2885*5ffb0c9bSToomas Soome ptr += DomainNameLength(&rdb->name);
2886*5ffb0c9bSToomas Soome }
2887*5ffb0c9bSToomas Soome if (ptr != end)
2888*5ffb0c9bSToomas Soome {
2889*5ffb0c9bSToomas Soome debugf("SetRData: Malformed CNAME/PTR RDATA name");
2890*5ffb0c9bSToomas Soome goto fail;
2891*5ffb0c9bSToomas Soome }
2892*5ffb0c9bSToomas Soome break;
2893*5ffb0c9bSToomas Soome
2894*5ffb0c9bSToomas Soome case kDNSType_SOA:
2895*5ffb0c9bSToomas Soome if (msg)
2896*5ffb0c9bSToomas Soome {
2897*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &rdb->soa.mname);
2898*5ffb0c9bSToomas Soome }
2899*5ffb0c9bSToomas Soome else
2900*5ffb0c9bSToomas Soome {
2901*5ffb0c9bSToomas Soome AssignDomainName(&rdb->soa.mname, (domainname *)ptr);
2902*5ffb0c9bSToomas Soome ptr += DomainNameLength(&rdb->soa.mname);
2903*5ffb0c9bSToomas Soome }
2904*5ffb0c9bSToomas Soome if (!ptr)
2905*5ffb0c9bSToomas Soome {
2906*5ffb0c9bSToomas Soome debugf("SetRData: Malformed SOA RDATA mname");
2907*5ffb0c9bSToomas Soome goto fail;
2908*5ffb0c9bSToomas Soome }
2909*5ffb0c9bSToomas Soome if (msg)
2910*5ffb0c9bSToomas Soome {
2911*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &rdb->soa.rname);
2912*5ffb0c9bSToomas Soome }
2913*5ffb0c9bSToomas Soome else
2914*5ffb0c9bSToomas Soome {
2915*5ffb0c9bSToomas Soome AssignDomainName(&rdb->soa.rname, (domainname *)ptr);
2916*5ffb0c9bSToomas Soome ptr += DomainNameLength(&rdb->soa.rname);
2917*5ffb0c9bSToomas Soome }
2918*5ffb0c9bSToomas Soome if (!ptr)
2919*5ffb0c9bSToomas Soome {
2920*5ffb0c9bSToomas Soome debugf("SetRData: Malformed SOA RDATA rname");
2921*5ffb0c9bSToomas Soome goto fail;
2922*5ffb0c9bSToomas Soome }
2923*5ffb0c9bSToomas Soome if (ptr + 0x14 != end)
2924*5ffb0c9bSToomas Soome {
2925*5ffb0c9bSToomas Soome debugf("SetRData: Malformed SOA RDATA");
2926*5ffb0c9bSToomas Soome goto fail;
2927*5ffb0c9bSToomas Soome }
2928*5ffb0c9bSToomas Soome rdb->soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]);
2929*5ffb0c9bSToomas Soome rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]);
2930*5ffb0c9bSToomas Soome rdb->soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]);
2931*5ffb0c9bSToomas Soome rdb->soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]);
2932*5ffb0c9bSToomas Soome rdb->soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
2933*5ffb0c9bSToomas Soome break;
2934*5ffb0c9bSToomas Soome
2935*5ffb0c9bSToomas Soome case kDNSType_NULL:
2936*5ffb0c9bSToomas Soome case kDNSType_HINFO:
2937*5ffb0c9bSToomas Soome case kDNSType_TXT:
2938*5ffb0c9bSToomas Soome case kDNSType_X25:
2939*5ffb0c9bSToomas Soome case kDNSType_ISDN:
2940*5ffb0c9bSToomas Soome case kDNSType_LOC:
2941*5ffb0c9bSToomas Soome case kDNSType_DHCID:
2942*5ffb0c9bSToomas Soome rr->resrec.rdlength = rdlength;
2943*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
2944*5ffb0c9bSToomas Soome break;
2945*5ffb0c9bSToomas Soome
2946*5ffb0c9bSToomas Soome case kDNSType_MX:
2947*5ffb0c9bSToomas Soome case kDNSType_AFSDB:
2948*5ffb0c9bSToomas Soome case kDNSType_RT:
2949*5ffb0c9bSToomas Soome case kDNSType_KX:
2950*5ffb0c9bSToomas Soome // Preference + domainname
2951*5ffb0c9bSToomas Soome if (rdlength < 3)
2952*5ffb0c9bSToomas Soome goto fail;
2953*5ffb0c9bSToomas Soome rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2954*5ffb0c9bSToomas Soome ptr += 2;
2955*5ffb0c9bSToomas Soome if (msg)
2956*5ffb0c9bSToomas Soome {
2957*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &rdb->mx.exchange);
2958*5ffb0c9bSToomas Soome }
2959*5ffb0c9bSToomas Soome else
2960*5ffb0c9bSToomas Soome {
2961*5ffb0c9bSToomas Soome AssignDomainName(&rdb->mx.exchange, (domainname *)ptr);
2962*5ffb0c9bSToomas Soome ptr += DomainNameLength(&rdb->mx.exchange);
2963*5ffb0c9bSToomas Soome }
2964*5ffb0c9bSToomas Soome if (ptr != end)
2965*5ffb0c9bSToomas Soome {
2966*5ffb0c9bSToomas Soome debugf("SetRData: Malformed MX name");
2967*5ffb0c9bSToomas Soome goto fail;
2968*5ffb0c9bSToomas Soome }
2969*5ffb0c9bSToomas Soome break;
2970*5ffb0c9bSToomas Soome
2971*5ffb0c9bSToomas Soome case kDNSType_MINFO:
2972*5ffb0c9bSToomas Soome case kDNSType_RP:
2973*5ffb0c9bSToomas Soome // Domainname + domainname
2974*5ffb0c9bSToomas Soome if (msg)
2975*5ffb0c9bSToomas Soome {
2976*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox);
2977*5ffb0c9bSToomas Soome }
2978*5ffb0c9bSToomas Soome else
2979*5ffb0c9bSToomas Soome {
2980*5ffb0c9bSToomas Soome AssignDomainName(&rdb->rp.mbox, (domainname *)ptr);
2981*5ffb0c9bSToomas Soome ptr += DomainNameLength(&rdb->rp.mbox);
2982*5ffb0c9bSToomas Soome }
2983*5ffb0c9bSToomas Soome if (!ptr)
2984*5ffb0c9bSToomas Soome {
2985*5ffb0c9bSToomas Soome debugf("SetRData: Malformed RP mbox");
2986*5ffb0c9bSToomas Soome goto fail;
2987*5ffb0c9bSToomas Soome }
2988*5ffb0c9bSToomas Soome if (msg)
2989*5ffb0c9bSToomas Soome {
2990*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &rdb->rp.txt);
2991*5ffb0c9bSToomas Soome }
2992*5ffb0c9bSToomas Soome else
2993*5ffb0c9bSToomas Soome {
2994*5ffb0c9bSToomas Soome AssignDomainName(&rdb->rp.txt, (domainname *)ptr);
2995*5ffb0c9bSToomas Soome ptr += DomainNameLength(&rdb->rp.txt);
2996*5ffb0c9bSToomas Soome }
2997*5ffb0c9bSToomas Soome if (ptr != end)
2998*5ffb0c9bSToomas Soome {
2999*5ffb0c9bSToomas Soome debugf("SetRData: Malformed RP txt");
3000*5ffb0c9bSToomas Soome goto fail;
3001*5ffb0c9bSToomas Soome }
3002*5ffb0c9bSToomas Soome break;
3003*5ffb0c9bSToomas Soome
3004*5ffb0c9bSToomas Soome case kDNSType_PX:
3005*5ffb0c9bSToomas Soome // Preference + domainname + domainname
3006*5ffb0c9bSToomas Soome if (rdlength < 4)
3007*5ffb0c9bSToomas Soome goto fail;
3008*5ffb0c9bSToomas Soome rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
3009*5ffb0c9bSToomas Soome ptr += 2;
3010*5ffb0c9bSToomas Soome if (msg)
3011*5ffb0c9bSToomas Soome {
3012*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &rdb->px.map822);
3013*5ffb0c9bSToomas Soome }
3014*5ffb0c9bSToomas Soome else
3015*5ffb0c9bSToomas Soome {
3016*5ffb0c9bSToomas Soome AssignDomainName(&rdb->px.map822, (domainname *)ptr);
3017*5ffb0c9bSToomas Soome ptr += DomainNameLength(&rdb->px.map822);
3018*5ffb0c9bSToomas Soome }
3019*5ffb0c9bSToomas Soome if (!ptr)
3020*5ffb0c9bSToomas Soome {
3021*5ffb0c9bSToomas Soome debugf("SetRData: Malformed PX map822");
3022*5ffb0c9bSToomas Soome goto fail;
3023*5ffb0c9bSToomas Soome }
3024*5ffb0c9bSToomas Soome if (msg)
3025*5ffb0c9bSToomas Soome {
3026*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400);
3027*5ffb0c9bSToomas Soome }
3028*5ffb0c9bSToomas Soome else
3029*5ffb0c9bSToomas Soome {
3030*5ffb0c9bSToomas Soome AssignDomainName(&rdb->px.mapx400, (domainname *)ptr);
3031*5ffb0c9bSToomas Soome ptr += DomainNameLength(&rdb->px.mapx400);
3032*5ffb0c9bSToomas Soome }
3033*5ffb0c9bSToomas Soome if (ptr != end)
3034*5ffb0c9bSToomas Soome {
3035*5ffb0c9bSToomas Soome debugf("SetRData: Malformed PX mapx400");
3036*5ffb0c9bSToomas Soome goto fail;
3037*5ffb0c9bSToomas Soome }
3038*5ffb0c9bSToomas Soome break;
3039*5ffb0c9bSToomas Soome
3040*5ffb0c9bSToomas Soome case kDNSType_AAAA:
3041*5ffb0c9bSToomas Soome if (rdlength != sizeof(mDNSv6Addr))
3042*5ffb0c9bSToomas Soome goto fail;
3043*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6));
3044*5ffb0c9bSToomas Soome break;
3045*5ffb0c9bSToomas Soome
3046*5ffb0c9bSToomas Soome case kDNSType_SRV:
3047*5ffb0c9bSToomas Soome // Priority + weight + port + domainname
3048*5ffb0c9bSToomas Soome if (rdlength < 7)
3049*5ffb0c9bSToomas Soome goto fail;
3050*5ffb0c9bSToomas Soome rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
3051*5ffb0c9bSToomas Soome rdb->srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
3052*5ffb0c9bSToomas Soome rdb->srv.port.b[0] = ptr[4];
3053*5ffb0c9bSToomas Soome rdb->srv.port.b[1] = ptr[5];
3054*5ffb0c9bSToomas Soome ptr += 6;
3055*5ffb0c9bSToomas Soome if (msg)
3056*5ffb0c9bSToomas Soome {
3057*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &rdb->srv.target);
3058*5ffb0c9bSToomas Soome }
3059*5ffb0c9bSToomas Soome else
3060*5ffb0c9bSToomas Soome {
3061*5ffb0c9bSToomas Soome AssignDomainName(&rdb->srv.target, (domainname *)ptr);
3062*5ffb0c9bSToomas Soome ptr += DomainNameLength(&rdb->srv.target);
3063*5ffb0c9bSToomas Soome }
3064*5ffb0c9bSToomas Soome if (ptr != end)
3065*5ffb0c9bSToomas Soome {
3066*5ffb0c9bSToomas Soome debugf("SetRData: Malformed SRV RDATA name");
3067*5ffb0c9bSToomas Soome goto fail;
3068*5ffb0c9bSToomas Soome }
3069*5ffb0c9bSToomas Soome break;
3070*5ffb0c9bSToomas Soome
3071*5ffb0c9bSToomas Soome case kDNSType_NAPTR:
3072*5ffb0c9bSToomas Soome {
3073*5ffb0c9bSToomas Soome int savelen, len;
3074*5ffb0c9bSToomas Soome domainname name;
3075*5ffb0c9bSToomas Soome const mDNSu8 *orig = ptr;
3076*5ffb0c9bSToomas Soome
3077*5ffb0c9bSToomas Soome // Make sure the data is parseable and within the limits. DNSSEC code looks at
3078*5ffb0c9bSToomas Soome // the domain name in the end for a valid domainname.
3079*5ffb0c9bSToomas Soome //
3080*5ffb0c9bSToomas Soome // Fixed length: Order, preference (4 bytes)
3081*5ffb0c9bSToomas Soome // Variable length: flags, service, regexp, domainname
3082*5ffb0c9bSToomas Soome
3083*5ffb0c9bSToomas Soome if (rdlength < 8)
3084*5ffb0c9bSToomas Soome goto fail;
3085*5ffb0c9bSToomas Soome // Order, preference.
3086*5ffb0c9bSToomas Soome ptr += 4;
3087*5ffb0c9bSToomas Soome // Parse flags, Service and Regexp
3088*5ffb0c9bSToomas Soome // length in the first byte does not include the length byte itself
3089*5ffb0c9bSToomas Soome len = *ptr + 1;
3090*5ffb0c9bSToomas Soome ptr += len;
3091*5ffb0c9bSToomas Soome if (ptr >= end)
3092*5ffb0c9bSToomas Soome {
3093*5ffb0c9bSToomas Soome LogInfo("SetRData: Malformed NAPTR flags");
3094*5ffb0c9bSToomas Soome goto fail;
3095*5ffb0c9bSToomas Soome }
3096*5ffb0c9bSToomas Soome
3097*5ffb0c9bSToomas Soome // Service
3098*5ffb0c9bSToomas Soome len = *ptr + 1;
3099*5ffb0c9bSToomas Soome ptr += len;
3100*5ffb0c9bSToomas Soome if (ptr >= end)
3101*5ffb0c9bSToomas Soome {
3102*5ffb0c9bSToomas Soome LogInfo("SetRData: Malformed NAPTR service");
3103*5ffb0c9bSToomas Soome goto fail;
3104*5ffb0c9bSToomas Soome }
3105*5ffb0c9bSToomas Soome
3106*5ffb0c9bSToomas Soome // Regexp
3107*5ffb0c9bSToomas Soome len = *ptr + 1;
3108*5ffb0c9bSToomas Soome ptr += len;
3109*5ffb0c9bSToomas Soome if (ptr >= end)
3110*5ffb0c9bSToomas Soome {
3111*5ffb0c9bSToomas Soome LogInfo("SetRData: Malformed NAPTR regexp");
3112*5ffb0c9bSToomas Soome goto fail;
3113*5ffb0c9bSToomas Soome }
3114*5ffb0c9bSToomas Soome
3115*5ffb0c9bSToomas Soome savelen = ptr - orig;
3116*5ffb0c9bSToomas Soome
3117*5ffb0c9bSToomas Soome // RFC 2915 states that name compression is not allowed for this field. But RFC 3597
3118*5ffb0c9bSToomas Soome // states that for NAPTR we should decompress. We make sure that we store the full
3119*5ffb0c9bSToomas Soome // name rather than the compressed name
3120*5ffb0c9bSToomas Soome if (msg)
3121*5ffb0c9bSToomas Soome {
3122*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &name);
3123*5ffb0c9bSToomas Soome }
3124*5ffb0c9bSToomas Soome else
3125*5ffb0c9bSToomas Soome {
3126*5ffb0c9bSToomas Soome AssignDomainName(&name, (domainname *)ptr);
3127*5ffb0c9bSToomas Soome ptr += DomainNameLength(&name);
3128*5ffb0c9bSToomas Soome }
3129*5ffb0c9bSToomas Soome if (ptr != end)
3130*5ffb0c9bSToomas Soome {
3131*5ffb0c9bSToomas Soome LogInfo("SetRData: Malformed NAPTR RDATA name");
3132*5ffb0c9bSToomas Soome goto fail;
3133*5ffb0c9bSToomas Soome }
3134*5ffb0c9bSToomas Soome
3135*5ffb0c9bSToomas Soome rr->resrec.rdlength = savelen + DomainNameLength(&name);
3136*5ffb0c9bSToomas Soome // The uncompressed size should not exceed the limits
3137*5ffb0c9bSToomas Soome if (rr->resrec.rdlength > MaximumRDSize)
3138*5ffb0c9bSToomas Soome {
3139*5ffb0c9bSToomas Soome LogInfo("SetRData: Malformed NAPTR rdlength %d, rr->resrec.rdlength %d, "
3140*5ffb0c9bSToomas Soome "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c);
3141*5ffb0c9bSToomas Soome goto fail;
3142*5ffb0c9bSToomas Soome }
3143*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(rdb->data, orig, savelen);
3144*5ffb0c9bSToomas Soome AssignDomainName((domainname *)(rdb->data + savelen), &name);
3145*5ffb0c9bSToomas Soome break;
3146*5ffb0c9bSToomas Soome }
3147*5ffb0c9bSToomas Soome case kDNSType_OPT: {
3148*5ffb0c9bSToomas Soome mDNSu8 *dataend = rr->resrec.rdata->u.data;
3149*5ffb0c9bSToomas Soome rdataOPT *opt = rr->resrec.rdata->u.opt;
3150*5ffb0c9bSToomas Soome rr->resrec.rdlength = 0;
3151*5ffb0c9bSToomas Soome while (ptr < end && (mDNSu8 *)(opt+1) < &dataend[MaximumRDSize])
3152*5ffb0c9bSToomas Soome {
3153*5ffb0c9bSToomas Soome const rdataOPT *const currentopt = opt;
3154*5ffb0c9bSToomas Soome if (ptr + 4 > end) { LogInfo("SetRData: OPT RDATA ptr + 4 > end"); goto fail; }
3155*5ffb0c9bSToomas Soome opt->opt = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
3156*5ffb0c9bSToomas Soome opt->optlen = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
3157*5ffb0c9bSToomas Soome ptr += 4;
3158*5ffb0c9bSToomas Soome if (ptr + opt->optlen > end) { LogInfo("SetRData: ptr + opt->optlen > end"); goto fail; }
3159*5ffb0c9bSToomas Soome switch (opt->opt)
3160*5ffb0c9bSToomas Soome {
3161*5ffb0c9bSToomas Soome case kDNSOpt_LLQ:
3162*5ffb0c9bSToomas Soome if (opt->optlen == DNSOpt_LLQData_Space - 4)
3163*5ffb0c9bSToomas Soome {
3164*5ffb0c9bSToomas Soome opt->u.llq.vers = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
3165*5ffb0c9bSToomas Soome opt->u.llq.llqOp = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
3166*5ffb0c9bSToomas Soome opt->u.llq.err = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
3167*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(opt->u.llq.id.b, ptr+6, 8);
3168*5ffb0c9bSToomas Soome opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[14] << 24 | (mDNSu32)ptr[15] << 16 | (mDNSu32)ptr[16] << 8 | ptr[17]);
3169*5ffb0c9bSToomas Soome if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond)
3170*5ffb0c9bSToomas Soome opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond;
3171*5ffb0c9bSToomas Soome opt++;
3172*5ffb0c9bSToomas Soome }
3173*5ffb0c9bSToomas Soome break;
3174*5ffb0c9bSToomas Soome case kDNSOpt_Lease:
3175*5ffb0c9bSToomas Soome if (opt->optlen == DNSOpt_LeaseData_Space - 4)
3176*5ffb0c9bSToomas Soome {
3177*5ffb0c9bSToomas Soome opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
3178*5ffb0c9bSToomas Soome if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond)
3179*5ffb0c9bSToomas Soome opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond;
3180*5ffb0c9bSToomas Soome opt++;
3181*5ffb0c9bSToomas Soome }
3182*5ffb0c9bSToomas Soome break;
3183*5ffb0c9bSToomas Soome case kDNSOpt_Owner:
3184*5ffb0c9bSToomas Soome if (ValidOwnerLength(opt->optlen))
3185*5ffb0c9bSToomas Soome {
3186*5ffb0c9bSToomas Soome opt->u.owner.vers = ptr[0];
3187*5ffb0c9bSToomas Soome opt->u.owner.seq = ptr[1];
3188*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6); // 6-byte MAC address
3189*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6); // 6-byte MAC address
3190*5ffb0c9bSToomas Soome opt->u.owner.password = zeroEthAddr;
3191*5ffb0c9bSToomas Soome if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
3192*5ffb0c9bSToomas Soome {
3193*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6); // 6-byte MAC address
3194*5ffb0c9bSToomas Soome // This mDNSPlatformMemCopy is safe because the ValidOwnerLength(opt->optlen) check above
3195*5ffb0c9bSToomas Soome // ensures that opt->optlen is no more than DNSOpt_OwnerData_ID_Wake_PW6_Space - 4
3196*5ffb0c9bSToomas Soome if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
3197*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4));
3198*5ffb0c9bSToomas Soome }
3199*5ffb0c9bSToomas Soome opt++;
3200*5ffb0c9bSToomas Soome }
3201*5ffb0c9bSToomas Soome break;
3202*5ffb0c9bSToomas Soome case kDNSOpt_Trace:
3203*5ffb0c9bSToomas Soome if (opt->optlen == DNSOpt_TraceData_Space - 4)
3204*5ffb0c9bSToomas Soome {
3205*5ffb0c9bSToomas Soome opt->u.tracer.platf = ptr[0];
3206*5ffb0c9bSToomas Soome opt->u.tracer.mDNSv = (mDNSu32) ((mDNSu32)ptr[1] << 24 | (mDNSu32)ptr[2] << 16 | (mDNSu32)ptr[3] << 8 | ptr[4]);
3207*5ffb0c9bSToomas Soome opt++;
3208*5ffb0c9bSToomas Soome }
3209*5ffb0c9bSToomas Soome else
3210*5ffb0c9bSToomas Soome {
3211*5ffb0c9bSToomas Soome opt->u.tracer.platf = 0xFF;
3212*5ffb0c9bSToomas Soome opt->u.tracer.mDNSv = 0xFFFFFFFF;
3213*5ffb0c9bSToomas Soome opt++;
3214*5ffb0c9bSToomas Soome }
3215*5ffb0c9bSToomas Soome break;
3216*5ffb0c9bSToomas Soome }
3217*5ffb0c9bSToomas Soome ptr += currentopt->optlen;
3218*5ffb0c9bSToomas Soome }
3219*5ffb0c9bSToomas Soome rr->resrec.rdlength = (mDNSu16)((mDNSu8*)opt - rr->resrec.rdata->u.data);
3220*5ffb0c9bSToomas Soome if (ptr != end) { LogInfo("SetRData: Malformed OptRdata"); goto fail; }
3221*5ffb0c9bSToomas Soome break;
3222*5ffb0c9bSToomas Soome }
3223*5ffb0c9bSToomas Soome
3224*5ffb0c9bSToomas Soome case kDNSType_NSEC: {
3225*5ffb0c9bSToomas Soome domainname name;
3226*5ffb0c9bSToomas Soome int len = rdlength;
3227*5ffb0c9bSToomas Soome int bmaplen, dlen;
3228*5ffb0c9bSToomas Soome const mDNSu8 *orig = ptr;
3229*5ffb0c9bSToomas Soome const mDNSu8 *bmap;
3230*5ffb0c9bSToomas Soome
3231*5ffb0c9bSToomas Soome if (msg)
3232*5ffb0c9bSToomas Soome {
3233*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &name);
3234*5ffb0c9bSToomas Soome }
3235*5ffb0c9bSToomas Soome else
3236*5ffb0c9bSToomas Soome {
3237*5ffb0c9bSToomas Soome AssignDomainName(&name, (domainname *)ptr);
3238*5ffb0c9bSToomas Soome ptr += DomainNameLength(&name);
3239*5ffb0c9bSToomas Soome }
3240*5ffb0c9bSToomas Soome if (!ptr)
3241*5ffb0c9bSToomas Soome {
3242*5ffb0c9bSToomas Soome LogInfo("SetRData: Malformed NSEC nextname");
3243*5ffb0c9bSToomas Soome goto fail;
3244*5ffb0c9bSToomas Soome }
3245*5ffb0c9bSToomas Soome
3246*5ffb0c9bSToomas Soome dlen = DomainNameLength(&name);
3247*5ffb0c9bSToomas Soome
3248*5ffb0c9bSToomas Soome // Multicast NSECs use name compression for this field unlike the unicast case which
3249*5ffb0c9bSToomas Soome // does not use compression. And multicast case always succeeds in compression. So,
3250*5ffb0c9bSToomas Soome // the rdlength includes only the compressed space in that case. So, can't
3251*5ffb0c9bSToomas Soome // use the DomainNameLength of name to reduce the length here.
3252*5ffb0c9bSToomas Soome len -= (ptr - orig);
3253*5ffb0c9bSToomas Soome bmaplen = len; // Save the length of the bitmap
3254*5ffb0c9bSToomas Soome bmap = ptr;
3255*5ffb0c9bSToomas Soome ptr = SanityCheckBitMap(bmap, end, len);
3256*5ffb0c9bSToomas Soome if (!ptr)
3257*5ffb0c9bSToomas Soome goto fail;
3258*5ffb0c9bSToomas Soome if (ptr != end)
3259*5ffb0c9bSToomas Soome {
3260*5ffb0c9bSToomas Soome LogInfo("SetRData: Malformed NSEC length not right");
3261*5ffb0c9bSToomas Soome goto fail;
3262*5ffb0c9bSToomas Soome }
3263*5ffb0c9bSToomas Soome
3264*5ffb0c9bSToomas Soome // Initialize the right length here. When we call SetNewRData below which in turn calls
3265*5ffb0c9bSToomas Soome // GetRDLength and for NSEC case, it assumes that rdlength is intitialized
3266*5ffb0c9bSToomas Soome rr->resrec.rdlength = DomainNameLength(&name) + bmaplen;
3267*5ffb0c9bSToomas Soome
3268*5ffb0c9bSToomas Soome // Do we have space after the name expansion ?
3269*5ffb0c9bSToomas Soome if (rr->resrec.rdlength > MaximumRDSize)
3270*5ffb0c9bSToomas Soome {
3271*5ffb0c9bSToomas Soome LogInfo("SetRData: Malformed NSEC rdlength %d, rr->resrec.rdlength %d, "
3272*5ffb0c9bSToomas Soome "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c);
3273*5ffb0c9bSToomas Soome goto fail;
3274*5ffb0c9bSToomas Soome }
3275*5ffb0c9bSToomas Soome AssignDomainName((domainname *)rdb->data, &name);
3276*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(rdb->data + dlen, bmap, bmaplen);
3277*5ffb0c9bSToomas Soome break;
3278*5ffb0c9bSToomas Soome }
3279*5ffb0c9bSToomas Soome case kDNSType_NSEC3:
3280*5ffb0c9bSToomas Soome {
3281*5ffb0c9bSToomas Soome rdataNSEC3 *nsec3 = (rdataNSEC3 *)ptr;
3282*5ffb0c9bSToomas Soome mDNSu8 *p = (mDNSu8 *)&nsec3->salt;
3283*5ffb0c9bSToomas Soome int hashLength, bitmaplen;
3284*5ffb0c9bSToomas Soome
3285*5ffb0c9bSToomas Soome if (rdlength < NSEC3_FIXED_SIZE + 1)
3286*5ffb0c9bSToomas Soome {
3287*5ffb0c9bSToomas Soome LogInfo("SetRData: NSEC3 too small length %d", rdlength);
3288*5ffb0c9bSToomas Soome goto fail;
3289*5ffb0c9bSToomas Soome }
3290*5ffb0c9bSToomas Soome if (nsec3->alg != SHA1_DIGEST_TYPE)
3291*5ffb0c9bSToomas Soome {
3292*5ffb0c9bSToomas Soome LogInfo("SetRData: nsec3 alg %d not supported", nsec3->alg);
3293*5ffb0c9bSToomas Soome goto fail;
3294*5ffb0c9bSToomas Soome }
3295*5ffb0c9bSToomas Soome if (swap16(nsec3->iterations) > NSEC3_MAX_ITERATIONS)
3296*5ffb0c9bSToomas Soome {
3297*5ffb0c9bSToomas Soome LogInfo("SetRData: nsec3 iteration count %d too big", swap16(nsec3->iterations));
3298*5ffb0c9bSToomas Soome goto fail;
3299*5ffb0c9bSToomas Soome }
3300*5ffb0c9bSToomas Soome p += nsec3->saltLength;
3301*5ffb0c9bSToomas Soome // There should at least be one byte beyond saltLength
3302*5ffb0c9bSToomas Soome if (p >= end)
3303*5ffb0c9bSToomas Soome {
3304*5ffb0c9bSToomas Soome LogInfo("SetRData: nsec3 too small, at saltlength %d, p %p, end %p", nsec3->saltLength, p, end);
3305*5ffb0c9bSToomas Soome goto fail;
3306*5ffb0c9bSToomas Soome }
3307*5ffb0c9bSToomas Soome // p is pointing at hashLength
3308*5ffb0c9bSToomas Soome hashLength = (int)*p++;
3309*5ffb0c9bSToomas Soome if (!hashLength)
3310*5ffb0c9bSToomas Soome {
3311*5ffb0c9bSToomas Soome LogInfo("SetRData: hashLength zero");
3312*5ffb0c9bSToomas Soome goto fail;
3313*5ffb0c9bSToomas Soome }
3314*5ffb0c9bSToomas Soome p += hashLength;
3315*5ffb0c9bSToomas Soome if (p > end)
3316*5ffb0c9bSToomas Soome {
3317*5ffb0c9bSToomas Soome LogInfo("SetRData: nsec3 too small, at hashLength %d, p %p, end %p", hashLength, p, end);
3318*5ffb0c9bSToomas Soome goto fail;
3319*5ffb0c9bSToomas Soome }
3320*5ffb0c9bSToomas Soome
3321*5ffb0c9bSToomas Soome bitmaplen = rdlength - (int)(p - ptr);
3322*5ffb0c9bSToomas Soome p = SanityCheckBitMap(p, end, bitmaplen);
3323*5ffb0c9bSToomas Soome if (!p)
3324*5ffb0c9bSToomas Soome goto fail;
3325*5ffb0c9bSToomas Soome rr->resrec.rdlength = rdlength;
3326*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3327*5ffb0c9bSToomas Soome break;
3328*5ffb0c9bSToomas Soome }
3329*5ffb0c9bSToomas Soome case kDNSType_TKEY:
3330*5ffb0c9bSToomas Soome case kDNSType_TSIG:
3331*5ffb0c9bSToomas Soome {
3332*5ffb0c9bSToomas Soome domainname name;
3333*5ffb0c9bSToomas Soome int dlen, rlen;
3334*5ffb0c9bSToomas Soome
3335*5ffb0c9bSToomas Soome // The name should not be compressed. But we take the conservative approach
3336*5ffb0c9bSToomas Soome // and uncompress the name before we store it.
3337*5ffb0c9bSToomas Soome if (msg)
3338*5ffb0c9bSToomas Soome {
3339*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &name);
3340*5ffb0c9bSToomas Soome }
3341*5ffb0c9bSToomas Soome else
3342*5ffb0c9bSToomas Soome {
3343*5ffb0c9bSToomas Soome AssignDomainName(&name, (domainname *)ptr);
3344*5ffb0c9bSToomas Soome ptr += DomainNameLength(&name);
3345*5ffb0c9bSToomas Soome }
3346*5ffb0c9bSToomas Soome if (!ptr)
3347*5ffb0c9bSToomas Soome {
3348*5ffb0c9bSToomas Soome LogInfo("SetRData: Malformed name for TSIG/TKEY type %d", rr->resrec.rrtype);
3349*5ffb0c9bSToomas Soome goto fail;
3350*5ffb0c9bSToomas Soome }
3351*5ffb0c9bSToomas Soome dlen = DomainNameLength(&name);
3352*5ffb0c9bSToomas Soome rlen = end - ptr;
3353*5ffb0c9bSToomas Soome rr->resrec.rdlength = dlen + rlen;
3354*5ffb0c9bSToomas Soome AssignDomainName((domainname *)rdb->data, &name);
3355*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(rdb->data + dlen, ptr, rlen);
3356*5ffb0c9bSToomas Soome break;
3357*5ffb0c9bSToomas Soome }
3358*5ffb0c9bSToomas Soome case kDNSType_RRSIG:
3359*5ffb0c9bSToomas Soome {
3360*5ffb0c9bSToomas Soome const mDNSu8 *sig = ptr + RRSIG_FIXED_SIZE;
3361*5ffb0c9bSToomas Soome const mDNSu8 *orig = sig;
3362*5ffb0c9bSToomas Soome domainname name;
3363*5ffb0c9bSToomas Soome if (rdlength < RRSIG_FIXED_SIZE + 1)
3364*5ffb0c9bSToomas Soome {
3365*5ffb0c9bSToomas Soome LogInfo("SetRData: RRSIG too small length %d", rdlength);
3366*5ffb0c9bSToomas Soome goto fail;
3367*5ffb0c9bSToomas Soome }
3368*5ffb0c9bSToomas Soome if (msg)
3369*5ffb0c9bSToomas Soome {
3370*5ffb0c9bSToomas Soome sig = getDomainName(msg, sig, end, &name);
3371*5ffb0c9bSToomas Soome }
3372*5ffb0c9bSToomas Soome else
3373*5ffb0c9bSToomas Soome {
3374*5ffb0c9bSToomas Soome AssignDomainName(&name, (domainname *)sig);
3375*5ffb0c9bSToomas Soome sig += DomainNameLength(&name);
3376*5ffb0c9bSToomas Soome }
3377*5ffb0c9bSToomas Soome if (!sig)
3378*5ffb0c9bSToomas Soome {
3379*5ffb0c9bSToomas Soome LogInfo("SetRData: Malformed RRSIG record");
3380*5ffb0c9bSToomas Soome goto fail;
3381*5ffb0c9bSToomas Soome }
3382*5ffb0c9bSToomas Soome
3383*5ffb0c9bSToomas Soome if ((sig - orig) != DomainNameLength(&name))
3384*5ffb0c9bSToomas Soome {
3385*5ffb0c9bSToomas Soome LogInfo("SetRData: Malformed RRSIG record, signer name compression");
3386*5ffb0c9bSToomas Soome goto fail;
3387*5ffb0c9bSToomas Soome }
3388*5ffb0c9bSToomas Soome // Just ensure that we have at least one byte of the signature
3389*5ffb0c9bSToomas Soome if (sig + 1 >= end)
3390*5ffb0c9bSToomas Soome {
3391*5ffb0c9bSToomas Soome LogInfo("SetRData: Not enough bytes for signature type %d", rr->resrec.rrtype);
3392*5ffb0c9bSToomas Soome goto fail;
3393*5ffb0c9bSToomas Soome }
3394*5ffb0c9bSToomas Soome rr->resrec.rdlength = rdlength;
3395*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3396*5ffb0c9bSToomas Soome break;
3397*5ffb0c9bSToomas Soome }
3398*5ffb0c9bSToomas Soome case kDNSType_DNSKEY:
3399*5ffb0c9bSToomas Soome {
3400*5ffb0c9bSToomas Soome if (rdlength < DNSKEY_FIXED_SIZE + 1)
3401*5ffb0c9bSToomas Soome {
3402*5ffb0c9bSToomas Soome LogInfo("SetRData: DNSKEY too small length %d", rdlength);
3403*5ffb0c9bSToomas Soome goto fail;
3404*5ffb0c9bSToomas Soome }
3405*5ffb0c9bSToomas Soome rr->resrec.rdlength = rdlength;
3406*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3407*5ffb0c9bSToomas Soome break;
3408*5ffb0c9bSToomas Soome }
3409*5ffb0c9bSToomas Soome case kDNSType_DS:
3410*5ffb0c9bSToomas Soome {
3411*5ffb0c9bSToomas Soome if (rdlength < DS_FIXED_SIZE + 1)
3412*5ffb0c9bSToomas Soome {
3413*5ffb0c9bSToomas Soome LogInfo("SetRData: DS too small length %d", rdlength);
3414*5ffb0c9bSToomas Soome goto fail;
3415*5ffb0c9bSToomas Soome }
3416*5ffb0c9bSToomas Soome rr->resrec.rdlength = rdlength;
3417*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3418*5ffb0c9bSToomas Soome break;
3419*5ffb0c9bSToomas Soome }
3420*5ffb0c9bSToomas Soome default:
3421*5ffb0c9bSToomas Soome debugf("SetRData: Warning! Reading resource type %d (%s) as opaque data",
3422*5ffb0c9bSToomas Soome rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
3423*5ffb0c9bSToomas Soome // Note: Just because we don't understand the record type, that doesn't
3424*5ffb0c9bSToomas Soome // mean we fail. The DNS protocol specifies rdlength, so we can
3425*5ffb0c9bSToomas Soome // safely skip over unknown records and ignore them.
3426*5ffb0c9bSToomas Soome // We also grab a binary copy of the rdata anyway, since the caller
3427*5ffb0c9bSToomas Soome // might know how to interpret it even if we don't.
3428*5ffb0c9bSToomas Soome rr->resrec.rdlength = rdlength;
3429*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
3430*5ffb0c9bSToomas Soome break;
3431*5ffb0c9bSToomas Soome }
3432*5ffb0c9bSToomas Soome return mDNStrue;
3433*5ffb0c9bSToomas Soome fail:
3434*5ffb0c9bSToomas Soome return mDNSfalse;
3435*5ffb0c9bSToomas Soome }
3436*5ffb0c9bSToomas Soome
GetLargeResourceRecord(mDNS * const m,const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * end,const mDNSInterfaceID InterfaceID,mDNSu8 RecordType,LargeCacheRecord * const largecr)3437*5ffb0c9bSToomas Soome mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr,
3438*5ffb0c9bSToomas Soome const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr)
3439*5ffb0c9bSToomas Soome {
3440*5ffb0c9bSToomas Soome CacheRecord *const rr = &largecr->r;
34414b22b933Srs200217 mDNSu16 pktrdlength;
34424b22b933Srs200217
3443*5ffb0c9bSToomas Soome if (largecr == &m->rec && m->rec.r.resrec.RecordType)
3444*5ffb0c9bSToomas Soome {
3445*5ffb0c9bSToomas Soome LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r));
3446*5ffb0c9bSToomas Soome #if ForceAlerts
3447*5ffb0c9bSToomas Soome *(long*)0 = 0;
3448*5ffb0c9bSToomas Soome #endif
3449*5ffb0c9bSToomas Soome }
34504b22b933Srs200217
34514b22b933Srs200217 rr->next = mDNSNULL;
34524b22b933Srs200217 rr->resrec.name = &largecr->namestorage;
34534b22b933Srs200217
34544b22b933Srs200217 rr->NextInKAList = mDNSNULL;
34554b22b933Srs200217 rr->TimeRcvd = m ? m->timenow : 0;
34564b22b933Srs200217 rr->DelayDelivery = 0;
3457*5ffb0c9bSToomas Soome rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord()
34584b22b933Srs200217 rr->LastUsed = m ? m->timenow : 0;
34594b22b933Srs200217 rr->CRActiveQuestion = mDNSNULL;
34604b22b933Srs200217 rr->UnansweredQueries = 0;
34614b22b933Srs200217 rr->LastUnansweredTime= 0;
3462*5ffb0c9bSToomas Soome #if ENABLE_MULTI_PACKET_QUERY_SNOOPING
34634b22b933Srs200217 rr->MPUnansweredQ = 0;
34644b22b933Srs200217 rr->MPLastUnansweredQT= 0;
34654b22b933Srs200217 rr->MPUnansweredKA = 0;
34664b22b933Srs200217 rr->MPExpectingKA = mDNSfalse;
3467*5ffb0c9bSToomas Soome #endif
34684b22b933Srs200217 rr->NextInCFList = mDNSNULL;
34694b22b933Srs200217
34704b22b933Srs200217 rr->resrec.InterfaceID = InterfaceID;
3471*5ffb0c9bSToomas Soome rr->resrec.rDNSServer = mDNSNULL;
34724b22b933Srs200217
3473*5ffb0c9bSToomas Soome ptr = getDomainName(msg, ptr, end, &largecr->namestorage); // Will bail out correctly if ptr is NULL
3474*5ffb0c9bSToomas Soome if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); }
3475*5ffb0c9bSToomas Soome rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
3476*5ffb0c9bSToomas Soome
3477*5ffb0c9bSToomas Soome if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
34784b22b933Srs200217
34794b22b933Srs200217 rr->resrec.rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]);
34804b22b933Srs200217 rr->resrec.rrclass = (mDNSu16)(((mDNSu16)ptr[2] << 8 | ptr[3]) & kDNSClass_Mask);
34814b22b933Srs200217 rr->resrec.rroriginalttl = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
34824b22b933Srs200217 if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1)
34834b22b933Srs200217 rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
34844b22b933Srs200217 // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
34854b22b933Srs200217 // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
34864b22b933Srs200217 pktrdlength = (mDNSu16)((mDNSu16)ptr[8] << 8 | ptr[9]);
3487*5ffb0c9bSToomas Soome
3488*5ffb0c9bSToomas Soome // If mDNS record has cache-flush bit set, we mark it unique
3489*5ffb0c9bSToomas Soome // For uDNS records, all are implicitly deemed unique (a single DNS server is always
3490*5ffb0c9bSToomas Soome // authoritative for the entire RRSet), unless this is a truncated response
3491*5ffb0c9bSToomas Soome if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || (!InterfaceID && !(msg->h.flags.b[0] & kDNSFlag0_TC)))
34924b22b933Srs200217 RecordType |= kDNSRecordTypePacketUniqueMask;
34934b22b933Srs200217 ptr += 10;
3494*5ffb0c9bSToomas Soome if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
34954b22b933Srs200217 end = ptr + pktrdlength; // Adjust end to indicate the end of the rdata for this resource record
34964b22b933Srs200217
3497*5ffb0c9bSToomas Soome rr->resrec.rdata = (RData*)&rr->smallrdatastorage;
34984b22b933Srs200217 rr->resrec.rdata->MaxRDLength = MaximumRDSize;
34994b22b933Srs200217
3500*5ffb0c9bSToomas Soome if (pktrdlength > MaximumRDSize)
3501*5ffb0c9bSToomas Soome {
3502*5ffb0c9bSToomas Soome LogInfo("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
3503*5ffb0c9bSToomas Soome DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
3504*5ffb0c9bSToomas Soome goto fail;
3505*5ffb0c9bSToomas Soome }
3506*5ffb0c9bSToomas Soome
35074b22b933Srs200217 if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c);
35084b22b933Srs200217
3509*5ffb0c9bSToomas Soome // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
3510*5ffb0c9bSToomas Soome // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
3511*5ffb0c9bSToomas Soome // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
3512*5ffb0c9bSToomas Soome // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
3513*5ffb0c9bSToomas Soome // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
3514*5ffb0c9bSToomas Soome if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0) // Used in update packets to mean "Delete An RRset" (RFC 2136)
3515*5ffb0c9bSToomas Soome rr->resrec.rdlength = 0;
3516*5ffb0c9bSToomas Soome else if (!SetRData(msg, ptr, end, largecr, pktrdlength))
3517*5ffb0c9bSToomas Soome goto fail;
35184b22b933Srs200217
3519*5ffb0c9bSToomas Soome SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rdlength, rdestimate, rdatahash for us
35204b22b933Srs200217
35214b22b933Srs200217 // Success! Now fill in RecordType to show this record contains valid data
35224b22b933Srs200217 rr->resrec.RecordType = RecordType;
3523*5ffb0c9bSToomas Soome return(end);
3524*5ffb0c9bSToomas Soome
3525*5ffb0c9bSToomas Soome fail:
3526*5ffb0c9bSToomas Soome // If we were unable to parse the rdata in this record, we indicate that by
3527*5ffb0c9bSToomas Soome // returing a 'kDNSRecordTypePacketNegative' record with rdlength set to zero
3528*5ffb0c9bSToomas Soome rr->resrec.RecordType = kDNSRecordTypePacketNegative;
3529*5ffb0c9bSToomas Soome rr->resrec.rdlength = 0;
3530*5ffb0c9bSToomas Soome rr->resrec.rdestimate = 0;
3531*5ffb0c9bSToomas Soome rr->resrec.rdatahash = 0;
3532*5ffb0c9bSToomas Soome return(end);
35334b22b933Srs200217 }
35344b22b933Srs200217
skipQuestion(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end)35354b22b933Srs200217 mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
35364b22b933Srs200217 {
35374b22b933Srs200217 ptr = skipDomainName(msg, ptr, end);
35384b22b933Srs200217 if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); }
35394b22b933Srs200217 if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
35404b22b933Srs200217 return(ptr+4);
35414b22b933Srs200217 }
35424b22b933Srs200217
getQuestion(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end,const mDNSInterfaceID InterfaceID,DNSQuestion * question)35434b22b933Srs200217 mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
35444b22b933Srs200217 DNSQuestion *question)
35454b22b933Srs200217 {
3546*5ffb0c9bSToomas Soome mDNSPlatformMemZero(question, sizeof(*question));
35474b22b933Srs200217 question->InterfaceID = InterfaceID;
3548*5ffb0c9bSToomas Soome if (!InterfaceID) question->TargetQID = onesID; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast
35494b22b933Srs200217 ptr = getDomainName(msg, ptr, end, &question->qname);
35504b22b933Srs200217 if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
35514b22b933Srs200217 if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
35524b22b933Srs200217
35534b22b933Srs200217 question->qnamehash = DomainNameHashValue(&question->qname);
35544b22b933Srs200217 question->qtype = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); // Get type
35554b22b933Srs200217 question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); // and class
35564b22b933Srs200217 return(ptr+4);
35574b22b933Srs200217 }
35584b22b933Srs200217
LocateAnswers(const DNSMessage * const msg,const mDNSu8 * const end)35594b22b933Srs200217 mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
35604b22b933Srs200217 {
35614b22b933Srs200217 int i;
35624b22b933Srs200217 const mDNSu8 *ptr = msg->data;
35634b22b933Srs200217 for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
35644b22b933Srs200217 return(ptr);
35654b22b933Srs200217 }
35664b22b933Srs200217
LocateAuthorities(const DNSMessage * const msg,const mDNSu8 * const end)35674b22b933Srs200217 mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
35684b22b933Srs200217 {
35694b22b933Srs200217 int i;
35704b22b933Srs200217 const mDNSu8 *ptr = LocateAnswers(msg, end);
35714b22b933Srs200217 for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
35724b22b933Srs200217 return(ptr);
35734b22b933Srs200217 }
35744b22b933Srs200217
LocateAdditionals(const DNSMessage * const msg,const mDNSu8 * const end)35754b22b933Srs200217 mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end)
35764b22b933Srs200217 {
35774b22b933Srs200217 int i;
35784b22b933Srs200217 const mDNSu8 *ptr = LocateAuthorities(msg, end);
35794b22b933Srs200217 for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end);
35804b22b933Srs200217 return (ptr);
35814b22b933Srs200217 }
35824b22b933Srs200217
LocateOptRR(const DNSMessage * const msg,const mDNSu8 * const end,int minsize)3583*5ffb0c9bSToomas Soome mDNSexport const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize)
3584*5ffb0c9bSToomas Soome {
3585*5ffb0c9bSToomas Soome int i;
3586*5ffb0c9bSToomas Soome const mDNSu8 *ptr = LocateAdditionals(msg, end);
3587*5ffb0c9bSToomas Soome
3588*5ffb0c9bSToomas Soome // Locate the OPT record.
3589*5ffb0c9bSToomas Soome // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
3590*5ffb0c9bSToomas Soome // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
3591*5ffb0c9bSToomas Soome // but not necessarily the *last* entry in the Additional Section.
3592*5ffb0c9bSToomas Soome for (i = 0; ptr && i < msg->h.numAdditionals; i++)
3593*5ffb0c9bSToomas Soome {
3594*5ffb0c9bSToomas Soome if (ptr + DNSOpt_Header_Space + minsize <= end && // Make sure we have 11+minsize bytes of data
3595*5ffb0c9bSToomas Soome ptr[0] == 0 && // Name must be root label
3596*5ffb0c9bSToomas Soome ptr[1] == (kDNSType_OPT >> 8 ) && // rrtype OPT
3597*5ffb0c9bSToomas Soome ptr[2] == (kDNSType_OPT & 0xFF) &&
3598*5ffb0c9bSToomas Soome ((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)minsize)
3599*5ffb0c9bSToomas Soome return(ptr);
3600*5ffb0c9bSToomas Soome else
3601*5ffb0c9bSToomas Soome ptr = skipResourceRecord(msg, ptr, end);
3602*5ffb0c9bSToomas Soome }
3603*5ffb0c9bSToomas Soome return(mDNSNULL);
3604*5ffb0c9bSToomas Soome }
3605*5ffb0c9bSToomas Soome
3606*5ffb0c9bSToomas Soome // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
3607*5ffb0c9bSToomas Soome // it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use
3608*5ffb0c9bSToomas Soome // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
3609*5ffb0c9bSToomas Soome // The code that currently calls this assumes there's only one, instead of iterating through the set
GetLLQOptData(mDNS * const m,const DNSMessage * const msg,const mDNSu8 * const end)3610*5ffb0c9bSToomas Soome mDNSexport const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end)
3611*5ffb0c9bSToomas Soome {
3612*5ffb0c9bSToomas Soome const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LLQData_Space);
3613*5ffb0c9bSToomas Soome if (ptr)
3614*5ffb0c9bSToomas Soome {
3615*5ffb0c9bSToomas Soome ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
3616*5ffb0c9bSToomas Soome if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) return(&m->rec.r.resrec.rdata->u.opt[0]);
3617*5ffb0c9bSToomas Soome }
3618*5ffb0c9bSToomas Soome return(mDNSNULL);
3619*5ffb0c9bSToomas Soome }
3620*5ffb0c9bSToomas Soome
3621*5ffb0c9bSToomas Soome // Get the lease life of records in a dynamic update
3622*5ffb0c9bSToomas Soome // returns 0 on error or if no lease present
GetPktLease(mDNS * m,DNSMessage * msg,const mDNSu8 * end)3623*5ffb0c9bSToomas Soome mDNSexport mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end)
3624*5ffb0c9bSToomas Soome {
3625*5ffb0c9bSToomas Soome mDNSu32 result = 0;
3626*5ffb0c9bSToomas Soome const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space);
3627*5ffb0c9bSToomas Soome if (ptr) ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
3628*5ffb0c9bSToomas Soome if (ptr && m->rec.r.resrec.rdlength >= DNSOpt_LeaseData_Space && m->rec.r.resrec.rdata->u.opt[0].opt == kDNSOpt_Lease)
3629*5ffb0c9bSToomas Soome result = m->rec.r.resrec.rdata->u.opt[0].u.updatelease;
3630*5ffb0c9bSToomas Soome m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
3631*5ffb0c9bSToomas Soome return(result);
3632*5ffb0c9bSToomas Soome }
3633*5ffb0c9bSToomas Soome
DumpRecords(mDNS * const m,const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * const end,int count,char * label)3634*5ffb0c9bSToomas Soome mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label)
3635*5ffb0c9bSToomas Soome {
3636*5ffb0c9bSToomas Soome int i;
3637*5ffb0c9bSToomas Soome LogMsg("%2d %s", count, label);
3638*5ffb0c9bSToomas Soome for (i = 0; i < count && ptr; i++)
3639*5ffb0c9bSToomas Soome {
3640*5ffb0c9bSToomas Soome // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
3641*5ffb0c9bSToomas Soome // but since it's only used for debugging (and probably only on OS X, not on
3642*5ffb0c9bSToomas Soome // embedded systems) putting a 9kB object on the stack isn't a big problem.
3643*5ffb0c9bSToomas Soome LargeCacheRecord largecr;
3644*5ffb0c9bSToomas Soome ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr);
3645*5ffb0c9bSToomas Soome if (ptr) LogMsg("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r));
3646*5ffb0c9bSToomas Soome }
3647*5ffb0c9bSToomas Soome if (!ptr) LogMsg("DumpRecords: ERROR: Premature end of packet data");
3648*5ffb0c9bSToomas Soome return(ptr);
3649*5ffb0c9bSToomas Soome }
3650*5ffb0c9bSToomas Soome
3651*5ffb0c9bSToomas Soome #define DNS_OP_Name(X) ( \
3652*5ffb0c9bSToomas Soome (X) == kDNSFlag0_OP_StdQuery ? "" : \
3653*5ffb0c9bSToomas Soome (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
3654*5ffb0c9bSToomas Soome (X) == kDNSFlag0_OP_Status ? "Status " : \
3655*5ffb0c9bSToomas Soome (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \
3656*5ffb0c9bSToomas Soome (X) == kDNSFlag0_OP_Notify ? "Notify " : \
3657*5ffb0c9bSToomas Soome (X) == kDNSFlag0_OP_Update ? "Update " : "?? " )
3658*5ffb0c9bSToomas Soome
3659*5ffb0c9bSToomas Soome #define DNS_RC_Name(X) ( \
3660*5ffb0c9bSToomas Soome (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \
3661*5ffb0c9bSToomas Soome (X) == kDNSFlag1_RC_FormErr ? "FormErr" : \
3662*5ffb0c9bSToomas Soome (X) == kDNSFlag1_RC_ServFail ? "ServFail" : \
3663*5ffb0c9bSToomas Soome (X) == kDNSFlag1_RC_NXDomain ? "NXDomain" : \
3664*5ffb0c9bSToomas Soome (X) == kDNSFlag1_RC_NotImpl ? "NotImpl" : \
3665*5ffb0c9bSToomas Soome (X) == kDNSFlag1_RC_Refused ? "Refused" : \
3666*5ffb0c9bSToomas Soome (X) == kDNSFlag1_RC_YXDomain ? "YXDomain" : \
3667*5ffb0c9bSToomas Soome (X) == kDNSFlag1_RC_YXRRSet ? "YXRRSet" : \
3668*5ffb0c9bSToomas Soome (X) == kDNSFlag1_RC_NXRRSet ? "NXRRSet" : \
3669*5ffb0c9bSToomas Soome (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
3670*5ffb0c9bSToomas Soome (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" )
3671*5ffb0c9bSToomas Soome
3672*5ffb0c9bSToomas Soome // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
DumpPacket(mDNS * const m,mStatus status,mDNSBool sent,char * transport,const mDNSAddr * srcaddr,mDNSIPPort srcport,const mDNSAddr * dstaddr,mDNSIPPort dstport,const DNSMessage * const msg,const mDNSu8 * const end)3673*5ffb0c9bSToomas Soome mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport,
3674*5ffb0c9bSToomas Soome const mDNSAddr *srcaddr, mDNSIPPort srcport,
3675*5ffb0c9bSToomas Soome const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end)
3676*5ffb0c9bSToomas Soome {
3677*5ffb0c9bSToomas Soome mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update);
3678*5ffb0c9bSToomas Soome const mDNSu8 *ptr = msg->data;
3679*5ffb0c9bSToomas Soome int i;
3680*5ffb0c9bSToomas Soome DNSQuestion q;
3681*5ffb0c9bSToomas Soome char tbuffer[64], sbuffer[64], dbuffer[64] = "";
3682*5ffb0c9bSToomas Soome if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received" )] = 0;
3683*5ffb0c9bSToomas Soome else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receive")] = 0;
3684*5ffb0c9bSToomas Soome if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port " )] = 0;
3685*5ffb0c9bSToomas Soome else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0;
3686*5ffb0c9bSToomas Soome if (dstaddr || !mDNSIPPortIsZero(dstport))
3687*5ffb0c9bSToomas Soome dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0;
3688*5ffb0c9bSToomas Soome
3689*5ffb0c9bSToomas Soome 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 --",
3690*5ffb0c9bSToomas Soome tbuffer, transport,
3691*5ffb0c9bSToomas Soome DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask),
3692*5ffb0c9bSToomas Soome msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query",
3693*5ffb0c9bSToomas Soome msg->h.flags.b[0], msg->h.flags.b[1],
3694*5ffb0c9bSToomas Soome DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask),
3695*5ffb0c9bSToomas Soome msg->h.flags.b[1] & kDNSFlag1_RC_Mask,
3696*5ffb0c9bSToomas Soome msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "",
3697*5ffb0c9bSToomas Soome msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "",
3698*5ffb0c9bSToomas Soome msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "",
3699*5ffb0c9bSToomas Soome msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "",
3700*5ffb0c9bSToomas Soome msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "",
3701*5ffb0c9bSToomas Soome msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "",
3702*5ffb0c9bSToomas Soome mDNSVal16(msg->h.id),
3703*5ffb0c9bSToomas Soome end - msg->data,
3704*5ffb0c9bSToomas Soome sbuffer, mDNSVal16(srcport), dbuffer,
3705*5ffb0c9bSToomas Soome (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : ""
3706*5ffb0c9bSToomas Soome );
3707*5ffb0c9bSToomas Soome
3708*5ffb0c9bSToomas Soome LogMsg("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions");
3709*5ffb0c9bSToomas Soome for (i = 0; i < msg->h.numQuestions && ptr; i++)
3710*5ffb0c9bSToomas Soome {
3711*5ffb0c9bSToomas Soome ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q);
3712*5ffb0c9bSToomas Soome if (ptr) LogMsg("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype));
3713*5ffb0c9bSToomas Soome }
3714*5ffb0c9bSToomas Soome ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers");
3715*5ffb0c9bSToomas Soome ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities");
3716*5ffb0c9bSToomas Soome ptr = DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals");
3717*5ffb0c9bSToomas Soome LogMsg("--------------");
3718*5ffb0c9bSToomas Soome }
3719*5ffb0c9bSToomas Soome
37204b22b933Srs200217 // ***************************************************************************
37214b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
37224b22b933Srs200217 #pragma mark -
37234b22b933Srs200217 #pragma mark - Packet Sending Functions
37244b22b933Srs200217 #endif
37254b22b933Srs200217
3726*5ffb0c9bSToomas Soome // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
3727*5ffb0c9bSToomas Soome struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ };
37284b22b933Srs200217
3729*5ffb0c9bSToomas Soome struct UDPSocket_struct
37304b22b933Srs200217 {
3731*5ffb0c9bSToomas Soome mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
3732*5ffb0c9bSToomas Soome };
3733*5ffb0c9bSToomas Soome
3734*5ffb0c9bSToomas Soome // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
3735*5ffb0c9bSToomas Soome // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
mDNSSendDNSMessage(mDNS * const m,DNSMessage * const msg,mDNSu8 * end,mDNSInterfaceID InterfaceID,UDPSocket * src,const mDNSAddr * dst,mDNSIPPort dstport,TCPSocket * sock,DomainAuthInfo * authInfo,mDNSBool useBackgroundTrafficClass)3736*5ffb0c9bSToomas Soome mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
3737*5ffb0c9bSToomas Soome mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst,
3738*5ffb0c9bSToomas Soome mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo,
3739*5ffb0c9bSToomas Soome mDNSBool useBackgroundTrafficClass)
3740*5ffb0c9bSToomas Soome {
3741*5ffb0c9bSToomas Soome mStatus status = mStatus_NoError;
3742*5ffb0c9bSToomas Soome const mDNSu16 numAdditionals = msg->h.numAdditionals;
3743*5ffb0c9bSToomas Soome mDNSu8 *newend;
3744*5ffb0c9bSToomas Soome mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
3745*5ffb0c9bSToomas Soome
3746*5ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder
3747*5ffb0c9bSToomas Soome // maintain outbound packet statistics
3748*5ffb0c9bSToomas Soome if (mDNSOpaque16IsZero(msg->h.id))
3749*5ffb0c9bSToomas Soome m->MulticastPacketsSent++;
3750*5ffb0c9bSToomas Soome else
3751*5ffb0c9bSToomas Soome m->UnicastPacketsSent++;
3752*5ffb0c9bSToomas Soome #endif // APPLE_OSX_mDNSResponder
3753*5ffb0c9bSToomas Soome
3754*5ffb0c9bSToomas Soome // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code
3755*5ffb0c9bSToomas Soome if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
3756*5ffb0c9bSToomas Soome {
3757*5ffb0c9bSToomas Soome LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data);
3758*5ffb0c9bSToomas Soome return mStatus_BadParamErr;
37594b22b933Srs200217 }
37604b22b933Srs200217
3761*5ffb0c9bSToomas Soome newend = putHINFO(m, msg, end, authInfo, limit);
3762*5ffb0c9bSToomas Soome if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed msg %p end %p, limit %p", msg->data, end, limit); // Not fatal
3763*5ffb0c9bSToomas Soome else end = newend;
37644b22b933Srs200217
3765*5ffb0c9bSToomas Soome // Put all the integer values in IETF byte-order (MSB first, LSB second)
3766*5ffb0c9bSToomas Soome SwapDNSHeaderBytes(msg);
3767*5ffb0c9bSToomas Soome
3768*5ffb0c9bSToomas Soome if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0); // DNSDigest_SignMessage operates on message in network byte order
3769*5ffb0c9bSToomas Soome if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; }
3770*5ffb0c9bSToomas Soome else
37714b22b933Srs200217 {
3772*5ffb0c9bSToomas Soome // Send the packet on the wire
3773*5ffb0c9bSToomas Soome if (!sock)
3774*5ffb0c9bSToomas Soome status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, src, dst, dstport, useBackgroundTrafficClass);
3775*5ffb0c9bSToomas Soome else
3776*5ffb0c9bSToomas Soome {
3777*5ffb0c9bSToomas Soome mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
3778*5ffb0c9bSToomas Soome mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) };
3779*5ffb0c9bSToomas Soome char *buf;
3780*5ffb0c9bSToomas Soome long nsent;
3781*5ffb0c9bSToomas Soome
3782*5ffb0c9bSToomas Soome // Try to send them in one packet if we can allocate enough memory
3783*5ffb0c9bSToomas Soome buf = mDNSPlatformMemAllocate(msglen + 2);
3784*5ffb0c9bSToomas Soome if (buf)
3785*5ffb0c9bSToomas Soome {
3786*5ffb0c9bSToomas Soome buf[0] = lenbuf[0];
3787*5ffb0c9bSToomas Soome buf[1] = lenbuf[1];
3788*5ffb0c9bSToomas Soome mDNSPlatformMemCopy(buf+2, msg, msglen);
3789*5ffb0c9bSToomas Soome nsent = mDNSPlatformWriteTCP(sock, buf, msglen+2);
3790*5ffb0c9bSToomas Soome if (nsent != (msglen + 2))
3791*5ffb0c9bSToomas Soome {
3792*5ffb0c9bSToomas Soome LogMsg("mDNSSendDNSMessage: write message failed %d/%d", nsent, msglen);
3793*5ffb0c9bSToomas Soome status = mStatus_ConnFailed;
3794*5ffb0c9bSToomas Soome }
3795*5ffb0c9bSToomas Soome mDNSPlatformMemFree(buf);
37964b22b933Srs200217 }
37974b22b933Srs200217 else
37984b22b933Srs200217 {
3799*5ffb0c9bSToomas Soome nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2);
3800*5ffb0c9bSToomas Soome if (nsent != 2)
3801*5ffb0c9bSToomas Soome {
3802*5ffb0c9bSToomas Soome LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2);
3803*5ffb0c9bSToomas Soome status = mStatus_ConnFailed;
3804*5ffb0c9bSToomas Soome }
3805*5ffb0c9bSToomas Soome else
3806*5ffb0c9bSToomas Soome {
3807*5ffb0c9bSToomas Soome nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen);
3808*5ffb0c9bSToomas Soome if (nsent != msglen)
3809*5ffb0c9bSToomas Soome {
3810*5ffb0c9bSToomas Soome LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen);
3811*5ffb0c9bSToomas Soome status = mStatus_ConnFailed;
3812*5ffb0c9bSToomas Soome }
3813*5ffb0c9bSToomas Soome }
3814*5ffb0c9bSToomas Soome }
3815*5ffb0c9bSToomas Soome }
38164b22b933Srs200217 }
38174b22b933Srs200217
3818*5ffb0c9bSToomas Soome // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage)
3819*5ffb0c9bSToomas Soome SwapDNSHeaderBytes(msg);
3820*5ffb0c9bSToomas Soome
3821*5ffb0c9bSToomas Soome // Dump the packet with the HINFO and TSIG
3822*5ffb0c9bSToomas Soome if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id)) {
3823*5ffb0c9bSToomas Soome mDNSIPPort port = MulticastDNSPort;
3824*5ffb0c9bSToomas Soome DumpPacket(m, status, mDNStrue, sock &&
3825*5ffb0c9bSToomas Soome (sock->flags & kTCPSocketFlags_UseTLS) ?
3826*5ffb0c9bSToomas Soome "TLS" : sock ? "TCP" : "UDP", mDNSNULL,
3827*5ffb0c9bSToomas Soome src ? src->port : port, dst, dstport, msg, end);
3828*5ffb0c9bSToomas Soome }
3829*5ffb0c9bSToomas Soome
3830*5ffb0c9bSToomas Soome // put the number of additionals back the way it was
3831*5ffb0c9bSToomas Soome msg->h.numAdditionals = numAdditionals;
38324b22b933Srs200217
38334b22b933Srs200217 return(status);
38344b22b933Srs200217 }
38354b22b933Srs200217
38364b22b933Srs200217 // ***************************************************************************
38374b22b933Srs200217 #if COMPILER_LIKES_PRAGMA_MARK
38384b22b933Srs200217 #pragma mark -
38394b22b933Srs200217 #pragma mark - RR List Management & Task Management
38404b22b933Srs200217 #endif
38414b22b933Srs200217
mDNS_Lock_(mDNS * const m,const char * const functionname)3842*5ffb0c9bSToomas Soome mDNSexport void mDNS_Lock_(mDNS *const m, const char * const functionname)
38434b22b933Srs200217 {
38444b22b933Srs200217 // MUST grab the platform lock FIRST!
38454b22b933Srs200217 mDNSPlatformLock(m);
38464b22b933Srs200217
38474b22b933Srs200217 // Normally, mDNS_reentrancy is zero and so is mDNS_busy
38484b22b933Srs200217 // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
38494b22b933Srs200217 // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
38504b22b933Srs200217 // If mDNS_busy != mDNS_reentrancy that's a bad sign
38514b22b933Srs200217 if (m->mDNS_busy != m->mDNS_reentrancy)
3852*5ffb0c9bSToomas Soome {
3853*5ffb0c9bSToomas Soome LogMsg("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy);
3854*5ffb0c9bSToomas Soome #if ForceAlerts
3855*5ffb0c9bSToomas Soome *(long*)0 = 0;
3856*5ffb0c9bSToomas Soome #endif
3857*5ffb0c9bSToomas Soome }
38584b22b933Srs200217
38594b22b933Srs200217 // If this is an initial entry into the mDNSCore code, set m->timenow
38604b22b933Srs200217 // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
38614b22b933Srs200217 if (m->mDNS_busy == 0)
38624b22b933Srs200217 {
38634b22b933Srs200217 if (m->timenow)
3864*5ffb0c9bSToomas Soome LogMsg("%s: mDNS_Lock: m->timenow already set (%ld/%ld)", functionname, m->timenow, mDNS_TimeNow_NoLock(m));
38654b22b933Srs200217 m->timenow = mDNS_TimeNow_NoLock(m);
38664b22b933Srs200217 if (m->timenow == 0) m->timenow = 1;
38674b22b933Srs200217 }
38684b22b933Srs200217 else if (m->timenow == 0)
38694b22b933Srs200217 {
3870*5ffb0c9bSToomas Soome LogMsg("%s: mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", functionname, m->mDNS_busy);
38714b22b933Srs200217 m->timenow = mDNS_TimeNow_NoLock(m);
38724b22b933Srs200217 if (m->timenow == 0) m->timenow = 1;
38734b22b933Srs200217 }
38744b22b933Srs200217
38754b22b933Srs200217 if (m->timenow_last - m->timenow > 0)
38764b22b933Srs200217 {
38774b22b933Srs200217 m->timenow_adjust += m->timenow_last - m->timenow;
3878*5ffb0c9bSToomas Soome LogMsg("%s: mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", functionname, m->timenow_last - m->timenow, m->timenow_adjust);
38794b22b933Srs200217 m->timenow = m->timenow_last;
38804b22b933Srs200217 }
38814b22b933Srs200217 m->timenow_last = m->timenow;
38824b22b933Srs200217
38834b22b933Srs200217 // Increment mDNS_busy so we'll recognise re-entrant calls
38844b22b933Srs200217 m->mDNS_busy++;
38854b22b933Srs200217 }
38864b22b933Srs200217
AnyLocalRecordReady(const mDNS * const m)3887*5ffb0c9bSToomas Soome mDNSlocal AuthRecord *AnyLocalRecordReady(const mDNS *const m)
3888*5ffb0c9bSToomas Soome {
3889*5ffb0c9bSToomas Soome AuthRecord *rr;
3890*5ffb0c9bSToomas Soome for (rr = m->NewLocalRecords; rr; rr = rr->next)
3891*5ffb0c9bSToomas Soome if (LocalRecordReady(rr)) return rr;
3892*5ffb0c9bSToomas Soome return mDNSNULL;
3893*5ffb0c9bSToomas Soome }
3894*5ffb0c9bSToomas Soome
GetNextScheduledEvent(const mDNS * const m)38954b22b933Srs200217 mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
38964b22b933Srs200217 {
38974b22b933Srs200217 mDNSs32 e = m->timenow + 0x78000000;
3898*5ffb0c9bSToomas Soome if (m->mDNSPlatformStatus != mStatus_NoError) return(e);
38994b22b933Srs200217 if (m->NewQuestions)
39004b22b933Srs200217 {
39014b22b933Srs200217 if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering;
39024b22b933Srs200217 else return(m->timenow);
39034b22b933Srs200217 }
39044b22b933Srs200217 if (m->NewLocalOnlyQuestions) return(m->timenow);
3905*5ffb0c9bSToomas Soome if (m->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow);
3906*5ffb0c9bSToomas Soome if (m->NewLocalOnlyRecords) return(m->timenow);
3907*5ffb0c9bSToomas Soome if (m->SPSProxyListChanged) return(m->timenow);
3908*5ffb0c9bSToomas Soome if (m->LocalRemoveEvents) return(m->timenow);
3909*5ffb0c9bSToomas Soome
39104b22b933Srs200217 #ifndef UNICAST_DISABLED
3911*5ffb0c9bSToomas Soome if (e - m->NextuDNSEvent > 0) e = m->NextuDNSEvent;
3912*5ffb0c9bSToomas Soome if (e - m->NextScheduledNATOp > 0) e = m->NextScheduledNATOp;
3913*5ffb0c9bSToomas Soome if (m->NextSRVUpdate && e - m->NextSRVUpdate > 0) e = m->NextSRVUpdate;
39144b22b933Srs200217 #endif
3915*5ffb0c9bSToomas Soome
39164b22b933Srs200217 if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck;
3917*5ffb0c9bSToomas Soome if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS;
3918*5ffb0c9bSToomas Soome if (e - m->NextScheduledKA > 0) e = m->NextScheduledKA;
3919*5ffb0c9bSToomas Soome
3920*5ffb0c9bSToomas Soome // NextScheduledSPRetry only valid when DelaySleep not set
3921*5ffb0c9bSToomas Soome if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry;
3922*5ffb0c9bSToomas Soome if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep;
3923*5ffb0c9bSToomas Soome
3924*5ffb0c9bSToomas Soome if (m->SuppressSending)
3925*5ffb0c9bSToomas Soome {
3926*5ffb0c9bSToomas Soome if (e - m->SuppressSending > 0) e = m->SuppressSending;
3927*5ffb0c9bSToomas Soome }
3928*5ffb0c9bSToomas Soome else
3929*5ffb0c9bSToomas Soome {
39304b22b933Srs200217 if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery;
39314b22b933Srs200217 if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe;
39324b22b933Srs200217 if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
3933*5ffb0c9bSToomas Soome }
3934*5ffb0c9bSToomas Soome if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime;
39354b22b933Srs200217 return(e);
39364b22b933Srs200217 }
39374b22b933Srs200217
ShowTaskSchedulingError(mDNS * const m)3938*5ffb0c9bSToomas Soome mDNSexport void ShowTaskSchedulingError(mDNS *const m)
3939*5ffb0c9bSToomas Soome {
3940*5ffb0c9bSToomas Soome AuthRecord *rr;
3941*5ffb0c9bSToomas Soome mDNS_Lock(m);
3942*5ffb0c9bSToomas Soome
3943*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: Continuously busy for more than a second");
3944*5ffb0c9bSToomas Soome
3945*5ffb0c9bSToomas Soome // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above
3946*5ffb0c9bSToomas Soome
3947*5ffb0c9bSToomas Soome if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0))
3948*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: NewQuestion %##s (%s)",
3949*5ffb0c9bSToomas Soome m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
3950*5ffb0c9bSToomas Soome
3951*5ffb0c9bSToomas Soome if (m->NewLocalOnlyQuestions)
3952*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
3953*5ffb0c9bSToomas Soome m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
3954*5ffb0c9bSToomas Soome
3955*5ffb0c9bSToomas Soome if (m->NewLocalRecords)
3956*5ffb0c9bSToomas Soome {
3957*5ffb0c9bSToomas Soome rr = AnyLocalRecordReady(m);
3958*5ffb0c9bSToomas Soome if (rr) LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr));
3959*5ffb0c9bSToomas Soome }
3960*5ffb0c9bSToomas Soome
3961*5ffb0c9bSToomas Soome if (m->NewLocalOnlyRecords) LogMsg("Task Scheduling Error: NewLocalOnlyRecords");
3962*5ffb0c9bSToomas Soome
3963*5ffb0c9bSToomas Soome if (m->SPSProxyListChanged) LogMsg("Task Scheduling Error: SPSProxyListChanged");
3964*5ffb0c9bSToomas Soome if (m->LocalRemoveEvents) LogMsg("Task Scheduling Error: LocalRemoveEvents");
3965*5ffb0c9bSToomas Soome
3966*5ffb0c9bSToomas Soome if (m->timenow - m->NextScheduledEvent >= 0)
3967*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m->timenow - m->NextScheduledEvent);
3968*5ffb0c9bSToomas Soome
3969*5ffb0c9bSToomas Soome #ifndef UNICAST_DISABLED
3970*5ffb0c9bSToomas Soome if (m->timenow - m->NextuDNSEvent >= 0)
3971*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->NextuDNSEvent %d", m->timenow - m->NextuDNSEvent);
3972*5ffb0c9bSToomas Soome if (m->timenow - m->NextScheduledNATOp >= 0)
3973*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp);
3974*5ffb0c9bSToomas Soome if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0)
3975*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->NextSRVUpdate %d", m->timenow - m->NextSRVUpdate);
3976*5ffb0c9bSToomas Soome #endif
3977*5ffb0c9bSToomas Soome
3978*5ffb0c9bSToomas Soome if (m->timenow - m->NextCacheCheck >= 0)
3979*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck);
3980*5ffb0c9bSToomas Soome if (m->timenow - m->NextScheduledSPS >= 0)
3981*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS);
3982*5ffb0c9bSToomas Soome if (m->timenow - m->NextScheduledKA >= 0)
3983*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->NextScheduledKA %d", m->timenow - m->NextScheduledKA);
3984*5ffb0c9bSToomas Soome if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0)
3985*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry);
3986*5ffb0c9bSToomas Soome if (m->DelaySleep && m->timenow - m->DelaySleep >= 0)
3987*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep);
3988*5ffb0c9bSToomas Soome
3989*5ffb0c9bSToomas Soome if (m->SuppressSending && m->timenow - m->SuppressSending >= 0)
3990*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending);
3991*5ffb0c9bSToomas Soome if (m->timenow - m->NextScheduledQuery >= 0)
3992*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery);
3993*5ffb0c9bSToomas Soome if (m->timenow - m->NextScheduledProbe >= 0)
3994*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe);
3995*5ffb0c9bSToomas Soome if (m->timenow - m->NextScheduledResponse >= 0)
3996*5ffb0c9bSToomas Soome LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse);
3997*5ffb0c9bSToomas Soome
3998*5ffb0c9bSToomas Soome mDNS_Unlock(m);
3999*5ffb0c9bSToomas Soome }
4000*5ffb0c9bSToomas Soome
mDNS_Unlock_(mDNS * const m,const char * const functionname)4001*5ffb0c9bSToomas Soome mDNSexport void mDNS_Unlock_(mDNS *const m, const char * const functionname)
40024b22b933Srs200217 {
40034b22b933Srs200217 // Decrement mDNS_busy
40044b22b933Srs200217 m->mDNS_busy--;
40054b22b933Srs200217
40064b22b933Srs200217 // Check for locking failures
40074b22b933Srs200217 if (m->mDNS_busy != m->mDNS_reentrancy)
4008*5ffb0c9bSToomas Soome {
4009*5ffb0c9bSToomas Soome LogMsg("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy);
4010*5ffb0c9bSToomas Soome #if ForceAlerts
4011*5ffb0c9bSToomas Soome *(long*)0 = 0;
4012*5ffb0c9bSToomas Soome #endif
4013*5ffb0c9bSToomas Soome }
40144b22b933Srs200217
40154b22b933Srs200217 // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
40164b22b933Srs200217 if (m->mDNS_busy == 0)
40174b22b933Srs200217 {
40184b22b933Srs200217 m->NextScheduledEvent = GetNextScheduledEvent(m);
4019*5ffb0c9bSToomas Soome if (m->timenow == 0) LogMsg("%s: mDNS_Unlock: ERROR! m->timenow aready zero", functionname);
40204b22b933Srs200217 m->timenow = 0;
40214b22b933Srs200217 }
40224b22b933Srs200217
40234b22b933Srs200217 // MUST release the platform lock LAST!
40244b22b933Srs200217 mDNSPlatformUnlock(m);
40254b22b933Srs200217 }
4026*5ffb0c9bSToomas Soome
4027*5ffb0c9bSToomas Soome // ***************************************************************************
4028*5ffb0c9bSToomas Soome #if COMPILER_LIKES_PRAGMA_MARK
4029*5ffb0c9bSToomas Soome #pragma mark -
4030*5ffb0c9bSToomas Soome #pragma mark - Specialized mDNS version of vsnprintf
4031*5ffb0c9bSToomas Soome #endif
4032*5ffb0c9bSToomas Soome
4033*5ffb0c9bSToomas Soome static const struct mDNSprintf_format
4034*5ffb0c9bSToomas Soome {
4035*5ffb0c9bSToomas Soome unsigned leftJustify : 1;
4036*5ffb0c9bSToomas Soome unsigned forceSign : 1;
4037*5ffb0c9bSToomas Soome unsigned zeroPad : 1;
4038*5ffb0c9bSToomas Soome unsigned havePrecision : 1;
4039*5ffb0c9bSToomas Soome unsigned hSize : 1;
4040*5ffb0c9bSToomas Soome unsigned lSize : 1;
4041*5ffb0c9bSToomas Soome char altForm;
4042*5ffb0c9bSToomas Soome char sign; // +, - or space
4043*5ffb0c9bSToomas Soome unsigned int fieldWidth;
4044*5ffb0c9bSToomas Soome unsigned int precision;
4045*5ffb0c9bSToomas Soome } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
4046*5ffb0c9bSToomas Soome
mDNS_vsnprintf(char * sbuffer,mDNSu32 buflen,const char * fmt,va_list arg)4047*5ffb0c9bSToomas Soome mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
4048*5ffb0c9bSToomas Soome {
4049*5ffb0c9bSToomas Soome mDNSu32 nwritten = 0;
4050*5ffb0c9bSToomas Soome int c;
4051*5ffb0c9bSToomas Soome if (buflen == 0) return(0);
4052*5ffb0c9bSToomas Soome buflen--; // Pre-reserve one space in the buffer for the terminating null
4053*5ffb0c9bSToomas Soome if (buflen == 0) goto exit;
4054*5ffb0c9bSToomas Soome
4055*5ffb0c9bSToomas Soome for (c = *fmt; c != 0; c = *++fmt)
4056*5ffb0c9bSToomas Soome {
4057*5ffb0c9bSToomas Soome if (c != '%')
4058*5ffb0c9bSToomas Soome {
4059*5ffb0c9bSToomas Soome *sbuffer++ = (char)c;
4060*5ffb0c9bSToomas Soome if (++nwritten >= buflen) goto exit;
4061*5ffb0c9bSToomas Soome }
4062*5ffb0c9bSToomas Soome else
4063*5ffb0c9bSToomas Soome {
4064*5ffb0c9bSToomas Soome unsigned int i=0, j;
4065*5ffb0c9bSToomas Soome // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
4066*5ffb0c9bSToomas Soome // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
4067*5ffb0c9bSToomas Soome // The size needs to be enough for a 256-byte domain name plus some error text.
4068*5ffb0c9bSToomas Soome #define mDNS_VACB_Size 300
4069*5ffb0c9bSToomas Soome char mDNS_VACB[mDNS_VACB_Size];
4070*5ffb0c9bSToomas Soome #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
4071*5ffb0c9bSToomas Soome #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
4072*5ffb0c9bSToomas Soome char *s = mDNS_VACB_Lim, *digits;
4073*5ffb0c9bSToomas Soome struct mDNSprintf_format F = mDNSprintf_format_default;
4074*5ffb0c9bSToomas Soome
4075*5ffb0c9bSToomas Soome while (1) // decode flags
4076*5ffb0c9bSToomas Soome {
4077*5ffb0c9bSToomas Soome c = *++fmt;
4078*5ffb0c9bSToomas Soome if (c == '-') F.leftJustify = 1;
4079*5ffb0c9bSToomas Soome else if (c == '+') F.forceSign = 1;
4080*5ffb0c9bSToomas Soome else if (c == ' ') F.sign = ' ';
4081*5ffb0c9bSToomas Soome else if (c == '#') F.altForm++;
4082*5ffb0c9bSToomas Soome else if (c == '0') F.zeroPad = 1;
4083*5ffb0c9bSToomas Soome else break;
4084*5ffb0c9bSToomas Soome }
4085*5ffb0c9bSToomas Soome
4086*5ffb0c9bSToomas Soome if (c == '*') // decode field width
4087*5ffb0c9bSToomas Soome {
4088*5ffb0c9bSToomas Soome int f = va_arg(arg, int);
4089*5ffb0c9bSToomas Soome if (f < 0) { f = -f; F.leftJustify = 1; }
4090*5ffb0c9bSToomas Soome F.fieldWidth = (unsigned int)f;
4091*5ffb0c9bSToomas Soome c = *++fmt;
4092*5ffb0c9bSToomas Soome }
4093*5ffb0c9bSToomas Soome else
4094*5ffb0c9bSToomas Soome {
4095*5ffb0c9bSToomas Soome for (; c >= '0' && c <= '9'; c = *++fmt)
4096*5ffb0c9bSToomas Soome F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
4097*5ffb0c9bSToomas Soome }
4098*5ffb0c9bSToomas Soome
4099*5ffb0c9bSToomas Soome if (c == '.') // decode precision
4100*5ffb0c9bSToomas Soome {
4101*5ffb0c9bSToomas Soome if ((c = *++fmt) == '*')
4102*5ffb0c9bSToomas Soome { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
4103*5ffb0c9bSToomas Soome else for (; c >= '0' && c <= '9'; c = *++fmt)
4104*5ffb0c9bSToomas Soome F.precision = (10 * F.precision) + (c - '0');
4105*5ffb0c9bSToomas Soome F.havePrecision = 1;
4106*5ffb0c9bSToomas Soome }
4107*5ffb0c9bSToomas Soome
4108*5ffb0c9bSToomas Soome if (F.leftJustify) F.zeroPad = 0;
4109*5ffb0c9bSToomas Soome
4110*5ffb0c9bSToomas Soome conv:
4111*5ffb0c9bSToomas Soome switch (c) // perform appropriate conversion
4112*5ffb0c9bSToomas Soome {
4113*5ffb0c9bSToomas Soome unsigned long n;
4114*5ffb0c9bSToomas Soome case 'h': F.hSize = 1; c = *++fmt; goto conv;
4115*5ffb0c9bSToomas Soome case 'l': // fall through
4116*5ffb0c9bSToomas Soome case 'L': F.lSize = 1; c = *++fmt; goto conv;
4117*5ffb0c9bSToomas Soome case 'd':
4118*5ffb0c9bSToomas Soome case 'i': if (F.lSize) n = (unsigned long)va_arg(arg, long);
4119*5ffb0c9bSToomas Soome else n = (unsigned long)va_arg(arg, int);
4120*5ffb0c9bSToomas Soome if (F.hSize) n = (short) n;
4121*5ffb0c9bSToomas Soome if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
4122*5ffb0c9bSToomas Soome else if (F.forceSign) F.sign = '+';
4123*5ffb0c9bSToomas Soome goto decimal;
4124*5ffb0c9bSToomas Soome case 'u': if (F.lSize) n = va_arg(arg, unsigned long);
4125*5ffb0c9bSToomas Soome else n = va_arg(arg, unsigned int);
4126*5ffb0c9bSToomas Soome if (F.hSize) n = (unsigned short) n;
4127*5ffb0c9bSToomas Soome F.sign = 0;
4128*5ffb0c9bSToomas Soome goto decimal;
4129*5ffb0c9bSToomas Soome decimal: if (!F.havePrecision)
4130*5ffb0c9bSToomas Soome {
4131*5ffb0c9bSToomas Soome if (F.zeroPad)
4132*5ffb0c9bSToomas Soome {
4133*5ffb0c9bSToomas Soome F.precision = F.fieldWidth;
4134*5ffb0c9bSToomas Soome if (F.sign) --F.precision;
4135*5ffb0c9bSToomas Soome }
4136*5ffb0c9bSToomas Soome if (F.precision < 1) F.precision = 1;
4137*5ffb0c9bSToomas Soome }
4138*5ffb0c9bSToomas Soome if (F.precision > mDNS_VACB_Size - 1)
4139*5ffb0c9bSToomas Soome F.precision = mDNS_VACB_Size - 1;
4140*5ffb0c9bSToomas Soome for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
4141*5ffb0c9bSToomas Soome for (; i < F.precision; i++) *--s = '0';
4142*5ffb0c9bSToomas Soome if (F.sign) { *--s = F.sign; i++; }
4143*5ffb0c9bSToomas Soome break;
4144*5ffb0c9bSToomas Soome
4145*5ffb0c9bSToomas Soome case 'o': if (F.lSize) n = va_arg(arg, unsigned long);
4146*5ffb0c9bSToomas Soome else n = va_arg(arg, unsigned int);
4147*5ffb0c9bSToomas Soome if (F.hSize) n = (unsigned short) n;
4148*5ffb0c9bSToomas Soome if (!F.havePrecision)
4149*5ffb0c9bSToomas Soome {
4150*5ffb0c9bSToomas Soome if (F.zeroPad) F.precision = F.fieldWidth;
4151*5ffb0c9bSToomas Soome if (F.precision < 1) F.precision = 1;
4152*5ffb0c9bSToomas Soome }
4153*5ffb0c9bSToomas Soome if (F.precision > mDNS_VACB_Size - 1)
4154*5ffb0c9bSToomas Soome F.precision = mDNS_VACB_Size - 1;
4155*5ffb0c9bSToomas Soome for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
4156*5ffb0c9bSToomas Soome if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
4157*5ffb0c9bSToomas Soome for (; i < F.precision; i++) *--s = '0';
4158*5ffb0c9bSToomas Soome break;
4159*5ffb0c9bSToomas Soome
4160*5ffb0c9bSToomas Soome case 'a': {
4161*5ffb0c9bSToomas Soome unsigned char *a = va_arg(arg, unsigned char *);
4162*5ffb0c9bSToomas Soome if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
4163*5ffb0c9bSToomas Soome else
4164*5ffb0c9bSToomas Soome {
4165*5ffb0c9bSToomas Soome s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
4166*5ffb0c9bSToomas Soome if (F.altForm)
4167*5ffb0c9bSToomas Soome {
4168*5ffb0c9bSToomas Soome mDNSAddr *ip = (mDNSAddr*)a;
4169*5ffb0c9bSToomas Soome switch (ip->type)
4170*5ffb0c9bSToomas Soome {
4171*5ffb0c9bSToomas Soome case mDNSAddrType_IPv4: F.precision = 4; a = (unsigned char *)&ip->ip.v4; break;
4172*5ffb0c9bSToomas Soome case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
4173*5ffb0c9bSToomas Soome default: F.precision = 0; break;
4174*5ffb0c9bSToomas Soome }
4175*5ffb0c9bSToomas Soome }
4176*5ffb0c9bSToomas Soome if (F.altForm && !F.precision)
4177*5ffb0c9bSToomas Soome i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "«ZERO ADDRESS»");
4178*5ffb0c9bSToomas Soome else switch (F.precision)
4179*5ffb0c9bSToomas Soome {
4180*5ffb0c9bSToomas Soome case 4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d",
4181*5ffb0c9bSToomas Soome a[0], a[1], a[2], a[3]); break;
4182*5ffb0c9bSToomas Soome case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
4183*5ffb0c9bSToomas Soome a[0], a[1], a[2], a[3], a[4], a[5]); break;
4184*5ffb0c9bSToomas Soome case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB),
4185*5ffb0c9bSToomas Soome "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
4186*5ffb0c9bSToomas Soome a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7],
4187*5ffb0c9bSToomas Soome a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break;
4188*5ffb0c9bSToomas Soome default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify"
4189*5ffb0c9bSToomas Soome " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
4190*5ffb0c9bSToomas Soome }
4191*5ffb0c9bSToomas Soome }
4192*5ffb0c9bSToomas Soome }
4193*5ffb0c9bSToomas Soome break;
4194*5ffb0c9bSToomas Soome
4195*5ffb0c9bSToomas Soome case 'p': F.havePrecision = F.lSize = 1;
4196*5ffb0c9bSToomas Soome F.precision = sizeof(void*) * 2; // 8 characters on 32-bit; 16 characters on 64-bit
4197*5ffb0c9bSToomas Soome case 'X': digits = "0123456789ABCDEF";
4198*5ffb0c9bSToomas Soome goto hexadecimal;
4199*5ffb0c9bSToomas Soome case 'x': digits = "0123456789abcdef";
4200*5ffb0c9bSToomas Soome hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long);
4201*5ffb0c9bSToomas Soome else n = va_arg(arg, unsigned int);
4202*5ffb0c9bSToomas Soome if (F.hSize) n = (unsigned short) n;
4203*5ffb0c9bSToomas Soome if (!F.havePrecision)
4204*5ffb0c9bSToomas Soome {
4205*5ffb0c9bSToomas Soome if (F.zeroPad)
4206*5ffb0c9bSToomas Soome {
4207*5ffb0c9bSToomas Soome F.precision = F.fieldWidth;
4208*5ffb0c9bSToomas Soome if (F.altForm) F.precision -= 2;
4209*5ffb0c9bSToomas Soome }
4210*5ffb0c9bSToomas Soome if (F.precision < 1) F.precision = 1;
4211*5ffb0c9bSToomas Soome }
4212*5ffb0c9bSToomas Soome if (F.precision > mDNS_VACB_Size - 1)
4213*5ffb0c9bSToomas Soome F.precision = mDNS_VACB_Size - 1;
4214*5ffb0c9bSToomas Soome for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
4215*5ffb0c9bSToomas Soome for (; i < F.precision; i++) *--s = '0';
4216*5ffb0c9bSToomas Soome if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
4217*5ffb0c9bSToomas Soome break;
4218*5ffb0c9bSToomas Soome
4219*5ffb0c9bSToomas Soome case 'c': *--s = (char)va_arg(arg, int); i = 1; break;
4220*5ffb0c9bSToomas Soome
4221*5ffb0c9bSToomas Soome case 's': s = va_arg(arg, char *);
4222*5ffb0c9bSToomas Soome if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
4223*5ffb0c9bSToomas Soome else switch (F.altForm)
4224*5ffb0c9bSToomas Soome {
4225*5ffb0c9bSToomas Soome case 0: i=0;
4226*5ffb0c9bSToomas Soome if (!F.havePrecision) // C string
4227*5ffb0c9bSToomas Soome while (s[i]) i++;
4228*5ffb0c9bSToomas Soome else
4229*5ffb0c9bSToomas Soome {
4230*5ffb0c9bSToomas Soome while ((i < F.precision) && s[i]) i++;
4231*5ffb0c9bSToomas Soome // Make sure we don't truncate in the middle of a UTF-8 character
4232*5ffb0c9bSToomas Soome // If last character we got was any kind of UTF-8 multi-byte character,
4233*5ffb0c9bSToomas Soome // then see if we have to back up.
4234*5ffb0c9bSToomas Soome // This is not as easy as the similar checks below, because
4235*5ffb0c9bSToomas Soome // here we can't assume it's safe to examine the *next* byte, so we
4236*5ffb0c9bSToomas Soome // have to confine ourselves to working only backwards in the string.
4237*5ffb0c9bSToomas Soome j = i; // Record where we got to
4238*5ffb0c9bSToomas Soome // Now, back up until we find first non-continuation-char
4239*5ffb0c9bSToomas Soome while (i>0 && (s[i-1] & 0xC0) == 0x80) i--;
4240*5ffb0c9bSToomas Soome // Now s[i-1] is the first non-continuation-char
4241*5ffb0c9bSToomas Soome // and (j-i) is the number of continuation-chars we found
4242*5ffb0c9bSToomas Soome if (i>0 && (s[i-1] & 0xC0) == 0xC0) // If we found a start-char
4243*5ffb0c9bSToomas Soome {
4244*5ffb0c9bSToomas Soome i--; // Tentatively eliminate this start-char as well
4245*5ffb0c9bSToomas Soome // Now (j-i) is the number of characters we're considering eliminating.
4246*5ffb0c9bSToomas Soome // To be legal UTF-8, the start-char must contain (j-i) one-bits,
4247*5ffb0c9bSToomas Soome // followed by a zero bit. If we shift it right by (7-(j-i)) bits
4248*5ffb0c9bSToomas Soome // (with sign extension) then the result has to be 0xFE.
4249*5ffb0c9bSToomas Soome // If this is right, then we reinstate the tentatively eliminated bytes.
4250*5ffb0c9bSToomas Soome if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j;
4251*5ffb0c9bSToomas Soome }
4252*5ffb0c9bSToomas Soome }
4253*5ffb0c9bSToomas Soome break;
4254*5ffb0c9bSToomas Soome case 1: i = (unsigned char) *s++; break; // Pascal string
4255*5ffb0c9bSToomas Soome case 2: { // DNS label-sequence name
4256*5ffb0c9bSToomas Soome unsigned char *a = (unsigned char *)s;
4257*5ffb0c9bSToomas Soome s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
4258*5ffb0c9bSToomas Soome if (*a == 0) *s++ = '.'; // Special case for root DNS name
4259*5ffb0c9bSToomas Soome while (*a)
4260*5ffb0c9bSToomas Soome {
4261*5ffb0c9bSToomas Soome char buf[63*4+1];
4262*5ffb0c9bSToomas Soome if (*a > 63)
4263*5ffb0c9bSToomas Soome { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
4264*5ffb0c9bSToomas Soome if (s + *a >= &mDNS_VACB[254])
4265*5ffb0c9bSToomas Soome { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
4266*5ffb0c9bSToomas Soome // Need to use ConvertDomainLabelToCString to do proper escaping here,
4267*5ffb0c9bSToomas Soome // so it's clear what's a literal dot and what's a label separator
4268*5ffb0c9bSToomas Soome ConvertDomainLabelToCString((domainlabel*)a, buf);
4269*5ffb0c9bSToomas Soome s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf);
4270*5ffb0c9bSToomas Soome a += 1 + *a;
4271*5ffb0c9bSToomas Soome }
4272*5ffb0c9bSToomas Soome i = (mDNSu32)(s - mDNS_VACB);
4273*5ffb0c9bSToomas Soome s = mDNS_VACB; // Reset s back to the start of the buffer
4274*5ffb0c9bSToomas Soome break;
4275*5ffb0c9bSToomas Soome }
4276*5ffb0c9bSToomas Soome }
4277*5ffb0c9bSToomas Soome // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
4278*5ffb0c9bSToomas Soome if (F.havePrecision && i > F.precision)
4279*5ffb0c9bSToomas Soome { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
4280*5ffb0c9bSToomas Soome break;
4281*5ffb0c9bSToomas Soome
4282*5ffb0c9bSToomas Soome case 'n': s = va_arg(arg, char *);
4283*5ffb0c9bSToomas Soome if (F.hSize) *(short *) s = (short)nwritten;
4284*5ffb0c9bSToomas Soome else if (F.lSize) *(long *) s = (long)nwritten;
4285*5ffb0c9bSToomas Soome else *(int *) s = (int)nwritten;
4286*5ffb0c9bSToomas Soome continue;
4287*5ffb0c9bSToomas Soome
4288*5ffb0c9bSToomas Soome default: s = mDNS_VACB;
4289*5ffb0c9bSToomas Soome i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
4290*5ffb0c9bSToomas Soome
4291*5ffb0c9bSToomas Soome case '%': *sbuffer++ = (char)c;
4292*5ffb0c9bSToomas Soome if (++nwritten >= buflen) goto exit;
4293*5ffb0c9bSToomas Soome break;
4294*5ffb0c9bSToomas Soome }
4295*5ffb0c9bSToomas Soome
4296*5ffb0c9bSToomas Soome if (i < F.fieldWidth && !F.leftJustify) // Pad on the left
4297*5ffb0c9bSToomas Soome do {
4298*5ffb0c9bSToomas Soome *sbuffer++ = ' ';
4299*5ffb0c9bSToomas Soome if (++nwritten >= buflen) goto exit;
4300*5ffb0c9bSToomas Soome } while (i < --F.fieldWidth);
4301*5ffb0c9bSToomas Soome
4302*5ffb0c9bSToomas Soome // Make sure we don't truncate in the middle of a UTF-8 character.
4303*5ffb0c9bSToomas Soome // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
4304*5ffb0c9bSToomas Soome // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
4305*5ffb0c9bSToomas Soome // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
4306*5ffb0c9bSToomas Soome // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
4307*5ffb0c9bSToomas Soome if (i > buflen - nwritten)
4308*5ffb0c9bSToomas Soome { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
4309*5ffb0c9bSToomas Soome for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
4310*5ffb0c9bSToomas Soome nwritten += i;
4311*5ffb0c9bSToomas Soome if (nwritten >= buflen) goto exit;
4312*5ffb0c9bSToomas Soome
4313*5ffb0c9bSToomas Soome for (; i < F.fieldWidth; i++) // Pad on the right
4314*5ffb0c9bSToomas Soome {
4315*5ffb0c9bSToomas Soome *sbuffer++ = ' ';
4316*5ffb0c9bSToomas Soome if (++nwritten >= buflen) goto exit;
4317*5ffb0c9bSToomas Soome }
4318*5ffb0c9bSToomas Soome }
4319*5ffb0c9bSToomas Soome }
4320*5ffb0c9bSToomas Soome exit:
4321*5ffb0c9bSToomas Soome *sbuffer++ = 0;
4322*5ffb0c9bSToomas Soome return(nwritten);
4323*5ffb0c9bSToomas Soome }
4324*5ffb0c9bSToomas Soome
mDNS_snprintf(char * sbuffer,mDNSu32 buflen,const char * fmt,...)4325*5ffb0c9bSToomas Soome mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
4326*5ffb0c9bSToomas Soome {
4327*5ffb0c9bSToomas Soome mDNSu32 length;
4328*5ffb0c9bSToomas Soome
4329*5ffb0c9bSToomas Soome va_list ptr;
4330*5ffb0c9bSToomas Soome va_start(ptr,fmt);
4331*5ffb0c9bSToomas Soome length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
4332*5ffb0c9bSToomas Soome va_end(ptr);
4333*5ffb0c9bSToomas Soome
4334*5ffb0c9bSToomas Soome return(length);
4335*5ffb0c9bSToomas Soome }
4336