PlatformDNSMac.cpp (5507B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=4 sw=2 sts=2 et cin: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "GetAddrInfo.h" 8 #include "mozilla/glean/NetwerkMetrics.h" 9 #include "mozilla/net/DNSPacket.h" 10 #include "nsIDNSService.h" 11 #include "mozilla/StaticPrefs_network.h" 12 13 #include <dns_sd.h> 14 #include <unistd.h> 15 #include <arpa/inet.h> 16 17 namespace mozilla::net { 18 19 #define LOG(msg, ...) \ 20 MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__)) 21 22 struct DNSContext { 23 nsresult mRv = NS_OK; 24 TypeRecordResultType* mResult; 25 nsCString mHost; 26 uint32_t* mTTL; 27 }; 28 29 // Callback for DNSServiceQueryRecord 30 void QueryCallback(DNSServiceRef aSDRef, DNSServiceFlags aFlags, 31 uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode, 32 const char* aFullname, uint16_t aRRType, uint16_t aRRClass, 33 uint16_t aRDLen, const void* aRdata, uint32_t aTtl, 34 void* aContext) { 35 struct DNSContext* context = (struct DNSContext*)aContext; 36 37 LOG("DNS response name: %s type: %u rdlen %u class %u ttl %u", aFullname, 38 aRRType, aRDLen, aRRClass, aTtl); 39 40 if (aErrorCode != kDNSServiceErr_NoError) { 41 LOG("Error resolving record: %d\n", aErrorCode); 42 context->mRv = NS_ERROR_UNKNOWN_HOST; 43 return; 44 } 45 46 if (NS_FAILED(context->mRv)) { 47 LOG("Parsing already failed for a previous record"); 48 return; 49 } 50 51 // Process the rdata for HTTPS records (type 65) 52 if (aRRType != TRRTYPE_HTTPSSVC || aRDLen == 0) { 53 context->mRv = NS_ERROR_UNKNOWN_HOST; 54 return; 55 } 56 57 nsDependentCString fullname(aFullname); 58 if (fullname.Length() && fullname.Last() == '.') { 59 // The fullname argument is always FQDN 60 fullname.Rebind(aFullname, fullname.Length() - 1); 61 } 62 63 struct SVCB parsed; 64 nsresult rv = DNSPacket::ParseHTTPS( 65 aRDLen, parsed, 0, (const unsigned char*)aRdata, aRDLen, fullname); 66 if (NS_FAILED(rv)) { 67 LOG("ParseHTTPS failed\n"); 68 context->mRv = rv; 69 return; 70 } 71 72 if (parsed.mSvcDomainName.IsEmpty() && parsed.mSvcFieldPriority == 0) { 73 // For AliasMode SVCB RRs, a TargetName of "." indicates that the 74 // service is not available or does not exist. 75 return; 76 } 77 78 if (parsed.mSvcFieldPriority == 0) { 79 // Alias form SvcDomainName must not have the "." value (empty) 80 if (parsed.mSvcDomainName.IsEmpty()) { 81 context->mRv = NS_ERROR_UNEXPECTED; 82 return; 83 } 84 LOG("alias mode %s -> %s", context->mHost.get(), 85 parsed.mSvcDomainName.get()); 86 context->mHost = parsed.mSvcDomainName; 87 ToLowerCase(context->mHost); 88 return; 89 } 90 91 if (!context->mResult->is<TypeRecordHTTPSSVC>()) { 92 *context->mResult = mozilla::AsVariant(CopyableTArray<SVCB>()); 93 } 94 auto& results = context->mResult->as<TypeRecordHTTPSSVC>(); 95 results.AppendElement(parsed); 96 *context->mTTL = std::min<uint32_t>(*context->mTTL, aTtl); 97 } 98 99 nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, 100 nsIDNSService::DNSFlags aFlags, 101 TypeRecordResultType& aResult, uint32_t& aTTL) { 102 nsAutoCString host(aHost); 103 nsAutoCString cname; 104 105 if (xpc::IsInAutomation() && 106 !StaticPrefs::network_dns_native_https_query_in_automation()) { 107 return NS_ERROR_UNKNOWN_HOST; 108 } 109 110 LOG("resolving %s\n", host.get()); 111 TimeStamp startTime = TimeStamp::Now(); 112 113 struct DNSContext context{ 114 .mResult = &aResult, 115 .mHost = host, 116 .mTTL = &aTTL, 117 }; 118 119 DNSServiceRef sdRef; 120 DNSServiceErrorType err; 121 122 err = DNSServiceQueryRecord(&sdRef, 123 0, // No flags 124 0, // All interfaces 125 host.get(), TRRTYPE_HTTPSSVC, kDNSServiceClass_IN, 126 QueryCallback, &context); 127 128 if (err != kDNSServiceErr_NoError) { 129 LOG("DNSServiceQueryRecord failed: %d\n", err); 130 return NS_ERROR_UNKNOWN_HOST; 131 } 132 133 int fd = DNSServiceRefSockFD(sdRef); 134 fd_set readfds; 135 FD_ZERO(&readfds); 136 FD_SET(fd, &readfds); 137 138 // If the domain queried results in NXDOMAIN, then QueryCallback will 139 // never get called, and select will hang forever. We need to use a 140 // timeout so that select() eventually returns. 141 struct timeval timeout; 142 timeout.tv_sec = 143 StaticPrefs::network_dns_native_https_timeout_mac_msec() / 1000; 144 timeout.tv_usec = 145 (StaticPrefs::network_dns_native_https_timeout_mac_msec() % 1000) * 1000; 146 147 int result = select(fd + 1, &readfds, NULL, NULL, &timeout); 148 if (result > 0 && FD_ISSET(fd, &readfds)) { 149 // Process the result 150 DNSServiceProcessResult(sdRef); 151 } else if (result < 0) { 152 LOG("select() failed"); 153 } else if (result == 0) { 154 LOG("select timed out"); 155 } 156 157 // Cleanup 158 DNSServiceRefDeallocate(sdRef); 159 mozilla::glean::networking::dns_native_https_call_time.AccumulateRawDuration( 160 TimeStamp::Now() - startTime); 161 162 LOG("resolving %s done %x ttl=%u", host.get(), context.mRv, aTTL); 163 if (NS_FAILED(context.mRv)) { 164 return context.mRv; 165 } 166 if (aResult.is<Nothing>()) { 167 // The call succeeded, but no HTTPS records were found. 168 return NS_ERROR_UNKNOWN_HOST; 169 } 170 if (aTTL == UINT32_MAX) { 171 aTTL = 60; // Defaults to 60 seconds 172 } 173 return NS_OK; 174 } 175 176 void DNSThreadShutdown() {} 177 178 } // namespace mozilla::net