blob: cf27e23821b9f44b30709f95ab18a20dc1b5b2f7 [file] [log] [blame]
Harald Alvestrand8d4a5f12023-08-22 13:53:161/*
2 * Copyright 2023 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "rtc_base/async_dns_resolver.h"
12
13#include <memory>
14#include <string>
15#include <utility>
16#include <vector>
17
Harald Alvestrand8219cc32023-09-02 05:26:5518#include "api/make_ref_counted.h"
Harald Alvestrand8d4a5f12023-08-22 13:53:1619#include "rtc_base/logging.h"
20#include "rtc_base/platform_thread.h"
21
22#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
23#include <dispatch/dispatch.h>
24#endif
25
26namespace webrtc {
27
28namespace {
29
30#ifdef __native_client__
31int ResolveHostname(absl::string_view hostname,
32 int family,
33 std::vector<rtc::IPAddress>* addresses) {
34 RTC_DCHECK_NOTREACHED();
35 RTC_LOG(LS_WARNING) << "ResolveHostname() is not implemented for NaCl";
36 return -1;
37}
38#else // notdef(__native_client__)
39int ResolveHostname(absl::string_view hostname,
40 int family,
41 std::vector<rtc::IPAddress>& addresses) {
42 addresses.clear();
43 struct addrinfo* result = nullptr;
44 struct addrinfo hints = {0};
45 hints.ai_family = family;
46 // `family` here will almost always be AF_UNSPEC, because `family` comes from
47 // AsyncResolver::addr_.family(), which comes from a SocketAddress constructed
48 // with a hostname. When a SocketAddress is constructed with a hostname, its
49 // family is AF_UNSPEC. However, if someday in the future we construct
50 // a SocketAddress with both a hostname and a family other than AF_UNSPEC,
51 // then it would be possible to get a specific family value here.
52
53 // The behavior of AF_UNSPEC is roughly "get both ipv4 and ipv6", as
54 // documented by the various operating systems:
55 // Linux: http://man7.org/linux/man-pages/man3/getaddrinfo.3.html
56 // Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/
57 // ms738520(v=vs.85).aspx
58 // Mac: https://developer.apple.com/legacy/library/documentation/Darwin/
59 // Reference/ManPages/man3/getaddrinfo.3.html
60 // Android (source code, not documentation):
61 // https://android.googlesource.com/platform/bionic/+/
62 // 7e0bfb511e85834d7c6cb9631206b62f82701d60/libc/netbsd/net/getaddrinfo.c#1657
63 hints.ai_flags = AI_ADDRCONFIG;
64 int ret =
65 getaddrinfo(std::string(hostname).c_str(), nullptr, &hints, &result);
66 if (ret != 0) {
67 return ret;
68 }
69 struct addrinfo* cursor = result;
70 for (; cursor; cursor = cursor->ai_next) {
71 if (family == AF_UNSPEC || cursor->ai_family == family) {
72 rtc::IPAddress ip;
73 if (IPFromAddrInfo(cursor, &ip)) {
74 addresses.push_back(ip);
75 }
76 }
77 }
78 freeaddrinfo(result);
79 return 0;
80}
81#endif // !__native_client__
82
83// Special task posting for Mac/iOS
84#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
85void GlobalGcdRunTask(void* context) {
86 std::unique_ptr<absl::AnyInvocable<void() &&>> task(
87 static_cast<absl::AnyInvocable<void() &&>*>(context));
88 std::move (*task)();
89}
90
91// Post a task into the system-defined global concurrent queue.
92void PostTaskToGlobalQueue(
93 std::unique_ptr<absl::AnyInvocable<void() &&>> task) {
94 dispatch_async_f(
95 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
96 task.release(), &GlobalGcdRunTask);
97}
98#endif // defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
99
100} // namespace
101
Harald Alvestrand8219cc32023-09-02 05:26:55102class AsyncDnsResolver::State : public rtc::RefCountedBase {
103 public:
104 enum class Status {
105 kActive, // Running request, or able to be passed one
106 kFinished, // Request has finished processing
107 kDead // The owning AsyncDnsResolver has been deleted
108 };
109 static rtc::scoped_refptr<AsyncDnsResolver::State> Create() {
110 return rtc::make_ref_counted<AsyncDnsResolver::State>();
111 }
112
113 // Execute the passed function if the state is Active.
114 void Finish(absl::AnyInvocable<void()> function) {
115 webrtc::MutexLock lock(&mutex_);
116 if (status_ != Status::kActive) {
117 return;
118 }
119 status_ = Status::kFinished;
120 function();
121 }
122 void Kill() {
123 webrtc::MutexLock lock(&mutex_);
124 status_ = Status::kDead;
125 }
126
127 private:
128 webrtc::Mutex mutex_;
129 Status status_ RTC_GUARDED_BY(mutex_) = Status::kActive;
130};
131
132AsyncDnsResolver::AsyncDnsResolver() : state_(State::Create()) {}
133
134AsyncDnsResolver::~AsyncDnsResolver() {
135 state_->Kill();
136}
137
Harald Alvestrand8d4a5f12023-08-22 13:53:16138void AsyncDnsResolver::Start(const rtc::SocketAddress& addr,
Harald Alvestrand96e18822023-08-30 19:39:05139 absl::AnyInvocable<void()> callback) {
Harald Alvestrand8d4a5f12023-08-22 13:53:16140 Start(addr, addr.family(), std::move(callback));
141}
142
143// Start address resolution of the hostname in `addr` matching `family`.
144void AsyncDnsResolver::Start(const rtc::SocketAddress& addr,
145 int family,
Harald Alvestrand96e18822023-08-30 19:39:05146 absl::AnyInvocable<void()> callback) {
Harald Alvestrand8d4a5f12023-08-22 13:53:16147 RTC_DCHECK_RUN_ON(&result_.sequence_checker_);
148 result_.addr_ = addr;
Harald Alvestrand96e18822023-08-30 19:39:05149 callback_ = std::move(callback);
Harald Alvestrandf45c2ce2023-08-24 09:19:52150 auto thread_function = [this, addr, family, flag = safety_.flag(),
Harald Alvestrand8219cc32023-09-02 05:26:55151 caller_task_queue = webrtc::TaskQueueBase::Current(),
152 state = state_] {
Harald Alvestrand8d4a5f12023-08-22 13:53:16153 std::vector<rtc::IPAddress> addresses;
154 int error = ResolveHostname(addr.hostname(), family, addresses);
Harald Alvestrand8219cc32023-09-02 05:26:55155 // We assume that the caller task queue is still around if the
156 // AsyncDnsResolver has not been destroyed.
157 state->Finish([this, error, flag, caller_task_queue,
Tom Sepeza21152a2024-10-21 23:24:12158 addresses = std::move(addresses)]() mutable {
Harald Alvestrand8219cc32023-09-02 05:26:55159 caller_task_queue->PostTask(
Tom Sepeza21152a2024-10-21 23:24:12160 SafeTask(flag, [this, error, addresses = std::move(addresses)]() {
Harald Alvestrand8219cc32023-09-02 05:26:55161 RTC_DCHECK_RUN_ON(&result_.sequence_checker_);
162 result_.addresses_ = addresses;
163 result_.error_ = error;
164 callback_();
165 }));
166 });
Harald Alvestrand8d4a5f12023-08-22 13:53:16167 };
168#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
169 PostTaskToGlobalQueue(
170 std::make_unique<absl::AnyInvocable<void() &&>>(thread_function));
171#else
172 rtc::PlatformThread::SpawnDetached(std::move(thread_function),
173 "AsyncResolver");
174#endif
175}
176
177const AsyncDnsResolverResult& AsyncDnsResolver::result() const {
178 return result_;
179}
180
181bool AsyncDnsResolverResultImpl::GetResolvedAddress(
182 int family,
183 rtc::SocketAddress* addr) const {
184 RTC_DCHECK_RUN_ON(&sequence_checker_);
185 RTC_DCHECK(addr);
186 if (error_ != 0 || addresses_.empty())
187 return false;
188
189 *addr = addr_;
190 for (const auto& address : addresses_) {
191 if (family == address.family()) {
192 addr->SetResolvedIP(address);
193 return true;
194 }
195 }
196 return false;
197}
198
199int AsyncDnsResolverResultImpl::GetError() const {
200 RTC_DCHECK_RUN_ON(&sequence_checker_);
201 return error_;
202}
203
204} // namespace webrtc