blob: 0e985c4ceb8d3c8a2cb7d453026453a1d7f9f8c6 [file] [log] [blame]
// Copyright 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 "remoting/protocol/rfc7050_ip_synthesizer.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/task/post_task.h"
#include "net/base/sys_addrinfo.h"
namespace {
// Well-known IPs: 192.0.0.170 and 192.0.0.171
constexpr char kIpv4OnlyHost[] = "ipv4only.arpa";
// Only /96 DNS64 prefixes are supported at this time.
constexpr int kPrefixLength = 96;
// Resolves |kIpv4OnlyHost| and returns the result of getaddrinfo.
int ResolveIpv4OnlyHost(rtc::IPAddress* out_ip) {
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
addrinfo* result = nullptr;
int error = getaddrinfo(kIpv4OnlyHost, nullptr, &hints, &result);
if (!error && !rtc::IPFromAddrInfo(result, out_ip)) {
LOG(ERROR) << "Failed to get IP from addrinfo";
error = EAI_FAIL;
}
if (result) {
freeaddrinfo(result);
}
return error;
}
} // namespace
namespace remoting {
namespace protocol {
// static
Rfc7050IpSynthesizer* Rfc7050IpSynthesizer::GetInstance() {
static base::NoDestructor<Rfc7050IpSynthesizer> instance;
return instance.get();
}
Rfc7050IpSynthesizer::Rfc7050IpSynthesizer()
: resolve_ipv4_only_host_(base::BindRepeating(&ResolveIpv4OnlyHost)) {}
Rfc7050IpSynthesizer::~Rfc7050IpSynthesizer() {
LOG(WARNING) << "Rfc7050IpSynthesizer should never be destructed unless it's "
"in unit test.";
}
void Rfc7050IpSynthesizer::UpdateDns64Prefix(base::OnceClosure on_done) {
base::PostTaskWithTraitsAndReply(
FROM_HERE, {base::MayBlock()},
// Safe to use raw pointer because the singleton is never destroyed.
base::BindOnce(&Rfc7050IpSynthesizer::UpdateDns64PrefixOnBackgroundThread,
base::Unretained(this)),
std::move(on_done));
}
rtc::SocketAddress Rfc7050IpSynthesizer::ToNativeSocket(
const rtc::SocketAddress& original_socket) const {
in6_addr new_ipv6_address;
{
// Ideally read operations should share the same lock, but Chromium has no
// ready-to-use readers-writer lock implementation.
base::AutoLock auto_lock(lock_);
if (dns64_prefix_.IsNil() || original_socket.family() != AF_INET) {
return original_socket;
}
DCHECK_EQ(AF_INET6, dns64_prefix_.family());
new_ipv6_address = dns64_prefix_.ipv6_address();
}
in_addr ipv4_address = original_socket.ipaddr().ipv4_address();
// Replace the last 4 bytes with the IPv4 address.
memcpy(&new_ipv6_address.s6_addr[12], &ipv4_address.s_addr,
sizeof(ipv4_address.s_addr));
rtc::IPAddress new_ip(new_ipv6_address);
return rtc::SocketAddress(new_ip, original_socket.port());
}
void Rfc7050IpSynthesizer::UpdateDns64PrefixOnBackgroundThread() {
rtc::IPAddress new_prefix = DiscoverDns64Prefix();
{
base::AutoLock auto_lock(lock_);
dns64_prefix_ = new_prefix;
}
}
rtc::IPAddress Rfc7050IpSynthesizer::DiscoverDns64Prefix() const {
// This is not the full RFC 7050 implementation. It only implements /96 prefix
// discovery, as Android CLAT does. See https://tools.ietf.org/html/rfc7050
rtc::IPAddress prefix;
rtc::IPAddress ip;
int error = resolve_ipv4_only_host_.Run(&ip);
if (error) {
if (error == EAI_NONAME) {
VLOG(0) << "Node is unknown. You are probably not in DNS64/NAT64 "
<< "network.";
} else {
LOG(WARNING) << "Failed to discover DNS64 prefix. Error: " << error
<< ", " << gai_strerror(error);
}
return prefix;
}
if (ip.family() != AF_INET6 || rtc::IPIsV4Mapped(ip)) {
VLOG(0)
<< "Resolved IP is not in IPv6. You probably don't have DNS64 prefix";
} else {
prefix = rtc::TruncateIP(ip, kPrefixLength);
VLOG(0) << "Discovered DNS64/NAT64 prefix: " << prefix.ToString();
}
return prefix;
}
} // namespace protocol
} // namespace remoting