blob: f0b667cae53ee7f98d864bf7131d9a6be76538a5 [file] [log] [blame]
// Copyright (c) 2016 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 "quic/platform/api/quic_socket_address.h"
#include <cstring>
#include <limits>
#include <string>
#include "absl/strings/str_cat.h"
#include "quic/platform/api/quic_bug_tracker.h"
#include "quic/platform/api/quic_ip_address.h"
#include "quic/platform/api/quic_ip_address_family.h"
namespace quic {
namespace {
uint32_t HashIP(const QuicIpAddress& ip) {
if (ip.IsIPv4()) {
return ip.GetIPv4().s_addr;
}
if (ip.IsIPv6()) {
auto v6addr = ip.GetIPv6();
const uint32_t* v6_as_ints =
reinterpret_cast<const uint32_t*>(&v6addr.s6_addr);
return v6_as_ints[0] ^ v6_as_ints[1] ^ v6_as_ints[2] ^ v6_as_ints[3];
}
return 0;
}
} // namespace
QuicSocketAddress::QuicSocketAddress(QuicIpAddress address, uint16_t port)
: host_(address), port_(port) {}
QuicSocketAddress::QuicSocketAddress(const struct sockaddr_storage& saddr) {
switch (saddr.ss_family) {
case AF_INET: {
const sockaddr_in* v4 = reinterpret_cast<const sockaddr_in*>(&saddr);
host_ = QuicIpAddress(v4->sin_addr);
port_ = ntohs(v4->sin_port);
break;
}
case AF_INET6: {
const sockaddr_in6* v6 = reinterpret_cast<const sockaddr_in6*>(&saddr);
host_ = QuicIpAddress(v6->sin6_addr);
port_ = ntohs(v6->sin6_port);
break;
}
default:
QUIC_BUG(quic_bug_10075_1)
<< "Unknown address family passed: " << saddr.ss_family;
break;
}
}
QuicSocketAddress::QuicSocketAddress(const sockaddr* saddr, socklen_t len) {
sockaddr_storage storage;
static_assert(std::numeric_limits<socklen_t>::max() >= sizeof(storage),
"Cannot cast sizeof(storage) to socklen_t as it does not fit");
if (len < static_cast<socklen_t>(sizeof(sockaddr)) ||
(saddr->sa_family == AF_INET &&
len < static_cast<socklen_t>(sizeof(sockaddr_in))) ||
(saddr->sa_family == AF_INET6 &&
len < static_cast<socklen_t>(sizeof(sockaddr_in6))) ||
len > static_cast<socklen_t>(sizeof(storage))) {
QUIC_BUG(quic_bug_10075_2) << "Socket address of invalid length provided";
return;
}
memcpy(&storage, saddr, len);
*this = QuicSocketAddress(storage);
}
bool operator==(const QuicSocketAddress& lhs, const QuicSocketAddress& rhs) {
return lhs.host_ == rhs.host_ && lhs.port_ == rhs.port_;
}
bool operator!=(const QuicSocketAddress& lhs, const QuicSocketAddress& rhs) {
return !(lhs == rhs);
}
bool QuicSocketAddress::IsInitialized() const {
return host_.IsInitialized();
}
std::string QuicSocketAddress::ToString() const {
switch (host_.address_family()) {
case IpAddressFamily::IP_V4:
return absl::StrCat(host_.ToString(), ":", port_);
case IpAddressFamily::IP_V6:
return absl::StrCat("[", host_.ToString(), "]:", port_);
default:
return "";
}
}
int QuicSocketAddress::FromSocket(int fd) {
sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
int result = getsockname(fd, reinterpret_cast<sockaddr*>(&addr), &addr_len);
bool success = result == 0 && addr_len > 0 &&
static_cast<size_t>(addr_len) <= sizeof(addr);
if (success) {
*this = QuicSocketAddress(addr);
return 0;
}
return -1;
}
QuicSocketAddress QuicSocketAddress::Normalized() const {
return QuicSocketAddress(host_.Normalized(), port_);
}
QuicIpAddress QuicSocketAddress::host() const {
return host_;
}
uint16_t QuicSocketAddress::port() const {
return port_;
}
sockaddr_storage QuicSocketAddress::generic_address() const {
union {
sockaddr_storage storage;
sockaddr_in v4;
sockaddr_in6 v6;
} result;
memset(&result.storage, 0, sizeof(result.storage));
switch (host_.address_family()) {
case IpAddressFamily::IP_V4:
result.v4.sin_family = AF_INET;
result.v4.sin_addr = host_.GetIPv4();
result.v4.sin_port = htons(port_);
break;
case IpAddressFamily::IP_V6:
result.v6.sin6_family = AF_INET6;
result.v6.sin6_addr = host_.GetIPv6();
result.v6.sin6_port = htons(port_);
break;
default:
result.storage.ss_family = AF_UNSPEC;
break;
}
return result.storage;
}
uint32_t QuicSocketAddress::Hash() const {
uint32_t value = 0;
value ^= HashIP(host_);
value ^= port_ | (port_ << 16);
return value;
}
} // namespace quic