xref: /aosp_15_r20/external/grpc-grpc/src/core/lib/event_engine/cf_engine/dns_service_resolver.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 // Copyright 2023 The gRPC Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <grpc/support/port_platform.h>
16 
17 #ifdef GPR_APPLE
18 #include <AvailabilityMacros.h>
19 #ifdef AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER
20 
21 #include "absl/strings/str_cat.h"
22 #include "absl/strings/str_format.h"
23 
24 #include "src/core/lib/address_utils/parse_address.h"
25 #include "src/core/lib/event_engine/cf_engine/dns_service_resolver.h"
26 #include "src/core/lib/event_engine/posix_engine/lockfree_event.h"
27 #include "src/core/lib/event_engine/tcp_socket_utils.h"
28 #include "src/core/lib/event_engine/trace.h"
29 #include "src/core/lib/gprpp/host_port.h"
30 
31 namespace grpc_event_engine {
32 namespace experimental {
33 
LookupHostname(EventEngine::DNSResolver::LookupHostnameCallback on_resolve,absl::string_view name,absl::string_view default_port)34 void DNSServiceResolverImpl::LookupHostname(
35     EventEngine::DNSResolver::LookupHostnameCallback on_resolve,
36     absl::string_view name, absl::string_view default_port) {
37   GRPC_EVENT_ENGINE_DNS_TRACE(
38       "DNSServiceResolverImpl::LookupHostname: name: %.*s, default_port: %.*s, "
39       "this: %p",
40       static_cast<int>(name.length()), name.data(),
41       static_cast<int>(default_port.length()), default_port.data(), this);
42 
43   absl::string_view host;
44   absl::string_view port_string;
45   if (!grpc_core::SplitHostPort(name, &host, &port_string)) {
46     engine_->Run([on_resolve = std::move(on_resolve),
47                   status = absl::InvalidArgumentError(
48                       absl::StrCat("Unparseable name: ", name))]() mutable {
49       on_resolve(status);
50     });
51     return;
52   }
53   if (host.empty()) {
54     engine_->Run([on_resolve = std::move(on_resolve),
55                   status = absl::InvalidArgumentError(absl::StrCat(
56                       "host must not be empty in name: ", name))]() mutable {
57       on_resolve(status);
58     });
59     return;
60   }
61   if (port_string.empty()) {
62     if (default_port.empty()) {
63       engine_->Run([on_resolve = std::move(on_resolve),
64                     status = absl::InvalidArgumentError(absl::StrFormat(
65                         "No port in name %s or default_port argument",
66                         name))]() mutable { on_resolve(std::move(status)); });
67       return;
68     }
69     port_string = default_port;
70   }
71 
72   int port = 0;
73   if (port_string == "http") {
74     port = 80;
75   } else if (port_string == "https") {
76     port = 443;
77   } else if (!absl::SimpleAtoi(port_string, &port)) {
78     engine_->Run([on_resolve = std::move(on_resolve),
79                   status = absl::InvalidArgumentError(absl::StrCat(
80                       "Failed to parse port in name: ", name))]() mutable {
81       on_resolve(std::move(status));
82     });
83     return;
84   }
85 
86   // TODO(yijiem): Change this when refactoring code in
87   // src/core/lib/address_utils to use EventEngine::ResolvedAddress.
88   grpc_resolved_address addr;
89   const std::string hostport = grpc_core::JoinHostPort(host, port);
90   if (grpc_parse_ipv4_hostport(hostport.c_str(), &addr,
91                                /*log_errors=*/false) ||
92       grpc_parse_ipv6_hostport(hostport.c_str(), &addr,
93                                /*log_errors=*/false)) {
94     // Early out if the target is an ipv4 or ipv6 literal, otherwise dns service
95     // responses with kDNSServiceErr_NoSuchRecord
96     std::vector<EventEngine::ResolvedAddress> result;
97     result.emplace_back(reinterpret_cast<sockaddr*>(addr.addr), addr.len);
98     engine_->Run([on_resolve = std::move(on_resolve),
99                   result = std::move(result)]() mutable {
100       on_resolve(std::move(result));
101     });
102     return;
103   }
104 
105   DNSServiceRef sdRef;
106   auto host_string = std::string{host};
107   auto error = DNSServiceGetAddrInfo(
108       &sdRef, kDNSServiceFlagsTimeout | kDNSServiceFlagsReturnIntermediates, 0,
109       kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, host_string.c_str(),
110       &DNSServiceResolverImpl::ResolveCallback, this /* do not Ref */);
111 
112   if (error != kDNSServiceErr_NoError) {
113     engine_->Run([on_resolve = std::move(on_resolve),
114                   status = absl::UnknownError(absl::StrFormat(
115                       "DNSServiceGetAddrInfo failed with error:%d",
116                       error))]() mutable { on_resolve(std::move(status)); });
117     return;
118   }
119 
120   grpc_core::ReleasableMutexLock lock(&request_mu_);
121 
122   error = DNSServiceSetDispatchQueue(sdRef, queue_);
123   if (error != kDNSServiceErr_NoError) {
124     engine_->Run([on_resolve = std::move(on_resolve),
125                   status = absl::UnknownError(absl::StrFormat(
126                       "DNSServiceSetDispatchQueue failed with error:%d",
127                       error))]() mutable { on_resolve(std::move(status)); });
128     return;
129   }
130 
131   requests_.try_emplace(
132       sdRef, DNSServiceRequest{
133                  std::move(on_resolve), static_cast<uint16_t>(port), {}});
134 }
135 
136 /* static */
ResolveCallback(DNSServiceRef sdRef,DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * hostname,const struct sockaddr * address,uint32_t ttl,void * context)137 void DNSServiceResolverImpl::ResolveCallback(
138     DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
139     DNSServiceErrorType errorCode, const char* hostname,
140     const struct sockaddr* address, uint32_t ttl, void* context) {
141   GRPC_EVENT_ENGINE_DNS_TRACE(
142       "DNSServiceResolverImpl::ResolveCallback: sdRef: %p, flags: %x, "
143       "interface: %d, errorCode: %d, hostname: %s, addressFamily: %d, ttl: "
144       "%d, "
145       "this: %p",
146       sdRef, flags, interfaceIndex, errorCode, hostname, address->sa_family,
147       ttl, context);
148 
149   // no need to increase refcount here, since ResolveCallback and Shutdown is
150   // called from the serial queue and it is guarenteed that it won't be called
151   // after the sdRef is deallocated
152   auto that = static_cast<DNSServiceResolverImpl*>(context);
153 
154   grpc_core::ReleasableMutexLock lock(&that->request_mu_);
155   auto request_it = that->requests_.find(sdRef);
156   GPR_ASSERT(request_it != that->requests_.end());
157 
158   if (errorCode != kDNSServiceErr_NoError &&
159       errorCode != kDNSServiceErr_NoSuchRecord) {
160     // extrace request and release lock before calling on_resolve
161     auto request_node = that->requests_.extract(request_it);
162     lock.Release();
163 
164     auto& request = request_node.mapped();
165     request.on_resolve(absl::UnknownError(absl::StrFormat(
166         "address lookup failed for %s: errorCode: %d", hostname, errorCode)));
167     DNSServiceRefDeallocate(sdRef);
168     return;
169   }
170 
171   auto& request = request_it->second;
172 
173   // set received ipv4 or ipv6 response, even for kDNSServiceErr_NoSuchRecord to
174   // mark that the response for the stack is received, it is possible that the
175   // one stack receives some results and the other stack gets
176   // kDNSServiceErr_NoSuchRecord error.
177   if (address->sa_family == AF_INET) {
178     request.has_ipv4_response = true;
179   } else if (address->sa_family == AF_INET6) {
180     request.has_ipv6_response = true;
181   }
182 
183   // collect results if there is no error (not kDNSServiceErr_NoSuchRecord)
184   if (errorCode == kDNSServiceErr_NoError) {
185     request.result.emplace_back(address, address->sa_len);
186     auto& resolved_address = request.result.back();
187     if (address->sa_family == AF_INET) {
188       (const_cast<sockaddr_in*>(
189            reinterpret_cast<const sockaddr_in*>(resolved_address.address())))
190           ->sin_port = htons(request.port);
191     } else if (address->sa_family == AF_INET6) {
192       (const_cast<sockaddr_in6*>(
193            reinterpret_cast<const sockaddr_in6*>(resolved_address.address())))
194           ->sin6_port = htons(request.port);
195     }
196 
197     GRPC_EVENT_ENGINE_DNS_TRACE(
198         "DNSServiceResolverImpl::ResolveCallback: "
199         "sdRef: %p, hostname: %s, addressPort: %s, this: %p",
200         sdRef, hostname,
201         ResolvedAddressToString(resolved_address).value_or("ERROR").c_str(),
202         context);
203   }
204 
205   // received both ipv4 and ipv6 responses, and no more responses (e.g. multiple
206   // IP addresses for a domain name) are coming, finish `LookupHostname` resolve
207   // with the collected results.
208   if (!(flags & kDNSServiceFlagsMoreComing) && request.has_ipv4_response &&
209       request.has_ipv6_response) {
210     // extrace request and release lock before calling on_resolve
211     auto request_node = that->requests_.extract(request_it);
212     lock.Release();
213 
214     auto& request = request_node.mapped();
215     if (request.result.empty()) {
216       request.on_resolve(absl::NotFoundError(absl::StrFormat(
217           "address lookup failed for %s: Domain name not found", hostname)));
218     } else {
219       request.on_resolve(std::move(request.result));
220     }
221     DNSServiceRefDeallocate(sdRef);
222   }
223 }
224 
Shutdown()225 void DNSServiceResolverImpl::Shutdown() {
226   dispatch_async_f(queue_, Ref().release(), [](void* thatPtr) {
227     grpc_core::RefCountedPtr<DNSServiceResolverImpl> that{
228         static_cast<DNSServiceResolverImpl*>(thatPtr)};
229     grpc_core::MutexLock lock(&that->request_mu_);
230     for (auto& kv : that->requests_) {
231       auto& sdRef = kv.first;
232       auto& request = kv.second;
233       GRPC_EVENT_ENGINE_DNS_TRACE(
234           "DNSServiceResolverImpl::Shutdown sdRef: %p, this: %p", sdRef,
235           thatPtr);
236 
237       request.on_resolve(
238           absl::CancelledError("DNSServiceResolverImpl::Shutdown"));
239       DNSServiceRefDeallocate(static_cast<DNSServiceRef>(sdRef));
240     }
241     that->requests_.clear();
242   });
243 }
244 
245 }  // namespace experimental
246 }  // namespace grpc_event_engine
247 
248 #endif  // AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER
249 #endif  // GPR_APPLE
250