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