| /* | 
 |  *  Copyright 2023 The WebRTC Project Authors. All rights reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "rtc_base/async_dns_resolver.h" | 
 |  | 
 | #include <memory> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "api/make_ref_counted.h" | 
 | #include "rtc_base/logging.h" | 
 | #include "rtc_base/platform_thread.h" | 
 |  | 
 | #if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) | 
 | #include <dispatch/dispatch.h> | 
 | #endif | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | namespace { | 
 |  | 
 | #ifdef __native_client__ | 
 | int ResolveHostname(absl::string_view hostname, | 
 |                     int family, | 
 |                     std::vector<rtc::IPAddress>* addresses) { | 
 |   RTC_DCHECK_NOTREACHED(); | 
 |   RTC_LOG(LS_WARNING) << "ResolveHostname() is not implemented for NaCl"; | 
 |   return -1; | 
 | } | 
 | #else   // notdef(__native_client__) | 
 | int ResolveHostname(absl::string_view hostname, | 
 |                     int family, | 
 |                     std::vector<IPAddress>& addresses) { | 
 |   addresses.clear(); | 
 |   struct addrinfo* result = nullptr; | 
 |   struct addrinfo hints = {0}; | 
 |   hints.ai_family = family; | 
 |   // `family` here will almost always be AF_UNSPEC, because `family` comes from | 
 |   // AsyncResolver::addr_.family(), which comes from a SocketAddress constructed | 
 |   // with a hostname. When a SocketAddress is constructed with a hostname, its | 
 |   // family is AF_UNSPEC. However, if someday in the future we construct | 
 |   // a SocketAddress with both a hostname and a family other than AF_UNSPEC, | 
 |   // then it would be possible to get a specific family value here. | 
 |  | 
 |   // The behavior of AF_UNSPEC is roughly "get both ipv4 and ipv6", as | 
 |   // documented by the various operating systems: | 
 |   // Linux: http://man7.org/linux/man-pages/man3/getaddrinfo.3.html | 
 |   // Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/ | 
 |   // ms738520(v=vs.85).aspx | 
 |   // Mac: https://developer.apple.com/legacy/library/documentation/Darwin/ | 
 |   // Reference/ManPages/man3/getaddrinfo.3.html | 
 |   // Android (source code, not documentation): | 
 |   // https://android.googlesource.com/platform/bionic/+/ | 
 |   // 7e0bfb511e85834d7c6cb9631206b62f82701d60/libc/netbsd/net/getaddrinfo.c#1657 | 
 |   hints.ai_flags = AI_ADDRCONFIG; | 
 |   int ret = | 
 |       getaddrinfo(std::string(hostname).c_str(), nullptr, &hints, &result); | 
 |   if (ret != 0) { | 
 |     return ret; | 
 |   } | 
 |   struct addrinfo* cursor = result; | 
 |   for (; cursor; cursor = cursor->ai_next) { | 
 |     if (family == AF_UNSPEC || cursor->ai_family == family) { | 
 |       IPAddress ip; | 
 |       if (IPFromAddrInfo(cursor, &ip)) { | 
 |         addresses.push_back(ip); | 
 |       } | 
 |     } | 
 |   } | 
 |   freeaddrinfo(result); | 
 |   return 0; | 
 | } | 
 | #endif  // !__native_client__ | 
 |  | 
 | // Special task posting for Mac/iOS | 
 | #if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) | 
 | void GlobalGcdRunTask(void* context) { | 
 |   std::unique_ptr<absl::AnyInvocable<void() &&>> task( | 
 |       static_cast<absl::AnyInvocable<void() &&>*>(context)); | 
 |   std::move (*task)(); | 
 | } | 
 |  | 
 | // Post a task into the system-defined global concurrent queue. | 
 | void PostTaskToGlobalQueue( | 
 |     std::unique_ptr<absl::AnyInvocable<void() &&>> task) { | 
 |   dispatch_async_f( | 
 |       dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), | 
 |       task.release(), &GlobalGcdRunTask); | 
 | } | 
 | #endif  // defined(WEBRTC_MAC) || defined(WEBRTC_IOS) | 
 |  | 
 | }  // namespace | 
 |  | 
 | class AsyncDnsResolver::State : public rtc::RefCountedBase { | 
 |  public: | 
 |   enum class Status { | 
 |     kActive,    // Running request, or able to be passed one | 
 |     kFinished,  // Request has finished processing | 
 |     kDead       // The owning AsyncDnsResolver has been deleted | 
 |   }; | 
 |   static rtc::scoped_refptr<AsyncDnsResolver::State> Create() { | 
 |     return rtc::make_ref_counted<AsyncDnsResolver::State>(); | 
 |   } | 
 |  | 
 |   // Execute the passed function if the state is Active. | 
 |   void Finish(absl::AnyInvocable<void()> function) { | 
 |     webrtc::MutexLock lock(&mutex_); | 
 |     if (status_ != Status::kActive) { | 
 |       return; | 
 |     } | 
 |     status_ = Status::kFinished; | 
 |     function(); | 
 |   } | 
 |   void Kill() { | 
 |     webrtc::MutexLock lock(&mutex_); | 
 |     status_ = Status::kDead; | 
 |   } | 
 |  | 
 |  private: | 
 |   webrtc::Mutex mutex_; | 
 |   Status status_ RTC_GUARDED_BY(mutex_) = Status::kActive; | 
 | }; | 
 |  | 
 | AsyncDnsResolver::AsyncDnsResolver() : state_(State::Create()) {} | 
 |  | 
 | AsyncDnsResolver::~AsyncDnsResolver() { | 
 |   state_->Kill(); | 
 | } | 
 |  | 
 | void AsyncDnsResolver::Start(const SocketAddress& addr, | 
 |                              absl::AnyInvocable<void()> callback) { | 
 |   Start(addr, addr.family(), std::move(callback)); | 
 | } | 
 |  | 
 | // Start address resolution of the hostname in `addr` matching `family`. | 
 | void AsyncDnsResolver::Start(const SocketAddress& addr, | 
 |                              int family, | 
 |                              absl::AnyInvocable<void()> callback) { | 
 |   RTC_DCHECK_RUN_ON(&result_.sequence_checker_); | 
 |   result_.addr_ = addr; | 
 |   callback_ = std::move(callback); | 
 |   auto thread_function = [this, addr, family, flag = safety_.flag(), | 
 |                           caller_task_queue = webrtc::TaskQueueBase::Current(), | 
 |                           state = state_] { | 
 |     std::vector<IPAddress> addresses; | 
 |     int error = ResolveHostname(addr.hostname(), family, addresses); | 
 |     // We assume that the caller task queue is still around if the | 
 |     // AsyncDnsResolver has not been destroyed. | 
 |     state->Finish([this, error, flag, caller_task_queue, | 
 |                    addresses = std::move(addresses)]() mutable { | 
 |       caller_task_queue->PostTask( | 
 |           SafeTask(flag, [this, error, addresses = std::move(addresses)]() { | 
 |             RTC_DCHECK_RUN_ON(&result_.sequence_checker_); | 
 |             result_.addresses_ = addresses; | 
 |             result_.error_ = error; | 
 |             callback_(); | 
 |           })); | 
 |     }); | 
 |   }; | 
 | #if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) | 
 |   PostTaskToGlobalQueue( | 
 |       std::make_unique<absl::AnyInvocable<void() &&>>(thread_function)); | 
 | #else | 
 |   rtc::PlatformThread::SpawnDetached(std::move(thread_function), | 
 |                                      "AsyncResolver"); | 
 | #endif | 
 | } | 
 |  | 
 | const AsyncDnsResolverResult& AsyncDnsResolver::result() const { | 
 |   return result_; | 
 | } | 
 |  | 
 | bool AsyncDnsResolverResultImpl::GetResolvedAddress(int family, | 
 |                                                     SocketAddress* addr) const { | 
 |   RTC_DCHECK_RUN_ON(&sequence_checker_); | 
 |   RTC_DCHECK(addr); | 
 |   if (error_ != 0 || addresses_.empty()) | 
 |     return false; | 
 |  | 
 |   *addr = addr_; | 
 |   for (const auto& address : addresses_) { | 
 |     if (family == address.family()) { | 
 |       addr->SetResolvedIP(address); | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | int AsyncDnsResolverResultImpl::GetError() const { | 
 |   RTC_DCHECK_RUN_ON(&sequence_checker_); | 
 |   return error_; | 
 | } | 
 |  | 
 | }  // namespace webrtc |