blob: d34b8ecb468d82d8f82a023d4e71aebe645367ee [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "kudu/util/net/sockaddr.h"
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <cerrno>
#include <cstddef>
#include <cstring>
#include <limits>
#include <ostream>
#include <string>
#include <glog/logging.h>
#include "kudu/gutil/dynamic_annotations.h"
#include "kudu/gutil/hash/string_hash.h"
#include "kudu/gutil/port.h"
#include "kudu/gutil/strings/join.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/util/net/net_util.h"
#include "kudu/util/slice.h"
#include "kudu/util/stopwatch.h"
using std::string;
using std::vector;
using strings::Substitute;
namespace kudu {
///
/// Sockaddr
///
Sockaddr::Sockaddr() {
set_length(0);
}
Sockaddr::Sockaddr(const Sockaddr& other) noexcept {
*this = other;
}
Sockaddr::~Sockaddr() {
ASAN_UNPOISON_MEMORY_REGION(&storage_, sizeof(storage_));
}
Sockaddr Sockaddr::Wildcard() {
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
return Sockaddr(addr);
}
Sockaddr& Sockaddr::operator=(const Sockaddr& other) noexcept {
if (&other == this) return *this;
set_length(other.len_);
memcpy(&storage_, &other.storage_, len_);
return *this;
}
Sockaddr::Sockaddr(const struct sockaddr& addr, socklen_t len) {
set_length(len);
memcpy(&storage_, &addr, len);
}
Sockaddr::Sockaddr(const struct sockaddr_in& addr) :
Sockaddr(reinterpret_cast<const struct sockaddr&>(addr), sizeof(addr)) {
DCHECK_EQ(AF_INET, addr.sin_family);
}
Status Sockaddr::ParseString(const string& s, uint16_t default_port) {
HostPort hp;
RETURN_NOT_OK(hp.ParseString(s, default_port));
return ParseFromNumericHostPort(hp);
}
Status Sockaddr::ParseFromNumericHostPort(const HostPort& hp) {
struct in_addr addr;
if (inet_pton(AF_INET, hp.host().c_str(), &addr) != 1) {
return Status::InvalidArgument("Invalid IP address", hp.host());
}
set_length(sizeof(struct sockaddr_in));
storage_.in.sin_family = AF_INET;
storage_.in.sin_addr = addr;
set_port(hp.port());
return Status::OK();
}
Status Sockaddr::ParseUnixDomainPath(const string& s) {
constexpr auto kMaxPathSize = SIZEOF_MEMBER(struct sockaddr_un, sun_path);
if (s[0] == '@') {
// Specify a path in the abstract namespace.
if (s.size() > kMaxPathSize) {
return Status::InvalidArgument(Substitute(
"UNIX domain socket path exceeds maximum length $0", kMaxPathSize));
}
set_length(offsetof(struct sockaddr_un, sun_path) + s.size());
storage_.un.sun_family = AF_UNIX;
memcpy(storage_.un.sun_path, s.data(), s.size());
storage_.un.sun_path[0] = '\0';
} else {
// Path names must be null-terminated. The null-terminated length
// may not match the length s.size().
int c_len = strlen(s.c_str());
if (c_len != s.size()) {
return Status::InvalidArgument("UNIX domain socket path must not contain null bytes");
}
// Per unix(7) the length of the path name, including the terminating null
// byte, should not exceed the size of sun_path.
if (c_len + 1 > kMaxPathSize) {
return Status::InvalidArgument(Substitute(
"UNIX domain socket path exceeds maximum length $0", kMaxPathSize));
}
// unix(7) says the addrlen can be specified as the full length of the
// structure.
set_length(sizeof(struct sockaddr_un));
storage_.un.sun_family = AF_UNIX;
memcpy(storage_.un.sun_path, s.c_str(), c_len + 1);
}
return Status::OK();
}
string Sockaddr::UnixDomainPath() const {
CHECK_EQ(family(), AF_UNIX);
switch (unix_address_type()) {
case UnixAddressType::kUnnamed:
return "<unnamed>";
case UnixAddressType::kPath:
return string(storage_.un.sun_path);
case UnixAddressType::kAbstractNamespace:
size_t len = len_ - offsetof(struct sockaddr_un, sun_path) - 1;
return "@" + string(storage_.un.sun_path + 1, len);
}
LOG(FATAL) << "unknown unix address type";
return "";
}
Sockaddr::UnixAddressType Sockaddr::unix_address_type() const {
CHECK_EQ(family(), AF_UNIX);
if (len_ == sizeof(sa_family_t)) {
return UnixAddressType::kUnnamed;
}
if (storage_.un.sun_path[0] == '\0') {
return UnixAddressType::kAbstractNamespace;
}
return UnixAddressType::kPath;
}
Sockaddr& Sockaddr::operator=(const struct sockaddr_in &addr) {
set_length(sizeof(addr));
memcpy(&storage_, &addr, sizeof(addr));
DCHECK_EQ(family(), AF_INET);
return *this;
}
bool Sockaddr::operator==(const Sockaddr& other) const {
return BytewiseCompare(*this, other) == 0;
}
int Sockaddr::BytewiseCompare(const Sockaddr& a, const Sockaddr& b) {
Slice a_slice(reinterpret_cast<const uint8_t*>(&a.storage_), a.len_);
Slice b_slice(reinterpret_cast<const uint8_t*>(&b.storage_), b.len_);
return a_slice.compare(b_slice);
}
void Sockaddr::set_length(socklen_t len) {
DCHECK(len == 0 || len >= sizeof(sa_family_t));
DCHECK_LE(len, sizeof(storage_));
len_ = len;
ASAN_UNPOISON_MEMORY_REGION(&storage_, len_);
ASAN_POISON_MEMORY_REGION(reinterpret_cast<uint8_t*>(&storage_) + len_,
sizeof(storage_) - len_);
}
uint32_t Sockaddr::HashCode() const {
return HashStringThoroughly(reinterpret_cast<const char*>(&storage_), addrlen());
}
void Sockaddr::set_port(int port) {
DCHECK_EQ(family(), AF_INET);
DCHECK_GE(port, 0);
DCHECK_LE(port, std::numeric_limits<uint16_t>::max());
storage_.in.sin_port = htons(port);
}
int Sockaddr::port() const {
DCHECK_EQ(family(), AF_INET);
return ntohs(storage_.in.sin_port);
}
std::string Sockaddr::host() const {
switch (family()) {
case AF_INET:
return HostPort::AddrToString(storage_.in.sin_addr.s_addr);
case AF_UNIX:
DCHECK(false) << "unexpected host() call on unix socket";
// In case we missed a host() call somewhere in a vlog or error message not
// covered by tests, better to at least return some string here.
return "<unix socket>";
default:
DCHECK(false) << "unexpected host() call on socket with family " << family();
return "<unknown socket type>";
}
}
const struct sockaddr_in& Sockaddr::ipv4_addr() const {
DCHECK_EQ(family(), AF_INET);
return storage_.in;
}
std::string Sockaddr::ToString() const {
if (!is_initialized()) {
return "<uninitialized>";
}
switch (family()) {
case AF_INET:
return Substitute("$0:$1", host(), port());
case AF_UNIX:
return Substitute("unix:$0", UnixDomainPath());
default:
return "<invalid sockaddr>";
}
}
bool Sockaddr::IsWildcard() const {
DCHECK_EQ(family(), AF_INET);
return storage_.in.sin_addr.s_addr == 0;
}
bool Sockaddr::IsAnyLocalAddress() const {
if (family() == AF_UNIX) return true;
DCHECK_EQ(family(), AF_INET);
return HostPort::IsLoopback(storage_.in.sin_addr.s_addr);
}
Status Sockaddr::LookupHostname(string* hostname) const {
char host[NI_MAXHOST];
int flags = 0;
auto* addr = reinterpret_cast<const sockaddr*>(&storage_);
int rc = 0;
LOG_SLOW_EXECUTION(WARNING, 200,
Substitute("DNS reverse-lookup for $0", ToString())) {
rc = getnameinfo(addr, addrlen(), host, NI_MAXHOST, nullptr, 0, flags);
}
if (PREDICT_FALSE(rc != 0)) {
if (rc == EAI_SYSTEM) {
int errno_saved = errno;
return Status::NetworkError(Substitute("getnameinfo: $0", gai_strerror(rc)),
strerror(errno_saved), errno_saved);
}
return Status::NetworkError("getnameinfo", gai_strerror(rc), rc);
}
*hostname = host;
return Status::OK();
}
string Sockaddr::ToCommaSeparatedString(const std::vector<Sockaddr>& addrs) {
vector<string> addrs_str;
addrs_str.reserve(addrs.size());
for (const Sockaddr& addr : addrs) {
addrs_str.push_back(addr.ToString());
}
return JoinStrings(addrs_str, ",");
}
} // namespace kudu