1*472cd20dSToomas Soome /* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108; indent-tabs-mode: nil; -*-
24b22b933Srs  *
3*472cd20dSToomas Soome  * Copyright (c) 2002-2020 Apple Inc. All rights reserved.
44b22b933Srs  *
54b22b933Srs  * Licensed under the Apache License, Version 2.0 (the "License");
64b22b933Srs  * you may not use this file except in compliance with the License.
74b22b933Srs  * You may obtain a copy of the License at
85ffb0c9bSToomas Soome  *
94b22b933Srs  *     http://www.apache.org/licenses/LICENSE-2.0
105ffb0c9bSToomas Soome  *
114b22b933Srs  * Unless required by applicable law or agreed to in writing, software
124b22b933Srs  * distributed under the License is distributed on an "AS IS" BASIS,
134b22b933Srs  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
144b22b933Srs  * See the License for the specific language governing permissions and
154b22b933Srs  * limitations under the License.
164b22b933Srs  */
174b22b933Srs 
18*472cd20dSToomas Soome #ifndef STANDALONE
194b22b933Srs // Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
204b22b933Srs #define mDNS_InstantiateInlines 1
214b22b933Srs #include "DNSCommon.h"
22*472cd20dSToomas Soome #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
23*472cd20dSToomas Soome #include "dnssec_v2.h"
24*472cd20dSToomas Soome #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
25c65ebfc7SToomas Soome 
264b22b933Srs // Disable certain benign warnings with Microsoft compilers
274b22b933Srs #if (defined(_MSC_VER))
285ffb0c9bSToomas Soome // Disable "conditional expression is constant" warning for debug macros.
295ffb0c9bSToomas Soome // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
305ffb0c9bSToomas Soome // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
315ffb0c9bSToomas Soome     #pragma warning(disable:4127)
325ffb0c9bSToomas Soome // Disable "array is too small to include a terminating null character" warning
335ffb0c9bSToomas Soome // -- domain labels have an initial length byte, not a terminating null character
345ffb0c9bSToomas Soome     #pragma warning(disable:4295)
354b22b933Srs #endif
364b22b933Srs 
374b22b933Srs // ***************************************************************************
384b22b933Srs #if COMPILER_LIKES_PRAGMA_MARK
395ffb0c9bSToomas Soome #pragma mark - Program Constants
404b22b933Srs #endif
414b22b933Srs 
425ffb0c9bSToomas Soome mDNSexport const mDNSInterfaceID mDNSInterface_Any       = 0;
435ffb0c9bSToomas Soome mDNSexport const mDNSInterfaceID mDNSInterfaceMark       = (mDNSInterfaceID)-1;
445ffb0c9bSToomas Soome mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2;
45*472cd20dSToomas Soome mDNSexport const mDNSInterfaceID mDNSInterface_P2P       = (mDNSInterfaceID)-3;
46*472cd20dSToomas Soome mDNSexport const mDNSInterfaceID uDNSInterfaceMark       = (mDNSInterfaceID)-4;
47*472cd20dSToomas Soome mDNSexport const mDNSInterfaceID mDNSInterface_BLE       = (mDNSInterfaceID)-5;
485ffb0c9bSToomas Soome 
495ffb0c9bSToomas Soome // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
505ffb0c9bSToomas Soome // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
515ffb0c9bSToomas Soome // port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
525ffb0c9bSToomas Soome // LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
535ffb0c9bSToomas Soome // Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
545ffb0c9bSToomas Soome // with Microsoft's LLMNR client code.
555ffb0c9bSToomas Soome 
565ffb0c9bSToomas Soome #define   DiscardPortAsNumber               9
575ffb0c9bSToomas Soome #define   SSHPortAsNumber                  22
585ffb0c9bSToomas Soome #define   UnicastDNSPortAsNumber           53
595ffb0c9bSToomas Soome #define   SSDPPortAsNumber               1900
605ffb0c9bSToomas Soome #define   IPSECPortAsNumber              4500
615ffb0c9bSToomas Soome #define   NSIPCPortAsNumber              5030       // Port used for dnsextd to talk to local nameserver bound to loopback
625ffb0c9bSToomas Soome #define   NATPMPAnnouncementPortAsNumber 5350
635ffb0c9bSToomas Soome #define   NATPMPPortAsNumber             5351
645ffb0c9bSToomas Soome #define   DNSEXTPortAsNumber             5352       // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
655ffb0c9bSToomas Soome #define   MulticastDNSPortAsNumber       5353
665ffb0c9bSToomas Soome #define   LoopbackIPCPortAsNumber        5354
675ffb0c9bSToomas Soome //#define MulticastDNSPortAsNumber       5355		// LLMNR
685ffb0c9bSToomas Soome #define   PrivateDNSPortAsNumber         5533
695ffb0c9bSToomas Soome 
705ffb0c9bSToomas Soome mDNSexport const mDNSIPPort DiscardPort            = { { DiscardPortAsNumber            >> 8, DiscardPortAsNumber            & 0xFF } };
715ffb0c9bSToomas Soome mDNSexport const mDNSIPPort SSHPort                = { { SSHPortAsNumber                >> 8, SSHPortAsNumber                & 0xFF } };
725ffb0c9bSToomas Soome mDNSexport const mDNSIPPort UnicastDNSPort         = { { UnicastDNSPortAsNumber         >> 8, UnicastDNSPortAsNumber         & 0xFF } };
735ffb0c9bSToomas Soome mDNSexport const mDNSIPPort SSDPPort               = { { SSDPPortAsNumber               >> 8, SSDPPortAsNumber               & 0xFF } };
745ffb0c9bSToomas Soome mDNSexport const mDNSIPPort IPSECPort              = { { IPSECPortAsNumber              >> 8, IPSECPortAsNumber              & 0xFF } };
755ffb0c9bSToomas Soome mDNSexport const mDNSIPPort NSIPCPort              = { { NSIPCPortAsNumber              >> 8, NSIPCPortAsNumber              & 0xFF } };
765ffb0c9bSToomas Soome mDNSexport const mDNSIPPort NATPMPAnnouncementPort = { { NATPMPAnnouncementPortAsNumber >> 8, NATPMPAnnouncementPortAsNumber & 0xFF } };
775ffb0c9bSToomas Soome mDNSexport const mDNSIPPort NATPMPPort             = { { NATPMPPortAsNumber             >> 8, NATPMPPortAsNumber             & 0xFF } };
785ffb0c9bSToomas Soome mDNSexport const mDNSIPPort DNSEXTPort             = { { DNSEXTPortAsNumber             >> 8, DNSEXTPortAsNumber             & 0xFF } };
795ffb0c9bSToomas Soome mDNSexport const mDNSIPPort MulticastDNSPort       = { { MulticastDNSPortAsNumber       >> 8, MulticastDNSPortAsNumber       & 0xFF } };
805ffb0c9bSToomas Soome mDNSexport const mDNSIPPort LoopbackIPCPort        = { { LoopbackIPCPortAsNumber        >> 8, LoopbackIPCPortAsNumber        & 0xFF } };
815ffb0c9bSToomas Soome mDNSexport const mDNSIPPort PrivateDNSPort         = { { PrivateDNSPortAsNumber         >> 8, PrivateDNSPortAsNumber         & 0xFF } };
825ffb0c9bSToomas Soome 
835ffb0c9bSToomas Soome mDNSexport const OwnerOptData zeroOwner         = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } };
845ffb0c9bSToomas Soome 
855ffb0c9bSToomas Soome mDNSexport const mDNSIPPort zeroIPPort        = { { 0 } };
865ffb0c9bSToomas Soome mDNSexport const mDNSv4Addr zerov4Addr        = { { 0 } };
875ffb0c9bSToomas Soome mDNSexport const mDNSv6Addr zerov6Addr        = { { 0 } };
885ffb0c9bSToomas Soome mDNSexport const mDNSEthAddr zeroEthAddr       = { { 0 } };
895ffb0c9bSToomas Soome mDNSexport const mDNSv4Addr onesIPv4Addr      = { { 255, 255, 255, 255 } };
905ffb0c9bSToomas Soome mDNSexport const mDNSv6Addr onesIPv6Addr      = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
915ffb0c9bSToomas Soome mDNSexport const mDNSEthAddr onesEthAddr       = { { 255, 255, 255, 255, 255, 255 } };
925ffb0c9bSToomas Soome mDNSexport const mDNSAddr zeroAddr          = { mDNSAddrType_None, {{{ 0 }}} };
935ffb0c9bSToomas Soome 
945ffb0c9bSToomas Soome mDNSexport const mDNSv4Addr AllDNSAdminGroup   = { { 239, 255, 255, 251 } };
955ffb0c9bSToomas Soome mDNSexport const mDNSv4Addr AllHosts_v4        = { { 224,   0,   0,   1 } };  // For NAT-PMP & PCP Annoucements
965ffb0c9bSToomas Soome mDNSexport const mDNSv6Addr AllHosts_v6        = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } };
975ffb0c9bSToomas 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
985ffb0c9bSToomas Soome mDNSexport const mDNSEthAddr AllHosts_v6_Eth    = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } };
995ffb0c9bSToomas Soome mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224,   0,   0, 251 } } } };
1005ffb0c9bSToomas Soome //mDNSexport const mDNSAddr  AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224,   0,   0, 252 } } } }; // LLMNR
1015ffb0c9bSToomas Soome mDNSexport const mDNSAddr AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
1025ffb0c9bSToomas 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
1035ffb0c9bSToomas Soome 
1045ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 zeroID          = { { 0, 0 } };
1055ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 onesID          = { { 255, 255 } };
1065ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 QueryFlags      = { { kDNSFlag0_QR_Query    | kDNSFlag0_OP_StdQuery,                0 } };
1075ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 uQueryFlags     = { { kDNSFlag0_QR_Query    | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } };
1085ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 ResponseFlags   = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
1095ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 UpdateReqFlags  = { { kDNSFlag0_QR_Query    | kDNSFlag0_OP_Update,                  0 } };
1105ffb0c9bSToomas Soome mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update,                  0 } };
1115ffb0c9bSToomas Soome 
112c65ebfc7SToomas Soome mDNSexport const mDNSOpaque64  zeroOpaque64     = { { 0 } };
113c65ebfc7SToomas Soome mDNSexport const mDNSOpaque128 zeroOpaque128    = { { 0 } };
1144b22b933Srs 
115*472cd20dSToomas Soome extern mDNS mDNSStorage;
116*472cd20dSToomas Soome 
1174b22b933Srs // ***************************************************************************
1184b22b933Srs #if COMPILER_LIKES_PRAGMA_MARK
1194b22b933Srs #pragma mark -
1204b22b933Srs #pragma mark - General Utility Functions
1214b22b933Srs #endif
1224b22b933Srs 
1234b22b933Srs // return true for RFC1918 private addresses
mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr)1245ffb0c9bSToomas Soome mDNSexport mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr)
1255ffb0c9bSToomas Soome {
1265ffb0c9bSToomas Soome     return ((addr->b[0] == 10) ||                                 // 10/8 prefix
1275ffb0c9bSToomas Soome             (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) ||   // 172.16/12
1285ffb0c9bSToomas Soome             (addr->b[0] == 192 && addr->b[1] == 168));            // 192.168/16
1295ffb0c9bSToomas Soome }
1305ffb0c9bSToomas Soome 
DNSScopeToString(mDNSu32 scope)131*472cd20dSToomas Soome mDNSexport const char *DNSScopeToString(mDNSu32 scope)
132*472cd20dSToomas Soome {
133*472cd20dSToomas Soome     switch (scope)
134*472cd20dSToomas Soome     {
135*472cd20dSToomas Soome         case kScopeNone:
136*472cd20dSToomas Soome             return "Unscoped";
137*472cd20dSToomas Soome         case kScopeInterfaceID:
138*472cd20dSToomas Soome             return "InterfaceScoped";
139*472cd20dSToomas Soome         case kScopeServiceID:
140*472cd20dSToomas Soome             return "ServiceScoped";
141*472cd20dSToomas Soome         default:
142*472cd20dSToomas Soome             return "Unknown";
143*472cd20dSToomas Soome     }
144*472cd20dSToomas Soome }
145*472cd20dSToomas Soome 
mDNSAddrMapIPv4toIPv6(mDNSv4Addr * in,mDNSv6Addr * out)1465ffb0c9bSToomas Soome mDNSexport void mDNSAddrMapIPv4toIPv6(mDNSv4Addr* in, mDNSv6Addr* out)
1475ffb0c9bSToomas Soome {
1485ffb0c9bSToomas Soome     out->l[0] = 0;
1495ffb0c9bSToomas Soome     out->l[1] = 0;
1505ffb0c9bSToomas Soome     out->w[4] = 0;
1515ffb0c9bSToomas Soome     out->w[5] = 0xffff;
1525ffb0c9bSToomas Soome     out->b[12] = in->b[0];
1535ffb0c9bSToomas Soome     out->b[13] = in->b[1];
1545ffb0c9bSToomas Soome     out->b[14] = in->b[2];
1555ffb0c9bSToomas Soome     out->b[15] = in->b[3];
1565ffb0c9bSToomas Soome }
1575ffb0c9bSToomas Soome 
mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr * in,mDNSv4Addr * out)1585ffb0c9bSToomas Soome mDNSexport mDNSBool mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr *in, mDNSv4Addr* out)
1595ffb0c9bSToomas Soome {
1605ffb0c9bSToomas Soome     if (in->l[0] != 0 || in->l[1] != 0 || in->w[4] != 0 || in->w[5] != 0xffff)
1615ffb0c9bSToomas Soome         return mDNSfalse;
1625ffb0c9bSToomas Soome 
1635ffb0c9bSToomas Soome     out->NotAnInteger = in->l[3];
1645ffb0c9bSToomas Soome     return mDNStrue;
1655ffb0c9bSToomas Soome }
1665ffb0c9bSToomas Soome 
GetFirstActiveInterface(NetworkInterfaceInfo * intf)1675ffb0c9bSToomas Soome mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf)
1685ffb0c9bSToomas Soome {
1695ffb0c9bSToomas Soome     while (intf && !intf->InterfaceActive) intf = intf->next;
1705ffb0c9bSToomas Soome     return(intf);
1715ffb0c9bSToomas Soome }
1724b22b933Srs 
GetNextActiveInterfaceID(const NetworkInterfaceInfo * intf)1734b22b933Srs mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
1745ffb0c9bSToomas Soome {
1755ffb0c9bSToomas Soome     const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
1765ffb0c9bSToomas Soome     if (next) return(next->InterfaceID);else return(mDNSNULL);
1775ffb0c9bSToomas Soome }
1784b22b933Srs 
NumCacheRecordsForInterfaceID(const mDNS * const m,mDNSInterfaceID id)1794b22b933Srs mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id)
1805ffb0c9bSToomas Soome {
1815ffb0c9bSToomas Soome     mDNSu32 slot, used = 0;
1825ffb0c9bSToomas Soome     CacheGroup *cg;
1835ffb0c9bSToomas Soome     const CacheRecord *rr;
1845ffb0c9bSToomas Soome     FORALL_CACHERECORDS(slot, cg, rr)
1855ffb0c9bSToomas Soome     {
1865ffb0c9bSToomas Soome         if (rr->resrec.InterfaceID == id)
1875ffb0c9bSToomas Soome             used++;
1885ffb0c9bSToomas Soome     }
1895ffb0c9bSToomas Soome     return(used);
1905ffb0c9bSToomas Soome }
1914b22b933Srs 
DNSTypeName(mDNSu16 rrtype)1924b22b933Srs mDNSexport char *DNSTypeName(mDNSu16 rrtype)
1935ffb0c9bSToomas Soome {
1945ffb0c9bSToomas Soome     switch (rrtype)
1955ffb0c9bSToomas Soome     {
1965ffb0c9bSToomas Soome     case kDNSType_A:    return("Addr");
1975ffb0c9bSToomas Soome     case kDNSType_NS:   return("NS");
1985ffb0c9bSToomas Soome     case kDNSType_CNAME: return("CNAME");
1995ffb0c9bSToomas Soome     case kDNSType_SOA:  return("SOA");
2005ffb0c9bSToomas Soome     case kDNSType_NULL: return("NULL");
2015ffb0c9bSToomas Soome     case kDNSType_PTR:  return("PTR");
2025ffb0c9bSToomas Soome     case kDNSType_HINFO: return("HINFO");
2035ffb0c9bSToomas Soome     case kDNSType_TXT:  return("TXT");
2045ffb0c9bSToomas Soome     case kDNSType_AAAA: return("AAAA");
2055ffb0c9bSToomas Soome     case kDNSType_SRV:  return("SRV");
2065ffb0c9bSToomas Soome     case kDNSType_OPT:  return("OPT");
2075ffb0c9bSToomas Soome     case kDNSType_NSEC: return("NSEC");
2085ffb0c9bSToomas Soome     case kDNSType_NSEC3: return("NSEC3");
2095ffb0c9bSToomas Soome     case kDNSType_NSEC3PARAM: return("NSEC3PARAM");
2105ffb0c9bSToomas Soome     case kDNSType_TSIG: return("TSIG");
2115ffb0c9bSToomas Soome     case kDNSType_RRSIG: return("RRSIG");
2125ffb0c9bSToomas Soome     case kDNSType_DNSKEY: return("DNSKEY");
2135ffb0c9bSToomas Soome     case kDNSType_DS: return("DS");
214*472cd20dSToomas Soome     case kDNSType_SVCB: return("SVCB");
215*472cd20dSToomas Soome     case kDNSType_HTTPS: return("HTTPS");
2165ffb0c9bSToomas Soome     case kDNSQType_ANY: return("ANY");
2175ffb0c9bSToomas Soome     default:            {
2185ffb0c9bSToomas Soome         static char buffer[16];
2195ffb0c9bSToomas Soome         mDNS_snprintf(buffer, sizeof(buffer), "TYPE%d", rrtype);
2205ffb0c9bSToomas Soome         return(buffer);
2215ffb0c9bSToomas Soome     }
2225ffb0c9bSToomas Soome     }
2235ffb0c9bSToomas Soome }
2245ffb0c9bSToomas Soome 
mStatusDescription(mStatus error)225*472cd20dSToomas Soome mDNSexport const char *mStatusDescription(mStatus error)
2265ffb0c9bSToomas Soome {
227*472cd20dSToomas Soome     const char *error_description;
228*472cd20dSToomas Soome     switch (error) {
229*472cd20dSToomas Soome         case mStatus_NoError:
230*472cd20dSToomas Soome             error_description = "mStatus_NoError";
231*472cd20dSToomas Soome             break;
232*472cd20dSToomas Soome         case mStatus_BadParamErr:
233*472cd20dSToomas Soome             error_description = "mStatus_BadParamErr";
234*472cd20dSToomas Soome             break;
2355ffb0c9bSToomas Soome 
236*472cd20dSToomas Soome         default:
237*472cd20dSToomas Soome             error_description = "mStatus_UnknownDescription";
238*472cd20dSToomas Soome             break;
2395ffb0c9bSToomas Soome     }
240*472cd20dSToomas Soome 
241*472cd20dSToomas Soome     return error_description;
2425ffb0c9bSToomas Soome }
2435ffb0c9bSToomas Soome 
swap32(mDNSu32 x)2445ffb0c9bSToomas Soome mDNSexport mDNSu32 swap32(mDNSu32 x)
2455ffb0c9bSToomas Soome {
2465ffb0c9bSToomas Soome     mDNSu8 *ptr = (mDNSu8 *)&x;
2475ffb0c9bSToomas Soome     return (mDNSu32)((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
2485ffb0c9bSToomas Soome }
2495ffb0c9bSToomas Soome 
swap16(mDNSu16 x)2505ffb0c9bSToomas Soome mDNSexport mDNSu16 swap16(mDNSu16 x)
2515ffb0c9bSToomas Soome {
2525ffb0c9bSToomas Soome     mDNSu8 *ptr = (mDNSu8 *)&x;
2535ffb0c9bSToomas Soome     return (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
2545ffb0c9bSToomas Soome }
2555ffb0c9bSToomas Soome 
PrintTypeBitmap(const mDNSu8 * bmap,int bitmaplen,char * const buffer,mDNSu32 length)2565ffb0c9bSToomas Soome mDNSlocal void PrintTypeBitmap(const mDNSu8 *bmap, int bitmaplen, char *const buffer, mDNSu32 length)
2575ffb0c9bSToomas Soome {
2585ffb0c9bSToomas Soome     int win, wlen, type;
2595ffb0c9bSToomas Soome 
2605ffb0c9bSToomas Soome     while (bitmaplen > 0)
2615ffb0c9bSToomas Soome     {
2625ffb0c9bSToomas Soome         int i;
2635ffb0c9bSToomas Soome 
2645ffb0c9bSToomas Soome         if (bitmaplen < 3)
2655ffb0c9bSToomas Soome         {
2665ffb0c9bSToomas Soome             LogMsg("PrintTypeBitmap: malformed bitmap, bitmaplen %d short", bitmaplen);
2675ffb0c9bSToomas Soome             break;
2685ffb0c9bSToomas Soome         }
2695ffb0c9bSToomas Soome 
2705ffb0c9bSToomas Soome         win = *bmap++;
2715ffb0c9bSToomas Soome         wlen = *bmap++;
2725ffb0c9bSToomas Soome         bitmaplen -= 2;
2735ffb0c9bSToomas Soome         if (bitmaplen < wlen || wlen < 1 || wlen > 32)
2745ffb0c9bSToomas Soome         {
2755ffb0c9bSToomas Soome             LogInfo("PrintTypeBitmap: malformed nsec, bitmaplen %d wlen %d", bitmaplen, wlen);
2765ffb0c9bSToomas Soome             break;
2775ffb0c9bSToomas Soome         }
2785ffb0c9bSToomas Soome         if (win < 0 || win >= 256)
2795ffb0c9bSToomas Soome         {
2805ffb0c9bSToomas Soome             LogInfo("PrintTypeBitmap: malformed nsec, bad window win %d", win);
2815ffb0c9bSToomas Soome             break;
2825ffb0c9bSToomas Soome         }
2835ffb0c9bSToomas Soome         type = win * 256;
2845ffb0c9bSToomas Soome         for (i = 0; i < wlen * 8; i++)
2855ffb0c9bSToomas Soome         {
2865ffb0c9bSToomas Soome             if (bmap[i>>3] & (128 >> (i&7)))
2875ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, (MaxMsg - 1) - length, "%s ", DNSTypeName(type + i));
2885ffb0c9bSToomas Soome         }
2895ffb0c9bSToomas Soome         bmap += wlen;
2905ffb0c9bSToomas Soome         bitmaplen -= wlen;
2915ffb0c9bSToomas Soome     }
2925ffb0c9bSToomas Soome }
2935ffb0c9bSToomas Soome 
2944b22b933Srs // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
2954b22b933Srs // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
2964b22b933Srs // 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)2975ffb0c9bSToomas Soome mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer)
2985ffb0c9bSToomas Soome {
2995ffb0c9bSToomas Soome     const RDataBody2 *const rd = (RDataBody2 *)rd1;
3005ffb0c9bSToomas Soome     #define RemSpc (MaxMsg-1-length)
3015ffb0c9bSToomas Soome     char *ptr = buffer;
3025ffb0c9bSToomas Soome     mDNSu32 length = mDNS_snprintf(buffer, MaxMsg-1, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype));
3035ffb0c9bSToomas Soome     if (rr->RecordType == kDNSRecordTypePacketNegative) return(buffer);
3045ffb0c9bSToomas Soome     if (!rr->rdlength && rr->rrtype != kDNSType_OPT) { mDNS_snprintf(buffer+length, RemSpc, "<< ZERO RDATA LENGTH >>"); return(buffer); }
3055ffb0c9bSToomas Soome 
3065ffb0c9bSToomas Soome     switch (rr->rrtype)
3075ffb0c9bSToomas Soome     {
3085ffb0c9bSToomas Soome     case kDNSType_A:    mDNS_snprintf(buffer+length, RemSpc, "%.4a", &rd->ipv4);          break;
3095ffb0c9bSToomas Soome 
3105ffb0c9bSToomas Soome     case kDNSType_NS:       // Same as PTR
3115ffb0c9bSToomas Soome     case kDNSType_CNAME:    // Same as PTR
3125ffb0c9bSToomas Soome     case kDNSType_PTR:  mDNS_snprintf(buffer+length, RemSpc, "%##s", rd->name.c);       break;
3135ffb0c9bSToomas Soome 
3145ffb0c9bSToomas Soome     case kDNSType_SOA:  mDNS_snprintf(buffer+length, RemSpc, "%##s %##s %d %d %d %d %d",
3155ffb0c9bSToomas Soome                                       rd->soa.mname.c, rd->soa.rname.c,
3165ffb0c9bSToomas Soome                                       rd->soa.serial, rd->soa.refresh, rd->soa.retry, rd->soa.expire, rd->soa.min);
3175ffb0c9bSToomas Soome         break;
3185ffb0c9bSToomas Soome 
3195ffb0c9bSToomas Soome     case kDNSType_HINFO:    // Display this the same as TXT (show all constituent strings)
3205ffb0c9bSToomas Soome     case kDNSType_TXT:  {
3215ffb0c9bSToomas Soome         const mDNSu8 *t = rd->txt.c;
322*472cd20dSToomas Soome         const mDNSu8 *const rdLimit = rd->data + rr->rdlength;
323*472cd20dSToomas Soome         const char *separator = "";
324*472cd20dSToomas Soome 
325*472cd20dSToomas Soome         while (t < rdLimit)
3265ffb0c9bSToomas Soome         {
327*472cd20dSToomas Soome             mDNSu32 characterStrLength = *t;
328*472cd20dSToomas Soome             if (characterStrLength + 1 > (mDNSu32)(rdLimit - t)) // Character string goes out of boundary.
329*472cd20dSToomas Soome             {
330*472cd20dSToomas Soome                 const mDNSu8 *const remainderStart = t + 1;
331*472cd20dSToomas Soome                 const mDNSu32 remainderLength = (mDNSu32)(rdLimit - remainderStart);
332*472cd20dSToomas Soome                 length += mDNS_snprintf(buffer + length, RemSpc, "%s%.*s<<OUT OF BOUNDARY CHARACTER STRING>>", separator,
333*472cd20dSToomas Soome                     remainderLength, remainderStart);
334*472cd20dSToomas Soome                 (void)length; // Acknowledge "dead store" analyzer warning.
335*472cd20dSToomas Soome                 break;
336*472cd20dSToomas Soome             }
337*472cd20dSToomas Soome             length += mDNS_snprintf(buffer+length, RemSpc, "%s%.*s", separator, characterStrLength, t + 1);
338*472cd20dSToomas Soome             separator = "¦";
339*472cd20dSToomas Soome             t += 1 + characterStrLength;
3405ffb0c9bSToomas Soome         }
341*472cd20dSToomas Soome     }
342*472cd20dSToomas Soome         break;
3435ffb0c9bSToomas Soome 
3445ffb0c9bSToomas Soome     case kDNSType_AAAA: mDNS_snprintf(buffer+length, RemSpc, "%.16a", &rd->ipv6);       break;
3455ffb0c9bSToomas Soome     case kDNSType_SRV:  mDNS_snprintf(buffer+length, RemSpc, "%u %u %u %##s",
3465ffb0c9bSToomas Soome                                       rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break;
3475ffb0c9bSToomas Soome 
3485ffb0c9bSToomas Soome     case kDNSType_OPT:  {
3495ffb0c9bSToomas Soome         const rdataOPT *opt;
3505ffb0c9bSToomas Soome         const rdataOPT *const end = (const rdataOPT *)&rd->data[rr->rdlength];
3515ffb0c9bSToomas Soome         length += mDNS_snprintf(buffer+length, RemSpc, "Max %d", rr->rrclass);
3525ffb0c9bSToomas Soome         for (opt = &rd->opt[0]; opt < end; opt++)
3535ffb0c9bSToomas Soome         {
3545ffb0c9bSToomas Soome             switch(opt->opt)
3555ffb0c9bSToomas Soome             {
3565ffb0c9bSToomas Soome             case kDNSOpt_LLQ:
3575ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " LLQ");
3585ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d",     opt->u.llq.vers);
3595ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " Op %d",       opt->u.llq.llqOp);
3605ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " Err/Port %d", opt->u.llq.err);
3615ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " ID %08X%08X", opt->u.llq.id.l[0], opt->u.llq.id.l[1]);
3625ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d",    opt->u.llq.llqlease);
3635ffb0c9bSToomas Soome                 break;
3645ffb0c9bSToomas Soome             case kDNSOpt_Lease:
3655ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d",    opt->u.updatelease);
3665ffb0c9bSToomas Soome                 break;
3675ffb0c9bSToomas Soome             case kDNSOpt_Owner:
3685ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " Owner");
3695ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d",     opt->u.owner.vers);
3705ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " Seq %3d", (mDNSu8)opt->u.owner.seq);                           // Display as unsigned
3715ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " MAC %.6a",    opt->u.owner.HMAC.b);
3725ffb0c9bSToomas Soome                 if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
3735ffb0c9bSToomas Soome                 {
3745ffb0c9bSToomas Soome                     length += mDNS_snprintf(buffer+length, RemSpc, " I-MAC %.6a", opt->u.owner.IMAC.b);
3755ffb0c9bSToomas Soome                     if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
3765ffb0c9bSToomas Soome                         length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b);
3775ffb0c9bSToomas Soome                 }
3785ffb0c9bSToomas Soome                 break;
3795ffb0c9bSToomas Soome             case kDNSOpt_Trace:
3805ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " Trace");
3815ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " Platform %d",    opt->u.tracer.platf);
3825ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " mDNSVers %d",    opt->u.tracer.mDNSv);
3835ffb0c9bSToomas Soome                 break;
3845ffb0c9bSToomas Soome             default:
3855ffb0c9bSToomas Soome                 length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d",  opt->opt);
3865ffb0c9bSToomas Soome                 break;
3875ffb0c9bSToomas Soome             }
3885ffb0c9bSToomas Soome         }
3895ffb0c9bSToomas Soome     }
3905ffb0c9bSToomas Soome     break;
3915ffb0c9bSToomas Soome 
3925ffb0c9bSToomas Soome     case kDNSType_NSEC: {
3935ffb0c9bSToomas Soome         domainname *next = (domainname *)rd->data;
3945ffb0c9bSToomas Soome         int len, bitmaplen;
3955ffb0c9bSToomas Soome         mDNSu8 *bmap;
3965ffb0c9bSToomas Soome         len = DomainNameLength(next);
3975ffb0c9bSToomas Soome         bitmaplen = rr->rdlength - len;
3985ffb0c9bSToomas Soome         bmap = (mDNSu8 *)((mDNSu8 *)next + len);
3995ffb0c9bSToomas Soome 
4005ffb0c9bSToomas Soome         if (UNICAST_NSEC(rr))
4015ffb0c9bSToomas Soome             length += mDNS_snprintf(buffer+length, RemSpc, "%##s ", next->c);
4025ffb0c9bSToomas Soome         PrintTypeBitmap(bmap, bitmaplen, buffer, length);
4035ffb0c9bSToomas Soome 
4045ffb0c9bSToomas Soome     }
4055ffb0c9bSToomas Soome     break;
4065ffb0c9bSToomas Soome 
407c65ebfc7SToomas Soome     default:            mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %.*s", rr->rdlength, rr->rdlength, rd->data);
4085ffb0c9bSToomas Soome         // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
4095ffb0c9bSToomas Soome         for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.';
4105ffb0c9bSToomas Soome         break;
4115ffb0c9bSToomas Soome     }
412*472cd20dSToomas Soome 
4135ffb0c9bSToomas Soome     return(buffer);
4145ffb0c9bSToomas Soome }
4155ffb0c9bSToomas Soome 
4165ffb0c9bSToomas Soome // See comments in mDNSEmbeddedAPI.h
4175ffb0c9bSToomas Soome #if _PLATFORM_HAS_STRONG_PRNG_
4185ffb0c9bSToomas Soome #define mDNSRandomNumber mDNSPlatformRandomNumber
4195ffb0c9bSToomas Soome #else
mDNSRandomFromSeed(mDNSu32 seed)4205ffb0c9bSToomas Soome mDNSlocal mDNSu32 mDNSRandomFromSeed(mDNSu32 seed)
4215ffb0c9bSToomas Soome {
4225ffb0c9bSToomas Soome     return seed * 21 + 1;
4235ffb0c9bSToomas Soome }
4245ffb0c9bSToomas Soome 
mDNSMixRandomSeed(mDNSu32 seed,mDNSu8 iteration)4255ffb0c9bSToomas Soome mDNSlocal mDNSu32 mDNSMixRandomSeed(mDNSu32 seed, mDNSu8 iteration)
4265ffb0c9bSToomas Soome {
4275ffb0c9bSToomas Soome     return iteration ? mDNSMixRandomSeed(mDNSRandomFromSeed(seed), --iteration) : seed;
4285ffb0c9bSToomas Soome }
4295ffb0c9bSToomas Soome 
mDNSRandomNumber()4305ffb0c9bSToomas Soome mDNSlocal mDNSu32 mDNSRandomNumber()
4315ffb0c9bSToomas Soome {
4325ffb0c9bSToomas Soome     static mDNSBool seeded = mDNSfalse;
4335ffb0c9bSToomas Soome     static mDNSu32 seed = 0;
4345ffb0c9bSToomas Soome     if (!seeded)
4355ffb0c9bSToomas Soome     {
4365ffb0c9bSToomas Soome         seed = mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100);
4375ffb0c9bSToomas Soome         seeded = mDNStrue;
4385ffb0c9bSToomas Soome     }
4395ffb0c9bSToomas Soome     return (seed = mDNSRandomFromSeed(seed));
4405ffb0c9bSToomas Soome }
4415ffb0c9bSToomas Soome #endif // ! _PLATFORM_HAS_STRONG_PRNG_
4425ffb0c9bSToomas Soome 
mDNSRandom(mDNSu32 max)4435ffb0c9bSToomas Soome mDNSexport mDNSu32 mDNSRandom(mDNSu32 max)      // Returns pseudo-random result from zero to max inclusive
4445ffb0c9bSToomas Soome {
4455ffb0c9bSToomas Soome     mDNSu32 ret = 0;
4465ffb0c9bSToomas Soome     mDNSu32 mask = 1;
4475ffb0c9bSToomas Soome 
4485ffb0c9bSToomas Soome     while (mask < max) mask = (mask << 1) | 1;
4495ffb0c9bSToomas Soome 
4505ffb0c9bSToomas Soome     do ret = mDNSRandomNumber() & mask;
4515ffb0c9bSToomas Soome     while (ret > max);
4525ffb0c9bSToomas Soome 
4535ffb0c9bSToomas Soome     return ret;
4545ffb0c9bSToomas Soome }
4554b22b933Srs 
mDNSSameAddress(const mDNSAddr * ip1,const mDNSAddr * ip2)4564b22b933Srs mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
4575ffb0c9bSToomas Soome {
4585ffb0c9bSToomas Soome     if (ip1->type == ip2->type)
4595ffb0c9bSToomas Soome     {
4605ffb0c9bSToomas Soome         switch (ip1->type)
4615ffb0c9bSToomas Soome         {
4625ffb0c9bSToomas Soome         case mDNSAddrType_None: return(mDNStrue);      // Empty addresses have no data and are therefore always equal
4635ffb0c9bSToomas Soome         case mDNSAddrType_IPv4: return (mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4));
4645ffb0c9bSToomas Soome         case mDNSAddrType_IPv6: return (mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6));
4655ffb0c9bSToomas Soome         }
4665ffb0c9bSToomas Soome     }
4675ffb0c9bSToomas Soome     return(mDNSfalse);
4685ffb0c9bSToomas Soome }
4694b22b933Srs 
mDNSAddrIsDNSMulticast(const mDNSAddr * ip)4704b22b933Srs mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
4715ffb0c9bSToomas Soome {
4725ffb0c9bSToomas Soome     switch(ip->type)
4735ffb0c9bSToomas Soome     {
4745ffb0c9bSToomas Soome     case mDNSAddrType_IPv4: return (mDNSBool)(mDNSSameIPv4Address(ip->ip.v4, AllDNSLinkGroup_v4.ip.v4));
4755ffb0c9bSToomas Soome     case mDNSAddrType_IPv6: return (mDNSBool)(mDNSSameIPv6Address(ip->ip.v6, AllDNSLinkGroup_v6.ip.v6));
4765ffb0c9bSToomas Soome     default: return(mDNSfalse);
4775ffb0c9bSToomas Soome     }
4785ffb0c9bSToomas Soome }
4794b22b933Srs 
4804b22b933Srs // ***************************************************************************
4814b22b933Srs #if COMPILER_LIKES_PRAGMA_MARK
4824b22b933Srs #pragma mark -
4834b22b933Srs #pragma mark - Domain Name Utility Functions
4844b22b933Srs #endif
4854b22b933Srs 
486c65ebfc7SToomas Soome #if !APPLE_OSX_mDNSResponder
487c65ebfc7SToomas Soome 
SameDomainLabel(const mDNSu8 * a,const mDNSu8 * b)4884b22b933Srs mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
4895ffb0c9bSToomas Soome {
4905ffb0c9bSToomas Soome     int i;
4915ffb0c9bSToomas Soome     const int len = *a++;
4925ffb0c9bSToomas Soome 
4935ffb0c9bSToomas Soome     if (len > MAX_DOMAIN_LABEL)
4945ffb0c9bSToomas Soome     { debugf("Malformed label (too long)"); return(mDNSfalse); }
4955ffb0c9bSToomas Soome 
4965ffb0c9bSToomas Soome     if (len != *b++) return(mDNSfalse);
4975ffb0c9bSToomas Soome     for (i=0; i<len; i++)
4985ffb0c9bSToomas Soome     {
4995ffb0c9bSToomas Soome         mDNSu8 ac = *a++;
5005ffb0c9bSToomas Soome         mDNSu8 bc = *b++;
5015ffb0c9bSToomas Soome         if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
5025ffb0c9bSToomas Soome         if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
5035ffb0c9bSToomas Soome         if (ac != bc) return(mDNSfalse);
5045ffb0c9bSToomas Soome     }
5055ffb0c9bSToomas Soome     return(mDNStrue);
5065ffb0c9bSToomas Soome }
5074b22b933Srs 
508c65ebfc7SToomas Soome #endif // !APPLE_OSX_mDNSResponder
509c65ebfc7SToomas Soome 
SameDomainName(const domainname * const d1,const domainname * const d2)5104b22b933Srs mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
5115ffb0c9bSToomas Soome {
5125ffb0c9bSToomas Soome     const mDNSu8 *      a   = d1->c;
5135ffb0c9bSToomas Soome     const mDNSu8 *      b   = d2->c;
5145ffb0c9bSToomas Soome     const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME;          // Maximum that's valid
5155ffb0c9bSToomas Soome 
5165ffb0c9bSToomas Soome     while (*a || *b)
5175ffb0c9bSToomas Soome     {
5185ffb0c9bSToomas Soome         if (a + 1 + *a >= max)
5195ffb0c9bSToomas Soome         { debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse); }
5205ffb0c9bSToomas Soome         if (!SameDomainLabel(a, b)) return(mDNSfalse);
5215ffb0c9bSToomas Soome         a += 1 + *a;
5225ffb0c9bSToomas Soome         b += 1 + *b;
5235ffb0c9bSToomas Soome     }
5245ffb0c9bSToomas Soome 
5255ffb0c9bSToomas Soome     return(mDNStrue);
5265ffb0c9bSToomas Soome }
5275ffb0c9bSToomas Soome 
SameDomainNameCS(const domainname * const d1,const domainname * const d2)5285ffb0c9bSToomas Soome mDNSexport mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2)
5295ffb0c9bSToomas Soome {
5305ffb0c9bSToomas Soome     mDNSu16 l1 = DomainNameLength(d1);
5315ffb0c9bSToomas Soome     mDNSu16 l2 = DomainNameLength(d2);
5325ffb0c9bSToomas Soome     return(l1 <= MAX_DOMAIN_NAME && l1 == l2 && mDNSPlatformMemSame(d1, d2, l1));
5335ffb0c9bSToomas Soome }
5344b22b933Srs 
IsLocalDomain(const domainname * d)5354b22b933Srs mDNSexport mDNSBool IsLocalDomain(const domainname *d)
5365ffb0c9bSToomas Soome {
5375ffb0c9bSToomas Soome     // Domains that are defined to be resolved via link-local multicast are:
5385ffb0c9bSToomas Soome     // local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
5395ffb0c9bSToomas Soome     static const domainname *nL = (const domainname*)"\x5" "local";
5405ffb0c9bSToomas Soome     static const domainname *nR = (const domainname*)"\x3" "254" "\x3" "169"         "\x7" "in-addr" "\x4" "arpa";
5415ffb0c9bSToomas Soome     static const domainname *n8 = (const domainname*)"\x1" "8"   "\x1" "e" "\x1" "f" "\x3" "ip6"     "\x4" "arpa";
5425ffb0c9bSToomas Soome     static const domainname *n9 = (const domainname*)"\x1" "9"   "\x1" "e" "\x1" "f" "\x3" "ip6"     "\x4" "arpa";
5435ffb0c9bSToomas Soome     static const domainname *nA = (const domainname*)"\x1" "a"   "\x1" "e" "\x1" "f" "\x3" "ip6"     "\x4" "arpa";
5445ffb0c9bSToomas Soome     static const domainname *nB = (const domainname*)"\x1" "b"   "\x1" "e" "\x1" "f" "\x3" "ip6"     "\x4" "arpa";
5455ffb0c9bSToomas Soome 
5465ffb0c9bSToomas Soome     const domainname *d1, *d2, *d3, *d4, *d5;   // Top-level domain, second-level domain, etc.
5475ffb0c9bSToomas Soome     d1 = d2 = d3 = d4 = d5 = mDNSNULL;
5485ffb0c9bSToomas Soome     while (d->c[0])
5495ffb0c9bSToomas Soome     {
5505ffb0c9bSToomas Soome         d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
5515ffb0c9bSToomas Soome         d = (const domainname*)(d->c + 1 + d->c[0]);
5525ffb0c9bSToomas Soome     }
5535ffb0c9bSToomas Soome 
5545ffb0c9bSToomas Soome     if (d1 && SameDomainName(d1, nL)) return(mDNStrue);
5555ffb0c9bSToomas Soome     if (d4 && SameDomainName(d4, nR)) return(mDNStrue);
5565ffb0c9bSToomas Soome     if (d5 && SameDomainName(d5, n8)) return(mDNStrue);
5575ffb0c9bSToomas Soome     if (d5 && SameDomainName(d5, n9)) return(mDNStrue);
5585ffb0c9bSToomas Soome     if (d5 && SameDomainName(d5, nA)) return(mDNStrue);
5595ffb0c9bSToomas Soome     if (d5 && SameDomainName(d5, nB)) return(mDNStrue);
5605ffb0c9bSToomas Soome     return(mDNSfalse);
5615ffb0c9bSToomas Soome }
5625ffb0c9bSToomas Soome 
LastLabel(const domainname * d)5635ffb0c9bSToomas Soome mDNSexport const mDNSu8 *LastLabel(const domainname *d)
5645ffb0c9bSToomas Soome {
5655ffb0c9bSToomas Soome     const mDNSu8 *p = d->c;
5665ffb0c9bSToomas Soome     while (d->c[0])
5675ffb0c9bSToomas Soome     {
5685ffb0c9bSToomas Soome         p = d->c;
5695ffb0c9bSToomas Soome         d = (const domainname*)(d->c + 1 + d->c[0]);
5705ffb0c9bSToomas Soome     }
5715ffb0c9bSToomas Soome     return(p);
5725ffb0c9bSToomas Soome }
5734b22b933Srs 
5744b22b933Srs // Returns length of a domain name INCLUDING the byte for the final null label
5755ffb0c9bSToomas Soome // e.g. for the root label "." it returns one
5764b22b933Srs // For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
5775ffb0c9bSToomas Soome // Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME)
5785ffb0c9bSToomas Soome // If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1)
DomainNameLengthLimit(const domainname * const name,const mDNSu8 * limit)5795ffb0c9bSToomas Soome mDNSexport mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit)
5805ffb0c9bSToomas Soome {
5815ffb0c9bSToomas Soome     const mDNSu8 *src = name->c;
5825ffb0c9bSToomas Soome     while (src < limit && *src <= MAX_DOMAIN_LABEL)
5835ffb0c9bSToomas Soome     {
5845ffb0c9bSToomas Soome         if (*src == 0) return((mDNSu16)(src - name->c + 1));
5855ffb0c9bSToomas Soome         src += 1 + *src;
5865ffb0c9bSToomas Soome     }
5875ffb0c9bSToomas Soome     return(MAX_DOMAIN_NAME+1);
5885ffb0c9bSToomas Soome }
5894b22b933Srs 
5904b22b933Srs // CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
5915ffb0c9bSToomas Soome // for the final null label, e.g. for the root label "." it returns one.
5924b22b933Srs // E.g. for the FQDN "foo.com." it returns 9
5934b22b933Srs // (length, three data bytes, length, three more data bytes, final zero).
5944b22b933Srs // In the case where a parent domain name is provided, and the given name is a child
5954b22b933Srs // of that parent, CompressedDomainNameLength returns the length of the prefix portion
5964b22b933Srs // of the child name, plus TWO bytes for the compression pointer.
5974b22b933Srs // E.g. for the name "foo.com." with parent "com.", it returns 6
5984b22b933Srs // (length, three data bytes, two-byte compression pointer).
CompressedDomainNameLength(const domainname * const name,const domainname * parent)5994b22b933Srs mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
6005ffb0c9bSToomas Soome {
6015ffb0c9bSToomas Soome     const mDNSu8 *src = name->c;
6025ffb0c9bSToomas Soome     if (parent && parent->c[0] == 0) parent = mDNSNULL;
6035ffb0c9bSToomas Soome     while (*src)
6045ffb0c9bSToomas Soome     {
6055ffb0c9bSToomas Soome         if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
6065ffb0c9bSToomas Soome         if (parent && SameDomainName((const domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
6075ffb0c9bSToomas Soome         src += 1 + *src;
6085ffb0c9bSToomas Soome         if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
6095ffb0c9bSToomas Soome     }
6105ffb0c9bSToomas Soome     return((mDNSu16)(src - name->c + 1));
6115ffb0c9bSToomas Soome }
6125ffb0c9bSToomas Soome 
6135ffb0c9bSToomas Soome // CountLabels() returns number of labels in name, excluding final root label
6145ffb0c9bSToomas Soome // (e.g. for "apple.com." CountLabels returns 2.)
CountLabels(const domainname * d)6155ffb0c9bSToomas Soome mDNSexport int CountLabels(const domainname *d)
6165ffb0c9bSToomas Soome {
6175ffb0c9bSToomas Soome     int count = 0;
6185ffb0c9bSToomas Soome     const mDNSu8 *ptr;
6195ffb0c9bSToomas Soome     for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++;
6205ffb0c9bSToomas Soome     return count;
6215ffb0c9bSToomas Soome }
6225ffb0c9bSToomas Soome 
6235ffb0c9bSToomas Soome // SkipLeadingLabels skips over the first 'skip' labels in the domainname,
6245ffb0c9bSToomas Soome // returning a pointer to the suffix with 'skip' labels removed.
SkipLeadingLabels(const domainname * d,int skip)6255ffb0c9bSToomas Soome mDNSexport const domainname *SkipLeadingLabels(const domainname *d, int skip)
6265ffb0c9bSToomas Soome {
6275ffb0c9bSToomas Soome     while (skip > 0 && d->c[0]) { d = (const domainname *)(d->c + 1 + d->c[0]); skip--; }
6285ffb0c9bSToomas Soome     return(d);
6295ffb0c9bSToomas Soome }
6304b22b933Srs 
6314b22b933Srs // AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
6324b22b933Srs // The C string contains the label as-is, with no escaping, etc.
6334b22b933Srs // Any dots in the name are literal dots, not label separators
6344b22b933Srs // If successful, AppendLiteralLabelString returns a pointer to the next unused byte
6355ffb0c9bSToomas Soome // in the domainname bufer (i.e. the next byte after the terminating zero).
6365ffb0c9bSToomas Soome // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
6374b22b933Srs // AppendLiteralLabelString returns mDNSNULL.
AppendLiteralLabelString(domainname * const name,const char * cstr)6384b22b933Srs mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr)
6395ffb0c9bSToomas Soome {
6405ffb0c9bSToomas Soome     mDNSu8       *      ptr  = name->c + DomainNameLength(name) - 1;    // Find end of current name
6415ffb0c9bSToomas Soome     const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1;           // Limit of how much we can add (not counting final zero)
6425ffb0c9bSToomas Soome     const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL;
6435ffb0c9bSToomas Soome     const mDNSu8 *const lim  = (lim1 < lim2) ? lim1 : lim2;
6445ffb0c9bSToomas Soome     mDNSu8       *lengthbyte = ptr++;                                   // Record where the length is going to go
6455ffb0c9bSToomas Soome 
6465ffb0c9bSToomas Soome     while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++;    // Copy the data
6475ffb0c9bSToomas Soome     *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1);           // Fill in the length byte
6485ffb0c9bSToomas Soome     *ptr++ = 0;                                             // Put the null root label on the end
6495ffb0c9bSToomas Soome     if (*cstr) return(mDNSNULL);                            // Failure: We didn't successfully consume all input
6505ffb0c9bSToomas Soome     else return(ptr);                                       // Success: return new value of ptr
6515ffb0c9bSToomas Soome }
6524b22b933Srs 
6534b22b933Srs // AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
6544b22b933Srs // The C string is in conventional DNS syntax:
6554b22b933Srs // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
6564b22b933Srs // If successful, AppendDNSNameString returns a pointer to the next unused byte
6575ffb0c9bSToomas Soome // in the domainname bufer (i.e. the next byte after the terminating zero).
6585ffb0c9bSToomas Soome // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
6594b22b933Srs // AppendDNSNameString returns mDNSNULL.
AppendDNSNameString(domainname * const name,const char * cstring)6604b22b933Srs mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring)
6615ffb0c9bSToomas Soome {
6625ffb0c9bSToomas Soome     const char   *cstr      = cstring;
6635ffb0c9bSToomas Soome     mDNSu8       *      ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
6645ffb0c9bSToomas Soome     const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1;        // Limit of how much we can add (not counting final zero)
6655ffb0c9bSToomas Soome     while (*cstr && ptr < lim)                                      // While more characters, and space to put them...
6665ffb0c9bSToomas Soome     {
6675ffb0c9bSToomas Soome         mDNSu8 *lengthbyte = ptr++;                                 // Record where the length is going to go
6685ffb0c9bSToomas Soome         if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); }
6695ffb0c9bSToomas Soome         while (*cstr && *cstr != '.' && ptr < lim)                  // While we have characters in the label...
6705ffb0c9bSToomas Soome         {
6715ffb0c9bSToomas Soome             mDNSu8 c = (mDNSu8)*cstr++;                             // Read the character
6725ffb0c9bSToomas Soome             if (c == '\\')                                          // If escape character, check next character
6735ffb0c9bSToomas Soome             {
674c65ebfc7SToomas Soome                 if (*cstr == '\0') break;                           // If this is the end of the string, then break
6755ffb0c9bSToomas Soome                 c = (mDNSu8)*cstr++;                                // Assume we'll just take the next character
6765ffb0c9bSToomas Soome                 if (mDNSIsDigit(cstr[-1]) && mDNSIsDigit(cstr[0]) && mDNSIsDigit(cstr[1]))
6775ffb0c9bSToomas Soome                 {                                                   // If three decimal digits,
6785ffb0c9bSToomas Soome                     int v0 = cstr[-1] - '0';                        // then interpret as three-digit decimal
6795ffb0c9bSToomas Soome                     int v1 = cstr[ 0] - '0';
6805ffb0c9bSToomas Soome                     int v2 = cstr[ 1] - '0';
6815ffb0c9bSToomas Soome                     int val = v0 * 100 + v1 * 10 + v2;
6825ffb0c9bSToomas Soome                     if (val <= 255) { c = (mDNSu8)val; cstr += 2; } // If valid three-digit decimal value, use it
6835ffb0c9bSToomas Soome                 }
6845ffb0c9bSToomas Soome             }
6855ffb0c9bSToomas Soome             *ptr++ = c;                                             // Write the character
6865ffb0c9bSToomas Soome         }
687c65ebfc7SToomas Soome         if (*cstr == '.') cstr++;                                   // Skip over the trailing dot (if present)
6885ffb0c9bSToomas Soome         if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL)                // If illegal label, abort
6895ffb0c9bSToomas Soome             return(mDNSNULL);
6905ffb0c9bSToomas Soome         *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1);               // Fill in the length byte
6915ffb0c9bSToomas Soome     }
6925ffb0c9bSToomas Soome 
6935ffb0c9bSToomas Soome     *ptr++ = 0;                                                     // Put the null root label on the end
6945ffb0c9bSToomas Soome     if (*cstr) return(mDNSNULL);                                    // Failure: We didn't successfully consume all input
6955ffb0c9bSToomas Soome     else return(ptr);                                               // Success: return new value of ptr
6965ffb0c9bSToomas Soome }
6974b22b933Srs 
6984b22b933Srs // AppendDomainLabel appends a single label to a name.
6994b22b933Srs // If successful, AppendDomainLabel returns a pointer to the next unused byte
7005ffb0c9bSToomas Soome // in the domainname bufer (i.e. the next byte after the terminating zero).
7015ffb0c9bSToomas Soome // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
7024b22b933Srs // AppendDomainLabel returns mDNSNULL.
AppendDomainLabel(domainname * const name,const domainlabel * const label)7034b22b933Srs mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label)
7045ffb0c9bSToomas Soome {
7055ffb0c9bSToomas Soome     int i;
7065ffb0c9bSToomas Soome     mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
7074b22b933Srs 
7085ffb0c9bSToomas Soome     // Check label is legal
7095ffb0c9bSToomas Soome     if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
7104b22b933Srs 
7115ffb0c9bSToomas Soome     // Check that ptr + length byte + data bytes + final zero does not exceed our limit
7125ffb0c9bSToomas Soome     if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL);
7134b22b933Srs 
7145ffb0c9bSToomas Soome     for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i];    // Copy the label data
7155ffb0c9bSToomas Soome     *ptr++ = 0;                             // Put the null root label on the end
7165ffb0c9bSToomas Soome     return(ptr);
7175ffb0c9bSToomas Soome }
7184b22b933Srs 
AppendDomainName(domainname * const name,const domainname * const append)7194b22b933Srs mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
7205ffb0c9bSToomas Soome {
7215ffb0c9bSToomas Soome     mDNSu8       *      ptr = name->c + DomainNameLength(name) - 1; // Find end of current name
7225ffb0c9bSToomas Soome     const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1;        // Limit of how much we can add (not counting final zero)
7235ffb0c9bSToomas Soome     const mDNSu8 *      src = append->c;
7245ffb0c9bSToomas Soome     while (src[0])
7255ffb0c9bSToomas Soome     {
7265ffb0c9bSToomas Soome         int i;
7275ffb0c9bSToomas Soome         if (ptr + 1 + src[0] > lim) return(mDNSNULL);
7285ffb0c9bSToomas Soome         for (i=0; i<=src[0]; i++) *ptr++ = src[i];
7295ffb0c9bSToomas Soome         *ptr = 0;   // Put the null root label on the end
7305ffb0c9bSToomas Soome         src += i;
7315ffb0c9bSToomas Soome     }
7325ffb0c9bSToomas Soome     return(ptr);
7335ffb0c9bSToomas Soome }
7344b22b933Srs 
7354b22b933Srs // MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
7364b22b933Srs // If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
7374b22b933Srs // If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
7384b22b933Srs // MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
7394b22b933Srs // In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
7404b22b933Srs // 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)7414b22b933Srs mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr)
7425ffb0c9bSToomas Soome {
7435ffb0c9bSToomas Soome     mDNSu8       *      ptr   = label->c + 1;                       // Where we're putting it
7445ffb0c9bSToomas Soome     const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL;    // The maximum we can put
7455ffb0c9bSToomas Soome     while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++;          // Copy the label
7465ffb0c9bSToomas Soome     label->c[0] = (mDNSu8)(ptr - label->c - 1);                     // Set the length byte
7475ffb0c9bSToomas Soome     return(*cstr == 0);                                             // Return mDNStrue if we successfully consumed all input
7485ffb0c9bSToomas Soome }
7494b22b933Srs 
7504b22b933Srs // MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
7514b22b933Srs // The C string is in conventional DNS syntax:
7524b22b933Srs // Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
7534b22b933Srs // If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
7545ffb0c9bSToomas Soome // in the domainname bufer (i.e. the next byte after the terminating zero).
7555ffb0c9bSToomas Soome // If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
7564b22b933Srs // MakeDomainNameFromDNSNameString returns mDNSNULL.
MakeDomainNameFromDNSNameString(domainname * const name,const char * cstr)7574b22b933Srs mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr)
7585ffb0c9bSToomas Soome {
7595ffb0c9bSToomas Soome     name->c[0] = 0;                                 // Make an empty domain name
7605ffb0c9bSToomas Soome     return(AppendDNSNameString(name, cstr));        // And then add this string to it
7615ffb0c9bSToomas Soome }
7624b22b933Srs 
ConvertDomainLabelToCString_withescape(const domainlabel * const label,char * ptr,char esc)7634b22b933Srs mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
7645ffb0c9bSToomas Soome {
7655ffb0c9bSToomas Soome     const mDNSu8 *      src = label->c;                         // Domain label we're reading
7665ffb0c9bSToomas Soome     const mDNSu8 len = *src++;                                  // Read length of this (non-null) label
7675ffb0c9bSToomas Soome     const mDNSu8 *const end = src + len;                        // Work out where the label ends
7685ffb0c9bSToomas Soome     if (len > MAX_DOMAIN_LABEL) return(mDNSNULL);               // If illegal label, abort
7695ffb0c9bSToomas Soome     while (src < end)                                           // While we have characters in the label
7705ffb0c9bSToomas Soome     {
7715ffb0c9bSToomas Soome         mDNSu8 c = *src++;
7725ffb0c9bSToomas Soome         if (esc)
7735ffb0c9bSToomas Soome         {
7745ffb0c9bSToomas Soome             if (c == '.' || c == esc)                           // If character is a dot or the escape character
7755ffb0c9bSToomas Soome                 *ptr++ = esc;                                   // Output escape character
7765ffb0c9bSToomas Soome             else if (c <= ' ')                                  // If non-printing ascii,
7775ffb0c9bSToomas Soome             {                                                   // Output decimal escape sequence
7785ffb0c9bSToomas Soome                 *ptr++ = esc;
7795ffb0c9bSToomas Soome                 *ptr++ = (char)  ('0' + (c / 100)     );
7805ffb0c9bSToomas Soome                 *ptr++ = (char)  ('0' + (c /  10) % 10);
7815ffb0c9bSToomas Soome                 c      = (mDNSu8)('0' + (c      ) % 10);
7825ffb0c9bSToomas Soome             }
7835ffb0c9bSToomas Soome         }
7845ffb0c9bSToomas Soome         *ptr++ = (char)c;                                       // Copy the character
7855ffb0c9bSToomas Soome     }
7865ffb0c9bSToomas Soome     *ptr = 0;                                                   // Null-terminate the string
7875ffb0c9bSToomas Soome     return(ptr);                                                // and return
7885ffb0c9bSToomas Soome }
7895ffb0c9bSToomas Soome 
7905ffb0c9bSToomas 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)7914b22b933Srs mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
7925ffb0c9bSToomas Soome {
7935ffb0c9bSToomas Soome     const mDNSu8 *src         = name->c;                            // Domain name we're reading
7945ffb0c9bSToomas Soome     const mDNSu8 *const max   = name->c + MAX_DOMAIN_NAME;          // Maximum that's valid
7954b22b933Srs 
7965ffb0c9bSToomas Soome     if (*src == 0) *ptr++ = '.';                                    // Special case: For root, just write a dot
7974b22b933Srs 
7985ffb0c9bSToomas Soome     while (*src)                                                    // While more characters in the domain name
7995ffb0c9bSToomas Soome     {
8005ffb0c9bSToomas Soome         if (src + 1 + *src >= max) return(mDNSNULL);
8015ffb0c9bSToomas Soome         ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
8025ffb0c9bSToomas Soome         if (!ptr) return(mDNSNULL);
8035ffb0c9bSToomas Soome         src += 1 + *src;
8045ffb0c9bSToomas Soome         *ptr++ = '.';                                               // Write the dot after the label
8055ffb0c9bSToomas Soome     }
8064b22b933Srs 
8075ffb0c9bSToomas Soome     *ptr++ = 0;                                                     // Null-terminate the string
8085ffb0c9bSToomas Soome     return(ptr);                                                    // and return
8095ffb0c9bSToomas Soome }
8104b22b933Srs 
8114b22b933Srs // RFC 1034 rules:
8124b22b933Srs // Host names must start with a letter, end with a letter or digit,
8134b22b933Srs // and have as interior characters only letters, digits, and hyphen.
8144b22b933Srs // This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
8154b22b933Srs 
ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[],domainlabel * const hostlabel)8164b22b933Srs mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
8175ffb0c9bSToomas Soome {
8185ffb0c9bSToomas Soome     const mDNSu8 *      src  = &UTF8Name[1];
8195ffb0c9bSToomas Soome     const mDNSu8 *const end  = &UTF8Name[1] + UTF8Name[0];
8205ffb0c9bSToomas Soome     mDNSu8 *      ptr  = &hostlabel->c[1];
8215ffb0c9bSToomas Soome     const mDNSu8 *const lim  = &hostlabel->c[1] + MAX_DOMAIN_LABEL;
8225ffb0c9bSToomas Soome     while (src < end)
8235ffb0c9bSToomas Soome     {
8245ffb0c9bSToomas Soome         // Delete apostrophes from source name
8255ffb0c9bSToomas Soome         if (src[0] == '\'') { src++; continue; }        // Standard straight single quote
8265ffb0c9bSToomas Soome         if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99)
8275ffb0c9bSToomas Soome         { src += 3; continue; }     // Unicode curly apostrophe
8285ffb0c9bSToomas Soome         if (ptr < lim)
8295ffb0c9bSToomas Soome         {
8305ffb0c9bSToomas Soome             if (mDNSValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
8315ffb0c9bSToomas Soome             else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
8325ffb0c9bSToomas Soome         }
8335ffb0c9bSToomas Soome         src++;
8345ffb0c9bSToomas Soome     }
8355ffb0c9bSToomas Soome     while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--; // Truncate trailing '-' marks
8365ffb0c9bSToomas Soome     hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
8375ffb0c9bSToomas Soome }
8385ffb0c9bSToomas Soome 
ConstructServiceName(domainname * const fqdn,const domainlabel * name,const domainname * type,const domainname * const domain)8394b22b933Srs mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
8405ffb0c9bSToomas Soome                                         const domainlabel *name, const domainname *type, const domainname *const domain)
8415ffb0c9bSToomas Soome {
8425ffb0c9bSToomas Soome     int i, len;
8435ffb0c9bSToomas Soome     mDNSu8 *dst = fqdn->c;
8445ffb0c9bSToomas Soome     const mDNSu8 *src;
8455ffb0c9bSToomas Soome     const char *errormsg;
8465ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder
8475ffb0c9bSToomas Soome     mDNSBool loggedUnderscore = mDNSfalse;
8485ffb0c9bSToomas Soome     static char typeBuf[MAX_ESCAPED_DOMAIN_NAME];
8495ffb0c9bSToomas Soome #endif
8505ffb0c9bSToomas Soome 
8515ffb0c9bSToomas Soome     // In the case where there is no name (and ONLY in that case),
8525ffb0c9bSToomas Soome     // a single-label subtype is allowed as the first label of a three-part "type"
853*472cd20dSToomas Soome     if (!name)
8545ffb0c9bSToomas Soome     {
8555ffb0c9bSToomas Soome         const mDNSu8 *s0 = type->c;
8565ffb0c9bSToomas Soome         if (s0[0] && s0[0] < 0x40)      // If legal first label (at least one character, and no more than 63)
8575ffb0c9bSToomas Soome         {
8585ffb0c9bSToomas Soome             const mDNSu8 * s1 = s0 + 1 + s0[0];
8595ffb0c9bSToomas Soome             if (s1[0] && s1[0] < 0x40)  // and legal second label (at least one character, and no more than 63)
8605ffb0c9bSToomas Soome             {
8615ffb0c9bSToomas Soome                 const mDNSu8 *s2 = s1 + 1 + s1[0];
8625ffb0c9bSToomas Soome                 if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0)  // and we have three and only three labels
8635ffb0c9bSToomas Soome                 {
8645ffb0c9bSToomas Soome                     static const mDNSu8 SubTypeLabel[5] = mDNSSubTypeLabel;
8655ffb0c9bSToomas Soome                     src = s0;                                   // Copy the first label
8665ffb0c9bSToomas Soome                     len = *src;
8675ffb0c9bSToomas Soome                     for (i=0; i <= len;                      i++) *dst++ = *src++;
8685ffb0c9bSToomas Soome                     for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i];
8695ffb0c9bSToomas Soome                     type = (const domainname *)s1;
8705ffb0c9bSToomas Soome 
8715ffb0c9bSToomas Soome                     // Special support to enable the DNSServiceBrowse call made by Bonjour Browser
8725ffb0c9bSToomas Soome                     // For these queries, we retract the "._sub" we just added between the subtype and the main type
8735ffb0c9bSToomas Soome                     // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
8745ffb0c9bSToomas Soome                     if (SameDomainName((domainname*)s0, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
8755ffb0c9bSToomas Soome                         dst -= sizeof(SubTypeLabel);
8765ffb0c9bSToomas Soome                 }
8775ffb0c9bSToomas Soome             }
8785ffb0c9bSToomas Soome         }
8795ffb0c9bSToomas Soome     }
8805ffb0c9bSToomas Soome 
8815ffb0c9bSToomas Soome     if (name && name->c[0])
8825ffb0c9bSToomas Soome     {
8835ffb0c9bSToomas Soome         src = name->c;                                  // Put the service name into the domain name
8845ffb0c9bSToomas Soome         len = *src;
8855ffb0c9bSToomas Soome         if (len >= 0x40) { errormsg = "Service instance name too long"; goto fail; }
8865ffb0c9bSToomas Soome         for (i=0; i<=len; i++) *dst++ = *src++;
8875ffb0c9bSToomas Soome     }
8885ffb0c9bSToomas Soome     else
8895ffb0c9bSToomas Soome         name = (domainlabel*)"";    // Set this up to be non-null, to avoid errors if we have to call LogMsg() below
8905ffb0c9bSToomas Soome 
8915ffb0c9bSToomas Soome     src = type->c;                                      // Put the service type into the domain name
8925ffb0c9bSToomas Soome     len = *src;
8935ffb0c9bSToomas Soome     if (len < 2 || len > 16)
8945ffb0c9bSToomas Soome     {
8955ffb0c9bSToomas Soome         LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-15 characters. "
8965ffb0c9bSToomas Soome                "See <http://www.dns-sd.org/ServiceTypes.html>", name->c, type->c, domain->c);
8975ffb0c9bSToomas Soome     }
8985ffb0c9bSToomas Soome     if (len < 2 || len >= 0x40 || (len > 16 && !SameDomainName(domain, &localdomain))) return(mDNSNULL);
8995ffb0c9bSToomas Soome     if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; }
9005ffb0c9bSToomas Soome     for (i=2; i<=len; i++)
9015ffb0c9bSToomas Soome     {
9025ffb0c9bSToomas Soome         // Letters and digits are allowed anywhere
9035ffb0c9bSToomas Soome         if (mDNSIsLetter(src[i]) || mDNSIsDigit(src[i])) continue;
9045ffb0c9bSToomas Soome         // Hyphens are only allowed as interior characters
9055ffb0c9bSToomas Soome         // Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
9065ffb0c9bSToomas Soome         // with the same rule as hyphens
9075ffb0c9bSToomas Soome         if ((src[i] == '-' || src[i] == '_') && i > 2 && i < len)
9085ffb0c9bSToomas Soome         {
9095ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder
9105ffb0c9bSToomas Soome             if (src[i] == '_' && loggedUnderscore == mDNSfalse)
9115ffb0c9bSToomas Soome             {
9125ffb0c9bSToomas Soome                 ConvertDomainNameToCString(type, typeBuf);
913c65ebfc7SToomas Soome                 LogInfo("ConstructServiceName: Service type with non-leading underscore %s", typeBuf);
9145ffb0c9bSToomas Soome                 loggedUnderscore = mDNStrue;
9155ffb0c9bSToomas Soome             }
9165ffb0c9bSToomas Soome #endif
9175ffb0c9bSToomas Soome             continue;
9185ffb0c9bSToomas Soome         }
9195ffb0c9bSToomas Soome         errormsg = "Application protocol name must contain only letters, digits, and hyphens";
9205ffb0c9bSToomas Soome         goto fail;
9215ffb0c9bSToomas Soome     }
9225ffb0c9bSToomas Soome     for (i=0; i<=len; i++) *dst++ = *src++;
9235ffb0c9bSToomas Soome 
9245ffb0c9bSToomas Soome     len = *src;
9255ffb0c9bSToomas Soome     if (!ValidTransportProtocol(src)) { errormsg = "Transport protocol name must be _udp or _tcp"; goto fail; }
9265ffb0c9bSToomas Soome     for (i=0; i<=len; i++) *dst++ = *src++;
9275ffb0c9bSToomas Soome 
9285ffb0c9bSToomas Soome     if (*src) { errormsg = "Service type must have only two labels"; goto fail; }
9295ffb0c9bSToomas Soome 
9305ffb0c9bSToomas Soome     *dst = 0;
9315ffb0c9bSToomas Soome     if (!domain->c[0]) { errormsg = "Service domain must be non-empty"; goto fail; }
9325ffb0c9bSToomas Soome     if (SameDomainName(domain, (const domainname*)"\x05" "local" "\x04" "arpa"))
9335ffb0c9bSToomas Soome     { errormsg = "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; }
9345ffb0c9bSToomas Soome     dst = AppendDomainName(fqdn, domain);
9355ffb0c9bSToomas Soome     if (!dst) { errormsg = "Service domain too long"; goto fail; }
9365ffb0c9bSToomas Soome     return(dst);
9374b22b933Srs 
9384b22b933Srs fail:
9395ffb0c9bSToomas Soome     LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
9405ffb0c9bSToomas Soome     return(mDNSNULL);
9415ffb0c9bSToomas Soome }
9424b22b933Srs 
9434b22b933Srs // A service name has the form: instance.application-protocol.transport-protocol.domain
9444b22b933Srs // DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
9454b22b933Srs // set or length limits for the protocol names, and the final domain is allowed to be empty.
9464b22b933Srs // However, if the given FQDN doesn't contain at least three labels,
9474b22b933Srs // DeconstructServiceName will reject it and return mDNSfalse.
DeconstructServiceName(const domainname * const fqdn,domainlabel * const name,domainname * const type,domainname * const domain)9484b22b933Srs mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
9495ffb0c9bSToomas Soome                                            domainlabel *const name, domainname *const type, domainname *const domain)
9505ffb0c9bSToomas Soome {
9515ffb0c9bSToomas Soome     int i, len;
9525ffb0c9bSToomas Soome     const mDNSu8 *src = fqdn->c;
9535ffb0c9bSToomas Soome     const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
9545ffb0c9bSToomas Soome     mDNSu8 *dst;
9555ffb0c9bSToomas Soome 
9565ffb0c9bSToomas Soome     dst = name->c;                                      // Extract the service name
9575ffb0c9bSToomas Soome     len = *src;
9585ffb0c9bSToomas Soome     if (!len)         { debugf("DeconstructServiceName: FQDN empty!");                             return(mDNSfalse); }
9595ffb0c9bSToomas Soome     if (len >= 0x40)  { debugf("DeconstructServiceName: Instance name too long");                  return(mDNSfalse); }
9605ffb0c9bSToomas Soome     for (i=0; i<=len; i++) *dst++ = *src++;
9615ffb0c9bSToomas Soome 
9625ffb0c9bSToomas Soome     dst = type->c;                                      // Extract the service type
9635ffb0c9bSToomas Soome     len = *src;
9645ffb0c9bSToomas Soome     if (!len)         { debugf("DeconstructServiceName: FQDN contains only one label!");           return(mDNSfalse); }
9655ffb0c9bSToomas Soome     if (len >= 0x40)  { debugf("DeconstructServiceName: Application protocol name too long");      return(mDNSfalse); }
9665ffb0c9bSToomas Soome     if (src[1] != '_') { debugf("DeconstructServiceName: No _ at start of application protocol");   return(mDNSfalse); }
9675ffb0c9bSToomas Soome     for (i=0; i<=len; i++) *dst++ = *src++;
9685ffb0c9bSToomas Soome 
9695ffb0c9bSToomas Soome     len = *src;
9705ffb0c9bSToomas Soome     if (!len)         { debugf("DeconstructServiceName: FQDN contains only two labels!");          return(mDNSfalse); }
9715ffb0c9bSToomas Soome     if (!ValidTransportProtocol(src))
9725ffb0c9bSToomas Soome     { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); }
9735ffb0c9bSToomas Soome     for (i=0; i<=len; i++) *dst++ = *src++;
9745ffb0c9bSToomas Soome     *dst++ = 0;                                         // Put terminator on the end of service type
9755ffb0c9bSToomas Soome 
9765ffb0c9bSToomas Soome     dst = domain->c;                                    // Extract the service domain
9775ffb0c9bSToomas Soome     while (*src)
9785ffb0c9bSToomas Soome     {
9795ffb0c9bSToomas Soome         len = *src;
9805ffb0c9bSToomas Soome         if (len >= 0x40)
9815ffb0c9bSToomas Soome         { debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); }
9825ffb0c9bSToomas Soome         if (src + 1 + len + 1 >= max)
9835ffb0c9bSToomas Soome         { debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); }
9845ffb0c9bSToomas Soome         for (i=0; i<=len; i++) *dst++ = *src++;
9855ffb0c9bSToomas Soome     }
9865ffb0c9bSToomas Soome     *dst++ = 0;     // Put the null root label on the end
9875ffb0c9bSToomas Soome 
9885ffb0c9bSToomas Soome     return(mDNStrue);
9895ffb0c9bSToomas Soome }
9905ffb0c9bSToomas Soome 
DNSNameToLowerCase(domainname * d,domainname * result)9915ffb0c9bSToomas Soome mDNSexport mStatus DNSNameToLowerCase(domainname *d, domainname *result)
9925ffb0c9bSToomas Soome {
9935ffb0c9bSToomas Soome     const mDNSu8 *a = d->c;
9945ffb0c9bSToomas Soome     mDNSu8 *b = result->c;
9955ffb0c9bSToomas Soome     const mDNSu8 *const max = d->c + MAX_DOMAIN_NAME;
9965ffb0c9bSToomas Soome     int i, len;
9975ffb0c9bSToomas Soome 
9985ffb0c9bSToomas Soome     while (*a)
9995ffb0c9bSToomas Soome     {
10005ffb0c9bSToomas Soome         if (a + 1 + *a >= max)
10015ffb0c9bSToomas Soome         {
10025ffb0c9bSToomas Soome             LogMsg("DNSNameToLowerCase: ERROR!! Malformed Domain name");
10035ffb0c9bSToomas Soome             return mStatus_BadParamErr;
10045ffb0c9bSToomas Soome         }
10055ffb0c9bSToomas Soome         len = *a++;
10065ffb0c9bSToomas Soome         *b++ = len;
10075ffb0c9bSToomas Soome         for (i = 0; i < len; i++)
10085ffb0c9bSToomas Soome         {
10095ffb0c9bSToomas Soome             mDNSu8 ac = *a++;
10105ffb0c9bSToomas Soome             if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
10115ffb0c9bSToomas Soome             *b++ = ac;
10125ffb0c9bSToomas Soome         }
10135ffb0c9bSToomas Soome     }
10145ffb0c9bSToomas Soome     *b = 0;
10155ffb0c9bSToomas Soome 
10165ffb0c9bSToomas Soome     return mStatus_NoError;
10175ffb0c9bSToomas Soome }
10185ffb0c9bSToomas Soome 
10194b22b933Srs // Notes on UTF-8:
10204b22b933Srs // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
10214b22b933Srs // 10xxxxxx is a continuation byte of a multi-byte character
10224b22b933Srs // 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x     80 - 0x     800-1)
10234b22b933Srs // 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x    800 - 0x   10000-1)
10244b22b933Srs // 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x  10000 - 0x  200000-1)
10254b22b933Srs // 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
10264b22b933Srs // 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
10274b22b933Srs //
10284b22b933Srs // UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
10294b22b933Srs // Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
10304b22b933Srs // about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
10314b22b933Srs // The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
10324b22b933Srs // and the second    is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
10334b22b933Srs 
TruncateUTF8ToLength(mDNSu8 * string,mDNSu32 length,mDNSu32 max)10344b22b933Srs mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
10355ffb0c9bSToomas Soome {
10365ffb0c9bSToomas Soome     if (length > max)
10375ffb0c9bSToomas Soome     {
10385ffb0c9bSToomas Soome         mDNSu8 c1 = string[max];                                        // First byte after cut point
10395ffb0c9bSToomas Soome         mDNSu8 c2 = (max+1 < length) ? string[max+1] : (mDNSu8)0xB0;    // Second byte after cut point
10405ffb0c9bSToomas Soome         length = max;   // Trim length down
10415ffb0c9bSToomas Soome         while (length > 0)
10425ffb0c9bSToomas Soome         {
10435ffb0c9bSToomas Soome             // Check if the byte right after the chop point is a UTF-8 continuation byte,
10445ffb0c9bSToomas Soome             // or if the character right after the chop point is the second of a UTF-16 surrogate pair.
10455ffb0c9bSToomas Soome             // If so, then we continue to chop more bytes until we get to a legal chop point.
10465ffb0c9bSToomas Soome             mDNSBool continuation    = ((c1 & 0xC0) == 0x80);
10475ffb0c9bSToomas Soome             mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0);
10485ffb0c9bSToomas Soome             if (!continuation && !secondsurrogate) break;
10495ffb0c9bSToomas Soome             c2 = c1;
10505ffb0c9bSToomas Soome             c1 = string[--length];
10515ffb0c9bSToomas Soome         }
10525ffb0c9bSToomas Soome         // Having truncated characters off the end of our string, also cut off any residual white space
10535ffb0c9bSToomas Soome         while (length > 0 && string[length-1] <= ' ') length--;
10545ffb0c9bSToomas Soome     }
10555ffb0c9bSToomas Soome     return(length);
10565ffb0c9bSToomas Soome }
10574b22b933Srs 
10584b22b933Srs // Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
10594b22b933Srs // name ends in "-nnn", where n is some decimal number.
LabelContainsSuffix(const domainlabel * const name,const mDNSBool RichText)10604b22b933Srs mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
10615ffb0c9bSToomas Soome {
10625ffb0c9bSToomas Soome     mDNSu16 l = name->c[0];
10635ffb0c9bSToomas Soome 
10645ffb0c9bSToomas Soome     if (RichText)
10655ffb0c9bSToomas Soome     {
10665ffb0c9bSToomas Soome         if (l < 4) return mDNSfalse;                            // Need at least " (2)"
10675ffb0c9bSToomas Soome         if (name->c[l--] != ')') return mDNSfalse;              // Last char must be ')'
10685ffb0c9bSToomas Soome         if (!mDNSIsDigit(name->c[l])) return mDNSfalse;         // Preceeded by a digit
10695ffb0c9bSToomas Soome         l--;
10705ffb0c9bSToomas Soome         while (l > 2 && mDNSIsDigit(name->c[l])) l--;           // Strip off digits
10715ffb0c9bSToomas Soome         return (name->c[l] == '(' && name->c[l - 1] == ' ');
10725ffb0c9bSToomas Soome     }
10735ffb0c9bSToomas Soome     else
10745ffb0c9bSToomas Soome     {
10755ffb0c9bSToomas Soome         if (l < 2) return mDNSfalse;                            // Need at least "-2"
10765ffb0c9bSToomas Soome         if (!mDNSIsDigit(name->c[l])) return mDNSfalse;         // Last char must be a digit
10775ffb0c9bSToomas Soome         l--;
10785ffb0c9bSToomas Soome         while (l > 2 && mDNSIsDigit(name->c[l])) l--;           // Strip off digits
10795ffb0c9bSToomas Soome         return (name->c[l] == '-');
10805ffb0c9bSToomas Soome     }
10815ffb0c9bSToomas Soome }
10824b22b933Srs 
10834b22b933Srs // removes an auto-generated suffix (appended on a name collision) from a label.  caller is
10844b22b933Srs // responsible for ensuring that the label does indeed contain a suffix.  returns the number
10854b22b933Srs // from the suffix that was removed.
RemoveLabelSuffix(domainlabel * name,mDNSBool RichText)10864b22b933Srs mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText)
10875ffb0c9bSToomas Soome {
10885ffb0c9bSToomas Soome     mDNSu32 val = 0, multiplier = 1;
10894b22b933Srs 
10905ffb0c9bSToomas Soome     // Chop closing parentheses from RichText suffix
10915ffb0c9bSToomas Soome     if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
10924b22b933Srs 
10935ffb0c9bSToomas Soome     // Get any existing numerical suffix off the name
10945ffb0c9bSToomas Soome     while (mDNSIsDigit(name->c[name->c[0]]))
10955ffb0c9bSToomas Soome     { val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
10964b22b933Srs 
10975ffb0c9bSToomas Soome     // Chop opening parentheses or dash from suffix
10985ffb0c9bSToomas Soome     if (RichText)
10995ffb0c9bSToomas Soome     {
11005ffb0c9bSToomas Soome         if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
11015ffb0c9bSToomas Soome     }
11025ffb0c9bSToomas Soome     else
11035ffb0c9bSToomas Soome     {
11045ffb0c9bSToomas Soome         if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
11055ffb0c9bSToomas Soome     }
11064b22b933Srs 
11075ffb0c9bSToomas Soome     return(val);
11085ffb0c9bSToomas Soome }
11094b22b933Srs 
11104b22b933Srs // appends a numerical suffix to a label, with the number following a whitespace and enclosed
11114b22b933Srs // in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
AppendLabelSuffix(domainlabel * const name,mDNSu32 val,const mDNSBool RichText)11125ffb0c9bSToomas Soome mDNSexport void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText)
11135ffb0c9bSToomas Soome {
11145ffb0c9bSToomas Soome     mDNSu32 divisor = 1, chars = 2; // Shortest possible RFC1034 name suffix is 2 characters ("-2")
11155ffb0c9bSToomas Soome     if (RichText) chars = 4;        // Shortest possible RichText suffix is 4 characters (" (2)")
11164b22b933Srs 
11175ffb0c9bSToomas Soome     // Truncate trailing spaces from RichText names
11185ffb0c9bSToomas Soome     if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
11194b22b933Srs 
11205ffb0c9bSToomas Soome     while (divisor < 0xFFFFFFFFUL/10 && val >= divisor * 10) { divisor *= 10; chars++; }
11214b22b933Srs 
11225ffb0c9bSToomas Soome     name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
11234b22b933Srs 
11245ffb0c9bSToomas Soome     if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
11255ffb0c9bSToomas Soome     else          { name->c[++name->c[0]] = '-'; }
11264b22b933Srs 
11275ffb0c9bSToomas Soome     while (divisor)
11285ffb0c9bSToomas Soome     {
11295ffb0c9bSToomas Soome         name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
11305ffb0c9bSToomas Soome         val     %= divisor;
11315ffb0c9bSToomas Soome         divisor /= 10;
11325ffb0c9bSToomas Soome     }
11334b22b933Srs 
11345ffb0c9bSToomas Soome     if (RichText) name->c[++name->c[0]] = ')';
11355ffb0c9bSToomas Soome }
11364b22b933Srs 
IncrementLabelSuffix(domainlabel * name,mDNSBool RichText)11374b22b933Srs mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
11385ffb0c9bSToomas Soome {
11395ffb0c9bSToomas Soome     mDNSu32 val = 0;
11404b22b933Srs 
11415ffb0c9bSToomas Soome     if (LabelContainsSuffix(name, RichText))
11425ffb0c9bSToomas Soome         val = RemoveLabelSuffix(name, RichText);
11434b22b933Srs 
11445ffb0c9bSToomas Soome     // If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
11455ffb0c9bSToomas Soome     // If existing suffix in the range 2-9, increment it.
11465ffb0c9bSToomas Soome     // If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
11475ffb0c9bSToomas Soome     // so add a random increment to improve the chances of finding an available name next time.
11485ffb0c9bSToomas Soome     if      (val == 0) val = 2;
11495ffb0c9bSToomas Soome     else if (val < 10) val++;
11505ffb0c9bSToomas Soome     else val += 1 + mDNSRandom(99);
11514b22b933Srs 
11525ffb0c9bSToomas Soome     AppendLabelSuffix(name, val, RichText);
11535ffb0c9bSToomas Soome }
11544b22b933Srs 
11554b22b933Srs // ***************************************************************************
11564b22b933Srs #if COMPILER_LIKES_PRAGMA_MARK
11574b22b933Srs #pragma mark -
11584b22b933Srs #pragma mark - Resource Record Utility Functions
11594b22b933Srs #endif
11604b22b933Srs 
11615ffb0c9bSToomas Soome // Set up a AuthRecord with sensible default values.
11625ffb0c9bSToomas 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)11635ffb0c9bSToomas Soome mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
11645ffb0c9bSToomas Soome                                          mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context)
11655ffb0c9bSToomas Soome {
11665ffb0c9bSToomas Soome     //
11675ffb0c9bSToomas Soome     // LocalOnly auth record can be created with LocalOnly InterfaceID or a valid InterfaceID.
11685ffb0c9bSToomas Soome     // Most of the applications normally create with LocalOnly InterfaceID and we store them as
11695ffb0c9bSToomas Soome     // such, so that we can deliver the response to questions that specify LocalOnly InterfaceID.
11705ffb0c9bSToomas Soome     // LocalOnly resource records can also be created with valid InterfaceID which happens today
11715ffb0c9bSToomas Soome     // when we create LocalOnly records for /etc/hosts.
11725ffb0c9bSToomas Soome 
11735ffb0c9bSToomas Soome     if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly)
11745ffb0c9bSToomas Soome     {
11755ffb0c9bSToomas Soome         LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype);
11765ffb0c9bSToomas Soome     }
11775ffb0c9bSToomas Soome     else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P)
11785ffb0c9bSToomas Soome     {
11795ffb0c9bSToomas Soome         LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype);
11805ffb0c9bSToomas Soome     }
11815ffb0c9bSToomas Soome     else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly))
11825ffb0c9bSToomas Soome     {
11835ffb0c9bSToomas Soome         LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype);
11845ffb0c9bSToomas Soome     }
11855ffb0c9bSToomas Soome 
11865ffb0c9bSToomas Soome     // Don't try to store a TTL bigger than we can represent in platform time units
11875ffb0c9bSToomas Soome     if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond)
11885ffb0c9bSToomas Soome         ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond;
11895ffb0c9bSToomas Soome     else if (ttl == 0)      // And Zero TTL is illegal
11905ffb0c9bSToomas Soome         ttl = DefaultTTLforRRType(rrtype);
11915ffb0c9bSToomas Soome 
11925ffb0c9bSToomas Soome     // Field Group 1: The actual information pertaining to this resource record
11935ffb0c9bSToomas Soome     rr->resrec.RecordType        = RecordType;
11945ffb0c9bSToomas Soome     rr->resrec.InterfaceID       = InterfaceID;
11955ffb0c9bSToomas Soome     rr->resrec.name              = &rr->namestorage;
11965ffb0c9bSToomas Soome     rr->resrec.rrtype            = rrtype;
11975ffb0c9bSToomas Soome     rr->resrec.rrclass           = kDNSClass_IN;
11985ffb0c9bSToomas Soome     rr->resrec.rroriginalttl     = ttl;
1199*472cd20dSToomas Soome #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
1200*472cd20dSToomas Soome     rr->resrec.dnsservice        = NULL;
1201*472cd20dSToomas Soome #else
12025ffb0c9bSToomas Soome     rr->resrec.rDNSServer        = mDNSNULL;
1203*472cd20dSToomas Soome #endif
12045ffb0c9bSToomas Soome //	rr->resrec.rdlength          = MUST set by client and/or in mDNS_Register_internal
12055ffb0c9bSToomas Soome //	rr->resrec.rdestimate        = set in mDNS_Register_internal
12065ffb0c9bSToomas Soome //	rr->resrec.rdata             = MUST be set by client
12075ffb0c9bSToomas Soome 
12085ffb0c9bSToomas Soome     if (RDataStorage)
12095ffb0c9bSToomas Soome         rr->resrec.rdata = RDataStorage;
12105ffb0c9bSToomas Soome     else
12115ffb0c9bSToomas Soome     {
12125ffb0c9bSToomas Soome         rr->resrec.rdata = &rr->rdatastorage;
12135ffb0c9bSToomas Soome         rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
12145ffb0c9bSToomas Soome     }
12155ffb0c9bSToomas Soome 
12165ffb0c9bSToomas Soome     // Field Group 2: Persistent metadata for Authoritative Records
12175ffb0c9bSToomas Soome     rr->Additional1       = mDNSNULL;
12185ffb0c9bSToomas Soome     rr->Additional2       = mDNSNULL;
12195ffb0c9bSToomas Soome     rr->DependentOn       = mDNSNULL;
12205ffb0c9bSToomas Soome     rr->RRSet             = mDNSNULL;
12215ffb0c9bSToomas Soome     rr->RecordCallback    = Callback;
12225ffb0c9bSToomas Soome     rr->RecordContext     = Context;
12235ffb0c9bSToomas Soome 
12245ffb0c9bSToomas Soome     rr->AutoTarget        = Target_Manual;
12255ffb0c9bSToomas Soome     rr->AllowRemoteQuery  = mDNSfalse;
12265ffb0c9bSToomas Soome     rr->ForceMCast        = mDNSfalse;
12275ffb0c9bSToomas Soome 
12285ffb0c9bSToomas Soome     rr->WakeUp            = zeroOwner;
12295ffb0c9bSToomas Soome     rr->AddressProxy      = zeroAddr;
12305ffb0c9bSToomas Soome     rr->TimeRcvd          = 0;
12315ffb0c9bSToomas Soome     rr->TimeExpire        = 0;
12325ffb0c9bSToomas Soome     rr->ARType            = artype;
12335ffb0c9bSToomas Soome     rr->AuthFlags         = 0;
12345ffb0c9bSToomas Soome 
12355ffb0c9bSToomas Soome     // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
12365ffb0c9bSToomas Soome     // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
12375ffb0c9bSToomas Soome 
12385ffb0c9bSToomas Soome     // For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
12395ffb0c9bSToomas Soome     // (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
12405ffb0c9bSToomas Soome     // of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
12415ffb0c9bSToomas Soome     rr->state             = regState_Zero;
12425ffb0c9bSToomas Soome     rr->uselease          = 0;
12435ffb0c9bSToomas Soome     rr->expire            = 0;
12445ffb0c9bSToomas Soome     rr->Private           = 0;
12455ffb0c9bSToomas Soome     rr->updateid          = zeroID;
12465ffb0c9bSToomas Soome     rr->zone              = rr->resrec.name;
12475ffb0c9bSToomas Soome     rr->nta               = mDNSNULL;
12485ffb0c9bSToomas Soome     rr->tcp               = mDNSNULL;
12495ffb0c9bSToomas Soome     rr->OrigRData         = 0;
12505ffb0c9bSToomas Soome     rr->OrigRDLen         = 0;
12515ffb0c9bSToomas Soome     rr->InFlightRData     = 0;
12525ffb0c9bSToomas Soome     rr->InFlightRDLen     = 0;
12535ffb0c9bSToomas Soome     rr->QueuedRData       = 0;
12545ffb0c9bSToomas Soome     rr->QueuedRDLen       = 0;
12555ffb0c9bSToomas Soome     mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo));
12565ffb0c9bSToomas Soome     rr->SRVChanged = mDNSfalse;
12575ffb0c9bSToomas Soome     rr->mState = mergeState_Zero;
12585ffb0c9bSToomas Soome 
12595ffb0c9bSToomas Soome     rr->namestorage.c[0]  = 0;      // MUST be set by client before calling mDNS_Register()
12605ffb0c9bSToomas Soome }
12615ffb0c9bSToomas Soome 
mDNS_SetupQuestion(DNSQuestion * const q,const mDNSInterfaceID InterfaceID,const domainname * const name,const mDNSu16 qtype,mDNSQuestionCallback * const callback,void * const context)12625ffb0c9bSToomas Soome mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name,
12635ffb0c9bSToomas Soome                                    const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context)
12645ffb0c9bSToomas Soome {
12655ffb0c9bSToomas Soome     q->InterfaceID         = InterfaceID;
12665ffb0c9bSToomas Soome     q->flags               = 0;
12675ffb0c9bSToomas Soome     AssignDomainName(&q->qname, name);
12685ffb0c9bSToomas Soome     q->qtype               = qtype;
12695ffb0c9bSToomas Soome     q->qclass              = kDNSClass_IN;
12705ffb0c9bSToomas Soome     q->LongLived           = (qtype == kDNSType_PTR);
12715ffb0c9bSToomas Soome     q->ExpectUnique        = (qtype != kDNSType_PTR);
12725ffb0c9bSToomas Soome     q->ForceMCast          = mDNSfalse;
12735ffb0c9bSToomas Soome     q->ReturnIntermed      = mDNSfalse;
12745ffb0c9bSToomas Soome     q->SuppressUnusable    = mDNSfalse;
12755ffb0c9bSToomas Soome     q->AppendSearchDomains = 0;
12765ffb0c9bSToomas Soome     q->TimeoutQuestion     = 0;
12775ffb0c9bSToomas Soome     q->WakeOnResolve       = 0;
1278*472cd20dSToomas Soome     q->UseBackgroundTraffic = mDNSfalse;
12795ffb0c9bSToomas Soome     q->ProxyQuestion       = 0;
12805ffb0c9bSToomas Soome     q->pid                 = mDNSPlatformGetPID();
1281cda73f64SToomas Soome     q->euid                = 0;
1282*472cd20dSToomas Soome     q->BlockedByPolicy     = mDNSfalse;
12835ffb0c9bSToomas Soome     q->ServiceID           = -1;
12845ffb0c9bSToomas Soome     q->QuestionCallback    = callback;
12855ffb0c9bSToomas Soome     q->QuestionContext     = context;
12865ffb0c9bSToomas Soome }
12875ffb0c9bSToomas Soome 
RDataHashValue(const ResourceRecord * const rr)12885ffb0c9bSToomas Soome mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr)
12895ffb0c9bSToomas Soome {
12905ffb0c9bSToomas Soome     int len = rr->rdlength;
12915ffb0c9bSToomas Soome     const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
12925ffb0c9bSToomas Soome     const mDNSu8 *ptr = rdb->data;
12935ffb0c9bSToomas Soome     mDNSu32 sum = 0;
12945ffb0c9bSToomas Soome 
12955ffb0c9bSToomas Soome     switch(rr->rrtype)
12965ffb0c9bSToomas Soome     {
12975ffb0c9bSToomas Soome     case kDNSType_NS:
12985ffb0c9bSToomas Soome     case kDNSType_MD:
12995ffb0c9bSToomas Soome     case kDNSType_MF:
13005ffb0c9bSToomas Soome     case kDNSType_CNAME:
13015ffb0c9bSToomas Soome     case kDNSType_MB:
13025ffb0c9bSToomas Soome     case kDNSType_MG:
13035ffb0c9bSToomas Soome     case kDNSType_MR:
13045ffb0c9bSToomas Soome     case kDNSType_PTR:
13055ffb0c9bSToomas Soome     case kDNSType_NSAP_PTR:
13065ffb0c9bSToomas Soome     case kDNSType_DNAME: return DomainNameHashValue(&rdb->name);
13075ffb0c9bSToomas Soome 
13085ffb0c9bSToomas Soome     case kDNSType_SOA:   return rdb->soa.serial  +
13095ffb0c9bSToomas Soome                rdb->soa.refresh +
13105ffb0c9bSToomas Soome                rdb->soa.retry   +
13115ffb0c9bSToomas Soome                rdb->soa.expire  +
13125ffb0c9bSToomas Soome                rdb->soa.min     +
13135ffb0c9bSToomas Soome                DomainNameHashValue(&rdb->soa.mname) +
13145ffb0c9bSToomas Soome                DomainNameHashValue(&rdb->soa.rname);
13155ffb0c9bSToomas Soome 
13165ffb0c9bSToomas Soome     case kDNSType_MX:
13175ffb0c9bSToomas Soome     case kDNSType_AFSDB:
13185ffb0c9bSToomas Soome     case kDNSType_RT:
13195ffb0c9bSToomas Soome     case kDNSType_KX:    return DomainNameHashValue(&rdb->mx.exchange);
13205ffb0c9bSToomas Soome 
13215ffb0c9bSToomas Soome     case kDNSType_MINFO:
13225ffb0c9bSToomas Soome     case kDNSType_RP:    return DomainNameHashValue(&rdb->rp.mbox)   + DomainNameHashValue(&rdb->rp.txt);
13235ffb0c9bSToomas Soome 
13245ffb0c9bSToomas Soome     case kDNSType_PX:    return DomainNameHashValue(&rdb->px.map822) + DomainNameHashValue(&rdb->px.mapx400);
13255ffb0c9bSToomas Soome 
13265ffb0c9bSToomas Soome     case kDNSType_SRV:   return DomainNameHashValue(&rdb->srv.target);
13275ffb0c9bSToomas Soome 
13285ffb0c9bSToomas Soome     case kDNSType_OPT:   return 0;      // OPT is a pseudo-RR container structure; makes no sense to compare
13295ffb0c9bSToomas Soome 
13305ffb0c9bSToomas Soome     case kDNSType_NSEC: {
13315ffb0c9bSToomas Soome         int dlen;
13325ffb0c9bSToomas Soome         dlen = DomainNameLength((domainname *)rdb->data);
13335ffb0c9bSToomas Soome         sum = DomainNameHashValue((domainname *)rdb->data);
13345ffb0c9bSToomas Soome         ptr += dlen;
13355ffb0c9bSToomas Soome         len -= dlen;
1336*472cd20dSToomas Soome         /* FALLTHROUGH */
13375ffb0c9bSToomas Soome     }
1338fff695d4SToomas Soome     /* FALLTHROUGH */
13395ffb0c9bSToomas Soome 
13405ffb0c9bSToomas Soome     default:
13415ffb0c9bSToomas Soome     {
13425ffb0c9bSToomas Soome         int i;
13435ffb0c9bSToomas Soome         for (i=0; i+1 < len; i+=2)
13445ffb0c9bSToomas Soome         {
13455ffb0c9bSToomas Soome             sum += (((mDNSu32)(ptr[i])) << 8) | ptr[i+1];
13465ffb0c9bSToomas Soome             sum = (sum<<3) | (sum>>29);
13475ffb0c9bSToomas Soome         }
13485ffb0c9bSToomas Soome         if (i < len)
13495ffb0c9bSToomas Soome         {
13505ffb0c9bSToomas Soome             sum += ((mDNSu32)(ptr[i])) << 8;
13515ffb0c9bSToomas Soome         }
13525ffb0c9bSToomas Soome         return(sum);
13535ffb0c9bSToomas Soome     }
13545ffb0c9bSToomas Soome     }
13555ffb0c9bSToomas Soome }
13564b22b933Srs 
13574b22b933Srs // r1 has to be a full ResourceRecord including rrtype and rdlength
13584b22b933Srs // 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)13595ffb0c9bSToomas Soome mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename)
13605ffb0c9bSToomas Soome {
13615ffb0c9bSToomas Soome     const RDataBody2 *const b1 = (RDataBody2 *)r1->rdata->u.data;
13625ffb0c9bSToomas Soome     const RDataBody2 *const b2 = (RDataBody2 *)r2;
13635ffb0c9bSToomas Soome     switch(r1->rrtype)
13645ffb0c9bSToomas Soome     {
13655ffb0c9bSToomas Soome     case kDNSType_NS:
13665ffb0c9bSToomas Soome     case kDNSType_MD:
13675ffb0c9bSToomas Soome     case kDNSType_MF:
13685ffb0c9bSToomas Soome     case kDNSType_CNAME:
13695ffb0c9bSToomas Soome     case kDNSType_MB:
13705ffb0c9bSToomas Soome     case kDNSType_MG:
13715ffb0c9bSToomas Soome     case kDNSType_MR:
13725ffb0c9bSToomas Soome     case kDNSType_PTR:
13735ffb0c9bSToomas Soome     case kDNSType_NSAP_PTR:
13745ffb0c9bSToomas Soome     case kDNSType_DNAME: return(SameDomainName(&b1->name, &b2->name));
13755ffb0c9bSToomas Soome 
13765ffb0c9bSToomas Soome     case kDNSType_SOA:  return (mDNSBool)(   b1->soa.serial   == b2->soa.serial             &&
13775ffb0c9bSToomas Soome                                              b1->soa.refresh  == b2->soa.refresh            &&
13785ffb0c9bSToomas Soome                                              b1->soa.retry    == b2->soa.retry              &&
13795ffb0c9bSToomas Soome                                              b1->soa.expire   == b2->soa.expire             &&
13805ffb0c9bSToomas Soome                                              b1->soa.min      == b2->soa.min                &&
13815ffb0c9bSToomas Soome                                              samename(&b1->soa.mname, &b2->soa.mname) &&
13825ffb0c9bSToomas Soome                                              samename(&b1->soa.rname, &b2->soa.rname));
13835ffb0c9bSToomas Soome 
13845ffb0c9bSToomas Soome     case kDNSType_MX:
13855ffb0c9bSToomas Soome     case kDNSType_AFSDB:
13865ffb0c9bSToomas Soome     case kDNSType_RT:
13875ffb0c9bSToomas Soome     case kDNSType_KX:   return (mDNSBool)(   b1->mx.preference == b2->mx.preference &&
13885ffb0c9bSToomas Soome                                              samename(&b1->mx.exchange, &b2->mx.exchange));
13895ffb0c9bSToomas Soome 
13905ffb0c9bSToomas Soome     case kDNSType_MINFO:
13915ffb0c9bSToomas Soome     case kDNSType_RP:   return (mDNSBool)(   samename(&b1->rp.mbox, &b2->rp.mbox) &&
13925ffb0c9bSToomas Soome                                              samename(&b1->rp.txt,  &b2->rp.txt));
13935ffb0c9bSToomas Soome 
13945ffb0c9bSToomas Soome     case kDNSType_PX:   return (mDNSBool)(   b1->px.preference == b2->px.preference          &&
13955ffb0c9bSToomas Soome                                              samename(&b1->px.map822,  &b2->px.map822) &&
13965ffb0c9bSToomas Soome                                              samename(&b1->px.mapx400, &b2->px.mapx400));
13975ffb0c9bSToomas Soome 
13985ffb0c9bSToomas Soome     case kDNSType_SRV:  return (mDNSBool)(   b1->srv.priority == b2->srv.priority       &&
13995ffb0c9bSToomas Soome                                              b1->srv.weight   == b2->srv.weight         &&
14005ffb0c9bSToomas Soome                                              mDNSSameIPPort(b1->srv.port, b2->srv.port) &&
14015ffb0c9bSToomas Soome                                              samename(&b1->srv.target, &b2->srv.target));
14025ffb0c9bSToomas Soome 
14035ffb0c9bSToomas Soome     case kDNSType_OPT:  return mDNSfalse;       // OPT is a pseudo-RR container structure; makes no sense to compare
14045ffb0c9bSToomas Soome     case kDNSType_NSEC: {
14055ffb0c9bSToomas Soome         // If the "nxt" name changes in case, we want to delete the old
14065ffb0c9bSToomas Soome         // and store just the new one. If the caller passes in SameDomainCS for "samename",
14075ffb0c9bSToomas Soome         // we would return "false" when the only change between the two rdata is the case
14085ffb0c9bSToomas Soome         // change in "nxt".
14095ffb0c9bSToomas Soome         //
14105ffb0c9bSToomas Soome         // Note: rdlength of both the RData are same (ensured by the caller) and hence we can
14115ffb0c9bSToomas Soome         // use just r1->rdlength below
14125ffb0c9bSToomas Soome 
14135ffb0c9bSToomas Soome         int dlen1 = DomainNameLength((domainname *)b1->data);
14145ffb0c9bSToomas Soome         int dlen2 = DomainNameLength((domainname *)b2->data);
14155ffb0c9bSToomas Soome         return (mDNSBool)(dlen1 == dlen2 &&
14165ffb0c9bSToomas Soome                           samename((domainname *)b1->data, (domainname *)b2->data) &&
14175ffb0c9bSToomas Soome                           mDNSPlatformMemSame(b1->data + dlen1, b2->data + dlen2, r1->rdlength - dlen1));
14185ffb0c9bSToomas Soome     }
14195ffb0c9bSToomas Soome 
14205ffb0c9bSToomas Soome     default:            return(mDNSPlatformMemSame(b1->data, b2->data, r1->rdlength));
14215ffb0c9bSToomas Soome     }
14225ffb0c9bSToomas Soome }
14235ffb0c9bSToomas Soome 
BitmapTypeCheck(mDNSu8 * bmap,int bitmaplen,mDNSu16 type)14245ffb0c9bSToomas Soome mDNSexport mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type)
14255ffb0c9bSToomas Soome {
14265ffb0c9bSToomas Soome     int win, wlen;
14275ffb0c9bSToomas Soome     int wintype;
14285ffb0c9bSToomas Soome 
14295ffb0c9bSToomas Soome     // The window that this type belongs to. NSEC has 256 windows that
14305ffb0c9bSToomas Soome     // comprises of 256 types.
14315ffb0c9bSToomas Soome     wintype = type >> 8;
14325ffb0c9bSToomas Soome 
14335ffb0c9bSToomas Soome     while (bitmaplen > 0)
14345ffb0c9bSToomas Soome     {
14355ffb0c9bSToomas Soome         if (bitmaplen < 3)
14365ffb0c9bSToomas Soome         {
14375ffb0c9bSToomas Soome             LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d short", bitmaplen);
14385ffb0c9bSToomas Soome             return mDNSfalse;
14395ffb0c9bSToomas Soome         }
14405ffb0c9bSToomas Soome 
14415ffb0c9bSToomas Soome         win = *bmap++;
14425ffb0c9bSToomas Soome         wlen = *bmap++;
14435ffb0c9bSToomas Soome         bitmaplen -= 2;
14445ffb0c9bSToomas Soome         if (bitmaplen < wlen || wlen < 1 || wlen > 32)
14455ffb0c9bSToomas Soome         {
14465ffb0c9bSToomas Soome             LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d wlen %d, win %d", bitmaplen, wlen, win);
14475ffb0c9bSToomas Soome             return mDNSfalse;
14485ffb0c9bSToomas Soome         }
14495ffb0c9bSToomas Soome         if (win < 0 || win >= 256)
14505ffb0c9bSToomas Soome         {
14515ffb0c9bSToomas Soome             LogInfo("BitmapTypeCheck: malformed nsec, wlen %d", wlen);
14525ffb0c9bSToomas Soome             return mDNSfalse;
14535ffb0c9bSToomas Soome         }
14545ffb0c9bSToomas Soome         if (win == wintype)
14555ffb0c9bSToomas Soome         {
14565ffb0c9bSToomas Soome             // First byte in the window serves 0 to 7, the next one serves 8 to 15 and so on.
14575ffb0c9bSToomas Soome             // Calculate the right byte offset first.
14585ffb0c9bSToomas Soome             int boff = (type & 0xff ) >> 3;
14595ffb0c9bSToomas Soome             if (wlen <= boff)
14605ffb0c9bSToomas Soome                 return mDNSfalse;
14615ffb0c9bSToomas Soome             // The last three bits values 0 to 7 corresponds to bit positions
14625ffb0c9bSToomas Soome             // within the byte.
14635ffb0c9bSToomas Soome             return (bmap[boff] & (0x80 >> (type & 7)));
14645ffb0c9bSToomas Soome         }
14655ffb0c9bSToomas Soome         else
14665ffb0c9bSToomas Soome         {
14675ffb0c9bSToomas Soome             // If the windows are ordered, then we could check to see
14685ffb0c9bSToomas Soome             // if wintype > win and then return early.
14695ffb0c9bSToomas Soome             bmap += wlen;
14705ffb0c9bSToomas Soome             bitmaplen -= wlen;
14715ffb0c9bSToomas Soome         }
14725ffb0c9bSToomas Soome     }
14735ffb0c9bSToomas Soome     return mDNSfalse;
14745ffb0c9bSToomas Soome }
14755ffb0c9bSToomas Soome 
14765ffb0c9bSToomas Soome // Don't call this function if the resource record is not NSEC. It will return false
14775ffb0c9bSToomas Soome // which means that the type does not exist.
RRAssertsExistence(const ResourceRecord * const rr,mDNSu16 type)14785ffb0c9bSToomas Soome mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type)
14795ffb0c9bSToomas Soome {
14805ffb0c9bSToomas Soome     const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
14815ffb0c9bSToomas Soome     mDNSu8 *nsec = (mDNSu8 *)rdb->data;
14825ffb0c9bSToomas Soome     int len, bitmaplen;
14835ffb0c9bSToomas Soome     mDNSu8 *bmap;
14845ffb0c9bSToomas Soome 
14855ffb0c9bSToomas Soome     if (rr->rrtype != kDNSType_NSEC) return mDNSfalse;
14865ffb0c9bSToomas Soome 
14875ffb0c9bSToomas Soome     len = DomainNameLength((domainname *)nsec);
14885ffb0c9bSToomas Soome 
14895ffb0c9bSToomas Soome     bitmaplen = rr->rdlength - len;
14905ffb0c9bSToomas Soome     bmap = nsec + len;
14915ffb0c9bSToomas Soome     return (BitmapTypeCheck(bmap, bitmaplen, type));
14925ffb0c9bSToomas Soome }
14935ffb0c9bSToomas Soome 
14945ffb0c9bSToomas Soome // Don't call this function if the resource record is not NSEC. It will return false
14955ffb0c9bSToomas Soome // which means that the type exists.
RRAssertsNonexistence(const ResourceRecord * const rr,mDNSu16 type)14965ffb0c9bSToomas Soome mDNSexport mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type)
14975ffb0c9bSToomas Soome {
14985ffb0c9bSToomas Soome     if (rr->rrtype != kDNSType_NSEC) return mDNSfalse;
14995ffb0c9bSToomas Soome 
15005ffb0c9bSToomas Soome     return !RRAssertsExistence(rr, type);
15015ffb0c9bSToomas Soome }
15025ffb0c9bSToomas Soome 
15035ffb0c9bSToomas Soome // ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
15045ffb0c9bSToomas Soome // SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
15055ffb0c9bSToomas Soome // SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
15065ffb0c9bSToomas Soome // because it has to check all the way to the end of the names to be sure.
15075ffb0c9bSToomas Soome // In cases where we know in advance that the names match it's especially advantageous to skip the
15085ffb0c9bSToomas Soome // SameDomainName() call because that's precisely the time when it's most expensive and least useful.
15095ffb0c9bSToomas Soome 
SameNameRecordAnswersQuestion(const ResourceRecord * const rr,mDNSBool isAuthRecord,const DNSQuestion * const q)1510*472cd20dSToomas Soome mDNSlocal mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, mDNSBool isAuthRecord, const DNSQuestion *const q)
15115ffb0c9bSToomas Soome {
15125ffb0c9bSToomas Soome     mDNSBool checkType = mDNStrue;
15135ffb0c9bSToomas Soome 
15145ffb0c9bSToomas Soome     // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
15155ffb0c9bSToomas Soome     // are handled in LocalOnlyRecordAnswersQuestion
1516c65ebfc7SToomas Soome     if (LocalOnlyOrP2PInterface(rr->InterfaceID))
15175ffb0c9bSToomas Soome     {
15185ffb0c9bSToomas Soome         LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
15195ffb0c9bSToomas Soome         return mDNSfalse;
15205ffb0c9bSToomas Soome     }
1521*472cd20dSToomas Soome     if (q->Suppressed)
15225ffb0c9bSToomas Soome         return mDNSfalse;
15235ffb0c9bSToomas Soome 
15245ffb0c9bSToomas Soome     if (rr->InterfaceID &&
15255ffb0c9bSToomas Soome         q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
15265ffb0c9bSToomas Soome         rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
15275ffb0c9bSToomas Soome 
15285ffb0c9bSToomas Soome     // Resource record received via unicast, the resolver group ID should match ?
1529*472cd20dSToomas Soome     if (!isAuthRecord && !rr->InterfaceID)
15305ffb0c9bSToomas Soome     {
1531*472cd20dSToomas Soome         if (mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
1532*472cd20dSToomas Soome #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
1533*472cd20dSToomas Soome         if (rr->dnsservice != q->dnsservice) return(mDNSfalse);
1534*472cd20dSToomas Soome #else
1535*472cd20dSToomas Soome         const mDNSu32 idr = rr->rDNSServer ? rr->rDNSServer->resGroupID : 0;
1536*472cd20dSToomas Soome         const mDNSu32 idq = q->qDNSServer ? q->qDNSServer->resGroupID : 0;
15375ffb0c9bSToomas Soome         if (idr != idq) return(mDNSfalse);
1538*472cd20dSToomas Soome #endif
15395ffb0c9bSToomas Soome     }
15405ffb0c9bSToomas Soome 
15415ffb0c9bSToomas Soome     // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
15425ffb0c9bSToomas Soome     if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
15435ffb0c9bSToomas Soome 
15445ffb0c9bSToomas Soome     // CNAME answers question of any type and a negative cache record should not prevent us from querying other
15455ffb0c9bSToomas Soome     // valid types at the same name.
15465ffb0c9bSToomas Soome     if (rr->rrtype == kDNSType_CNAME && rr->RecordType == kDNSRecordTypePacketNegative && rr->rrtype != q->qtype)
1547*472cd20dSToomas Soome          return mDNSfalse;
1548*472cd20dSToomas Soome 
1549*472cd20dSToomas Soome #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
1550*472cd20dSToomas Soome     if (enables_dnssec_validation(q) && record_type_answers_dnssec_question(rr, q->qtype)) checkType = mDNSfalse;
1551*472cd20dSToomas Soome #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
15525ffb0c9bSToomas Soome 
15535ffb0c9bSToomas Soome     // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
15545ffb0c9bSToomas Soome     if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
15555ffb0c9bSToomas Soome     if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
15565ffb0c9bSToomas Soome 
15575ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder
15585ffb0c9bSToomas Soome     if (!mDNSPlatformValidRecordForQuestion(rr, q))
15595ffb0c9bSToomas Soome         return mDNSfalse;
15605ffb0c9bSToomas Soome #endif // APPLE_OSX_mDNSResponder
15615ffb0c9bSToomas Soome 
15625ffb0c9bSToomas Soome     return(mDNStrue);
15635ffb0c9bSToomas Soome }
15644b22b933Srs 
SameNameCacheRecordAnswersQuestion(const CacheRecord * const cr,const DNSQuestion * const q)1565*472cd20dSToomas Soome mDNSexport mDNSBool SameNameCacheRecordAnswersQuestion(const CacheRecord *const cr, const DNSQuestion *const q)
1566*472cd20dSToomas Soome {
1567*472cd20dSToomas Soome     return SameNameRecordAnswersQuestion(&cr->resrec, mDNSfalse, q);
1568*472cd20dSToomas Soome }
1569*472cd20dSToomas Soome 
RecordAnswersQuestion(const ResourceRecord * const rr,mDNSBool isAuthRecord,const DNSQuestion * const q)1570*472cd20dSToomas Soome mDNSlocal mDNSBool RecordAnswersQuestion(const ResourceRecord *const rr, mDNSBool isAuthRecord, const DNSQuestion *const q)
15715ffb0c9bSToomas Soome {
1572*472cd20dSToomas Soome     if (!SameNameRecordAnswersQuestion(rr, isAuthRecord, q))
15735ffb0c9bSToomas Soome         return mDNSfalse;
15745ffb0c9bSToomas Soome 
15755ffb0c9bSToomas Soome     return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
15765ffb0c9bSToomas Soome }
15775ffb0c9bSToomas Soome 
ResourceRecordAnswersQuestion(const ResourceRecord * const rr,const DNSQuestion * const q)1578*472cd20dSToomas Soome mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
1579*472cd20dSToomas Soome {
1580*472cd20dSToomas Soome     return RecordAnswersQuestion(rr, mDNSfalse, q);
1581*472cd20dSToomas Soome }
1582*472cd20dSToomas Soome 
AuthRecordAnswersQuestion(const AuthRecord * const ar,const DNSQuestion * const q)1583*472cd20dSToomas Soome mDNSexport mDNSBool AuthRecordAnswersQuestion(const AuthRecord *const ar, const DNSQuestion *const q)
1584*472cd20dSToomas Soome {
1585*472cd20dSToomas Soome     return RecordAnswersQuestion(&ar->resrec, mDNStrue, q);
1586*472cd20dSToomas Soome }
1587*472cd20dSToomas Soome 
CacheRecordAnswersQuestion(const CacheRecord * const cr,const DNSQuestion * const q)1588*472cd20dSToomas Soome mDNSexport mDNSBool CacheRecordAnswersQuestion(const CacheRecord *const cr, const DNSQuestion *const q)
1589*472cd20dSToomas Soome {
1590*472cd20dSToomas Soome     return RecordAnswersQuestion(&cr->resrec, mDNSfalse, q);
1591*472cd20dSToomas Soome }
1592*472cd20dSToomas Soome 
15935ffb0c9bSToomas Soome // We have a separate function to handle LocalOnly AuthRecords because they can be created with
15945ffb0c9bSToomas Soome // a valid InterfaceID (e.g., scoped /etc/hosts) and can be used to answer unicast questions unlike
15955ffb0c9bSToomas Soome // multicast resource records (which has a valid InterfaceID) which can't be used to answer
15965ffb0c9bSToomas Soome // unicast questions. ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion can't tell whether
15975ffb0c9bSToomas Soome // a resource record is multicast or LocalOnly by just looking at the ResourceRecord because
15985ffb0c9bSToomas Soome // LocalOnly records are truly identified by ARType in the AuthRecord.  As P2P and LocalOnly record
15995ffb0c9bSToomas Soome // are kept in the same hash table, we use the same function to make it easy for the callers when
16005ffb0c9bSToomas Soome // they walk the hash table to answer LocalOnly/P2P questions
16015ffb0c9bSToomas Soome //
LocalOnlyRecordAnswersQuestion(AuthRecord * const ar,const DNSQuestion * const q)16025ffb0c9bSToomas Soome mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const DNSQuestion *const q)
16035ffb0c9bSToomas Soome {
16045ffb0c9bSToomas Soome     ResourceRecord *rr = &ar->resrec;
16055ffb0c9bSToomas Soome 
16065ffb0c9bSToomas Soome     // mDNSInterface_Any questions can be answered with LocalOnly/P2P records in this function. AuthRecord_Any
16075ffb0c9bSToomas Soome     // records are handled in ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion
16085ffb0c9bSToomas Soome     if (RRAny(ar))
16095ffb0c9bSToomas Soome     {
16105ffb0c9bSToomas Soome         LogMsg("LocalOnlyRecordAnswersQuestion: ERROR!! called with regular AuthRecordAny %##s", rr->name->c);
16115ffb0c9bSToomas Soome         return mDNSfalse;
16125ffb0c9bSToomas Soome     }
16135ffb0c9bSToomas Soome 
16145ffb0c9bSToomas Soome     // Questions with mDNSInterface_LocalOnly InterfaceID should be answered with all resource records that are
16155ffb0c9bSToomas Soome     // *local* to the machine. These include resource records that have InterfaceID set to mDNSInterface_LocalOnly,
16165ffb0c9bSToomas Soome     // mDNSInterface_Any and any other real InterfaceID. Hence, LocalOnly questions should not be checked against
16175ffb0c9bSToomas Soome     // the InterfaceID in the resource record.
16185ffb0c9bSToomas Soome 
16195ffb0c9bSToomas Soome     if (rr->InterfaceID &&
1620*472cd20dSToomas Soome         q->InterfaceID != mDNSInterface_LocalOnly &&
1621*472cd20dSToomas Soome         ((q->InterfaceID && rr->InterfaceID != q->InterfaceID) ||
1622*472cd20dSToomas Soome         (!q->InterfaceID && !LocalOnlyOrP2PInterface(rr->InterfaceID)))) return(mDNSfalse);
16235ffb0c9bSToomas Soome 
16245ffb0c9bSToomas Soome     // Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records
16255ffb0c9bSToomas Soome     // may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set
16265ffb0c9bSToomas Soome     // to mDNSInterface_Any, mDNSInterface_LocalOnly or a real InterfaceID (scoped).
16275ffb0c9bSToomas Soome     //
16285ffb0c9bSToomas Soome     // 1) Question: Any, LocalOnly Record: no scope. This question should be answered with this record.
16295ffb0c9bSToomas Soome     //
16305ffb0c9bSToomas Soome     // 2) Question: Any, LocalOnly Record: scoped.  This question should be answered with the record because
16315ffb0c9bSToomas Soome     //    traditionally applications never specify scope e.g., getaddrinfo, but need to be able
16325ffb0c9bSToomas Soome     //    to get to /etc/hosts entries.
16335ffb0c9bSToomas Soome     //
16345ffb0c9bSToomas Soome     // 3) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: no scope. This is the inverse of (2).
16355ffb0c9bSToomas Soome     //    If we register a LocalOnly record, we need to answer a LocalOnly question. If the /etc/hosts has a
16365ffb0c9bSToomas Soome     //    non scoped entry, it may not make sense to answer a scoped question. But we can't tell these two
16375ffb0c9bSToomas Soome     //    cases apart. As we currently answer LocalOnly question with LocalOnly record, we continue to do so.
16385ffb0c9bSToomas Soome     //
16395ffb0c9bSToomas Soome     // 4) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: scoped. LocalOnly questions should be
16405ffb0c9bSToomas Soome     //    answered with any resource record where as if it has a valid InterfaceID, the scope should match.
16415ffb0c9bSToomas Soome     //
16425ffb0c9bSToomas Soome     // (1) and (2) is bypassed because we check for a non-NULL InterfaceID above. For (3), the InterfaceID is NULL
16435ffb0c9bSToomas Soome     // and hence bypassed above. For (4) we bypassed LocalOnly questions and checked the scope of the record
16445ffb0c9bSToomas Soome     // against the question.
16455ffb0c9bSToomas Soome     //
16465ffb0c9bSToomas Soome     // For P2P, InterfaceIDs of the question and the record should match.
16475ffb0c9bSToomas Soome 
16485ffb0c9bSToomas Soome     // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
16495ffb0c9bSToomas Soome     // LocalOnly authoritative answers are exempt. LocalOnly authoritative answers are used for /etc/host entries.
16505ffb0c9bSToomas 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
16515ffb0c9bSToomas Soome     // cause other applications (e.g. Safari) to connect to the wrong address. The rpc to register records filters out records
16525ffb0c9bSToomas Soome     // with names that don't end in local and have mDNSInterface_LocalOnly set.
16535ffb0c9bSToomas Soome     //
16545ffb0c9bSToomas Soome     // Note: The check is bypassed for LocalOnly and for P2P it is not needed as only .local records are registered and for
16555ffb0c9bSToomas Soome     // a question to match its names, it also has to end in .local and that question can't be a unicast question (See
16565ffb0c9bSToomas Soome     // Question_uDNS macro and its usage). As P2P does not enforce .local only registrations we still make this check
16575ffb0c9bSToomas Soome     // and also makes it future proof.
16585ffb0c9bSToomas Soome 
16595ffb0c9bSToomas Soome     if (ar->ARType != AuthRecordLocalOnly && rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
16605ffb0c9bSToomas Soome 
16615ffb0c9bSToomas Soome     // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
16625ffb0c9bSToomas Soome     if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
16635ffb0c9bSToomas Soome     if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
16645ffb0c9bSToomas Soome 
16655ffb0c9bSToomas Soome     return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
16665ffb0c9bSToomas Soome }
16675ffb0c9bSToomas Soome 
AnyTypeRecordAnswersQuestion(const AuthRecord * const ar,const DNSQuestion * const q)1668*472cd20dSToomas Soome mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const AuthRecord *const ar, const DNSQuestion *const q)
16695ffb0c9bSToomas Soome {
1670*472cd20dSToomas Soome     const ResourceRecord *const rr = &ar->resrec;
16715ffb0c9bSToomas Soome     // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
16725ffb0c9bSToomas Soome     // are handled in LocalOnlyRecordAnswersQuestion
1673c65ebfc7SToomas Soome     if (LocalOnlyOrP2PInterface(rr->InterfaceID))
16745ffb0c9bSToomas Soome     {
16755ffb0c9bSToomas Soome         LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
16765ffb0c9bSToomas Soome         return mDNSfalse;
16775ffb0c9bSToomas Soome     }
16785ffb0c9bSToomas Soome     if (rr->InterfaceID &&
16795ffb0c9bSToomas Soome         q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
16805ffb0c9bSToomas Soome         rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
16815ffb0c9bSToomas Soome 
16825ffb0c9bSToomas Soome     // Resource record received via unicast, the resolver group ID should match ?
16835ffb0c9bSToomas Soome     // Note that Auth Records are normally setup with NULL InterfaceID and
16845ffb0c9bSToomas Soome     // both the DNSServers are assumed to be NULL in that case
16855ffb0c9bSToomas Soome     if (!rr->InterfaceID)
16865ffb0c9bSToomas Soome     {
1687*472cd20dSToomas Soome #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
1688*472cd20dSToomas Soome         if (rr->dnsservice != q->dnsservice) return(mDNSfalse);
1689*472cd20dSToomas Soome #else
1690*472cd20dSToomas Soome         const mDNSu32 idr = rr->rDNSServer ? rr->rDNSServer->resGroupID : 0;
1691*472cd20dSToomas Soome         const mDNSu32 idq = q->qDNSServer ? q->qDNSServer->resGroupID : 0;
16925ffb0c9bSToomas Soome         if (idr != idq) return(mDNSfalse);
1693*472cd20dSToomas Soome #endif
1694*472cd20dSToomas Soome #if MDNSRESPONDER_SUPPORTS(APPLE, RANDOM_AWDL_HOSTNAME)
1695*472cd20dSToomas Soome         if (!mDNSPlatformValidRecordForInterface(ar, q->InterfaceID)) return(mDNSfalse);
1696*472cd20dSToomas Soome #endif
16975ffb0c9bSToomas Soome     }
16985ffb0c9bSToomas Soome 
16995ffb0c9bSToomas Soome     // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
17005ffb0c9bSToomas Soome     if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
17015ffb0c9bSToomas Soome 
17025ffb0c9bSToomas Soome     if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
17035ffb0c9bSToomas Soome 
17045ffb0c9bSToomas Soome     return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
17055ffb0c9bSToomas Soome }
17065ffb0c9bSToomas Soome 
17075ffb0c9bSToomas Soome // This is called with both unicast resource record and multicast resource record. The question that
17085ffb0c9bSToomas Soome // received the unicast response could be the regular unicast response from a DNS server or a response
17095ffb0c9bSToomas Soome // to a mDNS QU query. The main reason we need this function is that we can't compare DNSServers between the
17105ffb0c9bSToomas Soome // question and the resource record because the resource record is not completely initialized in
17115ffb0c9bSToomas Soome // mDNSCoreReceiveResponse when this function is called.
ResourceRecordAnswersUnicastResponse(const ResourceRecord * const rr,const DNSQuestion * const q)17125ffb0c9bSToomas Soome mDNSexport mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q)
17135ffb0c9bSToomas Soome {
17145ffb0c9bSToomas Soome     mDNSBool checkType = mDNStrue;
17155ffb0c9bSToomas Soome 
1716*472cd20dSToomas Soome     if (q->Suppressed)
17175ffb0c9bSToomas Soome         return mDNSfalse;
17185ffb0c9bSToomas Soome 
17195ffb0c9bSToomas Soome     // For resource records created using multicast, the InterfaceIDs have to match
17205ffb0c9bSToomas Soome     if (rr->InterfaceID &&
17215ffb0c9bSToomas Soome         q->InterfaceID && rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
17225ffb0c9bSToomas Soome 
17235ffb0c9bSToomas Soome     // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
17245ffb0c9bSToomas Soome     if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
17255ffb0c9bSToomas Soome 
1726*472cd20dSToomas Soome #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
1727*472cd20dSToomas Soome     if (enables_dnssec_validation(q) && record_type_answers_dnssec_question(rr, q->qtype)) checkType = mDNSfalse;
1728*472cd20dSToomas Soome #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
17295ffb0c9bSToomas Soome 
17305ffb0c9bSToomas Soome     // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
17315ffb0c9bSToomas Soome     if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
17325ffb0c9bSToomas Soome 
17335ffb0c9bSToomas Soome     if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
17345ffb0c9bSToomas Soome 
17355ffb0c9bSToomas Soome     return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
17365ffb0c9bSToomas Soome }
17374b22b933Srs 
GetRDLength(const ResourceRecord * const rr,mDNSBool estimate)17384b22b933Srs mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
17395ffb0c9bSToomas Soome {
17405ffb0c9bSToomas Soome     const RDataBody2 *const rd = (RDataBody2 *)rr->rdata->u.data;
17415ffb0c9bSToomas Soome     const domainname *const name = estimate ? rr->name : mDNSNULL;
17425ffb0c9bSToomas Soome     if (rr->rrclass == kDNSQClass_ANY) return(rr->rdlength);    // Used in update packets to mean "Delete An RRset" (RFC 2136)
17435ffb0c9bSToomas Soome     else switch (rr->rrtype)
17445ffb0c9bSToomas Soome         {
17455ffb0c9bSToomas Soome         case kDNSType_A:    return(sizeof(rd->ipv4));
17465ffb0c9bSToomas Soome 
17475ffb0c9bSToomas Soome         case kDNSType_NS:
17485ffb0c9bSToomas Soome         case kDNSType_CNAME:
17495ffb0c9bSToomas Soome         case kDNSType_PTR:
17505ffb0c9bSToomas Soome         case kDNSType_DNAME: return(CompressedDomainNameLength(&rd->name, name));
17515ffb0c9bSToomas Soome 
17525ffb0c9bSToomas Soome         case kDNSType_SOA:  return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) +
17535ffb0c9bSToomas Soome                                              CompressedDomainNameLength(&rd->soa.rname, name) +
17545ffb0c9bSToomas Soome                                              5 * sizeof(mDNSOpaque32));
17555ffb0c9bSToomas Soome 
17565ffb0c9bSToomas Soome         case kDNSType_NULL:
17575ffb0c9bSToomas Soome         case kDNSType_TSIG:
17585ffb0c9bSToomas Soome         case kDNSType_TXT:
17595ffb0c9bSToomas Soome         case kDNSType_X25:
17605ffb0c9bSToomas Soome         case kDNSType_ISDN:
17615ffb0c9bSToomas Soome         case kDNSType_LOC:
17625ffb0c9bSToomas Soome         case kDNSType_DHCID: return(rr->rdlength); // Not self-describing, so have to just trust rdlength
17635ffb0c9bSToomas Soome 
17645ffb0c9bSToomas Soome         case kDNSType_HINFO: return (mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
17655ffb0c9bSToomas Soome 
17665ffb0c9bSToomas Soome         case kDNSType_MX:
17675ffb0c9bSToomas Soome         case kDNSType_AFSDB:
17685ffb0c9bSToomas Soome         case kDNSType_RT:
17695ffb0c9bSToomas Soome         case kDNSType_KX:   return (mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name));
17705ffb0c9bSToomas Soome 
1771*472cd20dSToomas Soome         case kDNSType_MINFO:
17725ffb0c9bSToomas Soome         case kDNSType_RP:   return (mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) +
17735ffb0c9bSToomas Soome                                              CompressedDomainNameLength(&rd->rp.txt, name));
17745ffb0c9bSToomas Soome 
17755ffb0c9bSToomas Soome         case kDNSType_PX:   return (mDNSu16)(2 + CompressedDomainNameLength(&rd->px.map822, name) +
17765ffb0c9bSToomas Soome                                              CompressedDomainNameLength(&rd->px.mapx400, name));
17775ffb0c9bSToomas Soome 
17785ffb0c9bSToomas Soome         case kDNSType_AAAA: return(sizeof(rd->ipv6));
17795ffb0c9bSToomas Soome 
17805ffb0c9bSToomas Soome         case kDNSType_SRV:  return (mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
17815ffb0c9bSToomas Soome 
17825ffb0c9bSToomas Soome         case kDNSType_OPT:  return(rr->rdlength);
17835ffb0c9bSToomas Soome 
17845ffb0c9bSToomas Soome         case kDNSType_NSEC: {
17855ffb0c9bSToomas Soome             domainname *next = (domainname *)rd->data;
17865ffb0c9bSToomas Soome             int dlen = DomainNameLength(next);
17875ffb0c9bSToomas Soome             //
17885ffb0c9bSToomas Soome             if (UNICAST_NSEC(rr))
17895ffb0c9bSToomas Soome                 return (mDNSu16)(CompressedDomainNameLength(next, name) + rr->rdlength - dlen);
17905ffb0c9bSToomas Soome             else
17915ffb0c9bSToomas Soome                 return (mDNSu16)((estimate ? 2 : dlen) + rr->rdlength - dlen);
17925ffb0c9bSToomas Soome         }
17935ffb0c9bSToomas Soome 
17945ffb0c9bSToomas Soome         default:            debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
17955ffb0c9bSToomas Soome             return(rr->rdlength);
17965ffb0c9bSToomas Soome         }
17975ffb0c9bSToomas Soome }
17985ffb0c9bSToomas Soome 
17995ffb0c9bSToomas Soome // When a local client registers (or updates) a record, we use this routine to do some simple validation checks
18005ffb0c9bSToomas Soome // to help reduce the risk of bogus malformed data on the network
ValidateRData(const mDNSu16 rrtype,const mDNSu16 rdlength,const RData * const rd)18014b22b933Srs mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
18025ffb0c9bSToomas Soome {
18035ffb0c9bSToomas Soome     mDNSu16 len;
18045ffb0c9bSToomas Soome 
18055ffb0c9bSToomas Soome     switch(rrtype)
18065ffb0c9bSToomas Soome     {
18075ffb0c9bSToomas Soome     case kDNSType_A:    return(rdlength == sizeof(mDNSv4Addr));
18085ffb0c9bSToomas Soome 
18095ffb0c9bSToomas Soome     case kDNSType_NS:       // Same as PTR
18105ffb0c9bSToomas Soome     case kDNSType_MD:       // Same as PTR
18115ffb0c9bSToomas Soome     case kDNSType_MF:       // Same as PTR
18125ffb0c9bSToomas Soome     case kDNSType_CNAME:    // Same as PTR
18135ffb0c9bSToomas Soome     //case kDNSType_SOA not checked
18145ffb0c9bSToomas Soome     case kDNSType_MB:       // Same as PTR
18155ffb0c9bSToomas Soome     case kDNSType_MG:       // Same as PTR
18165ffb0c9bSToomas Soome     case kDNSType_MR:       // Same as PTR
18175ffb0c9bSToomas Soome     //case kDNSType_NULL not checked (no specified format, so always valid)
18185ffb0c9bSToomas Soome     //case kDNSType_WKS not checked
18195ffb0c9bSToomas Soome     case kDNSType_PTR:  len = DomainNameLengthLimit(&rd->u.name, rd->u.data + rdlength);
18205ffb0c9bSToomas Soome         return(len <= MAX_DOMAIN_NAME && rdlength == len);
18215ffb0c9bSToomas Soome 
18225ffb0c9bSToomas Soome     case kDNSType_HINFO:    // Same as TXT (roughly)
18235ffb0c9bSToomas Soome     case kDNSType_MINFO:    // Same as TXT (roughly)
18245ffb0c9bSToomas Soome     case kDNSType_TXT:  if (!rdlength) return(mDNSfalse);     // TXT record has to be at least one byte (RFC 1035)
18255ffb0c9bSToomas Soome         {
18265ffb0c9bSToomas Soome             const mDNSu8 *ptr = rd->u.txt.c;
18275ffb0c9bSToomas Soome             const mDNSu8 *end = rd->u.txt.c + rdlength;
18285ffb0c9bSToomas Soome             while (ptr < end) ptr += 1 + ptr[0];
18295ffb0c9bSToomas Soome             return (ptr == end);
18305ffb0c9bSToomas Soome         }
18315ffb0c9bSToomas Soome 
18325ffb0c9bSToomas Soome     case kDNSType_AAAA: return(rdlength == sizeof(mDNSv6Addr));
18335ffb0c9bSToomas Soome 
18345ffb0c9bSToomas Soome     case kDNSType_MX:       // Must be at least two-byte preference, plus domainname
18355ffb0c9bSToomas Soome                             // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
18365ffb0c9bSToomas Soome         len = DomainNameLengthLimit(&rd->u.mx.exchange, rd->u.data + rdlength);
18375ffb0c9bSToomas Soome         return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
18385ffb0c9bSToomas Soome 
18395ffb0c9bSToomas Soome     case kDNSType_SRV:      // Must be at least priority+weight+port, plus domainname
18405ffb0c9bSToomas Soome                             // Call to DomainNameLengthLimit() implicitly enforces both requirements for us
18415ffb0c9bSToomas Soome         len = DomainNameLengthLimit(&rd->u.srv.target, rd->u.data + rdlength);
18425ffb0c9bSToomas Soome         return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
18435ffb0c9bSToomas Soome 
18445ffb0c9bSToomas Soome     //case kDNSType_NSEC not checked
18455ffb0c9bSToomas Soome 
18465ffb0c9bSToomas Soome     default:            return(mDNStrue);       // Allow all other types without checking
18475ffb0c9bSToomas Soome     }
18485ffb0c9bSToomas Soome }
18494b22b933Srs 
18504b22b933Srs // ***************************************************************************
18514b22b933Srs #if COMPILER_LIKES_PRAGMA_MARK
18524b22b933Srs #pragma mark -
18534b22b933Srs #pragma mark - DNS Message Creation Functions
18544b22b933Srs #endif
18554b22b933Srs 
InitializeDNSMessage(DNSMessageHeader * h,mDNSOpaque16 id,mDNSOpaque16 flags)18564b22b933Srs mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
18575ffb0c9bSToomas Soome {
18585ffb0c9bSToomas Soome     h->id             = id;
18595ffb0c9bSToomas Soome     h->flags          = flags;
18605ffb0c9bSToomas Soome     h->numQuestions   = 0;
18615ffb0c9bSToomas Soome     h->numAnswers     = 0;
18625ffb0c9bSToomas Soome     h->numAuthorities = 0;
18635ffb0c9bSToomas Soome     h->numAdditionals = 0;
18645ffb0c9bSToomas Soome }
18654b22b933Srs 
1866*472cd20dSToomas Soome #endif // !STANDALONE
1867*472cd20dSToomas Soome 
FindCompressionPointer(const mDNSu8 * const base,const mDNSu8 * const end,const mDNSu8 * const domname)18684b22b933Srs mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
18695ffb0c9bSToomas Soome {
18705ffb0c9bSToomas Soome     const mDNSu8 *result = end - *domname - 1;
18715ffb0c9bSToomas Soome 
18725ffb0c9bSToomas Soome     if (*domname == 0) return(mDNSNULL);    // There's no point trying to match just the root label
18735ffb0c9bSToomas Soome 
18745ffb0c9bSToomas Soome     // This loop examines each possible starting position in packet, starting end of the packet and working backwards
18755ffb0c9bSToomas Soome     while (result >= base)
18765ffb0c9bSToomas Soome     {
18775ffb0c9bSToomas Soome         // If the length byte and first character of the label match, then check further to see
18785ffb0c9bSToomas Soome         // if this location in the packet will yield a useful name compression pointer.
18795ffb0c9bSToomas Soome         if (result[0] == domname[0] && result[1] == domname[1])
18805ffb0c9bSToomas Soome         {
18815ffb0c9bSToomas Soome             const mDNSu8 *name = domname;
18825ffb0c9bSToomas Soome             const mDNSu8 *targ = result;
18835ffb0c9bSToomas Soome             while (targ + *name < end)
18845ffb0c9bSToomas Soome             {
18855ffb0c9bSToomas Soome                 // First see if this label matches
18865ffb0c9bSToomas Soome                 int i;
18875ffb0c9bSToomas Soome                 const mDNSu8 *pointertarget;
18885ffb0c9bSToomas Soome                 for (i=0; i <= *name; i++) if (targ[i] != name[i]) break;
18895ffb0c9bSToomas Soome                 if (i <= *name) break;                          // If label did not match, bail out
18905ffb0c9bSToomas Soome                 targ += 1 + *name;                              // Else, did match, so advance target pointer
18915ffb0c9bSToomas Soome                 name += 1 + *name;                              // and proceed to check next label
18925ffb0c9bSToomas Soome                 if (*name == 0 && *targ == 0) return(result);   // If no more labels, we found a match!
18935ffb0c9bSToomas Soome                 if (*name == 0) break;                          // If no more labels to match, we failed, so bail out
18945ffb0c9bSToomas Soome 
18955ffb0c9bSToomas Soome                 // The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
18965ffb0c9bSToomas Soome                 if (targ[0] < 0x40) continue;                   // If length value, continue to check next label
18975ffb0c9bSToomas Soome                 if (targ[0] < 0xC0) break;                      // If 40-BF, not valid
18985ffb0c9bSToomas Soome                 if (targ+1 >= end) break;                       // Second byte not present!
18995ffb0c9bSToomas Soome                 pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1];
19005ffb0c9bSToomas Soome                 if (targ < pointertarget) break;                // Pointertarget must point *backwards* in the packet
19015ffb0c9bSToomas Soome                 if (pointertarget[0] >= 0x40) break;            // Pointertarget must point to a valid length byte
19025ffb0c9bSToomas Soome                 targ = pointertarget;
19035ffb0c9bSToomas Soome             }
19045ffb0c9bSToomas Soome         }
19055ffb0c9bSToomas Soome         result--;   // We failed to match at this search position, so back up the tentative result pointer and try again
19065ffb0c9bSToomas Soome     }
19075ffb0c9bSToomas Soome     return(mDNSNULL);
19085ffb0c9bSToomas Soome }
19094b22b933Srs 
19104b22b933Srs // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
19114b22b933Srs // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
19124b22b933Srs // end points to the end of the message so far
19134b22b933Srs // ptr points to where we want to put the name
19144b22b933Srs // limit points to one byte past the end of the buffer that we must not overrun
19154b22b933Srs // domainname is the name to put
putDomainNameAsLabels(const DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,const domainname * const name)19164b22b933Srs mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg,
19175ffb0c9bSToomas Soome                                          mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name)
19185ffb0c9bSToomas Soome {
19195ffb0c9bSToomas Soome     const mDNSu8 *const base        = (const mDNSu8 *)msg;
19205ffb0c9bSToomas Soome     const mDNSu8 *      np          = name->c;
19215ffb0c9bSToomas Soome     const mDNSu8 *const max         = name->c + MAX_DOMAIN_NAME;    // Maximum that's valid
19225ffb0c9bSToomas Soome     const mDNSu8 *      pointer     = mDNSNULL;
19235ffb0c9bSToomas Soome     const mDNSu8 *const searchlimit = ptr;
19245ffb0c9bSToomas Soome 
19255ffb0c9bSToomas Soome     if (!ptr) { LogMsg("putDomainNameAsLabels %##s ptr is null", name->c); return(mDNSNULL); }
19265ffb0c9bSToomas Soome 
19275ffb0c9bSToomas Soome     if (!*np)       // If just writing one-byte root label, make sure we have space for that
19285ffb0c9bSToomas Soome     {
19295ffb0c9bSToomas Soome         if (ptr >= limit) return(mDNSNULL);
19305ffb0c9bSToomas Soome     }
19315ffb0c9bSToomas Soome     else            // else, loop through writing labels and/or a compression offset
19325ffb0c9bSToomas Soome     {
19335ffb0c9bSToomas Soome         do  {
19345ffb0c9bSToomas Soome             if (*np > MAX_DOMAIN_LABEL)
19355ffb0c9bSToomas Soome             { LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
19365ffb0c9bSToomas Soome 
19375ffb0c9bSToomas Soome             // This check correctly allows for the final trailing root label:
19385ffb0c9bSToomas Soome             // e.g.
19395ffb0c9bSToomas Soome             // Suppose our domain name is exactly 256 bytes long, including the final trailing root label.
19405ffb0c9bSToomas Soome             // Suppose np is now at name->c[249], and we're about to write our last non-null label ("local").
19415ffb0c9bSToomas Soome             // We know that max will be at name->c[256]
19425ffb0c9bSToomas Soome             // That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
19435ffb0c9bSToomas Soome             // six bytes, then exit the loop, write the final terminating root label, and the domain
19445ffb0c9bSToomas Soome             // name we've written is exactly 256 bytes long, exactly at the correct legal limit.
19455ffb0c9bSToomas Soome             // If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
19465ffb0c9bSToomas Soome             if (np + 1 + *np >= max)
19475ffb0c9bSToomas Soome             { LogMsg("Malformed domain name %##s (more than 256 bytes)", name->c); return(mDNSNULL); }
19485ffb0c9bSToomas Soome 
19495ffb0c9bSToomas Soome             if (base) pointer = FindCompressionPointer(base, searchlimit, np);
19505ffb0c9bSToomas Soome             if (pointer)                    // Use a compression pointer if we can
19515ffb0c9bSToomas Soome             {
19525ffb0c9bSToomas Soome                 const mDNSu16 offset = (mDNSu16)(pointer - base);
19535ffb0c9bSToomas Soome                 if (ptr+2 > limit) return(mDNSNULL);    // If we don't have two bytes of space left, give up
19545ffb0c9bSToomas Soome                 *ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
19555ffb0c9bSToomas Soome                 *ptr++ = (mDNSu8)(        offset &  0xFF);
19565ffb0c9bSToomas Soome                 return(ptr);
19575ffb0c9bSToomas Soome             }
19585ffb0c9bSToomas Soome             else                            // Else copy one label and try again
19595ffb0c9bSToomas Soome             {
19605ffb0c9bSToomas Soome                 int i;
19615ffb0c9bSToomas Soome                 mDNSu8 len = *np++;
19625ffb0c9bSToomas Soome                 // If we don't at least have enough space for this label *plus* a terminating zero on the end, give up
19635ffb0c9bSToomas Soome                 if (ptr + 1 + len >= limit) return(mDNSNULL);
19645ffb0c9bSToomas Soome                 *ptr++ = len;
19655ffb0c9bSToomas Soome                 for (i=0; i<len; i++) *ptr++ = *np++;
19665ffb0c9bSToomas Soome             }
19675ffb0c9bSToomas Soome         } while (*np);                      // While we've got characters remaining in the name, continue
19685ffb0c9bSToomas Soome     }
19695ffb0c9bSToomas Soome 
19705ffb0c9bSToomas Soome     *ptr++ = 0;     // Put the final root label
19715ffb0c9bSToomas Soome     return(ptr);
19725ffb0c9bSToomas Soome }
19734b22b933Srs 
1974*472cd20dSToomas Soome #ifndef STANDALONE
1975*472cd20dSToomas Soome 
putVal16(mDNSu8 * ptr,mDNSu16 val)19764b22b933Srs mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
19775ffb0c9bSToomas Soome {
19785ffb0c9bSToomas Soome     ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
19795ffb0c9bSToomas Soome     ptr[1] = (mDNSu8)((val      ) & 0xFF);
19805ffb0c9bSToomas Soome     return ptr + sizeof(mDNSOpaque16);
19815ffb0c9bSToomas Soome }
19824b22b933Srs 
putVal32(mDNSu8 * ptr,mDNSu32 val)19834b22b933Srs mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
19845ffb0c9bSToomas Soome {
19855ffb0c9bSToomas Soome     ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
19865ffb0c9bSToomas Soome     ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
19875ffb0c9bSToomas Soome     ptr[2] = (mDNSu8)((val >>  8) & 0xFF);
19885ffb0c9bSToomas Soome     ptr[3] = (mDNSu8)((val      ) & 0xFF);
19895ffb0c9bSToomas Soome     return ptr + sizeof(mDNSu32);
19905ffb0c9bSToomas Soome }
19915ffb0c9bSToomas Soome 
19925ffb0c9bSToomas Soome // Copy the RDATA information. The actual in memory storage for the data might be bigger than what the rdlength
19935ffb0c9bSToomas Soome // says. Hence, the only way to copy out the data from a resource record is to use putRData.
19945ffb0c9bSToomas 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)19955ffb0c9bSToomas Soome mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr)
19965ffb0c9bSToomas Soome {
19975ffb0c9bSToomas Soome     const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
19985ffb0c9bSToomas Soome     switch (rr->rrtype)
19995ffb0c9bSToomas Soome     {
20005ffb0c9bSToomas Soome     case kDNSType_A:    if (rr->rdlength != 4)
20015ffb0c9bSToomas Soome         { debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); return(mDNSNULL); }
20025ffb0c9bSToomas Soome         if (ptr + 4 > limit) return(mDNSNULL);
20035ffb0c9bSToomas Soome         *ptr++ = rdb->ipv4.b[0];
20045ffb0c9bSToomas Soome         *ptr++ = rdb->ipv4.b[1];
20055ffb0c9bSToomas Soome         *ptr++ = rdb->ipv4.b[2];
20065ffb0c9bSToomas Soome         *ptr++ = rdb->ipv4.b[3];
20075ffb0c9bSToomas Soome         return(ptr);
20085ffb0c9bSToomas Soome 
20095ffb0c9bSToomas Soome     case kDNSType_NS:
20105ffb0c9bSToomas Soome     case kDNSType_CNAME:
20115ffb0c9bSToomas Soome     case kDNSType_PTR:
20125ffb0c9bSToomas Soome     case kDNSType_DNAME: return(putDomainNameAsLabels(msg, ptr, limit, &rdb->name));
20135ffb0c9bSToomas Soome 
20145ffb0c9bSToomas Soome     case kDNSType_SOA:  ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.mname);
20155ffb0c9bSToomas Soome         if (!ptr) return(mDNSNULL);
20165ffb0c9bSToomas Soome         ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.rname);
20175ffb0c9bSToomas Soome         if (!ptr || ptr + 20 > limit) return(mDNSNULL);
20185ffb0c9bSToomas Soome         ptr = putVal32(ptr, rdb->soa.serial);
20195ffb0c9bSToomas Soome         ptr = putVal32(ptr, rdb->soa.refresh);
20205ffb0c9bSToomas Soome         ptr = putVal32(ptr, rdb->soa.retry);
20215ffb0c9bSToomas Soome         ptr = putVal32(ptr, rdb->soa.expire);
20225ffb0c9bSToomas Soome         ptr = putVal32(ptr, rdb->soa.min);
20235ffb0c9bSToomas Soome         return(ptr);
20245ffb0c9bSToomas Soome 
20255ffb0c9bSToomas Soome     case kDNSType_NULL:
20265ffb0c9bSToomas Soome     case kDNSType_HINFO:
20275ffb0c9bSToomas Soome     case kDNSType_TSIG:
20285ffb0c9bSToomas Soome     case kDNSType_TXT:
20295ffb0c9bSToomas Soome     case kDNSType_X25:
20305ffb0c9bSToomas Soome     case kDNSType_ISDN:
20315ffb0c9bSToomas Soome     case kDNSType_LOC:
20325ffb0c9bSToomas Soome     case kDNSType_DHCID: if (ptr + rr->rdlength > limit) return(mDNSNULL);
20335ffb0c9bSToomas Soome         mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
20345ffb0c9bSToomas Soome         return(ptr + rr->rdlength);
20355ffb0c9bSToomas Soome 
20365ffb0c9bSToomas Soome     case kDNSType_MX:
20375ffb0c9bSToomas Soome     case kDNSType_AFSDB:
20385ffb0c9bSToomas Soome     case kDNSType_RT:
20395ffb0c9bSToomas Soome     case kDNSType_KX:   if (ptr + 3 > limit) return(mDNSNULL);
20405ffb0c9bSToomas Soome         ptr = putVal16(ptr, rdb->mx.preference);
20415ffb0c9bSToomas Soome         return(putDomainNameAsLabels(msg, ptr, limit, &rdb->mx.exchange));
20425ffb0c9bSToomas Soome 
20435ffb0c9bSToomas Soome     case kDNSType_RP:   ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.mbox);
20445ffb0c9bSToomas Soome         if (!ptr) return(mDNSNULL);
20455ffb0c9bSToomas Soome         ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.txt);
20465ffb0c9bSToomas Soome         return(ptr);
20475ffb0c9bSToomas Soome 
20485ffb0c9bSToomas Soome     case kDNSType_PX:   if (ptr + 5 > limit) return(mDNSNULL);
20495ffb0c9bSToomas Soome         ptr = putVal16(ptr, rdb->px.preference);
20505ffb0c9bSToomas Soome         ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.map822);
20515ffb0c9bSToomas Soome         if (!ptr) return(mDNSNULL);
20525ffb0c9bSToomas Soome         ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.mapx400);
20535ffb0c9bSToomas Soome         return(ptr);
20545ffb0c9bSToomas Soome 
20555ffb0c9bSToomas Soome     case kDNSType_AAAA: if (rr->rdlength != sizeof(rdb->ipv6))
20565ffb0c9bSToomas Soome         { debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); return(mDNSNULL); }
20575ffb0c9bSToomas Soome         if (ptr + sizeof(rdb->ipv6) > limit) return(mDNSNULL);
20585ffb0c9bSToomas Soome         mDNSPlatformMemCopy(ptr, &rdb->ipv6, sizeof(rdb->ipv6));
20595ffb0c9bSToomas Soome         return(ptr + sizeof(rdb->ipv6));
20605ffb0c9bSToomas Soome 
20615ffb0c9bSToomas Soome     case kDNSType_SRV:  if (ptr + 7 > limit) return(mDNSNULL);
20625ffb0c9bSToomas Soome         *ptr++ = (mDNSu8)(rdb->srv.priority >> 8);
20635ffb0c9bSToomas Soome         *ptr++ = (mDNSu8)(rdb->srv.priority &  0xFF);
20645ffb0c9bSToomas Soome         *ptr++ = (mDNSu8)(rdb->srv.weight   >> 8);
20655ffb0c9bSToomas Soome         *ptr++ = (mDNSu8)(rdb->srv.weight   &  0xFF);
20665ffb0c9bSToomas Soome         *ptr++ = rdb->srv.port.b[0];
20675ffb0c9bSToomas Soome         *ptr++ = rdb->srv.port.b[1];
20685ffb0c9bSToomas Soome         return(putDomainNameAsLabels(msg, ptr, limit, &rdb->srv.target));
20695ffb0c9bSToomas Soome 
20705ffb0c9bSToomas Soome     case kDNSType_OPT:  {
20715ffb0c9bSToomas Soome         int len = 0;
20725ffb0c9bSToomas Soome         const rdataOPT *opt;
20735ffb0c9bSToomas Soome         const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength];
2074c65ebfc7SToomas Soome         for (opt = &rr->rdata->u.opt[0]; opt < end; opt++)
20755ffb0c9bSToomas Soome             len += DNSOpt_Data_Space(opt);
2076c65ebfc7SToomas Soome         if (ptr + len > limit)
2077c65ebfc7SToomas Soome         {
2078c65ebfc7SToomas Soome             LogMsg("ERROR: putOptRData - out of space");
2079c65ebfc7SToomas Soome             return mDNSNULL;
20805ffb0c9bSToomas Soome         }
20815ffb0c9bSToomas Soome         for (opt = &rr->rdata->u.opt[0]; opt < end; opt++)
20825ffb0c9bSToomas Soome         {
20835ffb0c9bSToomas Soome             const int space = DNSOpt_Data_Space(opt);
20845ffb0c9bSToomas Soome             ptr = putVal16(ptr, opt->opt);
20855ffb0c9bSToomas Soome             ptr = putVal16(ptr, (mDNSu16)space - 4);
20865ffb0c9bSToomas Soome             switch (opt->opt)
20875ffb0c9bSToomas Soome             {
20885ffb0c9bSToomas Soome             case kDNSOpt_LLQ:
20895ffb0c9bSToomas Soome                 ptr = putVal16(ptr, opt->u.llq.vers);
20905ffb0c9bSToomas Soome                 ptr = putVal16(ptr, opt->u.llq.llqOp);
20915ffb0c9bSToomas Soome                 ptr = putVal16(ptr, opt->u.llq.err);
20925ffb0c9bSToomas Soome                 mDNSPlatformMemCopy(ptr, opt->u.llq.id.b, 8);                          // 8-byte id
20935ffb0c9bSToomas Soome                 ptr += 8;
20945ffb0c9bSToomas Soome                 ptr = putVal32(ptr, opt->u.llq.llqlease);
20955ffb0c9bSToomas Soome                 break;
20965ffb0c9bSToomas Soome             case kDNSOpt_Lease:
20975ffb0c9bSToomas Soome                 ptr = putVal32(ptr, opt->u.updatelease);
20985ffb0c9bSToomas Soome                 break;
20995ffb0c9bSToomas Soome             case kDNSOpt_Owner:
21005ffb0c9bSToomas Soome                 *ptr++ = opt->u.owner.vers;
21015ffb0c9bSToomas Soome                 *ptr++ = opt->u.owner.seq;
21025ffb0c9bSToomas Soome                 mDNSPlatformMemCopy(ptr, opt->u.owner.HMAC.b, 6);                          // 6-byte Host identifier
21035ffb0c9bSToomas Soome                 ptr += 6;
21045ffb0c9bSToomas Soome                 if (space >= DNSOpt_OwnerData_ID_Wake_Space)
21055ffb0c9bSToomas Soome                 {
21065ffb0c9bSToomas Soome                     mDNSPlatformMemCopy(ptr, opt->u.owner.IMAC.b, 6);                           // 6-byte interface MAC
21075ffb0c9bSToomas Soome                     ptr += 6;
21085ffb0c9bSToomas Soome                     if (space > DNSOpt_OwnerData_ID_Wake_Space)
21095ffb0c9bSToomas Soome                     {
21105ffb0c9bSToomas Soome                         mDNSPlatformMemCopy(ptr, opt->u.owner.password.b, space - DNSOpt_OwnerData_ID_Wake_Space);
21115ffb0c9bSToomas Soome                         ptr += space - DNSOpt_OwnerData_ID_Wake_Space;
21125ffb0c9bSToomas Soome                     }
21135ffb0c9bSToomas Soome                 }
21145ffb0c9bSToomas Soome                 break;
21155ffb0c9bSToomas Soome             case kDNSOpt_Trace:
21165ffb0c9bSToomas Soome                 *ptr++ = opt->u.tracer.platf;
21175ffb0c9bSToomas Soome                 ptr    = putVal32(ptr, opt->u.tracer.mDNSv);
21185ffb0c9bSToomas Soome                 break;
21195ffb0c9bSToomas Soome             }
21205ffb0c9bSToomas Soome         }
21215ffb0c9bSToomas Soome         return ptr;
21225ffb0c9bSToomas Soome     }
21235ffb0c9bSToomas Soome 
21245ffb0c9bSToomas Soome     case kDNSType_NSEC: {
21255ffb0c9bSToomas Soome         // For NSEC records, rdlength represents the exact number of bytes
21265ffb0c9bSToomas Soome         // of in memory storage.
21275ffb0c9bSToomas Soome         mDNSu8 *nsec = (mDNSu8 *)rdb->data;
21285ffb0c9bSToomas Soome         domainname *name = (domainname *)nsec;
2129cda73f64SToomas Soome         const int dlen = DomainNameLength(name);
21305ffb0c9bSToomas Soome         nsec += dlen;
21315ffb0c9bSToomas Soome         // This function is called when we are sending a NSEC record as part of mDNS,
21325ffb0c9bSToomas Soome         // or to copy the data to any other buffer needed which could be a mDNS or uDNS
21335ffb0c9bSToomas Soome         // NSEC record. The only time compression is used that when we are sending it
21345ffb0c9bSToomas Soome         // in mDNS (indicated by non-NULL "msg") and hence we handle mDNS case
21355ffb0c9bSToomas Soome         // separately.
21365ffb0c9bSToomas Soome         if (!UNICAST_NSEC(rr))
21375ffb0c9bSToomas Soome         {
21385ffb0c9bSToomas Soome             mDNSu8 *save = ptr;
21395ffb0c9bSToomas Soome             int i, j, wlen;
21405ffb0c9bSToomas Soome             wlen = *(nsec + 1);
21415ffb0c9bSToomas Soome             nsec += 2;                     // Skip the window number and len
21425ffb0c9bSToomas Soome 
21435ffb0c9bSToomas Soome             // For our simplified use of NSEC synthetic records:
21445ffb0c9bSToomas Soome             //
21455ffb0c9bSToomas Soome             // nextname is always the record's own name,
21465ffb0c9bSToomas Soome             // the block number is always 0,
21475ffb0c9bSToomas Soome             // the count byte is a value in the range 1-32,
21485ffb0c9bSToomas Soome             // followed by the 1-32 data bytes
21495ffb0c9bSToomas Soome             //
21505ffb0c9bSToomas Soome             // Note: When we send the NSEC record in mDNS, the window size is set to 32.
21515ffb0c9bSToomas Soome             // We need to find out what the last non-NULL byte is.  If we are copying out
21525ffb0c9bSToomas Soome             // from an RDATA, we have the right length. As we need to handle both the case,
21535ffb0c9bSToomas Soome             // we loop to find the right value instead of blindly using len to copy.
21545ffb0c9bSToomas Soome 
21555ffb0c9bSToomas Soome             for (i=wlen; i>0; i--) if (nsec[i-1]) break;
21565ffb0c9bSToomas Soome 
21575ffb0c9bSToomas Soome             ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
21585ffb0c9bSToomas Soome             if (!ptr) { LogInfo("putRData: Can't put name, Length %d, record %##s", limit - save, rr->name->c); return(mDNSNULL); }
21595ffb0c9bSToomas Soome             if (i)                          // Only put a block if at least one type exists for this name
21605ffb0c9bSToomas Soome             {
21615ffb0c9bSToomas 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); }
21625ffb0c9bSToomas Soome                 *ptr++ = 0;
21635ffb0c9bSToomas Soome                 *ptr++ = (mDNSu8)i;
21645ffb0c9bSToomas Soome                 for (j=0; j<i; j++) *ptr++ = nsec[j];
21655ffb0c9bSToomas Soome             }
21665ffb0c9bSToomas Soome             return ptr;
21675ffb0c9bSToomas Soome         }
21685ffb0c9bSToomas Soome         else
21695ffb0c9bSToomas Soome         {
21705ffb0c9bSToomas Soome             int win, wlen;
2171cda73f64SToomas Soome             int len = rr->rdlength - dlen;
21725ffb0c9bSToomas Soome 
21735ffb0c9bSToomas Soome             // Sanity check whether the bitmap is good
21745ffb0c9bSToomas Soome             while (len)
21755ffb0c9bSToomas Soome             {
21765ffb0c9bSToomas Soome                 if (len < 3)
21775ffb0c9bSToomas Soome                 { LogMsg("putRData: invalid length %d", len); return mDNSNULL; }
21785ffb0c9bSToomas Soome 
21795ffb0c9bSToomas Soome                 win = *nsec++;
21805ffb0c9bSToomas Soome                 wlen = *nsec++;
21815ffb0c9bSToomas Soome                 len -= 2;
21825ffb0c9bSToomas Soome                 if (len < wlen || wlen < 1 || wlen > 32)
21835ffb0c9bSToomas Soome                 { LogMsg("putRData: invalid window length %d", wlen); return mDNSNULL; }
21845ffb0c9bSToomas Soome                 if (win < 0 || win >= 256)
21855ffb0c9bSToomas Soome                 { LogMsg("putRData: invalid window %d", win); return mDNSNULL; }
21865ffb0c9bSToomas Soome 
21875ffb0c9bSToomas Soome                 nsec += wlen;
21885ffb0c9bSToomas Soome                 len -= wlen;
21895ffb0c9bSToomas Soome             }
21905ffb0c9bSToomas 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);}
21915ffb0c9bSToomas Soome 
21925ffb0c9bSToomas Soome             // No compression allowed for "nxt", just copy the data.
21935ffb0c9bSToomas Soome             mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
21945ffb0c9bSToomas Soome             return(ptr + rr->rdlength);
21955ffb0c9bSToomas Soome         }
21965ffb0c9bSToomas Soome     }
21975ffb0c9bSToomas Soome 
21985ffb0c9bSToomas Soome     default:            debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype);
21995ffb0c9bSToomas Soome         if (ptr + rr->rdlength > limit) return(mDNSNULL);
22005ffb0c9bSToomas Soome         mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
22015ffb0c9bSToomas Soome         return(ptr + rr->rdlength);
22025ffb0c9bSToomas Soome     }
22035ffb0c9bSToomas Soome }
22045ffb0c9bSToomas Soome 
22055ffb0c9bSToomas Soome #define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update)
22064b22b933Srs 
PutResourceRecordTTLWithLimit(DNSMessage * const msg,mDNSu8 * ptr,mDNSu16 * count,const ResourceRecord * rr,mDNSu32 ttl,const mDNSu8 * limit)2207*472cd20dSToomas Soome mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count,
2208*472cd20dSToomas Soome     const ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit)
22095ffb0c9bSToomas Soome {
22105ffb0c9bSToomas Soome     mDNSu8 *endofrdata;
22115ffb0c9bSToomas Soome     mDNSu16 actualLength;
22125ffb0c9bSToomas 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)
22135ffb0c9bSToomas Soome     const DNSMessage *const rdatacompressionbase = (IsUnicastUpdate(msg) && rr->rrtype == kDNSType_SRV) ? mDNSNULL : msg;
22145ffb0c9bSToomas Soome 
22155ffb0c9bSToomas Soome     if (rr->RecordType == kDNSRecordTypeUnregistered)
22165ffb0c9bSToomas Soome     {
2217*472cd20dSToomas Soome         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
2218*472cd20dSToomas Soome             "Attempt to put kDNSRecordTypeUnregistered " PRI_DM_NAME " (" PUB_S ")",
2219*472cd20dSToomas Soome             DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype));
22205ffb0c9bSToomas Soome         return(ptr);
22215ffb0c9bSToomas Soome     }
22225ffb0c9bSToomas Soome 
22235ffb0c9bSToomas Soome     if (!ptr)
22245ffb0c9bSToomas Soome     {
2225*472cd20dSToomas Soome         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
2226*472cd20dSToomas Soome             "Pointer to message is NULL while filling resource record " PRI_DM_NAME " (" PUB_S ")",
2227*472cd20dSToomas Soome             DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype));
22285ffb0c9bSToomas Soome         return(mDNSNULL);
22295ffb0c9bSToomas Soome     }
22305ffb0c9bSToomas Soome 
22315ffb0c9bSToomas Soome     ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
22325ffb0c9bSToomas Soome     // If we're out-of-space, return mDNSNULL
22335ffb0c9bSToomas Soome     if (!ptr || ptr + 10 >= limit)
22345ffb0c9bSToomas Soome     {
2235*472cd20dSToomas Soome         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
2236*472cd20dSToomas Soome             "Can't put more names into current message, will possibly put it into the next message - "
2237*472cd20dSToomas Soome             "name: " PRI_DM_NAME " (" PUB_S "), remaining space: %ld",
2238*472cd20dSToomas Soome             DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype), (long)(limit - ptr));
22395ffb0c9bSToomas Soome         return(mDNSNULL);
22405ffb0c9bSToomas Soome     }
22415ffb0c9bSToomas Soome     ptr[0] = (mDNSu8)(rr->rrtype  >> 8);
22425ffb0c9bSToomas Soome     ptr[1] = (mDNSu8)(rr->rrtype  &  0xFF);
22435ffb0c9bSToomas Soome     ptr[2] = (mDNSu8)(rr->rrclass >> 8);
22445ffb0c9bSToomas Soome     ptr[3] = (mDNSu8)(rr->rrclass &  0xFF);
22455ffb0c9bSToomas Soome     ptr[4] = (mDNSu8)((ttl >> 24) &  0xFF);
22465ffb0c9bSToomas Soome     ptr[5] = (mDNSu8)((ttl >> 16) &  0xFF);
22475ffb0c9bSToomas Soome     ptr[6] = (mDNSu8)((ttl >>  8) &  0xFF);
22485ffb0c9bSToomas Soome     ptr[7] = (mDNSu8)( ttl        &  0xFF);
22495ffb0c9bSToomas Soome     // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes
22505ffb0c9bSToomas Soome 
22515ffb0c9bSToomas Soome     endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr);
22525ffb0c9bSToomas Soome     if (!endofrdata)
22535ffb0c9bSToomas Soome     {
2254*472cd20dSToomas Soome         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
2255*472cd20dSToomas Soome             "Can't put more rdata into current message, will possibly put it into the next message - "
2256*472cd20dSToomas Soome             "name: " PRI_DM_NAME " (" PUB_S "), remaining space: %ld",
2257*472cd20dSToomas Soome             DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype), (long)(limit - ptr - 10));
22585ffb0c9bSToomas Soome         return(mDNSNULL);
22595ffb0c9bSToomas Soome     }
22605ffb0c9bSToomas Soome 
22615ffb0c9bSToomas Soome     // Go back and fill in the actual number of data bytes we wrote
22625ffb0c9bSToomas Soome     // (actualLength can be less than rdlength when domain name compression is used)
22635ffb0c9bSToomas Soome     actualLength = (mDNSu16)(endofrdata - ptr - 10);
22645ffb0c9bSToomas Soome     ptr[8] = (mDNSu8)(actualLength >> 8);
22655ffb0c9bSToomas Soome     ptr[9] = (mDNSu8)(actualLength &  0xFF);
22665ffb0c9bSToomas Soome 
2267*472cd20dSToomas Soome     if (count)
2268*472cd20dSToomas Soome     {
2269*472cd20dSToomas Soome         (*count)++;
2270*472cd20dSToomas Soome     }
2271*472cd20dSToomas Soome     else
2272*472cd20dSToomas Soome     {
2273*472cd20dSToomas Soome         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
2274*472cd20dSToomas Soome             "No target count to update for " PRI_DM_NAME " (" PUB_S ")",
2275*472cd20dSToomas Soome             DM_NAME_PARAM(rr->name), DNSTypeName(rr->rrtype));
2276*472cd20dSToomas Soome     }
22775ffb0c9bSToomas Soome     return(endofrdata);
22785ffb0c9bSToomas Soome }
22795ffb0c9bSToomas Soome 
putEmptyResourceRecord(DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,mDNSu16 * count,const AuthRecord * rr)22805ffb0c9bSToomas Soome mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr)
22815ffb0c9bSToomas Soome {
22825ffb0c9bSToomas Soome     ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name);
22835ffb0c9bSToomas Soome     if (!ptr || ptr + 10 > limit) return(mDNSNULL);     // If we're out-of-space, return mDNSNULL
22845ffb0c9bSToomas Soome     ptr[0] = (mDNSu8)(rr->resrec.rrtype  >> 8);             // Put type
22855ffb0c9bSToomas Soome     ptr[1] = (mDNSu8)(rr->resrec.rrtype  &  0xFF);
22865ffb0c9bSToomas Soome     ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8);             // Put class
22875ffb0c9bSToomas Soome     ptr[3] = (mDNSu8)(rr->resrec.rrclass &  0xFF);
22885ffb0c9bSToomas Soome     ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0;              // TTL is zero
22895ffb0c9bSToomas Soome     ptr[8] = ptr[9] = 0;                                // RDATA length is zero
22905ffb0c9bSToomas Soome     (*count)++;
22915ffb0c9bSToomas Soome     return(ptr + 10);
22925ffb0c9bSToomas Soome }
22934b22b933Srs 
putQuestion(DNSMessage * const msg,mDNSu8 * ptr,const mDNSu8 * const limit,const domainname * const name,mDNSu16 rrtype,mDNSu16 rrclass)22944b22b933Srs mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
22955ffb0c9bSToomas Soome {
22965ffb0c9bSToomas Soome     ptr = putDomainNameAsLabels(msg, ptr, limit, name);
22975ffb0c9bSToomas Soome     if (!ptr || ptr+4 >= limit) return(mDNSNULL);           // If we're out-of-space, return mDNSNULL
22985ffb0c9bSToomas Soome     ptr[0] = (mDNSu8)(rrtype  >> 8);
22995ffb0c9bSToomas Soome     ptr[1] = (mDNSu8)(rrtype  &  0xFF);
23005ffb0c9bSToomas Soome     ptr[2] = (mDNSu8)(rrclass >> 8);
23015ffb0c9bSToomas Soome     ptr[3] = (mDNSu8)(rrclass &  0xFF);
23025ffb0c9bSToomas Soome     msg->h.numQuestions++;
23035ffb0c9bSToomas Soome     return(ptr+4);
23045ffb0c9bSToomas Soome }
23054b22b933Srs 
23064b22b933Srs // for dynamic updates
putZone(DNSMessage * const msg,mDNSu8 * ptr,mDNSu8 * limit,const domainname * zone,mDNSOpaque16 zoneClass)23074b22b933Srs mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass)
23085ffb0c9bSToomas Soome {
23095ffb0c9bSToomas Soome     ptr = putDomainNameAsLabels(msg, ptr, limit, zone);
23105ffb0c9bSToomas Soome     if (!ptr || ptr + 4 > limit) return mDNSNULL;       // If we're out-of-space, return NULL
23115ffb0c9bSToomas Soome     *ptr++ = (mDNSu8)(kDNSType_SOA  >> 8);
23125ffb0c9bSToomas Soome     *ptr++ = (mDNSu8)(kDNSType_SOA  &  0xFF);
23135ffb0c9bSToomas Soome     *ptr++ = zoneClass.b[0];
23145ffb0c9bSToomas Soome     *ptr++ = zoneClass.b[1];
23155ffb0c9bSToomas Soome     msg->h.mDNS_numZones++;
23165ffb0c9bSToomas Soome     return ptr;
23175ffb0c9bSToomas Soome }
23184b22b933Srs 
23194b22b933Srs // for dynamic updates
putPrereqNameNotInUse(const domainname * const name,DNSMessage * const msg,mDNSu8 * const ptr,mDNSu8 * const end)23205ffb0c9bSToomas Soome mDNSexport mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end)
23215ffb0c9bSToomas Soome {
23225ffb0c9bSToomas Soome     AuthRecord prereq;
23235ffb0c9bSToomas Soome     mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
23245ffb0c9bSToomas Soome     AssignDomainName(&prereq.namestorage, name);
23255ffb0c9bSToomas Soome     prereq.resrec.rrtype = kDNSQType_ANY;
23265ffb0c9bSToomas Soome     prereq.resrec.rrclass = kDNSClass_NONE;
23275ffb0c9bSToomas Soome     return putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq);
23285ffb0c9bSToomas Soome }
23294b22b933Srs 
23304b22b933Srs // for dynamic updates
putDeletionRecord(DNSMessage * msg,mDNSu8 * ptr,ResourceRecord * rr)23314b22b933Srs mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
23325ffb0c9bSToomas Soome {
23335ffb0c9bSToomas Soome     // deletion: specify record w/ TTL 0, class NONE
23345ffb0c9bSToomas Soome     const mDNSu16 origclass = rr->rrclass;
23355ffb0c9bSToomas Soome     rr->rrclass = kDNSClass_NONE;
23365ffb0c9bSToomas Soome     ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
23375ffb0c9bSToomas Soome     rr->rrclass = origclass;
23385ffb0c9bSToomas Soome     return ptr;
23395ffb0c9bSToomas Soome }
23405ffb0c9bSToomas Soome 
23415ffb0c9bSToomas Soome // for dynamic updates
putDeletionRecordWithLimit(DNSMessage * msg,mDNSu8 * ptr,ResourceRecord * rr,mDNSu8 * limit)23425ffb0c9bSToomas Soome mDNSexport mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit)
23435ffb0c9bSToomas Soome {
23445ffb0c9bSToomas Soome     // deletion: specify record w/ TTL 0, class NONE
23455ffb0c9bSToomas Soome     const mDNSu16 origclass = rr->rrclass;
23465ffb0c9bSToomas Soome     rr->rrclass = kDNSClass_NONE;
23475ffb0c9bSToomas Soome     ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0, limit);
23485ffb0c9bSToomas Soome     rr->rrclass = origclass;
23495ffb0c9bSToomas Soome     return ptr;
23505ffb0c9bSToomas Soome }
23515ffb0c9bSToomas Soome 
putDeleteRRSetWithLimit(DNSMessage * msg,mDNSu8 * ptr,const domainname * name,mDNSu16 rrtype,mDNSu8 * limit)23525ffb0c9bSToomas Soome mDNSexport mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit)
23535ffb0c9bSToomas Soome {
23545ffb0c9bSToomas Soome     mDNSu16 class = kDNSQClass_ANY;
23555ffb0c9bSToomas Soome 
23565ffb0c9bSToomas Soome     ptr = putDomainNameAsLabels(msg, ptr, limit, name);
23575ffb0c9bSToomas Soome     if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
23585ffb0c9bSToomas Soome     ptr[0] = (mDNSu8)(rrtype  >> 8);
23595ffb0c9bSToomas Soome     ptr[1] = (mDNSu8)(rrtype  &  0xFF);
23605ffb0c9bSToomas Soome     ptr[2] = (mDNSu8)(class >> 8);
23615ffb0c9bSToomas Soome     ptr[3] = (mDNSu8)(class &  0xFF);
23625ffb0c9bSToomas Soome     ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
23635ffb0c9bSToomas Soome     ptr[8] = ptr[9] = 0; // zero rdlength/rdata
23645ffb0c9bSToomas Soome 
23655ffb0c9bSToomas Soome     msg->h.mDNS_numUpdates++;
23665ffb0c9bSToomas Soome     return ptr + 10;
23675ffb0c9bSToomas Soome }
23684b22b933Srs 
23694b22b933Srs // for dynamic updates
putDeleteAllRRSets(DNSMessage * msg,mDNSu8 * ptr,const domainname * name)23704b22b933Srs mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name)
23715ffb0c9bSToomas Soome {
23725ffb0c9bSToomas Soome     const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
23735ffb0c9bSToomas Soome     mDNSu16 class = kDNSQClass_ANY;
23745ffb0c9bSToomas Soome     mDNSu16 rrtype = kDNSQType_ANY;
23755ffb0c9bSToomas Soome 
23765ffb0c9bSToomas Soome     ptr = putDomainNameAsLabels(msg, ptr, limit, name);
23775ffb0c9bSToomas Soome     if (!ptr || ptr + 10 >= limit) return mDNSNULL; // If we're out-of-space, return mDNSNULL
23785ffb0c9bSToomas Soome     ptr[0] = (mDNSu8)(rrtype >> 8);
23795ffb0c9bSToomas Soome     ptr[1] = (mDNSu8)(rrtype &  0xFF);
23805ffb0c9bSToomas Soome     ptr[2] = (mDNSu8)(class >> 8);
23815ffb0c9bSToomas Soome     ptr[3] = (mDNSu8)(class &  0xFF);
23825ffb0c9bSToomas Soome     ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
23835ffb0c9bSToomas Soome     ptr[8] = ptr[9] = 0; // zero rdlength/rdata
23845ffb0c9bSToomas Soome 
23855ffb0c9bSToomas Soome     msg->h.mDNS_numUpdates++;
23865ffb0c9bSToomas Soome     return ptr + 10;
23875ffb0c9bSToomas Soome }
23884b22b933Srs 
23894b22b933Srs // for dynamic updates
putUpdateLease(DNSMessage * msg,mDNSu8 * ptr,mDNSu32 lease)2390cda73f64SToomas Soome mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease)
23915ffb0c9bSToomas Soome {
23925ffb0c9bSToomas Soome     AuthRecord rr;
23935ffb0c9bSToomas Soome     mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
23945ffb0c9bSToomas Soome     rr.resrec.rrclass    = NormalMaxDNSMessageData;
23955ffb0c9bSToomas Soome     rr.resrec.rdlength   = sizeof(rdataOPT);    // One option in this OPT record
23965ffb0c9bSToomas Soome     rr.resrec.rdestimate = sizeof(rdataOPT);
23975ffb0c9bSToomas Soome     rr.resrec.rdata->u.opt[0].opt           = kDNSOpt_Lease;
23985ffb0c9bSToomas Soome     rr.resrec.rdata->u.opt[0].u.updatelease = lease;
2399cda73f64SToomas Soome     ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, &rr.resrec, 0);
2400cda73f64SToomas Soome     if (!ptr) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; }
2401cda73f64SToomas Soome     return ptr;
24025ffb0c9bSToomas Soome }
24035ffb0c9bSToomas Soome 
24045ffb0c9bSToomas Soome // for dynamic updates
putUpdateLeaseWithLimit(DNSMessage * msg,mDNSu8 * ptr,mDNSu32 lease,mDNSu8 * limit)2405cda73f64SToomas Soome mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit)
24065ffb0c9bSToomas Soome {
24075ffb0c9bSToomas Soome     AuthRecord rr;
24085ffb0c9bSToomas Soome     mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
24095ffb0c9bSToomas Soome     rr.resrec.rrclass    = NormalMaxDNSMessageData;
24105ffb0c9bSToomas Soome     rr.resrec.rdlength   = sizeof(rdataOPT);    // One option in this OPT record
24115ffb0c9bSToomas Soome     rr.resrec.rdestimate = sizeof(rdataOPT);
24125ffb0c9bSToomas Soome     rr.resrec.rdata->u.opt[0].opt           = kDNSOpt_Lease;
24135ffb0c9bSToomas Soome     rr.resrec.rdata->u.opt[0].u.updatelease = lease;
2414cda73f64SToomas Soome     ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.numAdditionals, &rr.resrec, 0, limit);
2415cda73f64SToomas Soome     if (!ptr) { LogMsg("ERROR: putUpdateLeaseWithLimit - PutResourceRecordTTLWithLimit"); return mDNSNULL; }
2416cda73f64SToomas Soome     return ptr;
24175ffb0c9bSToomas Soome }
24185ffb0c9bSToomas Soome 
24194b22b933Srs // ***************************************************************************
24204b22b933Srs #if COMPILER_LIKES_PRAGMA_MARK
24214b22b933Srs #pragma mark -
24224b22b933Srs #pragma mark - DNS Message Parsing Functions
24234b22b933Srs #endif
24244b22b933Srs 
DomainNameHashValue(const domainname * const name)24254b22b933Srs mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name)
24265ffb0c9bSToomas Soome {
24275ffb0c9bSToomas Soome     mDNSu32 sum = 0;
24285ffb0c9bSToomas Soome     const mDNSu8 *c;
24295ffb0c9bSToomas Soome 
24305ffb0c9bSToomas Soome     for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
24315ffb0c9bSToomas Soome     {
24325ffb0c9bSToomas Soome         sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) |
24335ffb0c9bSToomas Soome                (mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]);
24345ffb0c9bSToomas Soome         sum = (sum<<3) | (sum>>29);
24355ffb0c9bSToomas Soome     }
24365ffb0c9bSToomas Soome     if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
24375ffb0c9bSToomas Soome     return(sum);
24385ffb0c9bSToomas Soome }
24394b22b933Srs 
SetNewRData(ResourceRecord * const rr,RData * NewRData,mDNSu16 rdlength)24404b22b933Srs mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
24415ffb0c9bSToomas Soome {
24425ffb0c9bSToomas Soome     domainname *target;
24435ffb0c9bSToomas Soome     if (NewRData)
24445ffb0c9bSToomas Soome     {
24455ffb0c9bSToomas Soome         rr->rdata    = NewRData;
24465ffb0c9bSToomas Soome         rr->rdlength = rdlength;
24475ffb0c9bSToomas Soome     }
24485ffb0c9bSToomas Soome     // Must not try to get target pointer until after updating rr->rdata
24495ffb0c9bSToomas Soome     target = GetRRDomainNameTarget(rr);
24505ffb0c9bSToomas Soome     rr->rdlength   = GetRDLength(rr, mDNSfalse);
24515ffb0c9bSToomas Soome     rr->rdestimate = GetRDLength(rr, mDNStrue);
24525ffb0c9bSToomas Soome     rr->rdatahash  = target ? DomainNameHashValue(target) : RDataHashValue(rr);
24535ffb0c9bSToomas Soome }
24544b22b933Srs 
skipDomainName(const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * const end)24554b22b933Srs mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
24565ffb0c9bSToomas Soome {
24575ffb0c9bSToomas Soome     mDNSu16 total = 0;
24585ffb0c9bSToomas Soome 
24595ffb0c9bSToomas Soome     if (ptr < (mDNSu8*)msg || ptr >= end)
24605ffb0c9bSToomas Soome     { debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
24615ffb0c9bSToomas Soome 
24625ffb0c9bSToomas Soome     while (1)                       // Read sequence of labels
24635ffb0c9bSToomas Soome     {
24645ffb0c9bSToomas Soome         const mDNSu8 len = *ptr++;  // Read length of this label
24655ffb0c9bSToomas Soome         if (len == 0) return(ptr);  // If length is zero, that means this name is complete
24665ffb0c9bSToomas Soome         switch (len & 0xC0)
24675ffb0c9bSToomas Soome         {
24685ffb0c9bSToomas Soome         case 0x00:  if (ptr + len >= end)                       // Remember: expect at least one more byte for the root label
24695ffb0c9bSToomas Soome             { debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
24705ffb0c9bSToomas Soome             if (total + 1 + len >= MAX_DOMAIN_NAME)             // Remember: expect at least one more byte for the root label
24715ffb0c9bSToomas Soome             { debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
24725ffb0c9bSToomas Soome             ptr += len;
24735ffb0c9bSToomas Soome             total += 1 + len;
24745ffb0c9bSToomas Soome             break;
24755ffb0c9bSToomas Soome 
24765ffb0c9bSToomas Soome         case 0x40:  debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL);
24775ffb0c9bSToomas Soome         case 0x80:  debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL);
24785ffb0c9bSToomas Soome         case 0xC0:  return(ptr+1);
24795ffb0c9bSToomas Soome         }
24805ffb0c9bSToomas Soome     }
24815ffb0c9bSToomas Soome }
24824b22b933Srs 
24834b22b933Srs // 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)24844b22b933Srs mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
24855ffb0c9bSToomas Soome                                        domainname *const name)
24865ffb0c9bSToomas Soome {
24875ffb0c9bSToomas Soome     const mDNSu8 *nextbyte = mDNSNULL;                  // Record where we got to before we started following pointers
24885ffb0c9bSToomas Soome     mDNSu8       *np = name->c;                         // Name pointer
24895ffb0c9bSToomas Soome     const mDNSu8 *const limit = np + MAX_DOMAIN_NAME;   // Limit so we don't overrun buffer
24905ffb0c9bSToomas Soome 
24915ffb0c9bSToomas Soome     if (ptr < (mDNSu8*)msg || ptr >= end)
24925ffb0c9bSToomas Soome     { debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
24935ffb0c9bSToomas Soome 
24945ffb0c9bSToomas Soome     *np = 0;                        // Tentatively place the root label here (may be overwritten if we have more labels)
24955ffb0c9bSToomas Soome 
24965ffb0c9bSToomas Soome     while (1)                       // Read sequence of labels
24975ffb0c9bSToomas Soome     {
2498c65ebfc7SToomas Soome 		int i;
2499c65ebfc7SToomas Soome 		mDNSu16 offset;
25005ffb0c9bSToomas Soome         const mDNSu8 len = *ptr++;  // Read length of this label
25015ffb0c9bSToomas Soome         if (len == 0) break;        // If length is zero, that means this name is complete
25025ffb0c9bSToomas Soome         switch (len & 0xC0)
25035ffb0c9bSToomas Soome         {
25045ffb0c9bSToomas Soome 
25055ffb0c9bSToomas Soome         case 0x00:  if (ptr + len >= end)           // Remember: expect at least one more byte for the root label
25065ffb0c9bSToomas Soome             { debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
25075ffb0c9bSToomas Soome             if (np + 1 + len >= limit)              // Remember: expect at least one more byte for the root label
25085ffb0c9bSToomas Soome             { debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
25095ffb0c9bSToomas Soome             *np++ = len;
25105ffb0c9bSToomas Soome             for (i=0; i<len; i++) *np++ = *ptr++;
25115ffb0c9bSToomas Soome             *np = 0;                // Tentatively place the root label here (may be overwritten if we have more labels)
25125ffb0c9bSToomas Soome             break;
25135ffb0c9bSToomas Soome 
25145ffb0c9bSToomas Soome         case 0x40:  debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
25155ffb0c9bSToomas Soome             return(mDNSNULL);
25165ffb0c9bSToomas Soome 
25175ffb0c9bSToomas Soome         case 0x80:  debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
25185ffb0c9bSToomas Soome 
25193b436d06SToomas Soome         case 0xC0:  if (ptr >= end)
25203b436d06SToomas Soome             { debugf("getDomainName: Malformed compression label (overruns packet end)"); return(mDNSNULL); }
25213b436d06SToomas Soome             offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
25225ffb0c9bSToomas Soome             if (!nextbyte) nextbyte = ptr;              // Record where we got to before we started following pointers
25235ffb0c9bSToomas Soome             ptr = (mDNSu8 *)msg + offset;
25245ffb0c9bSToomas Soome             if (ptr < (mDNSu8*)msg || ptr >= end)
25255ffb0c9bSToomas Soome             { debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
25265ffb0c9bSToomas Soome             if (*ptr & 0xC0)
25275ffb0c9bSToomas Soome             { debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
25285ffb0c9bSToomas Soome             break;
25295ffb0c9bSToomas Soome         }
25305ffb0c9bSToomas Soome     }
25315ffb0c9bSToomas Soome 
25325ffb0c9bSToomas Soome     if (nextbyte) return(nextbyte);
25335ffb0c9bSToomas Soome     else return(ptr);
25345ffb0c9bSToomas Soome }
25354b22b933Srs 
skipResourceRecord(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end)25364b22b933Srs mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
25375ffb0c9bSToomas Soome {
25385ffb0c9bSToomas Soome     mDNSu16 pktrdlength;
25395ffb0c9bSToomas Soome 
25405ffb0c9bSToomas Soome     ptr = skipDomainName(msg, ptr, end);
25415ffb0c9bSToomas Soome     if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
25425ffb0c9bSToomas Soome 
25435ffb0c9bSToomas Soome     if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
25445ffb0c9bSToomas Soome     pktrdlength = (mDNSu16)((mDNSu16)ptr[8] <<  8 | ptr[9]);
25455ffb0c9bSToomas Soome     ptr += 10;
25465ffb0c9bSToomas Soome     if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
25475ffb0c9bSToomas Soome 
25485ffb0c9bSToomas Soome     return(ptr + pktrdlength);
25495ffb0c9bSToomas Soome }
25505ffb0c9bSToomas Soome 
25515ffb0c9bSToomas Soome // Sanity check whether the NSEC/NSEC3 bitmap is good
SanityCheckBitMap(const mDNSu8 * bmap,const mDNSu8 * end,int len)25525ffb0c9bSToomas Soome mDNSlocal mDNSu8 *SanityCheckBitMap(const mDNSu8 *bmap, const mDNSu8 *end, int len)
25535ffb0c9bSToomas Soome {
25545ffb0c9bSToomas Soome     int win, wlen;
25555ffb0c9bSToomas Soome 
25565ffb0c9bSToomas Soome     while (bmap < end)
25575ffb0c9bSToomas Soome     {
25585ffb0c9bSToomas Soome         if (len < 3)
25595ffb0c9bSToomas Soome         {
25605ffb0c9bSToomas Soome             LogInfo("SanityCheckBitMap: invalid length %d", len);
25615ffb0c9bSToomas Soome             return mDNSNULL;
25625ffb0c9bSToomas Soome         }
25635ffb0c9bSToomas Soome 
25645ffb0c9bSToomas Soome         win = *bmap++;
25655ffb0c9bSToomas Soome         wlen = *bmap++;
25665ffb0c9bSToomas Soome         len -= 2;
25675ffb0c9bSToomas Soome         if (len < wlen || wlen < 1 || wlen > 32)
25685ffb0c9bSToomas Soome         {
25695ffb0c9bSToomas Soome             LogInfo("SanityCheckBitMap: invalid window length %d", wlen);
25705ffb0c9bSToomas Soome             return mDNSNULL;
25715ffb0c9bSToomas Soome         }
25725ffb0c9bSToomas Soome         if (win < 0 || win >= 256)
25735ffb0c9bSToomas Soome         {
25745ffb0c9bSToomas Soome             LogInfo("SanityCheckBitMap: invalid window %d", win);
25755ffb0c9bSToomas Soome             return mDNSNULL;
25765ffb0c9bSToomas Soome         }
25775ffb0c9bSToomas Soome 
25785ffb0c9bSToomas Soome         bmap += wlen;
25795ffb0c9bSToomas Soome         len -= wlen;
25805ffb0c9bSToomas Soome     }
25815ffb0c9bSToomas Soome     return (mDNSu8 *)bmap;
25825ffb0c9bSToomas Soome }
25835ffb0c9bSToomas Soome 
AssignDomainNameWithLimit(domainname * const dst,const domainname * src,const mDNSu8 * const end)2584*472cd20dSToomas Soome mDNSlocal mDNSBool AssignDomainNameWithLimit(domainname *const dst, const domainname *src, const mDNSu8 *const end)
2585*472cd20dSToomas Soome {
2586*472cd20dSToomas Soome     const mDNSu32 len = DomainNameLengthLimit(src, end);
2587*472cd20dSToomas Soome     if ((len >= 1) && (len <= MAX_DOMAIN_NAME))
2588*472cd20dSToomas Soome     {
2589*472cd20dSToomas Soome         mDNSPlatformMemCopy(dst->c, src->c, len);
2590*472cd20dSToomas Soome         return mDNStrue;
2591*472cd20dSToomas Soome     }
2592*472cd20dSToomas Soome     else
2593*472cd20dSToomas Soome     {
2594*472cd20dSToomas Soome         dst->c[0] = 0;
2595*472cd20dSToomas Soome         return mDNSfalse;
2596*472cd20dSToomas Soome     }
2597*472cd20dSToomas Soome }
2598*472cd20dSToomas Soome 
25995ffb0c9bSToomas Soome // This function is called with "msg" when we receive a DNS message and needs to parse a single resource record
26005ffb0c9bSToomas Soome // pointed to by "ptr". Some resource records like SOA, SRV are converted to host order and also expanded
2601*472cd20dSToomas Soome // (domainnames are expanded to 256 bytes) when stored in memory.
26025ffb0c9bSToomas Soome //
26035ffb0c9bSToomas Soome // This function can also be called with "NULL" msg to parse a single resource record pointed to by ptr.
2604c65ebfc7SToomas Soome // The caller can do this only if the names in the resource records are not compressed and validity of the
2605*472cd20dSToomas Soome // resource record has already been done before.
SetRData(const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * end,ResourceRecord * const rr,const mDNSu16 rdlength)2606*472cd20dSToomas Soome mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, ResourceRecord *const rr,
2607*472cd20dSToomas Soome     const mDNSu16 rdlength)
26085ffb0c9bSToomas Soome {
2609*472cd20dSToomas Soome     RDataBody2 *const rdb = (RDataBody2 *)&rr->rdata->u;
26105ffb0c9bSToomas Soome 
2611*472cd20dSToomas Soome     switch (rr->rrtype)
26125ffb0c9bSToomas Soome     {
26135ffb0c9bSToomas Soome     case kDNSType_A:
26145ffb0c9bSToomas Soome         if (rdlength != sizeof(mDNSv4Addr))
26155ffb0c9bSToomas Soome             goto fail;
26165ffb0c9bSToomas Soome         rdb->ipv4.b[0] = ptr[0];
26175ffb0c9bSToomas Soome         rdb->ipv4.b[1] = ptr[1];
26185ffb0c9bSToomas Soome         rdb->ipv4.b[2] = ptr[2];
26195ffb0c9bSToomas Soome         rdb->ipv4.b[3] = ptr[3];
26205ffb0c9bSToomas Soome         break;
26215ffb0c9bSToomas Soome 
26225ffb0c9bSToomas Soome     case kDNSType_NS:
26235ffb0c9bSToomas Soome     case kDNSType_MD:
26245ffb0c9bSToomas Soome     case kDNSType_MF:
26255ffb0c9bSToomas Soome     case kDNSType_CNAME:
26265ffb0c9bSToomas Soome     case kDNSType_MB:
26275ffb0c9bSToomas Soome     case kDNSType_MG:
26285ffb0c9bSToomas Soome     case kDNSType_MR:
26295ffb0c9bSToomas Soome     case kDNSType_PTR:
26305ffb0c9bSToomas Soome     case kDNSType_NSAP_PTR:
26315ffb0c9bSToomas Soome     case kDNSType_DNAME:
26325ffb0c9bSToomas Soome         if (msg)
26335ffb0c9bSToomas Soome         {
26345ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &rdb->name);
26355ffb0c9bSToomas Soome         }
26365ffb0c9bSToomas Soome         else
26375ffb0c9bSToomas Soome         {
2638*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&rdb->name, (domainname *)ptr, end))
2639*472cd20dSToomas Soome             {
2640*472cd20dSToomas Soome                 goto fail;
2641*472cd20dSToomas Soome             }
26425ffb0c9bSToomas Soome             ptr += DomainNameLength(&rdb->name);
26435ffb0c9bSToomas Soome         }
26445ffb0c9bSToomas Soome         if (ptr != end)
26455ffb0c9bSToomas Soome         {
26465ffb0c9bSToomas Soome             debugf("SetRData: Malformed CNAME/PTR RDATA name");
26475ffb0c9bSToomas Soome             goto fail;
26485ffb0c9bSToomas Soome         }
26495ffb0c9bSToomas Soome         break;
26505ffb0c9bSToomas Soome 
26515ffb0c9bSToomas Soome     case kDNSType_SOA:
26525ffb0c9bSToomas Soome         if (msg)
26535ffb0c9bSToomas Soome         {
26545ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &rdb->soa.mname);
26555ffb0c9bSToomas Soome         }
26565ffb0c9bSToomas Soome         else
26575ffb0c9bSToomas Soome         {
2658*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&rdb->soa.mname, (domainname *)ptr, end))
2659*472cd20dSToomas Soome             {
2660*472cd20dSToomas Soome                 goto fail;
2661*472cd20dSToomas Soome             }
26625ffb0c9bSToomas Soome             ptr += DomainNameLength(&rdb->soa.mname);
26635ffb0c9bSToomas Soome         }
26645ffb0c9bSToomas Soome         if (!ptr)
26655ffb0c9bSToomas Soome         {
26665ffb0c9bSToomas Soome             debugf("SetRData: Malformed SOA RDATA mname");
26675ffb0c9bSToomas Soome             goto fail;
26685ffb0c9bSToomas Soome         }
26695ffb0c9bSToomas Soome         if (msg)
26705ffb0c9bSToomas Soome         {
26715ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &rdb->soa.rname);
26725ffb0c9bSToomas Soome         }
26735ffb0c9bSToomas Soome         else
26745ffb0c9bSToomas Soome         {
2675*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&rdb->soa.rname, (domainname *)ptr, end))
2676*472cd20dSToomas Soome             {
2677*472cd20dSToomas Soome                 goto fail;
2678*472cd20dSToomas Soome             }
26795ffb0c9bSToomas Soome             ptr += DomainNameLength(&rdb->soa.rname);
26805ffb0c9bSToomas Soome         }
26815ffb0c9bSToomas Soome         if (!ptr)
26825ffb0c9bSToomas Soome         {
26835ffb0c9bSToomas Soome             debugf("SetRData: Malformed SOA RDATA rname");
26845ffb0c9bSToomas Soome             goto fail;
26855ffb0c9bSToomas Soome         }
26865ffb0c9bSToomas Soome         if (ptr + 0x14 != end)
26875ffb0c9bSToomas Soome         {
26885ffb0c9bSToomas Soome             debugf("SetRData: Malformed SOA RDATA");
26895ffb0c9bSToomas Soome             goto fail;
26905ffb0c9bSToomas Soome         }
26915ffb0c9bSToomas Soome         rdb->soa.serial  = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]);
26925ffb0c9bSToomas Soome         rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]);
26935ffb0c9bSToomas Soome         rdb->soa.retry   = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]);
26945ffb0c9bSToomas Soome         rdb->soa.expire  = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]);
26955ffb0c9bSToomas Soome         rdb->soa.min     = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
26965ffb0c9bSToomas Soome         break;
26975ffb0c9bSToomas Soome 
26985ffb0c9bSToomas Soome     case kDNSType_HINFO:
2699*472cd20dSToomas Soome     // See https://tools.ietf.org/html/rfc1035#section-3.3.2 for HINFO RDATA format.
2700*472cd20dSToomas Soome     {
2701*472cd20dSToomas Soome         // HINFO should contain RDATA.
2702*472cd20dSToomas Soome         if (end <= ptr || rdlength != (mDNSu32)(end - ptr))
2703*472cd20dSToomas Soome         {
2704*472cd20dSToomas Soome             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
2705*472cd20dSToomas Soome                 "SetRData: Malformed HINFO RDATA - invalid RDATA length: %u", rdlength);
2706*472cd20dSToomas Soome             goto fail;
2707*472cd20dSToomas Soome         }
2708*472cd20dSToomas Soome 
2709*472cd20dSToomas Soome         const mDNSu8 *currentPtr = ptr;
2710*472cd20dSToomas Soome         // CPU character string length should be less than the RDATA length.
2711*472cd20dSToomas Soome         mDNSu32 cpuCharacterStrLength = currentPtr[0];
2712*472cd20dSToomas Soome         if (1 + cpuCharacterStrLength >= (mDNSu32)(end - currentPtr))
2713*472cd20dSToomas Soome         {
2714*472cd20dSToomas Soome             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
2715*472cd20dSToomas Soome                 "SetRData: Malformed HINFO RDATA - CPU character string goes out of boundary");
2716*472cd20dSToomas Soome             goto fail;
2717*472cd20dSToomas Soome         }
2718*472cd20dSToomas Soome         currentPtr += 1 + cpuCharacterStrLength;
2719*472cd20dSToomas Soome 
2720*472cd20dSToomas Soome         // OS character string should end at the RDATA ending.
2721*472cd20dSToomas Soome         mDNSu32 osCharacterStrLength = currentPtr[0];
2722*472cd20dSToomas Soome         if (1 + osCharacterStrLength != (mDNSu32)(end - currentPtr))
2723*472cd20dSToomas Soome         {
2724*472cd20dSToomas Soome             LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_DEBUG,
2725*472cd20dSToomas Soome                 "SetRData: Malformed HINFO RDATA - OS character string does not end at the RDATA ending");
2726*472cd20dSToomas Soome             goto fail;
2727*472cd20dSToomas Soome         }
2728*472cd20dSToomas Soome 
2729*472cd20dSToomas Soome         // Copy the validated RDATA.
2730*472cd20dSToomas Soome         rr->rdlength = rdlength;
2731*472cd20dSToomas Soome         mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
2732*472cd20dSToomas Soome         break;
2733*472cd20dSToomas Soome     }
2734*472cd20dSToomas Soome     case kDNSType_NULL:
27355ffb0c9bSToomas Soome     case kDNSType_TXT:
27365ffb0c9bSToomas Soome     case kDNSType_X25:
27375ffb0c9bSToomas Soome     case kDNSType_ISDN:
27385ffb0c9bSToomas Soome     case kDNSType_LOC:
27395ffb0c9bSToomas Soome     case kDNSType_DHCID:
2740*472cd20dSToomas Soome 	case kDNSType_SVCB:
2741*472cd20dSToomas Soome 	case kDNSType_HTTPS:
2742*472cd20dSToomas Soome         rr->rdlength = rdlength;
27435ffb0c9bSToomas Soome         mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
27445ffb0c9bSToomas Soome         break;
27455ffb0c9bSToomas Soome 
27465ffb0c9bSToomas Soome     case kDNSType_MX:
27475ffb0c9bSToomas Soome     case kDNSType_AFSDB:
27485ffb0c9bSToomas Soome     case kDNSType_RT:
27495ffb0c9bSToomas Soome     case kDNSType_KX:
27505ffb0c9bSToomas Soome         // Preference + domainname
27515ffb0c9bSToomas Soome         if (rdlength < 3)
27525ffb0c9bSToomas Soome             goto fail;
27535ffb0c9bSToomas Soome         rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
27545ffb0c9bSToomas Soome         ptr += 2;
27555ffb0c9bSToomas Soome         if (msg)
27565ffb0c9bSToomas Soome         {
27575ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &rdb->mx.exchange);
27585ffb0c9bSToomas Soome         }
27595ffb0c9bSToomas Soome         else
27605ffb0c9bSToomas Soome         {
2761*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&rdb->mx.exchange, (domainname *)ptr, end))
2762*472cd20dSToomas Soome             {
2763*472cd20dSToomas Soome                 goto fail;
2764*472cd20dSToomas Soome             }
27655ffb0c9bSToomas Soome             ptr += DomainNameLength(&rdb->mx.exchange);
27665ffb0c9bSToomas Soome         }
27675ffb0c9bSToomas Soome         if (ptr != end)
27685ffb0c9bSToomas Soome         {
27695ffb0c9bSToomas Soome             debugf("SetRData: Malformed MX name");
27705ffb0c9bSToomas Soome             goto fail;
27715ffb0c9bSToomas Soome         }
27725ffb0c9bSToomas Soome         break;
27735ffb0c9bSToomas Soome 
27745ffb0c9bSToomas Soome     case kDNSType_MINFO:
27755ffb0c9bSToomas Soome     case kDNSType_RP:
27765ffb0c9bSToomas Soome         // Domainname + domainname
27775ffb0c9bSToomas Soome         if (msg)
27785ffb0c9bSToomas Soome         {
27795ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox);
27805ffb0c9bSToomas Soome         }
27815ffb0c9bSToomas Soome         else
27825ffb0c9bSToomas Soome         {
2783*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&rdb->rp.mbox, (domainname *)ptr, end))
2784*472cd20dSToomas Soome             {
2785*472cd20dSToomas Soome                 goto fail;
2786*472cd20dSToomas Soome             }
27875ffb0c9bSToomas Soome             ptr += DomainNameLength(&rdb->rp.mbox);
27885ffb0c9bSToomas Soome         }
27895ffb0c9bSToomas Soome         if (!ptr)
27905ffb0c9bSToomas Soome         {
27915ffb0c9bSToomas Soome             debugf("SetRData: Malformed RP mbox");
27925ffb0c9bSToomas Soome             goto fail;
27935ffb0c9bSToomas Soome         }
27945ffb0c9bSToomas Soome         if (msg)
27955ffb0c9bSToomas Soome         {
27965ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &rdb->rp.txt);
27975ffb0c9bSToomas Soome         }
27985ffb0c9bSToomas Soome         else
27995ffb0c9bSToomas Soome         {
2800*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&rdb->rp.txt, (domainname *)ptr, end))
2801*472cd20dSToomas Soome             {
2802*472cd20dSToomas Soome                 goto fail;
2803*472cd20dSToomas Soome             }
28045ffb0c9bSToomas Soome             ptr += DomainNameLength(&rdb->rp.txt);
28055ffb0c9bSToomas Soome         }
28065ffb0c9bSToomas Soome         if (ptr != end)
28075ffb0c9bSToomas Soome         {
28085ffb0c9bSToomas Soome             debugf("SetRData: Malformed RP txt");
28095ffb0c9bSToomas Soome             goto fail;
28105ffb0c9bSToomas Soome         }
28115ffb0c9bSToomas Soome         break;
28125ffb0c9bSToomas Soome 
28135ffb0c9bSToomas Soome     case kDNSType_PX:
28145ffb0c9bSToomas Soome         // Preference + domainname + domainname
28155ffb0c9bSToomas Soome         if (rdlength < 4)
28165ffb0c9bSToomas Soome             goto fail;
28175ffb0c9bSToomas Soome         rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
28185ffb0c9bSToomas Soome         ptr += 2;
28195ffb0c9bSToomas Soome         if (msg)
28205ffb0c9bSToomas Soome         {
28215ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &rdb->px.map822);
28225ffb0c9bSToomas Soome         }
28235ffb0c9bSToomas Soome         else
28245ffb0c9bSToomas Soome         {
2825*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&rdb->px.map822, (domainname *)ptr, end))
2826*472cd20dSToomas Soome             {
2827*472cd20dSToomas Soome                 goto fail;
2828*472cd20dSToomas Soome             }
28295ffb0c9bSToomas Soome             ptr += DomainNameLength(&rdb->px.map822);
28305ffb0c9bSToomas Soome         }
28315ffb0c9bSToomas Soome         if (!ptr)
28325ffb0c9bSToomas Soome         {
28335ffb0c9bSToomas Soome             debugf("SetRData: Malformed PX map822");
28345ffb0c9bSToomas Soome             goto fail;
28355ffb0c9bSToomas Soome         }
28365ffb0c9bSToomas Soome         if (msg)
28375ffb0c9bSToomas Soome         {
28385ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400);
28395ffb0c9bSToomas Soome         }
28405ffb0c9bSToomas Soome         else
28415ffb0c9bSToomas Soome         {
2842*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&rdb->px.mapx400, (domainname *)ptr, end))
2843*472cd20dSToomas Soome             {
2844*472cd20dSToomas Soome                 goto fail;
2845*472cd20dSToomas Soome             }
28465ffb0c9bSToomas Soome             ptr += DomainNameLength(&rdb->px.mapx400);
28475ffb0c9bSToomas Soome         }
28485ffb0c9bSToomas Soome         if (ptr != end)
28495ffb0c9bSToomas Soome         {
28505ffb0c9bSToomas Soome             debugf("SetRData: Malformed PX mapx400");
28515ffb0c9bSToomas Soome             goto fail;
28525ffb0c9bSToomas Soome         }
28535ffb0c9bSToomas Soome         break;
28545ffb0c9bSToomas Soome 
28555ffb0c9bSToomas Soome     case kDNSType_AAAA:
28565ffb0c9bSToomas Soome         if (rdlength != sizeof(mDNSv6Addr))
28575ffb0c9bSToomas Soome             goto fail;
28585ffb0c9bSToomas Soome         mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6));
28595ffb0c9bSToomas Soome         break;
28605ffb0c9bSToomas Soome 
28615ffb0c9bSToomas Soome     case kDNSType_SRV:
28625ffb0c9bSToomas Soome         // Priority + weight + port + domainname
28635ffb0c9bSToomas Soome         if (rdlength < 7)
28645ffb0c9bSToomas Soome             goto fail;
28655ffb0c9bSToomas Soome         rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
28665ffb0c9bSToomas Soome         rdb->srv.weight   = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
28675ffb0c9bSToomas Soome         rdb->srv.port.b[0] = ptr[4];
28685ffb0c9bSToomas Soome         rdb->srv.port.b[1] = ptr[5];
28695ffb0c9bSToomas Soome         ptr += 6;
28705ffb0c9bSToomas Soome         if (msg)
28715ffb0c9bSToomas Soome         {
28725ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &rdb->srv.target);
28735ffb0c9bSToomas Soome         }
28745ffb0c9bSToomas Soome         else
28755ffb0c9bSToomas Soome         {
2876*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&rdb->srv.target, (domainname *)ptr, end))
2877*472cd20dSToomas Soome             {
2878*472cd20dSToomas Soome                 goto fail;
2879*472cd20dSToomas Soome             }
28805ffb0c9bSToomas Soome             ptr += DomainNameLength(&rdb->srv.target);
28815ffb0c9bSToomas Soome         }
28825ffb0c9bSToomas Soome         if (ptr != end)
28835ffb0c9bSToomas Soome         {
28845ffb0c9bSToomas Soome             debugf("SetRData: Malformed SRV RDATA name");
28855ffb0c9bSToomas Soome             goto fail;
28865ffb0c9bSToomas Soome         }
28875ffb0c9bSToomas Soome         break;
28885ffb0c9bSToomas Soome 
28895ffb0c9bSToomas Soome     case kDNSType_NAPTR:
28905ffb0c9bSToomas Soome     {
28915ffb0c9bSToomas Soome         int savelen, len;
28925ffb0c9bSToomas Soome         domainname name;
28935ffb0c9bSToomas Soome         const mDNSu8 *orig = ptr;
28945ffb0c9bSToomas Soome 
2895*472cd20dSToomas Soome         // Make sure the data is parseable and within the limits.
28965ffb0c9bSToomas Soome         //
28975ffb0c9bSToomas Soome         // Fixed length: Order, preference (4 bytes)
28985ffb0c9bSToomas Soome         // Variable length: flags, service, regexp, domainname
28995ffb0c9bSToomas Soome 
29005ffb0c9bSToomas Soome         if (rdlength < 8)
29015ffb0c9bSToomas Soome             goto fail;
29025ffb0c9bSToomas Soome         // Order, preference.
29035ffb0c9bSToomas Soome         ptr += 4;
29045ffb0c9bSToomas Soome         // Parse flags, Service and Regexp
29055ffb0c9bSToomas Soome         // length in the first byte does not include the length byte itself
29065ffb0c9bSToomas Soome         len = *ptr + 1;
29075ffb0c9bSToomas Soome         ptr += len;
29085ffb0c9bSToomas Soome         if (ptr >= end)
29095ffb0c9bSToomas Soome         {
29105ffb0c9bSToomas Soome             LogInfo("SetRData: Malformed NAPTR flags");
29115ffb0c9bSToomas Soome             goto fail;
29125ffb0c9bSToomas Soome         }
29135ffb0c9bSToomas Soome 
29145ffb0c9bSToomas Soome         // Service
29155ffb0c9bSToomas Soome         len = *ptr + 1;
29165ffb0c9bSToomas Soome         ptr += len;
29175ffb0c9bSToomas Soome         if (ptr >= end)
29185ffb0c9bSToomas Soome         {
29195ffb0c9bSToomas Soome             LogInfo("SetRData: Malformed NAPTR service");
29205ffb0c9bSToomas Soome             goto fail;
29215ffb0c9bSToomas Soome         }
29225ffb0c9bSToomas Soome 
29235ffb0c9bSToomas Soome         // Regexp
29245ffb0c9bSToomas Soome         len = *ptr + 1;
29255ffb0c9bSToomas Soome         ptr += len;
29265ffb0c9bSToomas Soome         if (ptr >= end)
29275ffb0c9bSToomas Soome         {
29285ffb0c9bSToomas Soome             LogInfo("SetRData: Malformed NAPTR regexp");
29295ffb0c9bSToomas Soome             goto fail;
29305ffb0c9bSToomas Soome         }
29315ffb0c9bSToomas Soome 
2932*472cd20dSToomas Soome         savelen = (int)(ptr - orig);
29335ffb0c9bSToomas Soome 
29345ffb0c9bSToomas Soome         // RFC 2915 states that name compression is not allowed for this field. But RFC 3597
29355ffb0c9bSToomas Soome         // states that for NAPTR we should decompress. We make sure that we store the full
29365ffb0c9bSToomas Soome         // name rather than the compressed name
29375ffb0c9bSToomas Soome         if (msg)
29385ffb0c9bSToomas Soome         {
29395ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &name);
29405ffb0c9bSToomas Soome         }
29415ffb0c9bSToomas Soome         else
29425ffb0c9bSToomas Soome         {
2943*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&name, (domainname *)ptr, end))
2944*472cd20dSToomas Soome             {
2945*472cd20dSToomas Soome                 goto fail;
2946*472cd20dSToomas Soome             }
29475ffb0c9bSToomas Soome             ptr += DomainNameLength(&name);
29485ffb0c9bSToomas Soome         }
29495ffb0c9bSToomas Soome         if (ptr != end)
29505ffb0c9bSToomas Soome         {
29515ffb0c9bSToomas Soome             LogInfo("SetRData: Malformed NAPTR RDATA name");
29525ffb0c9bSToomas Soome             goto fail;
29535ffb0c9bSToomas Soome         }
29545ffb0c9bSToomas Soome 
2955*472cd20dSToomas Soome         rr->rdlength = savelen + DomainNameLength(&name);
29565ffb0c9bSToomas Soome         // The uncompressed size should not exceed the limits
2957*472cd20dSToomas Soome         if (rr->rdlength > MaximumRDSize)
29585ffb0c9bSToomas Soome         {
2959*472cd20dSToomas Soome             LogInfo("SetRData: Malformed NAPTR rdlength %d, rr->rdlength %d, "
2960*472cd20dSToomas Soome                     "bmaplen %d, name %##s", rdlength, rr->rdlength, name.c);
29615ffb0c9bSToomas Soome             goto fail;
29625ffb0c9bSToomas Soome         }
29635ffb0c9bSToomas Soome         mDNSPlatformMemCopy(rdb->data, orig, savelen);
29645ffb0c9bSToomas Soome         AssignDomainName((domainname *)(rdb->data + savelen), &name);
29655ffb0c9bSToomas Soome         break;
29665ffb0c9bSToomas Soome     }
29675ffb0c9bSToomas Soome     case kDNSType_OPT:  {
2968*472cd20dSToomas Soome         const mDNSu8 * const dataend = &rr->rdata->u.data[rr->rdata->MaxRDLength];
2969*472cd20dSToomas Soome         rdataOPT *opt = rr->rdata->u.opt;
2970*472cd20dSToomas Soome         rr->rdlength = 0;
2971*472cd20dSToomas Soome         while ((ptr < end) && ((dataend - ((const mDNSu8 *)opt)) >= ((mDNSs32)sizeof(*opt))))
29725ffb0c9bSToomas Soome         {
29735ffb0c9bSToomas Soome             const rdataOPT *const currentopt = opt;
29745ffb0c9bSToomas Soome             if (ptr + 4 > end) { LogInfo("SetRData: OPT RDATA ptr + 4 > end"); goto fail; }
29755ffb0c9bSToomas Soome             opt->opt    = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
29765ffb0c9bSToomas Soome             opt->optlen = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
29775ffb0c9bSToomas Soome             ptr += 4;
29785ffb0c9bSToomas Soome             if (ptr + opt->optlen > end) { LogInfo("SetRData: ptr + opt->optlen > end"); goto fail; }
29795ffb0c9bSToomas Soome             switch (opt->opt)
29805ffb0c9bSToomas Soome             {
29815ffb0c9bSToomas Soome             case kDNSOpt_LLQ:
29825ffb0c9bSToomas Soome                 if (opt->optlen == DNSOpt_LLQData_Space - 4)
29835ffb0c9bSToomas Soome                 {
29845ffb0c9bSToomas Soome                     opt->u.llq.vers  = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
29855ffb0c9bSToomas Soome                     opt->u.llq.llqOp = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
29865ffb0c9bSToomas Soome                     opt->u.llq.err   = (mDNSu16)((mDNSu16)ptr[4] <<  8 | ptr[5]);
29875ffb0c9bSToomas Soome                     mDNSPlatformMemCopy(opt->u.llq.id.b, ptr+6, 8);
29885ffb0c9bSToomas Soome                     opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[14] << 24 | (mDNSu32)ptr[15] << 16 | (mDNSu32)ptr[16] << 8 | ptr[17]);
29895ffb0c9bSToomas Soome                     if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond)
29905ffb0c9bSToomas Soome                         opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond;
29915ffb0c9bSToomas Soome                     opt++;
29925ffb0c9bSToomas Soome                 }
29935ffb0c9bSToomas Soome                 break;
29945ffb0c9bSToomas Soome             case kDNSOpt_Lease:
29955ffb0c9bSToomas Soome                 if (opt->optlen == DNSOpt_LeaseData_Space - 4)
29965ffb0c9bSToomas Soome                 {
29975ffb0c9bSToomas Soome                     opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
29985ffb0c9bSToomas Soome                     if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond)
29995ffb0c9bSToomas Soome                         opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond;
30005ffb0c9bSToomas Soome                     opt++;
30015ffb0c9bSToomas Soome                 }
30025ffb0c9bSToomas Soome                 break;
30035ffb0c9bSToomas Soome             case kDNSOpt_Owner:
30045ffb0c9bSToomas Soome                 if (ValidOwnerLength(opt->optlen))
30055ffb0c9bSToomas Soome                 {
30065ffb0c9bSToomas Soome                     opt->u.owner.vers = ptr[0];
30075ffb0c9bSToomas Soome                     opt->u.owner.seq  = ptr[1];
30085ffb0c9bSToomas Soome                     mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6);                         // 6-byte MAC address
30095ffb0c9bSToomas Soome                     mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6);                         // 6-byte MAC address
30105ffb0c9bSToomas Soome                     opt->u.owner.password = zeroEthAddr;
30115ffb0c9bSToomas Soome                     if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
30125ffb0c9bSToomas Soome                     {
30135ffb0c9bSToomas Soome                         mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6);                     // 6-byte MAC address
30145ffb0c9bSToomas Soome                         // This mDNSPlatformMemCopy is safe because the ValidOwnerLength(opt->optlen) check above
30155ffb0c9bSToomas Soome                         // ensures that opt->optlen is no more than DNSOpt_OwnerData_ID_Wake_PW6_Space - 4
30165ffb0c9bSToomas Soome                         if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
30175ffb0c9bSToomas Soome                             mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4));
30185ffb0c9bSToomas Soome                     }
30195ffb0c9bSToomas Soome                     opt++;
30205ffb0c9bSToomas Soome                 }
30215ffb0c9bSToomas Soome                 break;
30225ffb0c9bSToomas Soome             case kDNSOpt_Trace:
30235ffb0c9bSToomas Soome                 if (opt->optlen == DNSOpt_TraceData_Space - 4)
30245ffb0c9bSToomas Soome                 {
30255ffb0c9bSToomas Soome                     opt->u.tracer.platf   = ptr[0];
30265ffb0c9bSToomas Soome                     opt->u.tracer.mDNSv   = (mDNSu32) ((mDNSu32)ptr[1] << 24 | (mDNSu32)ptr[2] << 16 | (mDNSu32)ptr[3] << 8 | ptr[4]);
30275ffb0c9bSToomas Soome                     opt++;
30285ffb0c9bSToomas Soome                 }
30295ffb0c9bSToomas Soome                 else
30305ffb0c9bSToomas Soome                 {
30315ffb0c9bSToomas Soome                     opt->u.tracer.platf   = 0xFF;
30325ffb0c9bSToomas Soome                     opt->u.tracer.mDNSv   = 0xFFFFFFFF;
30335ffb0c9bSToomas Soome                     opt++;
30345ffb0c9bSToomas Soome                 }
30355ffb0c9bSToomas Soome                 break;
30365ffb0c9bSToomas Soome             }
30375ffb0c9bSToomas Soome             ptr += currentopt->optlen;
30385ffb0c9bSToomas Soome         }
3039*472cd20dSToomas Soome         rr->rdlength = (mDNSu16)((mDNSu8*)opt - rr->rdata->u.data);
30405ffb0c9bSToomas Soome         if (ptr != end) { LogInfo("SetRData: Malformed OptRdata"); goto fail; }
30415ffb0c9bSToomas Soome         break;
30425ffb0c9bSToomas Soome     }
30435ffb0c9bSToomas Soome 
30445ffb0c9bSToomas Soome     case kDNSType_NSEC: {
30455ffb0c9bSToomas Soome         domainname name;
30465ffb0c9bSToomas Soome         int len = rdlength;
30475ffb0c9bSToomas Soome         int bmaplen, dlen;
30485ffb0c9bSToomas Soome         const mDNSu8 *orig = ptr;
30495ffb0c9bSToomas Soome         const mDNSu8 *bmap;
30505ffb0c9bSToomas Soome 
30515ffb0c9bSToomas Soome         if (msg)
30525ffb0c9bSToomas Soome         {
30535ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &name);
30545ffb0c9bSToomas Soome         }
30555ffb0c9bSToomas Soome         else
30565ffb0c9bSToomas Soome         {
3057*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&name, (domainname *)ptr, end))
3058*472cd20dSToomas Soome             {
3059*472cd20dSToomas Soome                 goto fail;
3060*472cd20dSToomas Soome             }
30615ffb0c9bSToomas Soome             ptr += DomainNameLength(&name);
30625ffb0c9bSToomas Soome         }
30635ffb0c9bSToomas Soome         if (!ptr)
30645ffb0c9bSToomas Soome         {
30655ffb0c9bSToomas Soome             LogInfo("SetRData: Malformed NSEC nextname");
30665ffb0c9bSToomas Soome             goto fail;
30675ffb0c9bSToomas Soome         }
30685ffb0c9bSToomas Soome 
30695ffb0c9bSToomas Soome         dlen = DomainNameLength(&name);
30705ffb0c9bSToomas Soome 
30715ffb0c9bSToomas Soome         // Multicast NSECs use name compression for this field unlike the unicast case which
30725ffb0c9bSToomas Soome         // does not use compression. And multicast case always succeeds in compression. So,
30735ffb0c9bSToomas Soome         // the rdlength includes only the compressed space in that case. So, can't
30745ffb0c9bSToomas Soome         // use the DomainNameLength of name to reduce the length here.
30755ffb0c9bSToomas Soome         len -= (ptr - orig);
30765ffb0c9bSToomas Soome         bmaplen = len;                  // Save the length of the bitmap
30775ffb0c9bSToomas Soome         bmap = ptr;
30785ffb0c9bSToomas Soome         ptr = SanityCheckBitMap(bmap, end, len);
30795ffb0c9bSToomas Soome         if (!ptr)
30805ffb0c9bSToomas Soome             goto fail;
30815ffb0c9bSToomas Soome         if (ptr != end)
30825ffb0c9bSToomas Soome         {
30835ffb0c9bSToomas Soome             LogInfo("SetRData: Malformed NSEC length not right");
30845ffb0c9bSToomas Soome             goto fail;
30855ffb0c9bSToomas Soome         }
30865ffb0c9bSToomas Soome 
30875ffb0c9bSToomas Soome         // Initialize the right length here. When we call SetNewRData below which in turn calls
30885ffb0c9bSToomas Soome         // GetRDLength and for NSEC case, it assumes that rdlength is intitialized
3089*472cd20dSToomas Soome         rr->rdlength = DomainNameLength(&name) + bmaplen;
30905ffb0c9bSToomas Soome 
30915ffb0c9bSToomas Soome         // Do we have space after the name expansion ?
3092*472cd20dSToomas Soome         if (rr->rdlength > MaximumRDSize)
30935ffb0c9bSToomas Soome         {
3094*472cd20dSToomas Soome             LogInfo("SetRData: Malformed NSEC rdlength %d, rr->rdlength %d, "
3095*472cd20dSToomas Soome                     "bmaplen %d, name %##s", rdlength, rr->rdlength, name.c);
30965ffb0c9bSToomas Soome             goto fail;
30975ffb0c9bSToomas Soome         }
30985ffb0c9bSToomas Soome         AssignDomainName((domainname *)rdb->data, &name);
30995ffb0c9bSToomas Soome         mDNSPlatformMemCopy(rdb->data + dlen, bmap, bmaplen);
31005ffb0c9bSToomas Soome         break;
31015ffb0c9bSToomas Soome     }
31025ffb0c9bSToomas Soome     case kDNSType_TKEY:
31035ffb0c9bSToomas Soome     case kDNSType_TSIG:
31045ffb0c9bSToomas Soome     {
31055ffb0c9bSToomas Soome         domainname name;
31065ffb0c9bSToomas Soome         int dlen, rlen;
31075ffb0c9bSToomas Soome 
31085ffb0c9bSToomas Soome         // The name should not be compressed. But we take the conservative approach
31095ffb0c9bSToomas Soome         // and uncompress the name before we store it.
31105ffb0c9bSToomas Soome         if (msg)
31115ffb0c9bSToomas Soome         {
31125ffb0c9bSToomas Soome             ptr = getDomainName(msg, ptr, end, &name);
31135ffb0c9bSToomas Soome         }
31145ffb0c9bSToomas Soome         else
31155ffb0c9bSToomas Soome         {
3116*472cd20dSToomas Soome             if (!AssignDomainNameWithLimit(&name, (domainname *)ptr, end))
3117*472cd20dSToomas Soome             {
3118*472cd20dSToomas Soome                 goto fail;
3119*472cd20dSToomas Soome             }
31205ffb0c9bSToomas Soome             ptr += DomainNameLength(&name);
31215ffb0c9bSToomas Soome         }
31223b436d06SToomas Soome         if (!ptr || ptr >= end)
31235ffb0c9bSToomas Soome         {
3124*472cd20dSToomas Soome             LogInfo("SetRData: Malformed name for TSIG/TKEY type %d", rr->rrtype);
31255ffb0c9bSToomas Soome             goto fail;
31265ffb0c9bSToomas Soome         }
31275ffb0c9bSToomas Soome         dlen = DomainNameLength(&name);
3128*472cd20dSToomas Soome         rlen = (int)(end - ptr);
3129*472cd20dSToomas Soome         rr->rdlength = dlen + rlen;
3130*472cd20dSToomas Soome         if (rr->rdlength > MaximumRDSize)
3131c65ebfc7SToomas Soome         {
3132*472cd20dSToomas Soome             LogInfo("SetRData: Malformed TSIG/TKEY rdlength %d, rr->rdlength %d, "
3133*472cd20dSToomas Soome                     "bmaplen %d, name %##s", rdlength, rr->rdlength, name.c);
3134c65ebfc7SToomas Soome             goto fail;
3135c65ebfc7SToomas Soome         }
31365ffb0c9bSToomas Soome         AssignDomainName((domainname *)rdb->data, &name);
31375ffb0c9bSToomas Soome         mDNSPlatformMemCopy(rdb->data + dlen, ptr, rlen);
31385ffb0c9bSToomas Soome         break;
31395ffb0c9bSToomas Soome     }
31405ffb0c9bSToomas Soome     default:
31415ffb0c9bSToomas Soome         debugf("SetRData: Warning! Reading resource type %d (%s) as opaque data",
3142*472cd20dSToomas Soome                rr->rrtype, DNSTypeName(rr->rrtype));
31435ffb0c9bSToomas Soome         // Note: Just because we don't understand the record type, that doesn't
31445ffb0c9bSToomas Soome         // mean we fail. The DNS protocol specifies rdlength, so we can
31455ffb0c9bSToomas Soome         // safely skip over unknown records and ignore them.
31465ffb0c9bSToomas Soome         // We also grab a binary copy of the rdata anyway, since the caller
31475ffb0c9bSToomas Soome         // might know how to interpret it even if we don't.
3148*472cd20dSToomas Soome         rr->rdlength = rdlength;
31495ffb0c9bSToomas Soome         mDNSPlatformMemCopy(rdb->data, ptr, rdlength);
31505ffb0c9bSToomas Soome         break;
31515ffb0c9bSToomas Soome     }
31525ffb0c9bSToomas Soome     return mDNStrue;
31535ffb0c9bSToomas Soome fail:
31545ffb0c9bSToomas Soome     return mDNSfalse;
31555ffb0c9bSToomas Soome }
31565ffb0c9bSToomas Soome 
GetLargeResourceRecord(mDNS * const m,const DNSMessage * const msg,const mDNSu8 * ptr,const mDNSu8 * end,const mDNSInterfaceID InterfaceID,mDNSu8 RecordType,LargeCacheRecord * const largecr)31575ffb0c9bSToomas Soome mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr,
31585ffb0c9bSToomas Soome                                                 const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr)
31595ffb0c9bSToomas Soome {
31605ffb0c9bSToomas Soome     CacheRecord *const rr = &largecr->r;
31615ffb0c9bSToomas Soome     mDNSu16 pktrdlength;
3162*472cd20dSToomas Soome     mDNSu32 maxttl = (!InterfaceID) ? mDNSMaximumUnicastTTLSeconds : mDNSMaximumMulticastTTLSeconds;
31635ffb0c9bSToomas Soome 
31645ffb0c9bSToomas Soome     if (largecr == &m->rec && m->rec.r.resrec.RecordType)
3165cda73f64SToomas Soome         LogFatalError("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r));
31665ffb0c9bSToomas Soome 
31675ffb0c9bSToomas Soome     rr->next              = mDNSNULL;
31685ffb0c9bSToomas Soome     rr->resrec.name       = &largecr->namestorage;
31695ffb0c9bSToomas Soome 
31705ffb0c9bSToomas Soome     rr->NextInKAList      = mDNSNULL;
31715ffb0c9bSToomas Soome     rr->TimeRcvd          = m ? m->timenow : 0;
31725ffb0c9bSToomas Soome     rr->DelayDelivery     = 0;
31735ffb0c9bSToomas Soome     rr->NextRequiredQuery = m ? m->timenow : 0;     // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord()
3174*472cd20dSToomas Soome #if MDNSRESPONDER_SUPPORTS(APPLE, CACHE_ANALYTICS)
3175*472cd20dSToomas Soome     rr->LastCachedAnswerTime = 0;
3176*472cd20dSToomas Soome #endif
31775ffb0c9bSToomas Soome     rr->CRActiveQuestion  = mDNSNULL;
31785ffb0c9bSToomas Soome     rr->UnansweredQueries = 0;
31795ffb0c9bSToomas Soome     rr->LastUnansweredTime= 0;
31805ffb0c9bSToomas Soome     rr->NextInCFList      = mDNSNULL;
31815ffb0c9bSToomas Soome 
31825ffb0c9bSToomas Soome     rr->resrec.InterfaceID       = InterfaceID;
3183*472cd20dSToomas Soome #if MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
3184*472cd20dSToomas Soome     mdns_forget(&rr->resrec.dnsservice);
3185*472cd20dSToomas Soome #else
31865ffb0c9bSToomas Soome     rr->resrec.rDNSServer = mDNSNULL;
3187*472cd20dSToomas Soome #endif
31885ffb0c9bSToomas Soome 
31895ffb0c9bSToomas Soome     ptr = getDomainName(msg, ptr, end, &largecr->namestorage);      // Will bail out correctly if ptr is NULL
31905ffb0c9bSToomas Soome     if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); }
31915ffb0c9bSToomas Soome     rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
31925ffb0c9bSToomas Soome 
31935ffb0c9bSToomas Soome     if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
31945ffb0c9bSToomas Soome 
31955ffb0c9bSToomas Soome     rr->resrec.rrtype            = (mDNSu16) ((mDNSu16)ptr[0] <<  8 | ptr[1]);
31965ffb0c9bSToomas Soome     rr->resrec.rrclass           = (mDNSu16)(((mDNSu16)ptr[2] <<  8 | ptr[3]) & kDNSClass_Mask);
31975ffb0c9bSToomas Soome     rr->resrec.rroriginalttl     = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
3198*472cd20dSToomas Soome     if (rr->resrec.rroriginalttl > maxttl && (mDNSs32)rr->resrec.rroriginalttl != -1)
3199*472cd20dSToomas Soome         rr->resrec.rroriginalttl = maxttl;
32005ffb0c9bSToomas Soome     // Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
32015ffb0c9bSToomas Soome     // us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
32025ffb0c9bSToomas Soome     pktrdlength           = (mDNSu16)((mDNSu16)ptr[8] <<  8 | ptr[9]);
32035ffb0c9bSToomas Soome 
32045ffb0c9bSToomas Soome     // If mDNS record has cache-flush bit set, we mark it unique
32053b436d06SToomas Soome     // For uDNS records, all are implicitly deemed unique (a single DNS server is always authoritative for the entire RRSet)
32063b436d06SToomas Soome     if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || !InterfaceID)
32075ffb0c9bSToomas Soome         RecordType |= kDNSRecordTypePacketUniqueMask;
32085ffb0c9bSToomas Soome     ptr += 10;
32095ffb0c9bSToomas Soome     if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
32105ffb0c9bSToomas Soome     end = ptr + pktrdlength;        // Adjust end to indicate the end of the rdata for this resource record
32115ffb0c9bSToomas Soome 
32125ffb0c9bSToomas Soome     rr->resrec.rdata = (RData*)&rr->smallrdatastorage;
32135ffb0c9bSToomas Soome     rr->resrec.rdata->MaxRDLength = MaximumRDSize;
32145ffb0c9bSToomas Soome 
32155ffb0c9bSToomas Soome     if (pktrdlength > MaximumRDSize)
32165ffb0c9bSToomas Soome     {
32175ffb0c9bSToomas Soome         LogInfo("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
32185ffb0c9bSToomas Soome                 DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
32195ffb0c9bSToomas Soome         goto fail;
32205ffb0c9bSToomas Soome     }
32215ffb0c9bSToomas Soome 
32225ffb0c9bSToomas Soome     if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c);
32235ffb0c9bSToomas Soome 
32245ffb0c9bSToomas Soome     // IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
32255ffb0c9bSToomas Soome     // cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
32265ffb0c9bSToomas Soome     // bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
32275ffb0c9bSToomas Soome     // Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
32285ffb0c9bSToomas Soome     // two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
32295ffb0c9bSToomas Soome     if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0)   // Used in update packets to mean "Delete An RRset" (RFC 2136)
32305ffb0c9bSToomas Soome         rr->resrec.rdlength = 0;
3231*472cd20dSToomas Soome     else if (!SetRData(msg, ptr, end, &rr->resrec, pktrdlength))
3232*472cd20dSToomas Soome     {
3233*472cd20dSToomas Soome         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
3234*472cd20dSToomas Soome             "GetLargeResourceRecord: SetRData failed for " PRI_DM_NAME " (" PUB_S ")",
3235*472cd20dSToomas Soome             DM_NAME_PARAM(rr->resrec.name), DNSTypeName(rr->resrec.rrtype));
32365ffb0c9bSToomas Soome         goto fail;
3237*472cd20dSToomas Soome     }
32385ffb0c9bSToomas Soome 
32395ffb0c9bSToomas Soome     SetNewRData(&rr->resrec, mDNSNULL, 0);      // Sets rdlength, rdestimate, rdatahash for us
32405ffb0c9bSToomas Soome 
32415ffb0c9bSToomas Soome     // Success! Now fill in RecordType to show this record contains valid data
32425ffb0c9bSToomas Soome     rr->resrec.RecordType = RecordType;
32435ffb0c9bSToomas Soome     return(end);
32445ffb0c9bSToomas Soome 
32455ffb0c9bSToomas Soome fail:
32465ffb0c9bSToomas Soome     // If we were unable to parse the rdata in this record, we indicate that by
32475ffb0c9bSToomas Soome     // returing a 'kDNSRecordTypePacketNegative' record with rdlength set to zero
32485ffb0c9bSToomas Soome     rr->resrec.RecordType = kDNSRecordTypePacketNegative;
32495ffb0c9bSToomas Soome     rr->resrec.rdlength   = 0;
32505ffb0c9bSToomas Soome     rr->resrec.rdestimate = 0;
32515ffb0c9bSToomas Soome     rr->resrec.rdatahash  = 0;
32525ffb0c9bSToomas Soome     return(end);
32535ffb0c9bSToomas Soome }
32544b22b933Srs 
skipQuestion(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end)32554b22b933Srs mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
32565ffb0c9bSToomas Soome {
32575ffb0c9bSToomas Soome     ptr = skipDomainName(msg, ptr, end);
32585ffb0c9bSToomas Soome     if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); }
32595ffb0c9bSToomas Soome     if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
32605ffb0c9bSToomas Soome     return(ptr+4);
32615ffb0c9bSToomas Soome }
32624b22b933Srs 
getQuestion(const DNSMessage * msg,const mDNSu8 * ptr,const mDNSu8 * end,const mDNSInterfaceID InterfaceID,DNSQuestion * question)32634b22b933Srs mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
32645ffb0c9bSToomas Soome                                      DNSQuestion *question)
32655ffb0c9bSToomas Soome {
32665ffb0c9bSToomas Soome     mDNSPlatformMemZero(question, sizeof(*question));
32675ffb0c9bSToomas Soome     question->InterfaceID = InterfaceID;
32685ffb0c9bSToomas Soome     if (!InterfaceID) question->TargetQID = onesID; // In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast
32695ffb0c9bSToomas Soome     ptr = getDomainName(msg, ptr, end, &question->qname);
32705ffb0c9bSToomas Soome     if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
32715ffb0c9bSToomas Soome     if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
32725ffb0c9bSToomas Soome 
32735ffb0c9bSToomas Soome     question->qnamehash = DomainNameHashValue(&question->qname);
32745ffb0c9bSToomas Soome     question->qtype  = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);            // Get type
32755ffb0c9bSToomas Soome     question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);            // and class
32765ffb0c9bSToomas Soome     return(ptr+4);
32775ffb0c9bSToomas Soome }
32784b22b933Srs 
LocateAnswers(const DNSMessage * const msg,const mDNSu8 * const end)32794b22b933Srs mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
32805ffb0c9bSToomas Soome {
32815ffb0c9bSToomas Soome     int i;
32825ffb0c9bSToomas Soome     const mDNSu8 *ptr = msg->data;
32835ffb0c9bSToomas Soome     for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
32845ffb0c9bSToomas Soome     return(ptr);
32855ffb0c9bSToomas Soome }
32864b22b933Srs 
LocateAuthorities(const DNSMessage * const msg,const mDNSu8 * const end)32874b22b933Srs mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
32885ffb0c9bSToomas Soome {
32895ffb0c9bSToomas Soome     int i;
32905ffb0c9bSToomas Soome     const mDNSu8 *ptr = LocateAnswers(msg, end);
32915ffb0c9bSToomas Soome     for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
32925ffb0c9bSToomas Soome     return(ptr);
32935ffb0c9bSToomas Soome }
32944b22b933Srs 
LocateAdditionals(const DNSMessage * const msg,const mDNSu8 * const end)32954b22b933Srs mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end)
32965ffb0c9bSToomas Soome {
32975ffb0c9bSToomas Soome     int i;
32985ffb0c9bSToomas Soome     const mDNSu8 *ptr = LocateAuthorities(msg, end);
32995ffb0c9bSToomas Soome     for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end);
33005ffb0c9bSToomas Soome     return (ptr);
33015ffb0c9bSToomas Soome }
33025ffb0c9bSToomas Soome 
LocateOptRR(const DNSMessage * const msg,const mDNSu8 * const end,int minsize)33035ffb0c9bSToomas Soome mDNSexport const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize)
33045ffb0c9bSToomas Soome {
33055ffb0c9bSToomas Soome     int i;
33065ffb0c9bSToomas Soome     const mDNSu8 *ptr = LocateAdditionals(msg, end);
33075ffb0c9bSToomas Soome 
33085ffb0c9bSToomas Soome     // Locate the OPT record.
33095ffb0c9bSToomas Soome     // According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
33105ffb0c9bSToomas Soome     // This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
33115ffb0c9bSToomas Soome     // but not necessarily the *last* entry in the Additional Section.
33125ffb0c9bSToomas Soome     for (i = 0; ptr && i < msg->h.numAdditionals; i++)
33135ffb0c9bSToomas Soome     {
33145ffb0c9bSToomas Soome         if (ptr + DNSOpt_Header_Space + minsize <= end &&   // Make sure we have 11+minsize bytes of data
33155ffb0c9bSToomas Soome             ptr[0] == 0                                &&   // Name must be root label
33165ffb0c9bSToomas Soome             ptr[1] == (kDNSType_OPT >> 8  )            &&   // rrtype OPT
33175ffb0c9bSToomas Soome             ptr[2] == (kDNSType_OPT & 0xFF)            &&
33185ffb0c9bSToomas Soome             ((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)minsize)
33195ffb0c9bSToomas Soome             return(ptr);
33205ffb0c9bSToomas Soome         else
33215ffb0c9bSToomas Soome             ptr = skipResourceRecord(msg, ptr, end);
33225ffb0c9bSToomas Soome     }
33235ffb0c9bSToomas Soome     return(mDNSNULL);
33245ffb0c9bSToomas Soome }
33255ffb0c9bSToomas Soome 
33265ffb0c9bSToomas Soome // On success, GetLLQOptData returns pointer to storage within shared "m->rec";
33275ffb0c9bSToomas Soome // it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use
33285ffb0c9bSToomas Soome // Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
33295ffb0c9bSToomas 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)33305ffb0c9bSToomas Soome mDNSexport const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end)
33315ffb0c9bSToomas Soome {
33325ffb0c9bSToomas Soome     const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LLQData_Space);
33335ffb0c9bSToomas Soome     if (ptr)
33345ffb0c9bSToomas Soome     {
33355ffb0c9bSToomas Soome         ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
33365ffb0c9bSToomas Soome         if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) return(&m->rec.r.resrec.rdata->u.opt[0]);
33375ffb0c9bSToomas Soome     }
33385ffb0c9bSToomas Soome     return(mDNSNULL);
33395ffb0c9bSToomas Soome }
33405ffb0c9bSToomas Soome 
33415ffb0c9bSToomas Soome // Get the lease life of records in a dynamic update
GetPktLease(mDNS * const m,const DNSMessage * const msg,const mDNSu8 * const end,mDNSu32 * const lease)3342c65ebfc7SToomas Soome mDNSexport mDNSBool GetPktLease(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, mDNSu32 *const lease)
33435ffb0c9bSToomas Soome {
33445ffb0c9bSToomas Soome     const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space);
3345c65ebfc7SToomas Soome     if (ptr)
3346c65ebfc7SToomas Soome     {
3347c65ebfc7SToomas Soome         ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
3348c65ebfc7SToomas Soome         if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT)
3349c65ebfc7SToomas Soome         {
3350c65ebfc7SToomas Soome             const rdataOPT *o;
3351c65ebfc7SToomas Soome             const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength];
3352c65ebfc7SToomas Soome             for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++)
3353c65ebfc7SToomas Soome                 if (o->opt == kDNSOpt_Lease)
3354c65ebfc7SToomas Soome                 {
3355c65ebfc7SToomas Soome                     *lease = o->u.updatelease;
3356c65ebfc7SToomas Soome                     m->rec.r.resrec.RecordType = 0;     // Clear RecordType to show we're not still using it
3357c65ebfc7SToomas Soome                     return mDNStrue;
3358c65ebfc7SToomas Soome                 }
3359c65ebfc7SToomas Soome         }
3360c65ebfc7SToomas Soome         m->rec.r.resrec.RecordType = 0;     // Clear RecordType to show we're not still using it
3361c65ebfc7SToomas Soome     }
3362c65ebfc7SToomas Soome     return mDNSfalse;
33635ffb0c9bSToomas Soome }
33645ffb0c9bSToomas Soome 
33655ffb0c9bSToomas Soome #define DNS_OP_Name(X) (                              \
33665ffb0c9bSToomas Soome         (X) == kDNSFlag0_OP_StdQuery ? ""         :       \
33675ffb0c9bSToomas Soome         (X) == kDNSFlag0_OP_Iquery   ? "Iquery "  :       \
33685ffb0c9bSToomas Soome         (X) == kDNSFlag0_OP_Status   ? "Status "  :       \
33695ffb0c9bSToomas Soome         (X) == kDNSFlag0_OP_Unused3  ? "Unused3 " :       \
33705ffb0c9bSToomas Soome         (X) == kDNSFlag0_OP_Notify   ? "Notify "  :       \
3371c65ebfc7SToomas Soome         (X) == kDNSFlag0_OP_Update   ? "Update "  :       \
3372*472cd20dSToomas Soome         (X) == kDNSFlag0_OP_DSO      ? "DSO "  : "?? " )
33735ffb0c9bSToomas Soome 
33745ffb0c9bSToomas Soome #define DNS_RC_Name(X) (                             \
3375*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_NoErr     ? "NoErr"    :      \
3376*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_FormErr   ? "FormErr"  :      \
3377*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_ServFail  ? "ServFail" :      \
3378*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_NXDomain  ? "NXDomain" :      \
3379*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_NotImpl   ? "NotImpl"  :      \
3380*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_Refused   ? "Refused"  :      \
3381*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_YXDomain  ? "YXDomain" :      \
3382*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_YXRRSet   ? "YXRRSet"  :      \
3383*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_NXRRSet   ? "NXRRSet"  :      \
3384*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_NotAuth   ? "NotAuth"  :      \
3385*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_NotZone   ? "NotZone"  :      \
3386*472cd20dSToomas Soome         (X) == kDNSFlag1_RC_DSOTypeNI ? "DSOTypeNI" : "??" )
3387*472cd20dSToomas Soome 
mDNS_snprintf_add(char ** ptr,const char * lim,const char * fmt,...)3388*472cd20dSToomas Soome mDNSexport void mDNS_snprintf_add(char **ptr, const char *lim, const char *fmt, ...)
33895ffb0c9bSToomas Soome {
33903b436d06SToomas Soome     va_list args;
33913b436d06SToomas Soome     mDNSu32 buflen, n;
33923b436d06SToomas Soome     char *const dst = *ptr;
33933b436d06SToomas Soome 
33943b436d06SToomas Soome     buflen = (mDNSu32)(lim - dst);
33953b436d06SToomas Soome     if (buflen > 0)
33963b436d06SToomas Soome     {
33973b436d06SToomas Soome         va_start(args, fmt);
33983b436d06SToomas Soome         n = mDNS_vsnprintf(dst, buflen, fmt, args);
33993b436d06SToomas Soome         va_end(args);
34003b436d06SToomas Soome         *ptr = dst + n;
34013b436d06SToomas Soome     }
34023b436d06SToomas Soome }
34033b436d06SToomas Soome 
34043b436d06SToomas Soome #define DNSTypeString(X) (((X) == kDNSType_A) ? "A" : DNSTypeName(X))
34053b436d06SToomas Soome 
34063b436d06SToomas Soome #define ReadField16(PTR) ((mDNSu16)((((mDNSu16)((mDNSu8 *)(PTR))[0]) << 8) | ((mDNSu16)((mDNSu8 *)(PTR))[1])))
34073b436d06SToomas Soome #define ReadField32(PTR) \
34083b436d06SToomas Soome     ((mDNSu32)( \
34093b436d06SToomas Soome         (((mDNSu32)((mDNSu8 *)(PTR))[0]) << 24) | \
34103b436d06SToomas Soome         (((mDNSu32)((mDNSu8 *)(PTR))[1]) << 16) | \
34113b436d06SToomas Soome         (((mDNSu32)((mDNSu8 *)(PTR))[2]) <<  8) | \
34123b436d06SToomas Soome          ((mDNSu32)((mDNSu8 *)(PTR))[3])))
34133b436d06SToomas Soome 
DNSMessageDumpToLog(const DNSMessage * const msg,const mDNSu8 * const end)3414*472cd20dSToomas Soome mDNSlocal void DNSMessageDumpToLog(const DNSMessage *const msg, const mDNSu8 *const end)
34153b436d06SToomas Soome {
3416*472cd20dSToomas Soome     domainname *name = mDNSNULL;
3417*472cd20dSToomas Soome     const mDNSu8 *ptr = msg->data;
34183b436d06SToomas Soome     domainname nameStorage[2];
34193b436d06SToomas Soome 
3420*472cd20dSToomas Soome     char questions[512];
3421*472cd20dSToomas Soome     questions[0] = '\0';
3422*472cd20dSToomas Soome     char *questions_dst = questions;
3423*472cd20dSToomas Soome     const char *const questions_lim = &questions[512];
3424*472cd20dSToomas Soome     for (mDNSu32 i = 0; i < msg->h.numQuestions; i++)
34253b436d06SToomas Soome     {
34263b436d06SToomas Soome         mDNSu16 qtype, qclass;
34273b436d06SToomas Soome 
34283b436d06SToomas Soome         name = &nameStorage[0];
34293b436d06SToomas Soome         ptr = getDomainName(msg, ptr, end, name);
34303b436d06SToomas Soome         if (!ptr) goto exit;
34313b436d06SToomas Soome 
34323b436d06SToomas Soome         if ((end - ptr) < 4) goto exit;
34333b436d06SToomas Soome         qtype  = ReadField16(&ptr[0]);
34343b436d06SToomas Soome         qclass = ReadField16(&ptr[2]);
34353b436d06SToomas Soome         ptr += 4;
34363b436d06SToomas Soome 
3437*472cd20dSToomas Soome         mDNS_snprintf_add(&questions_dst, questions_lim, " %##s %s", name->c, DNSTypeString(qtype));
3438*472cd20dSToomas Soome         if (qclass != kDNSClass_IN) mDNS_snprintf_add(&questions_dst, questions_lim, "/%u", qclass);
3439*472cd20dSToomas Soome         mDNS_snprintf_add(&questions_dst, questions_lim, "?");
34403b436d06SToomas Soome     }
34413b436d06SToomas Soome 
3442*472cd20dSToomas Soome     char rrs[512];
3443*472cd20dSToomas Soome     rrs[0] = '\0';
3444*472cd20dSToomas Soome     char *rrs_dst = rrs;
3445*472cd20dSToomas Soome     const char *const rrs_lim = &rrs[512];
3446*472cd20dSToomas Soome     const mDNSu32 rrcount = msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals;
3447*472cd20dSToomas Soome     for (mDNSu32 i = 0; i < rrcount; i++)
34485ffb0c9bSToomas Soome     {
34493b436d06SToomas Soome         mDNSu16 rrtype, rrclass, rdlength;
34503b436d06SToomas Soome         mDNSu32 ttl;
34513b436d06SToomas Soome         int handled;
34523b436d06SToomas Soome         const mDNSu8 *rdata;
34533b436d06SToomas Soome         const domainname *const previousName = name;
34543b436d06SToomas Soome 
34553b436d06SToomas Soome         name = &nameStorage[(name == &nameStorage[0]) ? 1 : 0];
34563b436d06SToomas Soome         ptr = getDomainName(msg, ptr, end, name);
34573b436d06SToomas Soome         if (!ptr) goto exit;
34583b436d06SToomas Soome 
34593b436d06SToomas Soome         if ((end - ptr) < 10) goto exit;
34603b436d06SToomas Soome         rrtype   = ReadField16(&ptr[0]);
34613b436d06SToomas Soome         rrclass  = ReadField16(&ptr[2]);
34623b436d06SToomas Soome         ttl      = ReadField32(&ptr[4]);
34633b436d06SToomas Soome         rdlength = ReadField16(&ptr[8]);
34643b436d06SToomas Soome         ptr += 10;
34653b436d06SToomas Soome 
34663b436d06SToomas Soome         if ((end - ptr) < rdlength) goto exit;
34673b436d06SToomas Soome         rdata = ptr;
34683b436d06SToomas Soome 
3469*472cd20dSToomas Soome         if (i > 0) mDNS_snprintf_add(&rrs_dst, rrs_lim, ",");
3470*472cd20dSToomas Soome         if (!previousName || !SameDomainName(name, previousName)) mDNS_snprintf_add(&rrs_dst, rrs_lim, " %##s", name);
34713b436d06SToomas Soome 
3472*472cd20dSToomas Soome         mDNS_snprintf_add(&rrs_dst, rrs_lim, " %s", DNSTypeString(rrtype));
3473*472cd20dSToomas Soome         if (rrclass != kDNSClass_IN) mDNS_snprintf_add(&rrs_dst, rrs_lim, "/%u", rrclass);
3474*472cd20dSToomas Soome         mDNS_snprintf_add(&rrs_dst, rrs_lim, " ");
34753b436d06SToomas Soome 
34763b436d06SToomas Soome         handled = mDNSfalse;
34773b436d06SToomas Soome         switch (rrtype)
34783b436d06SToomas Soome         {
3479*472cd20dSToomas Soome             case kDNSType_A:
3480*472cd20dSToomas Soome                 if (rdlength == 4)
3481*472cd20dSToomas Soome                 {
3482*472cd20dSToomas Soome                     mDNS_snprintf_add(&rrs_dst, rrs_lim, "%.4a", rdata);
3483*472cd20dSToomas Soome                     handled = mDNStrue;
3484*472cd20dSToomas Soome                 }
3485*472cd20dSToomas Soome                 break;
34863b436d06SToomas Soome 
3487*472cd20dSToomas Soome             case kDNSType_AAAA:
3488*472cd20dSToomas Soome                 if (rdlength == 16)
3489*472cd20dSToomas Soome                 {
3490*472cd20dSToomas Soome                     mDNS_snprintf_add(&rrs_dst, rrs_lim, "%.16a", rdata);
3491*472cd20dSToomas Soome                     handled = mDNStrue;
3492*472cd20dSToomas Soome                 }
3493*472cd20dSToomas Soome                 break;
34943b436d06SToomas Soome 
3495*472cd20dSToomas Soome             case kDNSType_CNAME:
3496*472cd20dSToomas Soome                 ptr = getDomainName(msg, rdata, end, name);
3497*472cd20dSToomas Soome                 if (!ptr) goto exit;
34983b436d06SToomas Soome 
3499*472cd20dSToomas Soome                 mDNS_snprintf_add(&rrs_dst, rrs_lim, "%##s", name);
3500*472cd20dSToomas Soome                 handled = mDNStrue;
3501*472cd20dSToomas Soome                 break;
35023b436d06SToomas Soome 
3503*472cd20dSToomas Soome             case kDNSType_SOA:
3504*472cd20dSToomas Soome             {
3505*472cd20dSToomas Soome                 mDNSu32 serial, refresh, retry, expire, minimum;
3506*472cd20dSToomas Soome                 domainname *const mname = &nameStorage[0];
3507*472cd20dSToomas Soome                 domainname *const rname = &nameStorage[1];
3508*472cd20dSToomas Soome                 name = mDNSNULL;
35093b436d06SToomas Soome 
3510*472cd20dSToomas Soome                 ptr = getDomainName(msg, rdata, end, mname);
3511*472cd20dSToomas Soome                 if (!ptr) goto exit;
35123b436d06SToomas Soome 
3513*472cd20dSToomas Soome                 ptr = getDomainName(msg, ptr, end, rname);
3514*472cd20dSToomas Soome                 if (!ptr) goto exit;
35153b436d06SToomas Soome 
3516*472cd20dSToomas Soome                 if ((end - ptr) < 20) goto exit;
3517*472cd20dSToomas Soome                 serial  = ReadField32(&ptr[0]);
3518*472cd20dSToomas Soome                 refresh = ReadField32(&ptr[4]);
3519*472cd20dSToomas Soome                 retry   = ReadField32(&ptr[8]);
3520*472cd20dSToomas Soome                 expire  = ReadField32(&ptr[12]);
3521*472cd20dSToomas Soome                 minimum = ReadField32(&ptr[16]);
35223b436d06SToomas Soome 
3523*472cd20dSToomas Soome                 mDNS_snprintf_add(&rrs_dst, rrs_lim, "%##s %##s %lu %lu %lu %lu %lu", mname, rname, (unsigned long)serial,
3524*472cd20dSToomas Soome                                   (unsigned long)refresh, (unsigned long)retry, (unsigned long)expire, (unsigned long)minimum);
35253b436d06SToomas Soome 
3526*472cd20dSToomas Soome                 handled = mDNStrue;
3527*472cd20dSToomas Soome                 break;
3528*472cd20dSToomas Soome             }
35293b436d06SToomas Soome 
3530*472cd20dSToomas Soome             default:
3531*472cd20dSToomas Soome                 break;
35323b436d06SToomas Soome         }
3533*472cd20dSToomas Soome         if (!handled) mDNS_snprintf_add(&rrs_dst, rrs_lim, "RDATA[%u]: %.*H", rdlength, rdlength, rdata);
3534*472cd20dSToomas Soome         mDNS_snprintf_add(&rrs_dst, rrs_lim, " (%lu)", (unsigned long)ttl);
35353b436d06SToomas Soome         ptr = rdata + rdlength;
35365ffb0c9bSToomas Soome     }
35373b436d06SToomas Soome 
3538*472cd20dSToomas Soome     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO,
3539*472cd20dSToomas Soome         "[Q%u] DNS " PUB_S PUB_S " (%lu) (flags %02X%02X) RCODE: " PUB_S " (%d)" PUB_S PUB_S PUB_S PUB_S PUB_S PUB_S ":"
3540*472cd20dSToomas Soome         PRI_S " %u/%u/%u " PRI_S,
3541*472cd20dSToomas Soome         mDNSVal16(msg->h.id),
3542*472cd20dSToomas Soome         DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask),
3543*472cd20dSToomas Soome         (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "Response" : "Query",
3544*472cd20dSToomas Soome         (unsigned long)(end - (const mDNSu8 *)msg),
3545*472cd20dSToomas Soome         msg->h.flags.b[0], msg->h.flags.b[1],
3546*472cd20dSToomas Soome         DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask),
3547*472cd20dSToomas Soome         msg->h.flags.b[1] & kDNSFlag1_RC_Mask,
3548*472cd20dSToomas Soome         (msg->h.flags.b[0] & kDNSFlag0_AA) ? " AA" : "",
3549*472cd20dSToomas Soome         (msg->h.flags.b[0] & kDNSFlag0_TC) ? " TC" : "",
3550*472cd20dSToomas Soome         (msg->h.flags.b[0] & kDNSFlag0_RD) ? " RD" : "",
3551*472cd20dSToomas Soome         (msg->h.flags.b[1] & kDNSFlag1_RA) ? " RA" : "",
3552*472cd20dSToomas Soome         (msg->h.flags.b[1] & kDNSFlag1_AD) ? " AD" : "",
3553*472cd20dSToomas Soome         (msg->h.flags.b[1] & kDNSFlag1_CD) ? " CD" : "",
3554*472cd20dSToomas Soome         questions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, rrs);
3555*472cd20dSToomas Soome 
35563b436d06SToomas Soome exit:
35573b436d06SToomas Soome     return;
35583b436d06SToomas Soome }
35593b436d06SToomas Soome 
35603b436d06SToomas Soome // Note: DumpPacket expects the packet header fields in host byte order, not network byte order
DumpPacket(mStatus status,mDNSBool sent,const char * transport,const mDNSAddr * srcaddr,mDNSIPPort srcport,const mDNSAddr * dstaddr,mDNSIPPort dstport,const DNSMessage * const msg,const mDNSu8 * const end,mDNSInterfaceID interfaceID)3561*472cd20dSToomas Soome mDNSexport void DumpPacket(mStatus status, mDNSBool sent, const char *transport,
3562*472cd20dSToomas Soome     const mDNSAddr *srcaddr, mDNSIPPort srcport,const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg,
3563*472cd20dSToomas Soome     const mDNSu8 *const end, mDNSInterfaceID interfaceID)
35643b436d06SToomas Soome {
3565*472cd20dSToomas Soome     const mDNSAddr zeroIPv4Addr = { mDNSAddrType_IPv4, {{{ 0 }}} };
3566*472cd20dSToomas Soome     char action[32];
3567*472cd20dSToomas Soome     const char* interfaceName = "interface";
35683b436d06SToomas Soome 
3569*472cd20dSToomas Soome     if (!status) mDNS_snprintf(action, sizeof(action), sent ? "Sent" : "Received");
3570*472cd20dSToomas Soome     else         mDNS_snprintf(action, sizeof(action), "ERROR %d %sing", status, sent ? "Send" : "Receiv");
35713b436d06SToomas Soome 
3572*472cd20dSToomas Soome #if MDNSRESPONDER_SUPPORTS(APPLE, OS_LOG)
3573*472cd20dSToomas Soome     interfaceName = InterfaceNameForID(&mDNSStorage, interfaceID);
3574*472cd20dSToomas Soome #endif
35753b436d06SToomas Soome 
3576*472cd20dSToomas Soome     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO,
3577*472cd20dSToomas Soome         "[Q%u] " PUB_S " " PUB_S " DNS Message %lu bytes from " PRI_IP_ADDR ":%d to " PRI_IP_ADDR ":%d via " PUB_S " (%p)",
3578*472cd20dSToomas Soome         mDNSVal16(msg->h.id), action, transport, (unsigned long)(end - (const mDNSu8 *)msg),
3579*472cd20dSToomas Soome         srcaddr ? srcaddr : &zeroIPv4Addr, mDNSVal16(srcport), dstaddr ? dstaddr : &zeroIPv4Addr, mDNSVal16(dstport),
3580*472cd20dSToomas Soome         interfaceName, interfaceID);
3581*472cd20dSToomas Soome     DNSMessageDumpToLog(msg, end);
35825ffb0c9bSToomas Soome }
35834b22b933Srs 
35844b22b933Srs // ***************************************************************************
35854b22b933Srs #if COMPILER_LIKES_PRAGMA_MARK
35864b22b933Srs #pragma mark -
35874b22b933Srs #pragma mark - Packet Sending Functions
35884b22b933Srs #endif
35894b22b933Srs 
35905ffb0c9bSToomas Soome // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
3591*472cd20dSToomas Soome struct TCPSocket_struct { mDNSIPPort port; TCPSocketFlags flags; /* ... */ };
3592cda73f64SToomas Soome // Stub definition of UDPSocket_struct so we can access port field. (Rest of UDPSocket_struct is platform-dependent.)
3593cda73f64SToomas Soome struct UDPSocket_struct { mDNSIPPort     port;  /* ... */ };
35945ffb0c9bSToomas Soome 
35955ffb0c9bSToomas Soome // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
35965ffb0c9bSToomas 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,TCPSocket * tcpSrc,UDPSocket * udpSrc,const mDNSAddr * dst,mDNSIPPort dstport,DomainAuthInfo * authInfo,mDNSBool useBackgroundTrafficClass)35975ffb0c9bSToomas Soome mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
3598*472cd20dSToomas Soome                                       mDNSInterfaceID InterfaceID, TCPSocket *tcpSrc, UDPSocket *udpSrc, const mDNSAddr *dst,
3599*472cd20dSToomas Soome                                       mDNSIPPort dstport, DomainAuthInfo *authInfo, mDNSBool useBackgroundTrafficClass)
36005ffb0c9bSToomas Soome {
36015ffb0c9bSToomas Soome     mStatus status = mStatus_NoError;
36025ffb0c9bSToomas Soome     const mDNSu16 numAdditionals = msg->h.numAdditionals;
36035ffb0c9bSToomas Soome 
36045ffb0c9bSToomas Soome #if APPLE_OSX_mDNSResponder
36055ffb0c9bSToomas Soome     // maintain outbound packet statistics
36065ffb0c9bSToomas Soome     if (mDNSOpaque16IsZero(msg->h.id))
36075ffb0c9bSToomas Soome         m->MulticastPacketsSent++;
36085ffb0c9bSToomas Soome     else
36095ffb0c9bSToomas Soome         m->UnicastPacketsSent++;
36105ffb0c9bSToomas Soome #endif // APPLE_OSX_mDNSResponder
36115ffb0c9bSToomas Soome 
36125ffb0c9bSToomas 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
36135ffb0c9bSToomas Soome     if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
36145ffb0c9bSToomas Soome     {
36155ffb0c9bSToomas Soome         LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data);
36165ffb0c9bSToomas Soome         return mStatus_BadParamErr;
36175ffb0c9bSToomas Soome     }
36185ffb0c9bSToomas Soome 
36195ffb0c9bSToomas Soome     // Put all the integer values in IETF byte-order (MSB first, LSB second)
36205ffb0c9bSToomas Soome     SwapDNSHeaderBytes(msg);
36215ffb0c9bSToomas Soome 
36225ffb0c9bSToomas Soome     if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0);    // DNSDigest_SignMessage operates on message in network byte order
36235ffb0c9bSToomas Soome     if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; }
36245ffb0c9bSToomas Soome     else
36255ffb0c9bSToomas Soome     {
36265ffb0c9bSToomas Soome         // Send the packet on the wire
3627*472cd20dSToomas Soome         if (!tcpSrc)
3628*472cd20dSToomas Soome             status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, udpSrc, dst, dstport, useBackgroundTrafficClass);
36295ffb0c9bSToomas Soome         else
36305ffb0c9bSToomas Soome         {
36315ffb0c9bSToomas Soome             mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
36325ffb0c9bSToomas Soome             mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) };
36335ffb0c9bSToomas Soome             char *buf;
36345ffb0c9bSToomas Soome             long nsent;
36355ffb0c9bSToomas Soome 
36365ffb0c9bSToomas Soome             // Try to send them in one packet if we can allocate enough memory
3637*472cd20dSToomas Soome             buf = (char *) mDNSPlatformMemAllocate(msglen + 2);
36385ffb0c9bSToomas Soome             if (buf)
36395ffb0c9bSToomas Soome             {
36405ffb0c9bSToomas Soome                 buf[0] = lenbuf[0];
36415ffb0c9bSToomas Soome                 buf[1] = lenbuf[1];
36425ffb0c9bSToomas Soome                 mDNSPlatformMemCopy(buf+2, msg, msglen);
3643*472cd20dSToomas Soome                 nsent = mDNSPlatformWriteTCP(tcpSrc, buf, msglen+2);
36445ffb0c9bSToomas Soome                 if (nsent != (msglen + 2))
36455ffb0c9bSToomas Soome                 {
36465ffb0c9bSToomas Soome                     LogMsg("mDNSSendDNSMessage: write message failed %d/%d", nsent, msglen);
36475ffb0c9bSToomas Soome                     status = mStatus_ConnFailed;
36485ffb0c9bSToomas Soome                 }
36495ffb0c9bSToomas Soome                 mDNSPlatformMemFree(buf);
36505ffb0c9bSToomas Soome             }
36515ffb0c9bSToomas Soome             else
36525ffb0c9bSToomas Soome             {
3653*472cd20dSToomas Soome                 nsent = mDNSPlatformWriteTCP(tcpSrc, (char*)lenbuf, 2);
36545ffb0c9bSToomas Soome                 if (nsent != 2)
36555ffb0c9bSToomas Soome                 {
36565ffb0c9bSToomas Soome                     LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2);
36575ffb0c9bSToomas Soome                     status = mStatus_ConnFailed;
36585ffb0c9bSToomas Soome                 }
36595ffb0c9bSToomas Soome                 else
36605ffb0c9bSToomas Soome                 {
3661*472cd20dSToomas Soome                     nsent = mDNSPlatformWriteTCP(tcpSrc, (char *)msg, msglen);
36625ffb0c9bSToomas Soome                     if (nsent != msglen)
36635ffb0c9bSToomas Soome                     {
36645ffb0c9bSToomas Soome                         LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen);
36655ffb0c9bSToomas Soome                         status = mStatus_ConnFailed;
36665ffb0c9bSToomas Soome                     }
36675ffb0c9bSToomas Soome                 }
36685ffb0c9bSToomas Soome             }
36695ffb0c9bSToomas Soome         }
36705ffb0c9bSToomas Soome     }
36715ffb0c9bSToomas Soome 
36725ffb0c9bSToomas Soome     // Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage)
36735ffb0c9bSToomas Soome     SwapDNSHeaderBytes(msg);
36745ffb0c9bSToomas Soome 
36755ffb0c9bSToomas Soome     // Dump the packet with the HINFO and TSIG
3676c65ebfc7SToomas Soome     if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id))
3677*472cd20dSToomas Soome     {
3678*472cd20dSToomas Soome         char *transport = "UDP";
3679*472cd20dSToomas Soome         mDNSIPPort portNumber = udpSrc ? udpSrc->port : MulticastDNSPort;
3680*472cd20dSToomas Soome         if (tcpSrc)
3681*472cd20dSToomas Soome         {
3682*472cd20dSToomas Soome             if (tcpSrc->flags)
3683*472cd20dSToomas Soome                 transport = "TLS";
3684*472cd20dSToomas Soome             else
3685*472cd20dSToomas Soome                 transport = "TCP";
3686*472cd20dSToomas Soome             portNumber = tcpSrc->port;
3687*472cd20dSToomas Soome         }
3688*472cd20dSToomas Soome         DumpPacket(status, mDNStrue, transport, mDNSNULL, portNumber, dst, dstport, msg, end, InterfaceID);
3689*472cd20dSToomas Soome     }
36905ffb0c9bSToomas Soome 
36915ffb0c9bSToomas Soome     // put the number of additionals back the way it was
36925ffb0c9bSToomas Soome     msg->h.numAdditionals = numAdditionals;
36935ffb0c9bSToomas Soome 
36945ffb0c9bSToomas Soome     return(status);
36955ffb0c9bSToomas Soome }
36964b22b933Srs 
36974b22b933Srs // ***************************************************************************
36984b22b933Srs #if COMPILER_LIKES_PRAGMA_MARK
36994b22b933Srs #pragma mark -
37004b22b933Srs #pragma mark - RR List Management & Task Management
37014b22b933Srs #endif
37024b22b933Srs 
mDNS_Lock_(mDNS * const m,const char * const functionname)37035ffb0c9bSToomas Soome mDNSexport void mDNS_Lock_(mDNS *const m, const char * const functionname)
37045ffb0c9bSToomas Soome {
37055ffb0c9bSToomas Soome     // MUST grab the platform lock FIRST!
37065ffb0c9bSToomas Soome     mDNSPlatformLock(m);
37075ffb0c9bSToomas Soome 
37085ffb0c9bSToomas Soome     // Normally, mDNS_reentrancy is zero and so is mDNS_busy
37095ffb0c9bSToomas Soome     // However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
37105ffb0c9bSToomas Soome     // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
37115ffb0c9bSToomas Soome     // If mDNS_busy != mDNS_reentrancy that's a bad sign
37125ffb0c9bSToomas Soome     if (m->mDNS_busy != m->mDNS_reentrancy)
3713cda73f64SToomas Soome         LogFatalError("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy);
37145ffb0c9bSToomas Soome 
37155ffb0c9bSToomas Soome     // If this is an initial entry into the mDNSCore code, set m->timenow
37165ffb0c9bSToomas Soome     // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
37175ffb0c9bSToomas Soome     if (m->mDNS_busy == 0)
37185ffb0c9bSToomas Soome     {
37195ffb0c9bSToomas Soome         if (m->timenow)
37205ffb0c9bSToomas Soome             LogMsg("%s: mDNS_Lock: m->timenow already set (%ld/%ld)", functionname, m->timenow, mDNS_TimeNow_NoLock(m));
37215ffb0c9bSToomas Soome         m->timenow = mDNS_TimeNow_NoLock(m);
37225ffb0c9bSToomas Soome         if (m->timenow == 0) m->timenow = 1;
37235ffb0c9bSToomas Soome     }
37245ffb0c9bSToomas Soome     else if (m->timenow == 0)
37255ffb0c9bSToomas Soome     {
37265ffb0c9bSToomas Soome         LogMsg("%s: mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", functionname, m->mDNS_busy);
37275ffb0c9bSToomas Soome         m->timenow = mDNS_TimeNow_NoLock(m);
37285ffb0c9bSToomas Soome         if (m->timenow == 0) m->timenow = 1;
37295ffb0c9bSToomas Soome     }
37305ffb0c9bSToomas Soome 
37315ffb0c9bSToomas Soome     if (m->timenow_last - m->timenow > 0)
37325ffb0c9bSToomas Soome     {
37335ffb0c9bSToomas Soome         m->timenow_adjust += m->timenow_last - m->timenow;
37345ffb0c9bSToomas Soome         LogMsg("%s: mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", functionname, m->timenow_last - m->timenow, m->timenow_adjust);
37355ffb0c9bSToomas Soome         m->timenow = m->timenow_last;
37365ffb0c9bSToomas Soome     }
37375ffb0c9bSToomas Soome     m->timenow_last = m->timenow;
37385ffb0c9bSToomas Soome 
37395ffb0c9bSToomas Soome     // Increment mDNS_busy so we'll recognise re-entrant calls
37405ffb0c9bSToomas Soome     m->mDNS_busy++;
37415ffb0c9bSToomas Soome }
37425ffb0c9bSToomas Soome 
AnyLocalRecordReady(const mDNS * const m)37435ffb0c9bSToomas Soome mDNSlocal AuthRecord *AnyLocalRecordReady(const mDNS *const m)
37445ffb0c9bSToomas Soome {
37455ffb0c9bSToomas Soome     AuthRecord *rr;
37465ffb0c9bSToomas Soome     for (rr = m->NewLocalRecords; rr; rr = rr->next)
37475ffb0c9bSToomas Soome         if (LocalRecordReady(rr)) return rr;
37485ffb0c9bSToomas Soome     return mDNSNULL;
37495ffb0c9bSToomas Soome }
37504b22b933Srs 
GetNextScheduledEvent(const mDNS * const m)37514b22b933Srs mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
37525ffb0c9bSToomas Soome {
3753c65ebfc7SToomas Soome     mDNSs32 e = m->timenow + FutureTime;
37545ffb0c9bSToomas Soome     if (m->mDNSPlatformStatus != mStatus_NoError) return(e);
37555ffb0c9bSToomas Soome     if (m->NewQuestions)
37565ffb0c9bSToomas Soome     {
37575ffb0c9bSToomas Soome         if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering;
37585ffb0c9bSToomas Soome         else return(m->timenow);
37595ffb0c9bSToomas Soome     }
37605ffb0c9bSToomas Soome     if (m->NewLocalOnlyQuestions) return(m->timenow);
37615ffb0c9bSToomas Soome     if (m->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow);
37625ffb0c9bSToomas Soome     if (m->NewLocalOnlyRecords) return(m->timenow);
37635ffb0c9bSToomas Soome     if (m->SPSProxyListChanged) return(m->timenow);
37645ffb0c9bSToomas Soome     if (m->LocalRemoveEvents) return(m->timenow);
37655ffb0c9bSToomas Soome 
37665ffb0c9bSToomas Soome #ifndef UNICAST_DISABLED
37675ffb0c9bSToomas Soome     if (e - m->NextuDNSEvent         > 0) e = m->NextuDNSEvent;
37685ffb0c9bSToomas Soome     if (e - m->NextScheduledNATOp    > 0) e = m->NextScheduledNATOp;
37695ffb0c9bSToomas Soome     if (m->NextSRVUpdate && e - m->NextSRVUpdate > 0) e = m->NextSRVUpdate;
37705ffb0c9bSToomas Soome #endif
37715ffb0c9bSToomas Soome 
37725ffb0c9bSToomas Soome     if (e - m->NextCacheCheck        > 0) e = m->NextCacheCheck;
37735ffb0c9bSToomas Soome     if (e - m->NextScheduledSPS      > 0) e = m->NextScheduledSPS;
37745ffb0c9bSToomas Soome     if (e - m->NextScheduledKA       > 0) e = m->NextScheduledKA;
37755ffb0c9bSToomas Soome 
3776*472cd20dSToomas Soome #if MDNSRESPONDER_SUPPORTS(APPLE, BONJOUR_ON_DEMAND)
3777c65ebfc7SToomas Soome     if (m->NextBonjourDisableTime && (e - m->NextBonjourDisableTime > 0)) e = m->NextBonjourDisableTime;
3778*472cd20dSToomas Soome #endif
3779c65ebfc7SToomas Soome 
37805ffb0c9bSToomas Soome     // NextScheduledSPRetry only valid when DelaySleep not set
37815ffb0c9bSToomas Soome     if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry;
37825ffb0c9bSToomas Soome     if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep;
37835ffb0c9bSToomas Soome 
37845ffb0c9bSToomas Soome     if (m->SuppressSending)
37855ffb0c9bSToomas Soome     {
37865ffb0c9bSToomas Soome         if (e - m->SuppressSending       > 0) e = m->SuppressSending;
37875ffb0c9bSToomas Soome     }
37885ffb0c9bSToomas Soome     else
37895ffb0c9bSToomas Soome     {
37905ffb0c9bSToomas Soome         if (e - m->NextScheduledQuery    > 0) e = m->NextScheduledQuery;
37915ffb0c9bSToomas Soome         if (e - m->NextScheduledProbe    > 0) e = m->NextScheduledProbe;
37925ffb0c9bSToomas Soome         if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
37935ffb0c9bSToomas Soome     }
37945ffb0c9bSToomas Soome     if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime;
3795c65ebfc7SToomas Soome 
3796c65ebfc7SToomas Soome     if (m->NextBLEServiceTime && (e - m->NextBLEServiceTime > 0)) e = m->NextBLEServiceTime;
3797c65ebfc7SToomas Soome 
37985ffb0c9bSToomas Soome     return(e);
37995ffb0c9bSToomas Soome }
38005ffb0c9bSToomas Soome 
3801cda73f64SToomas Soome #define LogTSE TSE++,LogMsg
3802cda73f64SToomas Soome 
ShowTaskSchedulingError(mDNS * const m)38035ffb0c9bSToomas Soome mDNSexport void ShowTaskSchedulingError(mDNS *const m)
38045ffb0c9bSToomas Soome {
3805cda73f64SToomas Soome     int TSE = 0;
38065ffb0c9bSToomas Soome     AuthRecord *rr;
38075ffb0c9bSToomas Soome     mDNS_Lock(m);
38085ffb0c9bSToomas Soome 
3809cda73f64SToomas Soome     LogMsg("Task Scheduling Error: *** Continuously busy for more than a second");
38105ffb0c9bSToomas Soome 
38115ffb0c9bSToomas Soome     // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above
38125ffb0c9bSToomas Soome 
38135ffb0c9bSToomas Soome     if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0))
3814cda73f64SToomas Soome         LogTSE("Task Scheduling Error: NewQuestion %##s (%s)",
38155ffb0c9bSToomas Soome                m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
38165ffb0c9bSToomas Soome 
38175ffb0c9bSToomas Soome     if (m->NewLocalOnlyQuestions)
3818cda73f64SToomas Soome         LogTSE("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
38195ffb0c9bSToomas Soome                m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
38205ffb0c9bSToomas Soome 
38215ffb0c9bSToomas Soome     if (m->NewLocalRecords)
38225ffb0c9bSToomas Soome     {
38235ffb0c9bSToomas Soome         rr = AnyLocalRecordReady(m);
3824cda73f64SToomas Soome         if (rr) LogTSE("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr));
38255ffb0c9bSToomas Soome     }
38265ffb0c9bSToomas Soome 
3827cda73f64SToomas Soome     if (m->NewLocalOnlyRecords) LogTSE("Task Scheduling Error: NewLocalOnlyRecords");
38285ffb0c9bSToomas Soome 
3829cda73f64SToomas Soome     if (m->SPSProxyListChanged) LogTSE("Task Scheduling Error: SPSProxyListChanged");
38305ffb0c9bSToomas Soome 
3831cda73f64SToomas Soome     if (m->LocalRemoveEvents) LogTSE("Task Scheduling Error: LocalRemoveEvents");
38325ffb0c9bSToomas Soome 
38334b22b933Srs #ifndef UNICAST_DISABLED
38345ffb0c9bSToomas Soome     if (m->timenow - m->NextuDNSEvent         >= 0)
3835cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextuDNSEvent %d",         m->timenow - m->NextuDNSEvent);
38365ffb0c9bSToomas Soome     if (m->timenow - m->NextScheduledNATOp    >= 0)
3837cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextScheduledNATOp %d",    m->timenow - m->NextScheduledNATOp);
38385ffb0c9bSToomas Soome     if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0)
3839cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextSRVUpdate %d",         m->timenow - m->NextSRVUpdate);
38404b22b933Srs #endif
38415ffb0c9bSToomas Soome 
38425ffb0c9bSToomas Soome     if (m->timenow - m->NextCacheCheck        >= 0)
3843cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextCacheCheck %d",        m->timenow - m->NextCacheCheck);
38445ffb0c9bSToomas Soome     if (m->timenow - m->NextScheduledSPS      >= 0)
3845cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextScheduledSPS %d",      m->timenow - m->NextScheduledSPS);
38465ffb0c9bSToomas Soome     if (m->timenow - m->NextScheduledKA       >= 0)
3847cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextScheduledKA %d",      m->timenow - m->NextScheduledKA);
38485ffb0c9bSToomas Soome     if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0)
3849cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextScheduledSPRetry %d",  m->timenow - m->NextScheduledSPRetry);
38505ffb0c9bSToomas Soome     if (m->DelaySleep && m->timenow - m->DelaySleep >= 0)
3851cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->DelaySleep %d",            m->timenow - m->DelaySleep);
38525ffb0c9bSToomas Soome 
38535ffb0c9bSToomas Soome     if (m->SuppressSending && m->timenow - m->SuppressSending >= 0)
3854cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->SuppressSending %d",       m->timenow - m->SuppressSending);
38555ffb0c9bSToomas Soome     if (m->timenow - m->NextScheduledQuery    >= 0)
3856cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextScheduledQuery %d",    m->timenow - m->NextScheduledQuery);
38575ffb0c9bSToomas Soome     if (m->timenow - m->NextScheduledProbe    >= 0)
3858cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextScheduledProbe %d",    m->timenow - m->NextScheduledProbe);
38595ffb0c9bSToomas Soome     if (m->timenow - m->NextScheduledResponse >= 0)
3860cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse);
3861cda73f64SToomas Soome     if (m->timenow - m->NextScheduledStopTime >= 0)
3862cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextScheduledStopTime %d", m->timenow - m->NextScheduledStopTime);
3863cda73f64SToomas Soome 
3864cda73f64SToomas Soome     if (m->timenow - m->NextScheduledEvent    >= 0)
3865cda73f64SToomas Soome         LogTSE("Task Scheduling Error: m->NextScheduledEvent %d",    m->timenow - m->NextScheduledEvent);
3866cda73f64SToomas Soome 
3867cda73f64SToomas Soome     if (m->NetworkChanged && m->timenow - m->NetworkChanged >= 0)
3868cda73f64SToomas Soome         LogTSE("Task Scheduling Error: NetworkChanged %d",           m->timenow - m->NetworkChanged);
3869cda73f64SToomas Soome 
3870cda73f64SToomas Soome     if (!TSE) LogMsg("Task Scheduling Error: *** No likely causes identified");
3871cda73f64SToomas Soome     else LogMsg("Task Scheduling Error: *** %d potential cause%s identified (significant only if the same cause consistently appears)", TSE, TSE > 1 ? "s" : "");
38725ffb0c9bSToomas Soome 
38735ffb0c9bSToomas Soome     mDNS_Unlock(m);
38745ffb0c9bSToomas Soome }
38755ffb0c9bSToomas Soome 
mDNS_Unlock_(mDNS * const m,const char * const functionname)3876cda73f64SToomas Soome mDNSexport void mDNS_Unlock_(mDNS *const m, const char *const functionname)
38775ffb0c9bSToomas Soome {
38785ffb0c9bSToomas Soome     // Decrement mDNS_busy
38795ffb0c9bSToomas Soome     m->mDNS_busy--;
38805ffb0c9bSToomas Soome 
38815ffb0c9bSToomas Soome     // Check for locking failures
38825ffb0c9bSToomas Soome     if (m->mDNS_busy != m->mDNS_reentrancy)
3883cda73f64SToomas Soome         LogFatalError("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy);
38845ffb0c9bSToomas Soome 
38855ffb0c9bSToomas Soome     // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
38865ffb0c9bSToomas Soome     if (m->mDNS_busy == 0)
38875ffb0c9bSToomas Soome     {
38885ffb0c9bSToomas Soome         m->NextScheduledEvent = GetNextScheduledEvent(m);
38895ffb0c9bSToomas Soome         if (m->timenow == 0) LogMsg("%s: mDNS_Unlock: ERROR! m->timenow aready zero", functionname);
38905ffb0c9bSToomas Soome         m->timenow = 0;
38915ffb0c9bSToomas Soome     }
38925ffb0c9bSToomas Soome 
38935ffb0c9bSToomas Soome     // MUST release the platform lock LAST!
38945ffb0c9bSToomas Soome     mDNSPlatformUnlock(m);
38955ffb0c9bSToomas Soome }
38965ffb0c9bSToomas Soome 
38975ffb0c9bSToomas Soome // ***************************************************************************
38985ffb0c9bSToomas Soome #if COMPILER_LIKES_PRAGMA_MARK
38995ffb0c9bSToomas Soome #pragma mark -
39005ffb0c9bSToomas Soome #pragma mark - Specialized mDNS version of vsnprintf
39015ffb0c9bSToomas Soome #endif
39025ffb0c9bSToomas Soome 
39035ffb0c9bSToomas Soome static const struct mDNSprintf_format
39045ffb0c9bSToomas Soome {
39055ffb0c9bSToomas Soome     unsigned leftJustify : 1;
39065ffb0c9bSToomas Soome     unsigned forceSign : 1;
39075ffb0c9bSToomas Soome     unsigned zeroPad : 1;
39085ffb0c9bSToomas Soome     unsigned havePrecision : 1;
39095ffb0c9bSToomas Soome     unsigned hSize : 1;
39105ffb0c9bSToomas Soome     unsigned lSize : 1;
39115ffb0c9bSToomas Soome     char altForm;
39125ffb0c9bSToomas Soome     char sign;              // +, - or space
39135ffb0c9bSToomas Soome     unsigned int fieldWidth;
39145ffb0c9bSToomas Soome     unsigned int precision;
39155ffb0c9bSToomas Soome } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
39165ffb0c9bSToomas Soome 
39173b436d06SToomas Soome #define kHexDigitsLowercase "0123456789abcdef"
39183b436d06SToomas Soome #define kHexDigitsUppercase "0123456789ABCDEF";
39193b436d06SToomas Soome 
mDNS_vsnprintf(char * sbuffer,mDNSu32 buflen,const char * fmt,va_list arg)39205ffb0c9bSToomas Soome mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
39215ffb0c9bSToomas Soome {
39225ffb0c9bSToomas Soome     mDNSu32 nwritten = 0;
39235ffb0c9bSToomas Soome     int c;
39245ffb0c9bSToomas Soome     if (buflen == 0) return(0);
39255ffb0c9bSToomas Soome     buflen--;       // Pre-reserve one space in the buffer for the terminating null
39265ffb0c9bSToomas Soome     if (buflen == 0) goto exit;
39275ffb0c9bSToomas Soome 
39285ffb0c9bSToomas Soome     for (c = *fmt; c != 0; c = *++fmt)
39295ffb0c9bSToomas Soome     {
3930c65ebfc7SToomas Soome         unsigned long n;
39313b436d06SToomas Soome         int hexdump = mDNSfalse;
3932c65ebfc7SToomas Soome 		if (c != '%')
39335ffb0c9bSToomas Soome         {
39345ffb0c9bSToomas Soome             *sbuffer++ = (char)c;
39355ffb0c9bSToomas Soome             if (++nwritten >= buflen) goto exit;
39365ffb0c9bSToomas Soome         }
39375ffb0c9bSToomas Soome         else
39385ffb0c9bSToomas Soome         {
39395ffb0c9bSToomas Soome             unsigned int i=0, j;
39405ffb0c9bSToomas Soome             // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
39415ffb0c9bSToomas Soome             // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
39425ffb0c9bSToomas Soome             // The size needs to be enough for a 256-byte domain name plus some error text.
39435ffb0c9bSToomas Soome             #define mDNS_VACB_Size 300
39445ffb0c9bSToomas Soome             char mDNS_VACB[mDNS_VACB_Size];
39455ffb0c9bSToomas Soome             #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
39465ffb0c9bSToomas Soome             #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
39475ffb0c9bSToomas Soome             char *s = mDNS_VACB_Lim, *digits;
39485ffb0c9bSToomas Soome             struct mDNSprintf_format F = mDNSprintf_format_default;
39495ffb0c9bSToomas Soome 
39505ffb0c9bSToomas Soome             while (1)   //  decode flags
39515ffb0c9bSToomas Soome             {
39525ffb0c9bSToomas Soome                 c = *++fmt;
39535ffb0c9bSToomas Soome                 if      (c == '-') F.leftJustify = 1;
39545ffb0c9bSToomas Soome                 else if (c == '+') F.forceSign = 1;
39555ffb0c9bSToomas Soome                 else if (c == ' ') F.sign = ' ';
39565ffb0c9bSToomas Soome                 else if (c == '#') F.altForm++;
39575ffb0c9bSToomas Soome                 else if (c == '0') F.zeroPad = 1;
39585ffb0c9bSToomas Soome                 else break;
39595ffb0c9bSToomas Soome             }
39605ffb0c9bSToomas Soome 
39615ffb0c9bSToomas Soome             if (c == '*')   //  decode field width
39625ffb0c9bSToomas Soome             {
39635ffb0c9bSToomas Soome                 int f = va_arg(arg, int);
39645ffb0c9bSToomas Soome                 if (f < 0) { f = -f; F.leftJustify = 1; }
39655ffb0c9bSToomas Soome                 F.fieldWidth = (unsigned int)f;
39665ffb0c9bSToomas Soome                 c = *++fmt;
39675ffb0c9bSToomas Soome             }
39685ffb0c9bSToomas Soome             else
39695ffb0c9bSToomas Soome             {
39705ffb0c9bSToomas Soome                 for (; c >= '0' && c <= '9'; c = *++fmt)
39715ffb0c9bSToomas Soome                     F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
39725ffb0c9bSToomas Soome             }
39735ffb0c9bSToomas Soome 
39745ffb0c9bSToomas Soome             if (c == '.')   //  decode precision
39755ffb0c9bSToomas Soome             {
39765ffb0c9bSToomas Soome                 if ((c = *++fmt) == '*')
39775ffb0c9bSToomas Soome                 { F.precision = va_arg(arg, unsigned int); c = *++fmt; }
39785ffb0c9bSToomas Soome                 else for (; c >= '0' && c <= '9'; c = *++fmt)
39795ffb0c9bSToomas Soome                         F.precision = (10 * F.precision) + (c - '0');
39805ffb0c9bSToomas Soome                 F.havePrecision = 1;
39815ffb0c9bSToomas Soome             }
39825ffb0c9bSToomas Soome 
39835ffb0c9bSToomas Soome             if (F.leftJustify) F.zeroPad = 0;
39845ffb0c9bSToomas Soome 
39855ffb0c9bSToomas Soome conv:
39865ffb0c9bSToomas Soome             switch (c)  //  perform appropriate conversion
39875ffb0c9bSToomas Soome             {
39885ffb0c9bSToomas Soome             case 'h':  F.hSize = 1; c = *++fmt; goto conv;
39895ffb0c9bSToomas Soome             case 'l':       // fall through
39905ffb0c9bSToomas Soome             case 'L':  F.lSize = 1; c = *++fmt; goto conv;
39915ffb0c9bSToomas Soome             case 'd':
39925ffb0c9bSToomas Soome             case 'i':  if (F.lSize) n = (unsigned long)va_arg(arg, long);
39935ffb0c9bSToomas Soome                 else n = (unsigned long)va_arg(arg, int);
39945ffb0c9bSToomas Soome                 if (F.hSize) n = (short) n;
39955ffb0c9bSToomas Soome                 if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
39965ffb0c9bSToomas Soome                 else if (F.forceSign) F.sign = '+';
39975ffb0c9bSToomas Soome                 goto decimal;
39985ffb0c9bSToomas Soome             case 'u':  if (F.lSize) n = va_arg(arg, unsigned long);
39995ffb0c9bSToomas Soome                 else n = va_arg(arg, unsigned int);
40005ffb0c9bSToomas Soome                 if (F.hSize) n = (unsigned short) n;
40015ffb0c9bSToomas Soome                 F.sign = 0;
40025ffb0c9bSToomas Soome                 goto decimal;
40035ffb0c9bSToomas Soome decimal:    if (!F.havePrecision)
40045ffb0c9bSToomas Soome                 {
40055ffb0c9bSToomas Soome                     if (F.zeroPad)
40065ffb0c9bSToomas Soome                     {
40075ffb0c9bSToomas Soome                         F.precision = F.fieldWidth;
40085ffb0c9bSToomas Soome                         if (F.sign) --F.precision;
40095ffb0c9bSToomas Soome                     }
40105ffb0c9bSToomas Soome                     if (F.precision < 1) F.precision = 1;
40115ffb0c9bSToomas Soome                 }
40125ffb0c9bSToomas Soome                 if (F.precision > mDNS_VACB_Size - 1)
40135ffb0c9bSToomas Soome                     F.precision = mDNS_VACB_Size - 1;
40145ffb0c9bSToomas Soome                 for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
40155ffb0c9bSToomas Soome                 for (; i < F.precision; i++) *--s = '0';
40165ffb0c9bSToomas Soome                 if (F.sign) { *--s = F.sign; i++; }
40175ffb0c9bSToomas Soome                 break;
40185ffb0c9bSToomas Soome 
40195ffb0c9bSToomas Soome             case 'o':  if (F.lSize) n = va_arg(arg, unsigned long);
40205ffb0c9bSToomas Soome                 else n = va_arg(arg, unsigned int);
40215ffb0c9bSToomas Soome                 if (F.hSize) n = (unsigned short) n;
40225ffb0c9bSToomas Soome                 if (!F.havePrecision)
40235ffb0c9bSToomas Soome                 {
40245ffb0c9bSToomas Soome                     if (F.zeroPad) F.precision = F.fieldWidth;
40255ffb0c9bSToomas Soome                     if (F.precision < 1) F.precision = 1;
40265ffb0c9bSToomas Soome                 }
40275ffb0c9bSToomas Soome                 if (F.precision > mDNS_VACB_Size - 1)
40285ffb0c9bSToomas Soome                     F.precision = mDNS_VACB_Size - 1;
40295ffb0c9bSToomas Soome                 for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
40305ffb0c9bSToomas Soome                 if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
40315ffb0c9bSToomas Soome                 for (; i < F.precision; i++) *--s = '0';
40325ffb0c9bSToomas Soome                 break;
40335ffb0c9bSToomas Soome 
40345ffb0c9bSToomas Soome             case 'a':  {
40355ffb0c9bSToomas Soome                 unsigned char *a = va_arg(arg, unsigned char *);
40365ffb0c9bSToomas Soome                 if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
40375ffb0c9bSToomas Soome                 else
40385ffb0c9bSToomas Soome                 {
40395ffb0c9bSToomas Soome                     s = mDNS_VACB;              // Adjust s to point to the start of the buffer, not the end
40405ffb0c9bSToomas Soome                     if (F.altForm)
40415ffb0c9bSToomas Soome                     {
40425ffb0c9bSToomas Soome                         mDNSAddr *ip = (mDNSAddr*)a;
40435ffb0c9bSToomas Soome                         switch (ip->type)
40445ffb0c9bSToomas Soome                         {
40455ffb0c9bSToomas Soome                         case mDNSAddrType_IPv4: F.precision =  4; a = (unsigned char *)&ip->ip.v4; break;
40465ffb0c9bSToomas Soome                         case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
40475ffb0c9bSToomas Soome                         default:                F.precision =  0; break;
40485ffb0c9bSToomas Soome                         }
40495ffb0c9bSToomas Soome                     }
40505ffb0c9bSToomas Soome                     if (F.altForm && !F.precision)
40515ffb0c9bSToomas Soome                         i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "«ZERO ADDRESS»");
40525ffb0c9bSToomas Soome                     else switch (F.precision)
40535ffb0c9bSToomas Soome                         {
40545ffb0c9bSToomas Soome                         case  4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d",
40555ffb0c9bSToomas Soome                                                    a[0], a[1], a[2], a[3]); break;
40565ffb0c9bSToomas Soome                         case  6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
40575ffb0c9bSToomas Soome                                                    a[0], a[1], a[2], a[3], a[4], a[5]); break;
40583b436d06SToomas Soome                         case 16: {
40593b436d06SToomas Soome                             // Print IPv6 addresses according to RFC 5952, A Recommendation for IPv6 Address Text
40603b436d06SToomas Soome                             // Representation. See <https://tools.ietf.org/html/rfc5952>.
40613b436d06SToomas Soome 
40623b436d06SToomas Soome                             int idx, runLen = 0, runStart = 0, maxRunLen = 0, maxRunStart = 0, maxRunEnd;
40633b436d06SToomas Soome 
40643b436d06SToomas Soome                             // Find the leftmost longest run of consecutive zero hextets.
40653b436d06SToomas Soome                             for (idx = 0; idx < 8; ++idx)
40663b436d06SToomas Soome                             {
40673b436d06SToomas Soome                                 const unsigned int hextet = (a[idx * 2] << 8) | a[(idx * 2) + 1];
40683b436d06SToomas Soome                                 if (hextet == 0)
40693b436d06SToomas Soome                                 {
40703b436d06SToomas Soome                                     if (runLen++ == 0) runStart = idx;
40713b436d06SToomas Soome                                     if (runLen > maxRunLen)
40723b436d06SToomas Soome                                     {
40733b436d06SToomas Soome                                         maxRunStart = runStart;
40743b436d06SToomas Soome                                         maxRunLen   = runLen;
40753b436d06SToomas Soome                                     }
40763b436d06SToomas Soome                                 }
40773b436d06SToomas Soome                                 else
40783b436d06SToomas Soome                                 {
40793b436d06SToomas Soome                                     // If the number of remaining hextets is less than or equal to the length of the longest
40803b436d06SToomas Soome                                     // run so far, then we've found the leftmost longest run.
40813b436d06SToomas Soome                                     if ((8 - (idx + 1)) <= maxRunLen) break;
40823b436d06SToomas Soome                                     runLen = 0;
40833b436d06SToomas Soome                                 }
40843b436d06SToomas Soome                             }
40853b436d06SToomas Soome 
40863b436d06SToomas Soome                             // Compress the leftmost longest run of two or more consecutive zero hextets as "::".
40873b436d06SToomas Soome                             // For each reminaing hextet, suppress zeros leading up to the least-significant nibble, which
40883b436d06SToomas Soome                             // is always written, even if it's zero. Because of this requirement, it's easier to write the
40893b436d06SToomas Soome                             // IPv6 address in reverse. Also, write a colon separator before each hextet except for the
40903b436d06SToomas Soome                             // first one.
40913b436d06SToomas Soome                             s = mDNS_VACB_Lim;
40923b436d06SToomas Soome                             maxRunEnd = (maxRunLen >= 2) ? (maxRunStart + maxRunLen - 1) : -1;
40933b436d06SToomas Soome                             for (idx = 7; idx >= 0; --idx)
40943b436d06SToomas Soome                             {
40953b436d06SToomas Soome                                 if (idx == maxRunEnd)
40963b436d06SToomas Soome                                 {
40973b436d06SToomas Soome                                     if (idx == 7) *--s = ':';
40983b436d06SToomas Soome                                     idx = maxRunStart;
40993b436d06SToomas Soome                                     *--s = ':';
41003b436d06SToomas Soome                                 }
41013b436d06SToomas Soome                                 else
41023b436d06SToomas Soome                                 {
41033b436d06SToomas Soome                                     unsigned int hextet = (a[idx * 2] << 8) | a[(idx * 2) + 1];
41043b436d06SToomas Soome                                     do {
41053b436d06SToomas Soome                                         *--s = kHexDigitsLowercase[hextet % 16];
41063b436d06SToomas Soome                                         hextet /= 16;
41073b436d06SToomas Soome                                     } while (hextet);
41083b436d06SToomas Soome                                     if (idx > 0) *--s = ':';
41093b436d06SToomas Soome                                 }
41103b436d06SToomas Soome                             }
41113b436d06SToomas Soome                             i = (unsigned int)(mDNS_VACB_Lim - s);
41123b436d06SToomas Soome                         }
41133b436d06SToomas Soome                         break;
41143b436d06SToomas Soome 
41155ffb0c9bSToomas Soome                         default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify"
41165ffb0c9bSToomas Soome                                                    " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
41175ffb0c9bSToomas Soome                         }
41185ffb0c9bSToomas Soome                 }
41195ffb0c9bSToomas Soome             }
41205ffb0c9bSToomas Soome             break;
41215ffb0c9bSToomas Soome 
41225ffb0c9bSToomas Soome             case 'p':  F.havePrecision = F.lSize = 1;
41235ffb0c9bSToomas Soome                 F.precision = sizeof(void*) * 2;                // 8 characters on 32-bit; 16 characters on 64-bit
4124fff695d4SToomas Soome 		/* FALLTHROUGH */
41253b436d06SToomas Soome             case 'X':  digits = kHexDigitsUppercase;
41265ffb0c9bSToomas Soome                 goto hexadecimal;
41273b436d06SToomas Soome             case 'x':  digits = kHexDigitsLowercase;
41285ffb0c9bSToomas Soome hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long);
41295ffb0c9bSToomas Soome                 else n = va_arg(arg, unsigned int);
41305ffb0c9bSToomas Soome                 if (F.hSize) n = (unsigned short) n;
41315ffb0c9bSToomas Soome                 if (!F.havePrecision)
41325ffb0c9bSToomas Soome                 {
41335ffb0c9bSToomas Soome                     if (F.zeroPad)
41345ffb0c9bSToomas Soome                     {
41355ffb0c9bSToomas Soome                         F.precision = F.fieldWidth;
41365ffb0c9bSToomas Soome                         if (F.altForm) F.precision -= 2;
41375ffb0c9bSToomas Soome                     }
41385ffb0c9bSToomas Soome                     if (F.precision < 1) F.precision = 1;
41395ffb0c9bSToomas Soome                 }
41405ffb0c9bSToomas Soome                 if (F.precision > mDNS_VACB_Size - 1)
41415ffb0c9bSToomas Soome                     F.precision = mDNS_VACB_Size - 1;
41425ffb0c9bSToomas Soome                 for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
41435ffb0c9bSToomas Soome                 for (; i < F.precision; i++) *--s = '0';
41445ffb0c9bSToomas Soome                 if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
41455ffb0c9bSToomas Soome                 break;
41465ffb0c9bSToomas Soome 
41475ffb0c9bSToomas Soome             case 'c':  *--s = (char)va_arg(arg, int); i = 1; break;
41485ffb0c9bSToomas Soome 
41495ffb0c9bSToomas Soome             case 's':  s = va_arg(arg, char *);
41505ffb0c9bSToomas Soome                 if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
41515ffb0c9bSToomas Soome                 else switch (F.altForm)
41525ffb0c9bSToomas Soome                     {
41535ffb0c9bSToomas Soome                     case 0: i=0;
41545ffb0c9bSToomas Soome                         if (!F.havePrecision)                               // C string
41555ffb0c9bSToomas Soome                             while (s[i]) i++;
41565ffb0c9bSToomas Soome                         else
41575ffb0c9bSToomas Soome                         {
41585ffb0c9bSToomas Soome                             while ((i < F.precision) && s[i]) i++;
41595ffb0c9bSToomas Soome                             // Make sure we don't truncate in the middle of a UTF-8 character
41605ffb0c9bSToomas Soome                             // If last character we got was any kind of UTF-8 multi-byte character,
41615ffb0c9bSToomas Soome                             // then see if we have to back up.
41625ffb0c9bSToomas Soome                             // This is not as easy as the similar checks below, because
41635ffb0c9bSToomas Soome                             // here we can't assume it's safe to examine the *next* byte, so we
41645ffb0c9bSToomas Soome                             // have to confine ourselves to working only backwards in the string.
41655ffb0c9bSToomas Soome                             j = i;                      // Record where we got to
41665ffb0c9bSToomas Soome                             // Now, back up until we find first non-continuation-char
41675ffb0c9bSToomas Soome                             while (i>0 && (s[i-1] & 0xC0) == 0x80) i--;
41685ffb0c9bSToomas Soome                             // Now s[i-1] is the first non-continuation-char
41695ffb0c9bSToomas Soome                             // and (j-i) is the number of continuation-chars we found
41705ffb0c9bSToomas Soome                             if (i>0 && (s[i-1] & 0xC0) == 0xC0)                 // If we found a start-char
41715ffb0c9bSToomas Soome                             {
41725ffb0c9bSToomas Soome                                 i--;                        // Tentatively eliminate this start-char as well
41735ffb0c9bSToomas Soome                                 // Now (j-i) is the number of characters we're considering eliminating.
41745ffb0c9bSToomas Soome                                 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
41755ffb0c9bSToomas Soome                                 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
41765ffb0c9bSToomas Soome                                 // (with sign extension) then the result has to be 0xFE.
41775ffb0c9bSToomas Soome                                 // If this is right, then we reinstate the tentatively eliminated bytes.
41785ffb0c9bSToomas Soome                                 if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j;
41795ffb0c9bSToomas Soome                             }
41805ffb0c9bSToomas Soome                         }
41815ffb0c9bSToomas Soome                         break;
41825ffb0c9bSToomas Soome                     case 1: i = (unsigned char) *s++; break;                // Pascal string
41835ffb0c9bSToomas Soome                     case 2: {                                               // DNS label-sequence name
41845ffb0c9bSToomas Soome                         unsigned char *a = (unsigned char *)s;
41855ffb0c9bSToomas Soome                         s = mDNS_VACB;                  // Adjust s to point to the start of the buffer, not the end
41865ffb0c9bSToomas Soome                         if (*a == 0) *s++ = '.';                    // Special case for root DNS name
41875ffb0c9bSToomas Soome                         while (*a)
41885ffb0c9bSToomas Soome                         {
41895ffb0c9bSToomas Soome                             char buf[63*4+1];
41905ffb0c9bSToomas Soome                             if (*a > 63)
41915ffb0c9bSToomas Soome                             { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
41925ffb0c9bSToomas Soome                             if (s + *a >= &mDNS_VACB[254])
41935ffb0c9bSToomas Soome                             { s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
41945ffb0c9bSToomas Soome                             // Need to use ConvertDomainLabelToCString to do proper escaping here,
41955ffb0c9bSToomas Soome                             // so it's clear what's a literal dot and what's a label separator
41965ffb0c9bSToomas Soome                             ConvertDomainLabelToCString((domainlabel*)a, buf);
41975ffb0c9bSToomas Soome                             s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf);
41985ffb0c9bSToomas Soome                             a += 1 + *a;
41995ffb0c9bSToomas Soome                         }
42005ffb0c9bSToomas Soome                         i = (mDNSu32)(s - mDNS_VACB);
42015ffb0c9bSToomas Soome                         s = mDNS_VACB;                  // Reset s back to the start of the buffer
42025ffb0c9bSToomas Soome                         break;
42035ffb0c9bSToomas Soome                     }
42045ffb0c9bSToomas Soome                     }
42055ffb0c9bSToomas Soome                 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
42065ffb0c9bSToomas Soome                 if (F.havePrecision && i > F.precision)
42075ffb0c9bSToomas Soome                 { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
42085ffb0c9bSToomas Soome                 break;
42095ffb0c9bSToomas Soome 
42103b436d06SToomas Soome             case 'H': {
42113b436d06SToomas Soome                     s = va_arg(arg, char *);
42123b436d06SToomas Soome                     hexdump = mDNStrue;
42133b436d06SToomas Soome                 }
42143b436d06SToomas Soome                 break;
42153b436d06SToomas Soome 
42165ffb0c9bSToomas Soome             case 'n':  s = va_arg(arg, char *);
42175ffb0c9bSToomas Soome                 if      (F.hSize) *(short *) s = (short)nwritten;
42185ffb0c9bSToomas Soome                 else if (F.lSize) *(long  *) s = (long)nwritten;
42195ffb0c9bSToomas Soome                 else *(int   *) s = (int)nwritten;
42205ffb0c9bSToomas Soome                 continue;
42215ffb0c9bSToomas Soome 
42225ffb0c9bSToomas Soome             default:    s = mDNS_VACB;
42235ffb0c9bSToomas Soome                 i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
4224*472cd20dSToomas Soome                 break;
42255ffb0c9bSToomas Soome 
42265ffb0c9bSToomas Soome             case '%':  *sbuffer++ = (char)c;
42275ffb0c9bSToomas Soome                 if (++nwritten >= buflen) goto exit;
42285ffb0c9bSToomas Soome                 break;
42295ffb0c9bSToomas Soome             }
42305ffb0c9bSToomas Soome 
42315ffb0c9bSToomas Soome             if (i < F.fieldWidth && !F.leftJustify)         // Pad on the left
42325ffb0c9bSToomas Soome                 do  {
42335ffb0c9bSToomas Soome                     *sbuffer++ = ' ';
42345ffb0c9bSToomas Soome                     if (++nwritten >= buflen) goto exit;
42355ffb0c9bSToomas Soome                 } while (i < --F.fieldWidth);
42365ffb0c9bSToomas Soome 
42373b436d06SToomas Soome             if (hexdump)
42383b436d06SToomas Soome             {
42393b436d06SToomas Soome                 char *dst = sbuffer;
42403b436d06SToomas Soome                 const char *const lim = &sbuffer[buflen - nwritten];
42413b436d06SToomas Soome                 if (F.havePrecision)
42423b436d06SToomas Soome                 {
42433b436d06SToomas Soome                     for (i = 0; (i < F.precision) && (dst < lim); i++)
42443b436d06SToomas Soome                     {
42453b436d06SToomas Soome                         const unsigned int b = (unsigned int) *s++;
42463b436d06SToomas Soome                         if (i > 0)     *dst++ = ' ';
42473b436d06SToomas Soome                         if (dst < lim) *dst++ = kHexDigitsLowercase[(b >> 4) & 0xF];
42483b436d06SToomas Soome                         if (dst < lim) *dst++ = kHexDigitsLowercase[ b       & 0xF];
42493b436d06SToomas Soome                     }
42503b436d06SToomas Soome                 }
42513b436d06SToomas Soome                 i = (unsigned int)(dst - sbuffer);
42523b436d06SToomas Soome                 sbuffer = dst;
42533b436d06SToomas Soome             }
42543b436d06SToomas Soome             else
42553b436d06SToomas Soome             {
42563b436d06SToomas Soome                 // Make sure we don't truncate in the middle of a UTF-8 character.
42573b436d06SToomas Soome                 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
42583b436d06SToomas Soome                 // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
42593b436d06SToomas Soome                 // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
42603b436d06SToomas Soome                 // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
42613b436d06SToomas Soome                 if (i > buflen - nwritten)
42623b436d06SToomas Soome                 { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
42633b436d06SToomas Soome                 for (j=0; j<i; j++) *sbuffer++ = *s++;          // Write the converted result
42643b436d06SToomas Soome             }
42655ffb0c9bSToomas Soome             nwritten += i;
42665ffb0c9bSToomas Soome             if (nwritten >= buflen) goto exit;
42675ffb0c9bSToomas Soome 
42685ffb0c9bSToomas Soome             for (; i < F.fieldWidth; i++)                   // Pad on the right
42695ffb0c9bSToomas Soome             {
42705ffb0c9bSToomas Soome                 *sbuffer++ = ' ';
42715ffb0c9bSToomas Soome                 if (++nwritten >= buflen) goto exit;
42725ffb0c9bSToomas Soome             }
42735ffb0c9bSToomas Soome         }
42745ffb0c9bSToomas Soome     }
42755ffb0c9bSToomas Soome exit:
42765ffb0c9bSToomas Soome     *sbuffer++ = 0;
42775ffb0c9bSToomas Soome     return(nwritten);
42785ffb0c9bSToomas Soome }
42795ffb0c9bSToomas Soome 
mDNS_snprintf(char * sbuffer,mDNSu32 buflen,const char * fmt,...)42805ffb0c9bSToomas Soome mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
42815ffb0c9bSToomas Soome {
42825ffb0c9bSToomas Soome     mDNSu32 length;
42835ffb0c9bSToomas Soome 
42845ffb0c9bSToomas Soome     va_list ptr;
42855ffb0c9bSToomas Soome     va_start(ptr,fmt);
42865ffb0c9bSToomas Soome     length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
42875ffb0c9bSToomas Soome     va_end(ptr);
42885ffb0c9bSToomas Soome 
42895ffb0c9bSToomas Soome     return(length);
42905ffb0c9bSToomas Soome }
4291*472cd20dSToomas Soome 
4292*472cd20dSToomas Soome #if !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
mDNS_GetNextResolverGroupID(void)4293*472cd20dSToomas Soome mDNSexport mDNSu32 mDNS_GetNextResolverGroupID(void)
4294*472cd20dSToomas Soome {
4295*472cd20dSToomas Soome     static mDNSu32 lastID = 0;
4296*472cd20dSToomas Soome     if (++lastID == 0) lastID = 1; // Valid resolver group IDs are non-zero.
4297*472cd20dSToomas Soome     return(lastID);
4298*472cd20dSToomas Soome }
4299*472cd20dSToomas Soome #endif
4300*472cd20dSToomas Soome 
4301*472cd20dSToomas Soome #define kReverseIPv6Domain  ((const domainname *) "\x3" "ip6" "\x4" "arpa")
4302*472cd20dSToomas Soome 
GetReverseIPv6Addr(const domainname * name,mDNSu8 outIPv6[16])4303*472cd20dSToomas Soome mDNSexport mDNSBool GetReverseIPv6Addr(const domainname *name, mDNSu8 outIPv6[16])
4304*472cd20dSToomas Soome {
4305*472cd20dSToomas Soome     const mDNSu8 *      ptr;
4306*472cd20dSToomas Soome     int                 i;
4307*472cd20dSToomas Soome     mDNSu8              ipv6[16];
4308*472cd20dSToomas Soome 
4309*472cd20dSToomas Soome     // If the name is of the form "x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa.", where each x
4310*472cd20dSToomas Soome     // is a hex digit, then the sequence of 32 hex digit labels represents the nibbles of an IPv6 address in reverse order.
4311*472cd20dSToomas Soome     // See <https://tools.ietf.org/html/rfc3596#section-2.5>.
4312*472cd20dSToomas Soome 
4313*472cd20dSToomas Soome     ptr = name->c;
4314*472cd20dSToomas Soome     for (i = 0; i < 32; i++)
4315*472cd20dSToomas Soome     {
4316*472cd20dSToomas Soome         unsigned int c, nibble;
4317*472cd20dSToomas Soome         const int j = 15 - (i / 2);
4318*472cd20dSToomas Soome         if (*ptr++ != 1) return (mDNSfalse);                    // If this label's length is not 1, then fail.
4319*472cd20dSToomas Soome         c = *ptr++;                                             // Get label byte.
4320*472cd20dSToomas Soome         if (     (c >= '0') && (c <= '9')) nibble =  c - '0';   // If it's a hex digit, get its numeric value.
4321*472cd20dSToomas Soome         else if ((c >= 'a') && (c <= 'f')) nibble = (c - 'a') + 10;
4322*472cd20dSToomas Soome         else if ((c >= 'A') && (c <= 'F')) nibble = (c - 'A') + 10;
4323*472cd20dSToomas Soome         else                               return (mDNSfalse);  // Otherwise, fail.
4324*472cd20dSToomas Soome         if ((i % 2) == 0)
4325*472cd20dSToomas Soome         {
4326*472cd20dSToomas Soome             ipv6[j] = (mDNSu8)nibble;
4327*472cd20dSToomas Soome         }
4328*472cd20dSToomas Soome         else
4329*472cd20dSToomas Soome         {
4330*472cd20dSToomas Soome             ipv6[j] |= (mDNSu8)(nibble << 4);
4331*472cd20dSToomas Soome         }
4332*472cd20dSToomas Soome     }
4333*472cd20dSToomas Soome 
4334*472cd20dSToomas Soome     // The rest of the name needs to be "ip6.arpa.". If it isn't, fail.
4335*472cd20dSToomas Soome 
4336*472cd20dSToomas Soome     if (!SameDomainName((const domainname *)ptr, kReverseIPv6Domain)) return (mDNSfalse);
4337*472cd20dSToomas Soome     if (outIPv6) mDNSPlatformMemCopy(outIPv6, ipv6, 16);
4338*472cd20dSToomas Soome     return (mDNStrue);
4339*472cd20dSToomas Soome }
4340*472cd20dSToomas Soome #endif // !STANDALONE
4341