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;