Commit 135dadb4 authored by Scott Sheehan's avatar Scott Sheehan Committed by Commit Bot

Encapsulate addrinfo in RAII type.

This change encapsulates |addrinfo| in an RAII class and abstracts the
implementation details of invoking |getaddrinfo| from callers. (The callers
still need to know how to use |hints| however).

One helper function moved from host_resolver_proc to the |AddressInfo|
class, and some massaging of error codes from host_resolver_proc was
also moved to address_info.cc.

The actual call to |getaddrinfo| is wrapped in a class with two virtual
calls (eponymously named |getaddrinfo| and |freeaddrinfo|) so that they
can be replaced in unit tests.

Unit tests are added for the code in address_info.cc.

Change-Id: Iada36ebbe0b783cd395f100c4e9d5753b5de2265
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1760461Reviewed-by: default avatarEric Orth <ericorth@chromium.org>
Commit-Queue: Scott Sheehan <ssheehan@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#710863}
parent 7e10150a
......@@ -19,6 +19,8 @@ AddressList::AddressList() = default;
AddressList::AddressList(const AddressList&) = default;
AddressList& AddressList::operator=(const AddressList&) = default;
AddressList::~AddressList() = default;
AddressList::AddressList(const IPEndPoint& endpoint) {
......
......@@ -28,6 +28,7 @@ class NET_EXPORT AddressList {
public:
AddressList();
AddressList(const AddressList&);
AddressList& operator=(const AddressList&);
~AddressList();
// Creates an address list for a single IP literal.
......
......@@ -32,6 +32,8 @@ source_set("dns") {
if (!is_nacl) {
sources += [
"address_info.cc",
"address_info.h",
"address_sorter.h",
"address_sorter_win.cc",
"context_host_resolver.cc",
......@@ -374,6 +376,7 @@ source_set("mdns_client") {
source_set("tests") {
testonly = true
sources = [
"address_info_unittest.cc",
"context_host_resolver_unittest.cc",
"dns_config_service_unittest.cc",
"dns_config_service_win_unittest.cc",
......@@ -427,11 +430,13 @@ source_set("tests") {
source_set("test_support") {
testonly = true
sources = [
"address_info_test_util.cc",
"dns_test_util.cc",
"mock_host_resolver.cc",
"test_dns_config_service.cc",
]
public = [
"address_info_test_util.h",
"dns_test_util.h",
"mock_host_resolver.h",
"test_dns_config_service.h",
......
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/dns/address_info.h"
#include "base/logging.h"
#include "base/sys_byteorder.h"
#include "net/base/address_list.h"
#include "net/base/net_errors.h"
#include "net/base/sys_addrinfo.h"
namespace net {
namespace {
const addrinfo* Next(const addrinfo* ai) {
return ai->ai_next;
}
} // namespace
//// iterator
AddressInfo::const_iterator::const_iterator(const addrinfo* ai) : ai_(ai) {}
bool AddressInfo::const_iterator::operator!=(
const AddressInfo::const_iterator& o) const {
return ai_ != o.ai_;
}
AddressInfo::const_iterator& AddressInfo::const_iterator::operator++() {
ai_ = Next(ai_);
return *this;
}
const addrinfo* AddressInfo::const_iterator::operator->() const {
return ai_;
}
const addrinfo& AddressInfo::const_iterator::operator*() const {
return *ai_;
}
//// constructors
AddressInfo::AddressInfoAndResult AddressInfo::Get(
const std::string& host,
const addrinfo& hints,
std::unique_ptr<AddrInfoGetter> getter) {
if (getter == nullptr)
getter = std::make_unique<AddrInfoGetter>();
int err = OK;
int os_error = 0;
addrinfo* ai = getter->getaddrinfo(host, &hints, &os_error);
if (!ai) {
err = ERR_NAME_NOT_RESOLVED;
// If the call to getaddrinfo() failed because of a system error, report
// it separately from ERR_NAME_NOT_RESOLVED.
#if defined(OS_WIN)
if (os_error != WSAHOST_NOT_FOUND && os_error != WSANO_DATA)
err = ERR_NAME_RESOLUTION_FAILED;
#elif defined(OS_ANDROID)
// Workaround for Android's getaddrinfo leaving ai==nullptr without an
// error.
// http://crbug.com/134142
err = ERR_NAME_NOT_RESOLVED;
#elif defined(OS_POSIX) && !defined(OS_FREEBSD)
if (os_error != EAI_NONAME && os_error != EAI_NODATA)
err = ERR_NAME_RESOLUTION_FAILED;
#endif
return AddressInfoAndResult(base::Optional<AddressInfo>(), err, os_error);
}
return AddressInfoAndResult(
base::Optional<AddressInfo>(AddressInfo(ai, std::move(getter))), OK, 0);
}
AddressInfo::AddressInfo(AddressInfo&& other)
: ai_(other.ai_), getter_(std::move(other.getter_)) {
other.ai_ = nullptr;
}
AddressInfo& AddressInfo::operator=(AddressInfo&& other) {
ai_ = other.ai_;
other.ai_ = nullptr;
getter_ = std::move(other.getter_);
return *this;
}
AddressInfo::~AddressInfo() {
if (ai_)
getter_->freeaddrinfo(ai_);
}
//// public methods
AddressInfo::const_iterator AddressInfo::begin() const {
return const_iterator(ai_);
}
AddressInfo::const_iterator AddressInfo::end() const {
return const_iterator(nullptr);
}
base::Optional<std::string> AddressInfo::GetCanonicalName() const {
return (ai_->ai_canonname != nullptr)
? base::Optional<std::string>(std::string(ai_->ai_canonname))
: base::Optional<std::string>();
}
bool AddressInfo::IsAllLocalhostOfOneFamily() const {
bool saw_v4_localhost = false;
bool saw_v6_localhost = false;
const auto* ai = ai_;
for (; ai != nullptr; ai = Next(ai)) {
switch (ai->ai_family) {
case AF_INET: {
const struct sockaddr_in* addr_in =
reinterpret_cast<struct sockaddr_in*>(ai->ai_addr);
if ((base::NetToHost32(addr_in->sin_addr.s_addr) & 0xff000000) ==
0x7f000000)
saw_v4_localhost = true;
else
return false;
break;
}
case AF_INET6: {
const struct sockaddr_in6* addr_in6 =
reinterpret_cast<struct sockaddr_in6*>(ai->ai_addr);
if (IN6_IS_ADDR_LOOPBACK(&addr_in6->sin6_addr))
saw_v6_localhost = true;
else
return false;
break;
}
default:
NOTREACHED();
return false;
}
}
return saw_v4_localhost != saw_v6_localhost;
}
AddressList AddressInfo::CreateAddressList() const {
AddressList list;
auto canonical_name = GetCanonicalName();
if (canonical_name)
list.set_canonical_name(*canonical_name);
for (auto&& ai : *this) {
IPEndPoint ipe;
// NOTE: Ignoring non-INET* families.
if (ipe.FromSockAddr(ai.ai_addr, ai.ai_addrlen))
list.push_back(ipe);
else
DLOG(WARNING) << "Unknown family found in addrinfo: " << ai.ai_family;
}
return list;
}
//// private methods
AddressInfo::AddressInfo(addrinfo* ai, std::unique_ptr<AddrInfoGetter> getter)
: ai_(ai), getter_(std::move(getter)) {}
//// AddrInfoGetter
AddrInfoGetter::AddrInfoGetter() = default;
AddrInfoGetter::~AddrInfoGetter() = default;
addrinfo* AddrInfoGetter::getaddrinfo(const std::string& host,
const addrinfo* hints,
int* out_os_error) {
addrinfo* ai;
*out_os_error = ::getaddrinfo(host.c_str(), nullptr, hints, &ai);
if (*out_os_error) {
#if defined(OS_WIN)
*out_os_error = WSAGetLastError();
#endif
return nullptr;
}
return ai;
}
void AddrInfoGetter::freeaddrinfo(addrinfo* ai) {
::freeaddrinfo(ai);
}
} // namespace net
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_DNS_ADDRESS_INFO_H_
#define NET_DNS_ADDRESS_INFO_H_
#include <string>
#include <tuple>
#include "base/macros.h"
#include "base/optional.h"
#include "build/build_config.h"
#include "net/base/address_family.h"
#include "net/base/net_export.h"
#include "net/base/sys_addrinfo.h"
namespace net {
class AddressList;
class AddrInfoGetter;
// AddressInfo -- this encapsulates the system call to getaddrinfo and the
// data structure that it populates and returns.
class NET_EXPORT_PRIVATE AddressInfo {
public:
// Types
class NET_EXPORT_PRIVATE const_iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = const addrinfo;
using difference_type = std::ptrdiff_t;
using pointer = const addrinfo*;
using reference = const addrinfo&;
const_iterator(const const_iterator& other) = default;
explicit const_iterator(const addrinfo* ai);
bool operator!=(const const_iterator& o) const;
const_iterator& operator++(); // prefix
const addrinfo* operator->() const;
const addrinfo& operator*() const;
private:
const addrinfo* ai_;
};
// Constructors
using AddressInfoAndResult = std::
tuple<base::Optional<AddressInfo>, int /* err */, int /* os_error */>;
// Invokes AddrInfoGetter with provided |host| and |hints|. If |getter| is
// null, the system's getaddrinfo will be invoked. (A non-null |getter| is
// primarily for tests).
static AddressInfoAndResult Get(
const std::string& host,
const addrinfo& hints,
std::unique_ptr<AddrInfoGetter> getter = nullptr);
AddressInfo(AddressInfo&& other);
AddressInfo& operator=(AddressInfo&& other);
~AddressInfo();
// Accessors
const_iterator begin() const;
const_iterator end() const;
// Methods
base::Optional<std::string> GetCanonicalName() const;
bool IsAllLocalhostOfOneFamily() const;
AddressList CreateAddressList() const;
private:
// Constructors
AddressInfo(addrinfo* ai, std::unique_ptr<AddrInfoGetter> getter);
// Data.
addrinfo* ai_; // Never null (except after move)
std::unique_ptr<AddrInfoGetter> getter_;
DISALLOW_COPY_AND_ASSIGN(AddressInfo);
};
// Encapsulates calls to getaddrinfo and freeaddrinfo for tests.
class NET_EXPORT_PRIVATE AddrInfoGetter {
public:
AddrInfoGetter();
// Virtual for tests.
virtual ~AddrInfoGetter();
virtual addrinfo* getaddrinfo(const std::string& host,
const addrinfo* hints,
int* out_os_error);
virtual void freeaddrinfo(addrinfo* ai);
DISALLOW_COPY_AND_ASSIGN(AddrInfoGetter);
};
} // namespace net
#endif // NET_DNS_ADDRESS_INFO_H_
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/dns/address_info_test_util.h"
#include "base/logging.h"
#include "base/sys_byteorder.h"
#include "net/base/sys_addrinfo.h"
namespace net {
namespace test {
template <unsigned int N>
std::unique_ptr<char[]> make_addrinfo_list(std::vector<IpAndPort> ipp,
const std::string& canonical_name) {
struct Buffer {
addrinfo ai[N];
sockaddr_in addr[N];
char canonical_name[256];
};
CHECK(ipp.size() == N);
CHECK(canonical_name.length() <= 255);
std::unique_ptr<char[]> data(new char[sizeof(Buffer)]);
memset(data.get(), 0x0, sizeof(Buffer));
Buffer* buffer = reinterpret_cast<Buffer*>(data.get());
memcpy(&buffer->canonical_name[0], canonical_name.data(),
canonical_name.length() + 1);
for (unsigned int i = 0; i < N; ++i) {
std::uint8_t ip[4] = {ipp[i].ip.a, ipp[i].ip.b, ipp[i].ip.c, ipp[i].ip.d};
sockaddr_in* addr = &buffer->addr[i];
memcpy(&addr->sin_addr, ip, 4);
addr->sin_family = AF_INET;
addr->sin_port = base::HostToNet16(static_cast<std::uint16_t>(ipp[i].port));
addrinfo* ai = &buffer->ai[i];
ai->ai_family = AF_INET;
ai->ai_socktype = SOCK_STREAM;
ai->ai_addrlen = sizeof(sockaddr_in);
ai->ai_addr = reinterpret_cast<sockaddr*>(addr);
ai->ai_canonname = reinterpret_cast<decltype(buffer->ai[0].ai_canonname)>(
buffer->canonical_name);
if (i < (N - 1))
ai->ai_next = &buffer->ai[i + 1];
}
return data;
}
template std::unique_ptr<char[]> make_addrinfo_list<1>(
std::vector<IpAndPort> ipp,
const std::string& canonical_name);
template std::unique_ptr<char[]> make_addrinfo_list<3>(
std::vector<IpAndPort> ipp,
const std::string& canonical_name);
std::unique_ptr<char[]> make_addrinfo(IpAndPort ipp,
const std::string& canonical_name) {
return make_addrinfo_list<1>({ipp}, canonical_name);
}
} // namespace test
} // namespace net
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_DNS_ADDRESS_INFO_TEST_UTIL_H_
#define NET_DNS_ADDRESS_INFO_TEST_UTIL_H_
#include <memory>
#include <string>
#include <vector>
namespace net {
namespace test {
struct IpAndPort {
struct Ip {
int a;
int b;
int c;
int d;
};
Ip ip;
int port;
};
// |N| is the length of the IpAndPort vector.
// (The templating greatly simplifies the internals of this function).
template <unsigned int N>
std::unique_ptr<char[]> make_addrinfo_list(std::vector<IpAndPort> ipp,
const std::string& canonical_name);
std::unique_ptr<char[]> make_addrinfo(IpAndPort ipp,
const std::string& canonical_name);
} // namespace test
} // namespace net
#endif // NET_DNS_ADDRESS_INFO_TEST_UTIL_H_
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/dns/address_info.h"
#include <memory>
#include <tuple>
#include "base/sys_byteorder.h"
#include "build/build_config.h"
#include "net/base/address_list.h"
#include "net/base/net_errors.h"
#include "net/base/sys_addrinfo.h"
#include "net/dns/address_info_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
class MockAddrInfoGetter : public AddrInfoGetter {
public:
addrinfo* getaddrinfo(const std::string& host,
const addrinfo* hints,
int* out_os_error) override;
void freeaddrinfo(addrinfo* ai) override;
};
addrinfo* MockAddrInfoGetter::getaddrinfo(const std::string& host,
const addrinfo* /* hints */,
int* out_os_error) {
// Presume success
*out_os_error = 0;
if (host == std::string("canonical.bar.com"))
return reinterpret_cast<addrinfo*>(
test::make_addrinfo({{1, 2, 3, 4}, 80}, "canonical.bar.com").release());
else if (host == "iteration.test")
return reinterpret_cast<addrinfo*>(
test::make_addrinfo_list<3>({{{10, 20, 30, 40}, 80},
{{11, 21, 31, 41}, 81},
{{12, 22, 32, 42}, 82}},
"iteration.test")
.release());
else if (host == "alllocalhost.com")
return reinterpret_cast<addrinfo*>(
test::make_addrinfo_list<3>(
{{{127, 0, 0, 1}, 80}, {{127, 0, 0, 2}, 80}, {{127, 0, 0, 3}, 80}},
"alllocalhost.com")
.release());
else if (host == "not.alllocalhost.com")
return reinterpret_cast<addrinfo*>(
test::make_addrinfo_list<3>(
{{{128, 0, 0, 1}, 80}, {{127, 0, 0, 2}, 80}, {{127, 0, 0, 3}, 80}},
"not.alllocalhost.com")
.release());
else if (host == "www.example.com")
return reinterpret_cast<addrinfo*>(
test::make_addrinfo({{8, 8, 8, 8}, 80}, "www.example.com").release());
// Failure
*out_os_error = 1;
return nullptr;
}
void MockAddrInfoGetter::freeaddrinfo(addrinfo* ai) {
std::unique_ptr<char[]> mock_addrinfo(reinterpret_cast<char*>(ai));
}
std::unique_ptr<addrinfo> MakeHints(AddressFamily address_family,
HostResolverFlags host_resolver_flags) {
auto hints = std::make_unique<addrinfo>();
*hints = {0};
switch (address_family) {
case ADDRESS_FAMILY_IPV4:
hints->ai_family = AF_INET;
break;
case ADDRESS_FAMILY_IPV6:
hints->ai_family = AF_INET6;
break;
case ADDRESS_FAMILY_UNSPECIFIED:
hints->ai_family = AF_UNSPEC;
break;
}
if (host_resolver_flags & HOST_RESOLVER_CANONNAME)
hints->ai_flags |= AI_CANONNAME;
hints->ai_socktype = SOCK_STREAM;
return hints;
}
TEST(AddressInfoTest, Failure) {
base::Optional<AddressInfo> ai;
int err;
int os_error;
auto getter = std::make_unique<MockAddrInfoGetter>();
std::tie(ai, err, os_error) = AddressInfo::Get(
"failure.com", *MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
std::move(getter));
EXPECT_FALSE(ai);
EXPECT_NE(err, OK);
EXPECT_NE(os_error, 0);
}
#if defined(OS_WIN)
// Note: this test is descriptive, not prescriptive.
TEST(AddressInfoTest, FailureWin) {
base::Optional<AddressInfo> ai;
int err;
int os_error;
auto getter = std::make_unique<MockAddrInfoGetter>();
std::tie(ai, err, os_error) = AddressInfo::Get(
"failure.com", *MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
std::move(getter));
EXPECT_FALSE(ai);
EXPECT_EQ(err, ERR_NAME_RESOLUTION_FAILED);
EXPECT_NE(os_error, 0);
}
#endif // OS_WIN
#if defined(OS_ANDROID)
// Note: this test is descriptive, not prescriptive.
TEST(AddressInfoTest, FailureAndroid) {
base::Optional<AddressInfo> ai;
int err;
int os_error;
auto getter = std::make_unique<MockAddrInfoGetter>();
std::tie(ai, err, os_error) = AddressInfo::Get(
"failure.com", *MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
std::move(getter));
EXPECT_FALSE(ai);
EXPECT_EQ(err, ERR_NAME_NOT_RESOLVED);
EXPECT_NE(os_error, 0);
}
#endif // OS_ANDROID
TEST(AddressInfoTest, Canonical) {
base::Optional<AddressInfo> ai;
int err;
int os_error;
std::tie(ai, err, os_error) =
AddressInfo::Get("canonical.bar.com",
*MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
std::make_unique<MockAddrInfoGetter>());
EXPECT_TRUE(ai);
EXPECT_EQ(err, OK);
EXPECT_EQ(os_error, 0);
EXPECT_THAT(ai->GetCanonicalName(),
base::Optional<std::string>("canonical.bar.com"));
}
TEST(AddressInfoTest, Iteration) {
base::Optional<AddressInfo> ai;
int err;
int os_error;
std::tie(ai, err, os_error) =
AddressInfo::Get("iteration.test",
*MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
std::make_unique<MockAddrInfoGetter>());
EXPECT_TRUE(ai);
EXPECT_EQ(err, OK);
EXPECT_EQ(os_error, 0);
{
int count = 0;
for (auto aii = ai->begin(); aii != ai->end(); ++aii) {
const sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(aii->ai_addr);
EXPECT_EQ(base::HostToNet16(addr->sin_port) % 10, count % 10);
++count;
}
EXPECT_EQ(count, 3);
}
{
int count = 0;
for (auto&& aii : ai.value()) {
const sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(aii.ai_addr);
EXPECT_EQ(base::HostToNet16(addr->sin_port) % 10, count % 10);
++count;
}
EXPECT_EQ(count, 3);
}
}
TEST(AddressInfoTest, IsAllLocalhostOfOneFamily) {
base::Optional<AddressInfo> ai;
int err;
int os_error;
std::tie(ai, err, os_error) =
AddressInfo::Get("alllocalhost.com",
*MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
std::make_unique<MockAddrInfoGetter>());
EXPECT_TRUE(ai);
EXPECT_EQ(err, OK);
EXPECT_EQ(os_error, 0);
EXPECT_TRUE(ai->IsAllLocalhostOfOneFamily());
}
TEST(AddressInfoTest, IsAllLocalhostOfOneFamilyFalse) {
base::Optional<AddressInfo> ai;
int err;
int os_error;
std::tie(ai, err, os_error) =
AddressInfo::Get("not.alllocalhost.com",
*MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
std::make_unique<MockAddrInfoGetter>());
EXPECT_TRUE(ai);
EXPECT_EQ(err, OK);
EXPECT_EQ(os_error, 0);
EXPECT_FALSE(ai->IsAllLocalhostOfOneFamily());
}
TEST(AddressInfoTest, CreateAddressList) {
base::Optional<AddressInfo> ai;
int err;
int os_error;
std::tie(ai, err, os_error) =
AddressInfo::Get("www.example.com",
*MakeHints(ADDRESS_FAMILY_IPV4, HOST_RESOLVER_CANONNAME),
std::make_unique<MockAddrInfoGetter>());
EXPECT_TRUE(ai);
EXPECT_EQ(err, OK);
EXPECT_EQ(os_error, 0);
AddressList list = ai->CreateAddressList();
// Verify one result.
ASSERT_EQ(1u, list.size());
ASSERT_EQ(ADDRESS_FAMILY_IPV4, list[0].GetFamily());
// Check if operator= works.
AddressList copy;
copy = list;
ASSERT_EQ(1u, copy.size());
}
} // namespace
} // namespace net
......@@ -4,14 +4,16 @@
#include "net/dns/host_resolver_proc.h"
#include <tuple>
#include "build/build_config.h"
#include "base/logging.h"
#include "base/sys_byteorder.h"
#include "base/threading/scoped_blocking_call.h"
#include "net/base/address_list.h"
#include "net/base/net_errors.h"
#include "net/base/sys_addrinfo.h"
#include "net/dns/address_info.h"
#include "net/dns/dns_reloader.h"
#include "net/dns/dns_util.h"
#include "net/dns/host_resolver.h"
......@@ -22,43 +24,6 @@
namespace net {
namespace {
bool IsAllLocalhostOfOneFamily(const struct addrinfo* ai) {
bool saw_v4_localhost = false;
bool saw_v6_localhost = false;
for (; ai != nullptr; ai = ai->ai_next) {
switch (ai->ai_family) {
case AF_INET: {
const struct sockaddr_in* addr_in =
reinterpret_cast<struct sockaddr_in*>(ai->ai_addr);
if ((base::NetToHost32(addr_in->sin_addr.s_addr) & 0xff000000) ==
0x7f000000)
saw_v4_localhost = true;
else
return false;
break;
}
case AF_INET6: {
const struct sockaddr_in6* addr_in6 =
reinterpret_cast<struct sockaddr_in6*>(ai->ai_addr);
if (IN6_IS_ADDR_LOOPBACK(&addr_in6->sin6_addr))
saw_v6_localhost = true;
else
return false;
break;
}
default:
NOTREACHED();
return false;
}
}
return saw_v4_localhost != saw_v6_localhost;
}
} // namespace
HostResolverProc* HostResolverProc::default_proc_ = nullptr;
HostResolverProc::HostResolverProc(HostResolverProc* previous) {
......@@ -121,35 +86,32 @@ HostResolverProc* HostResolverProc::GetDefault() {
return default_proc_;
}
namespace {
int AddressFamilyToAF(AddressFamily address_family) {
switch (address_family) {
case ADDRESS_FAMILY_IPV4:
return AF_INET;
case ADDRESS_FAMILY_IPV6:
return AF_INET6;
case ADDRESS_FAMILY_UNSPECIFIED:
return AF_UNSPEC;
}
}
} // namespace
int SystemHostResolverCall(const std::string& host,
AddressFamily address_family,
HostResolverFlags host_resolver_flags,
AddressList* addrlist,
int* os_error) {
int* os_error_opt) {
// |host| should be a valid domain name. HostResolverImpl::Resolve has checks
// to fail early if this is not the case.
DCHECK(IsValidDNSDomain(host));
if (os_error)
*os_error = 0;
struct addrinfo* ai = nullptr;
struct addrinfo hints = {0};
switch (address_family) {
case ADDRESS_FAMILY_IPV4:
hints.ai_family = AF_INET;
break;
case ADDRESS_FAMILY_IPV6:
hints.ai_family = AF_INET6;
break;
case ADDRESS_FAMILY_UNSPECIFIED:
hints.ai_family = AF_UNSPEC;
break;
default:
NOTREACHED();
hints.ai_family = AF_UNSPEC;
}
hints.ai_family = AddressFamilyToAF(address_family);
#if defined(OS_WIN)
// DO NOT USE AI_ADDRCONFIG ON WINDOWS.
......@@ -179,7 +141,7 @@ int SystemHostResolverCall(const std::string& host,
hints.ai_flags = AI_ADDRCONFIG;
#endif
// On Linux AI_ADDRCONFIG doesn't consider loopback addreses, even if only
// On Linux AI_ADDRCONFIG doesn't consider loopback addresses, even if only
// loopback addresses are configured. So don't use it when there are only
// loopback addresses.
if (host_resolver_flags & HOST_RESOLVER_LOOPBACK_ONLY)
......@@ -201,14 +163,17 @@ int SystemHostResolverCall(const std::string& host,
!defined(OS_ANDROID) && !defined(OS_FUCHSIA)
DnsReloaderMaybeReload();
#endif
int err = getaddrinfo(host.c_str(), nullptr, &hints, &ai);
base::Optional<AddressInfo> ai;
int err = 0;
int os_error = 0;
std::tie(ai, err, os_error) = AddressInfo::Get(host, hints);
bool should_retry = false;
// If the lookup was restricted (either by address family, or address
// detection), and the results where all localhost of a single family,
// maybe we should retry. There were several bugs related to these
// issues, for example http://crbug.com/42058 and http://crbug.com/49024
if ((hints.ai_family != AF_UNSPEC || hints.ai_flags & AI_ADDRCONFIG) &&
err == 0 && IsAllLocalhostOfOneFamily(ai)) {
if ((hints.ai_family != AF_UNSPEC || hints.ai_flags & AI_ADDRCONFIG) && ai &&
ai->IsAllLocalhostOfOneFamily()) {
if (host_resolver_flags & HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6) {
hints.ai_family = AF_UNSPEC;
should_retry = true;
......@@ -219,44 +184,16 @@ int SystemHostResolverCall(const std::string& host,
}
}
if (should_retry) {
if (ai != nullptr) {
freeaddrinfo(ai);
ai = nullptr;
}
err = getaddrinfo(host.c_str(), nullptr, &hints, &ai);
std::tie(ai, err, os_error) = AddressInfo::Get(host, hints);
}
if (err) {
#if defined(OS_WIN)
err = WSAGetLastError();
#endif
// Return the OS error to the caller.
if (os_error)
*os_error = err;
// If the call to getaddrinfo() failed because of a system error, report
// it separately from ERR_NAME_NOT_RESOLVED.
#if defined(OS_WIN)
if (err != WSAHOST_NOT_FOUND && err != WSANO_DATA)
return ERR_NAME_RESOLUTION_FAILED;
#elif defined(OS_POSIX) && !defined(OS_FREEBSD)
if (err != EAI_NONAME && err != EAI_NODATA)
return ERR_NAME_RESOLUTION_FAILED;
#endif
if (os_error_opt)
*os_error_opt = os_error;
return ERR_NAME_NOT_RESOLVED;
}
#if defined(OS_ANDROID)
// Workaround for Android's getaddrinfo leaving ai==NULL without an error.
// http://crbug.com/134142
if (ai == NULL)
return ERR_NAME_NOT_RESOLVED;
#endif
if (!ai)
return err;
*addrlist = AddressList::CreateFromAddrinfo(ai);
freeaddrinfo(ai);
*addrlist = ai->CreateAddressList();
return OK;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment