xref: /titanic_50/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c (revision 5ffb0c9b03b5149ff4f5821a62be4a52408ada2a)
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