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