• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/log/check.h"
22 #include "absl/strings/str_cat.h"
23 #include "absl/strings/str_format.h"
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/util/host_port.h"
29 
30 namespace grpc_event_engine {
31 namespace experimental {
32 
LookupHostname(EventEngine::DNSResolver::LookupHostnameCallback on_resolve,absl::string_view name,absl::string_view default_port)33 void DNSServiceResolverImpl::LookupHostname(
34     EventEngine::DNSResolver::LookupHostnameCallback on_resolve,
35     absl::string_view name, absl::string_view default_port) {
36   GRPC_TRACE_LOG(event_engine_dns, INFO)
37       << "DNSServiceResolverImpl::LookupHostname: name: " << name
38       << ", default_port: " << default_port << ", this: " << this;
39 
40   absl::string_view host;
41   absl::string_view port_string;
42   if (!grpc_core::SplitHostPort(name, &host, &port_string)) {
43     engine_->Run([on_resolve = std::move(on_resolve),
44                   status = absl::InvalidArgumentError(
45                       absl::StrCat("Unparsable name: ", name))]() mutable {
46       on_resolve(status);
47     });
48     return;
49   }
50   if (host.empty()) {
51     engine_->Run([on_resolve = std::move(on_resolve),
52                   status = absl::InvalidArgumentError(absl::StrCat(
53                       "host must not be empty in name: ", name))]() mutable {
54       on_resolve(status);
55     });
56     return;
57   }
58   if (port_string.empty()) {
59     if (default_port.empty()) {
60       engine_->Run([on_resolve = std::move(on_resolve),
61                     status = absl::InvalidArgumentError(absl::StrFormat(
62                         "No port in name %s or default_port argument",
63                         name))]() mutable { on_resolve(std::move(status)); });
64       return;
65     }
66     port_string = default_port;
67   }
68 
69   int port = 0;
70   if (port_string == "http") {
71     port = 80;
72   } else if (port_string == "https") {
73     port = 443;
74   } else if (!absl::SimpleAtoi(port_string, &port)) {
75     engine_->Run([on_resolve = std::move(on_resolve),
76                   status = absl::InvalidArgumentError(absl::StrCat(
77                       "Failed to parse port in name: ", name))]() mutable {
78       on_resolve(std::move(status));
79     });
80     return;
81   }
82 
83   // TODO(yijiem): Change this when refactoring code in
84   // src/core/lib/address_utils to use EventEngine::ResolvedAddress.
85   grpc_resolved_address addr;
86   const std::string hostport = grpc_core::JoinHostPort(host, port);
87   if (grpc_parse_ipv4_hostport(hostport.c_str(), &addr,
88                                /*log_errors=*/false) ||
89       grpc_parse_ipv6_hostport(hostport.c_str(), &addr,
90                                /*log_errors=*/false)) {
91     // Early out if the target is an ipv4 or ipv6 literal, otherwise dns service
92     // responses with kDNSServiceErr_NoSuchRecord
93     std::vector<EventEngine::ResolvedAddress> result;
94     result.emplace_back(reinterpret_cast<sockaddr*>(addr.addr), addr.len);
95     engine_->Run([on_resolve = std::move(on_resolve),
96                   result = std::move(result)]() mutable {
97       on_resolve(std::move(result));
98     });
99     return;
100   }
101 
102   DNSServiceRef sdRef;
103   auto host_string = std::string{host};
104   auto error = DNSServiceGetAddrInfo(
105       &sdRef, kDNSServiceFlagsTimeout | kDNSServiceFlagsReturnIntermediates, 0,
106       kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, host_string.c_str(),
107       &DNSServiceResolverImpl::ResolveCallback, this /* do not Ref */);
108 
109   if (error != kDNSServiceErr_NoError) {
110     engine_->Run([on_resolve = std::move(on_resolve),
111                   status = absl::UnknownError(absl::StrFormat(
112                       "DNSServiceGetAddrInfo failed with error:%d",
113                       error))]() mutable { on_resolve(std::move(status)); });
114     return;
115   }
116 
117   grpc_core::ReleasableMutexLock lock(&request_mu_);
118 
119   error = DNSServiceSetDispatchQueue(sdRef, queue_);
120   if (error != kDNSServiceErr_NoError) {
121     engine_->Run([on_resolve = std::move(on_resolve),
122                   status = absl::UnknownError(absl::StrFormat(
123                       "DNSServiceSetDispatchQueue failed with error:%d",
124                       error))]() mutable { on_resolve(std::move(status)); });
125     return;
126   }
127 
128   requests_.try_emplace(
129       sdRef, DNSServiceRequest{
130                  std::move(on_resolve), static_cast<uint16_t>(port), {}});
131 }
132 
133 /* static */
ResolveCallback(DNSServiceRef sdRef,DNSServiceFlags flags,uint32_t interfaceIndex,DNSServiceErrorType errorCode,const char * hostname,const struct sockaddr * address,uint32_t ttl,void * context)134 void DNSServiceResolverImpl::ResolveCallback(
135     DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
136     DNSServiceErrorType errorCode, const char* hostname,
137     const struct sockaddr* address, uint32_t ttl, void* context) {
138   GRPC_TRACE_LOG(event_engine_dns, INFO)
139       << "DNSServiceResolverImpl::ResolveCallback: sdRef: " << sdRef
140       << ", flags: " << flags << ", interface: " << interfaceIndex
141       << ", errorCode: " << errorCode << ", hostname: " << hostname
142       << ", addressFamily: " << address->sa_family << ", ttl: " << ttl
143       << ", this: " << context;
144 
145   // no need to increase refcount here, since ResolveCallback and Shutdown is
146   // called from the serial queue and it is guaranteed that it won't be called
147   // after the sdRef is deallocated
148   auto that = static_cast<DNSServiceResolverImpl*>(context);
149 
150   grpc_core::ReleasableMutexLock lock(&that->request_mu_);
151   auto request_it = that->requests_.find(sdRef);
152   CHECK(request_it != that->requests_.end());
153 
154   if (errorCode != kDNSServiceErr_NoError &&
155       errorCode != kDNSServiceErr_NoSuchRecord) {
156     // extrace request and release lock before calling on_resolve
157     auto request_node = that->requests_.extract(request_it);
158     lock.Release();
159 
160     auto& request = request_node.mapped();
161     request.on_resolve(absl::UnknownError(absl::StrFormat(
162         "address lookup failed for %s: errorCode: %d", hostname, errorCode)));
163     DNSServiceRefDeallocate(sdRef);
164     return;
165   }
166 
167   auto& request = request_it->second;
168 
169   // set received ipv4 or ipv6 response, even for kDNSServiceErr_NoSuchRecord to
170   // mark that the response for the stack is received, it is possible that the
171   // one stack receives some results and the other stack gets
172   // kDNSServiceErr_NoSuchRecord error.
173   if (address->sa_family == AF_INET) {
174     request.has_ipv4_response = true;
175   } else if (address->sa_family == AF_INET6) {
176     request.has_ipv6_response = true;
177   }
178 
179   // collect results if there is no error (not kDNSServiceErr_NoSuchRecord)
180   if (errorCode == kDNSServiceErr_NoError) {
181     request.result.emplace_back(address, address->sa_len);
182     auto& resolved_address = request.result.back();
183     if (address->sa_family == AF_INET) {
184       (const_cast<sockaddr_in*>(
185            reinterpret_cast<const sockaddr_in*>(resolved_address.address())))
186           ->sin_port = htons(request.port);
187     } else if (address->sa_family == AF_INET6) {
188       (const_cast<sockaddr_in6*>(
189            reinterpret_cast<const sockaddr_in6*>(resolved_address.address())))
190           ->sin6_port = htons(request.port);
191     }
192 
193     GRPC_TRACE_LOG(event_engine_dns, INFO)
194         << "DNSServiceResolverImpl::ResolveCallback: sdRef: " << sdRef
195         << ", hostname: " << hostname << ", addressPort: "
196         << ResolvedAddressToString(resolved_address).value_or("ERROR")
197         << ", this: " << context;
198   }
199 
200   // received both ipv4 and ipv6 responses, and no more responses (e.g. multiple
201   // IP addresses for a domain name) are coming, finish `LookupHostname` resolve
202   // with the collected results.
203   if (!(flags & kDNSServiceFlagsMoreComing) && request.has_ipv4_response &&
204       request.has_ipv6_response) {
205     // extrace request and release lock before calling on_resolve
206     auto request_node = that->requests_.extract(request_it);
207     lock.Release();
208 
209     auto& request = request_node.mapped();
210     if (request.result.empty()) {
211       request.on_resolve(absl::NotFoundError(absl::StrFormat(
212           "address lookup failed for %s: Domain name not found", hostname)));
213     } else {
214       request.on_resolve(std::move(request.result));
215     }
216     DNSServiceRefDeallocate(sdRef);
217   }
218 }
219 
Shutdown()220 void DNSServiceResolverImpl::Shutdown() {
221   dispatch_async_f(queue_, Ref().release(), [](void* thatPtr) {
222     grpc_core::RefCountedPtr<DNSServiceResolverImpl> that{
223         static_cast<DNSServiceResolverImpl*>(thatPtr)};
224 
225     grpc_core::ReleasableMutexLock lock(&that->request_mu_);
226     auto requests = std::exchange(that->requests_, {});
227     lock.Release();
228 
229     for (auto& kv : requests) {
230       auto& sdRef = kv.first;
231       auto& request = kv.second;
232       GRPC_TRACE_LOG(event_engine_dns, INFO)
233           << "DNSServiceResolverImpl::Shutdown sdRef: " << sdRef
234           << ", this: " << thatPtr;
235       request.on_resolve(
236           absl::CancelledError("DNSServiceResolverImpl::Shutdown"));
237       DNSServiceRefDeallocate(static_cast<DNSServiceRef>(sdRef));
238     }
239   });
240 }
241 
242 }  // namespace experimental
243 }  // namespace grpc_event_engine
244 
245 #endif  // AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER
246 #endif  // GPR_APPLE
247