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