blob: 4ff59d21136b1d59e7f2c2947893e5303b6e1b6b [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 "addr.h"
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <unistd.h>
#include "log.h"
#include "util.h"
constexpr const char* kInterfaceIdPrefix = "%";
constexpr const char* kAny = "ANY";
constexpr const char* kAny6 = "ANY6";
constexpr const char* kAnyLower = "any";
constexpr const char* kAny6Lower = "any6";
constexpr const char* kNull = "NULL";
constexpr const char* kNullLower = "null";
namespace {
inline bool IsAny4(const std::string& str) { return str == kAny || str == kAnyLower; }
inline bool IsAny6(const std::string& str) { return str == kAny6 || str == kAny6Lower; }
inline bool IsNull(const std::string& str) { return str == kNull || str == kNullLower; }
} // namespace
bool InAddr::Set(const std::string& ip_str_arg) {
Reset();
if (ip_str_arg.empty()) {
LOG(ERROR) << "Error: Empty-string given to create InAddr!";
return false;
}
std::string ip_str;
if (ip_str_arg[0] == '[') {
auto e = ip_str_arg.find_first_of(']');
if (e == std::string::npos) {
LOG(ERROR) << "Error-Given InAddr ip_str_arg='" << ip_str_arg
<< "' is not a valid interface ID or IP - "
<< "missing closing address bracket ']'!";
return false;
}
ip_str = ip_str_arg.substr(1, e - 1);
LOG(ERROR) << "ip_str='" << ip_str << " e=" << e;
} else {
ip_str = ip_str_arg;
}
if (IsAny4(ip_str)) {
family_ = AF_INET;
addr_.addr4_.s_addr = htonl(INADDR_ANY);
} else if (IsAny6(ip_str)) {
family_ = AF_INET6;
addr_.addr6_ = in6addr_any;
} else if (IsNull(ip_str)) {
Reset();
} else if (inet_pton(AF_INET, ip_str.c_str(), &addr_.addr4_) == 1) {
family_ = AF_INET;
} else if (inet_pton(AF_INET6, ip_str.c_str(), &addr_.addr6_) == 1) {
family_ = AF_INET6;
} else {
LOG(ERROR) << "Error-Given InAddr ip_str_arg='" << ip_str_arg
<< "' is not a valid interface ID or IP!";
return false;
}
// Store the original string.
ip_str_ = ip_str;
return true;
}
bool InAddr::Set(const void* addr, int addr_len) {
Reset();
char ip_str[64];
if (addr_len == sizeof(addr_.addr4_)) {
family_ = AF_INET;
addr_.addr4_ = *static_cast<const struct in_addr*>(addr);
} else if (addr_len == sizeof(addr_.addr6_)) {
family_ = AF_INET6;
addr_.addr6_ = *static_cast<const struct in6_addr*>(addr);
} else {
LOG(ERROR) << "Error-given in-addr is not valid!";
return false;
}
if (!inet_ntop(family_, addr, ip_str, sizeof(ip_str))) {
LOG(ERROR) << "Error-inet_ntop failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
ip_str_.assign(ip_str);
return true;
}
std::string InAddr::Name() {
std::string name;
switch (family_) {
case AF_INET:
return addr_.addr4_.s_addr == INADDR_ANY ? std::string(kAny) : ip_str_;
case AF_INET6:
for (unsigned i = 0; i < sizeof(addr_.addr6_); i++) {
if (addr_.addr6_.s6_addr[i] != 0) {
return std::string("[") + ip_str_ + "]";
}
}
return std::string(kAny6);
case AF_UNSPEC:
return std::string("<unspec>");
default:
return std::string("<unknown>");
}
}
bool LocalIfAddr::Set(const std::string& ip_id_str) {
Reset();
if (ip_id_str.empty()) {
LOG(ERROR) << "Error: Empty string given to create LocalIfAddr!";
return false;
}
if (IsAny4(ip_id_str)) {
id_ = 0;
return in_addr_.Set(kAny);
}
// [<IP>][%ID]
std::string ip_str, id_str;
if (ip_id_str[0] == kInterfaceIdPrefix[0]) {
id_str = ip_id_str.substr(1);
} else {
auto e = ip_id_str.find_first_of(kInterfaceIdPrefix[0]);
if (e != std::string::npos) {
id_str = ip_id_str.substr(e + 1);
}
ip_str = ip_id_str.substr(0, e);
}
if (!ip_str.empty()) {
if (!in_addr_.Set(ip_str)) {
return false;
}
}
if (!id_str.empty()) {
int id;
if (!str2int(id_str, &id) || id < 0) {
LOG(ERROR) << "Error: Invalid interface ID='" << id_str << "'!";
return false;
}
id_ = id;
}
return true;
}
bool LocalIfAddr::Set(const void* addr, int addr_len) {
Reset();
return in_addr_.Set(addr, addr_len);
}
std::string LocalIfAddr::Name() {
if (!IsSet()) {
return std::string("<unknown>");
}
std::string out;
if (in_addr_.IsSet()) {
out += in_addr_.Name();
}
if (HasId()) {
out += std::string(kInterfaceIdPrefix) + std::to_string(id_);
}
return out;
}
bool SockAddrIn::Set(const std::string& ip_port_str) {
if (ip_port_str.empty()) {
LOG(ERROR) << "Error: Empty string given to create SockAddrIn!";
return false;
}
if (ip_port_str == "null" || ip_port_str == "NULL") {
port_ = 0;
addr_.Reset();
return true;
}
std::string ip_str;
size_t col_pos = 0;
if (ip_port_str[0] == '[') {
auto addr_end_pos = ip_port_str.find_first_of(']');
if (addr_end_pos == std::string::npos) {
LOG(ERROR) << "Error-Cannot parse ip_port_str='" << ip_port_str
<< "' for <ip>:<port> - missing address closing brack ']'!";
return false;
}
ip_str = ip_port_str.substr(1, addr_end_pos - 1);
col_pos = addr_end_pos + 1;
} else {
col_pos = ip_port_str.find_first_of(':');
if (col_pos == std::string::npos) {
LOG(ERROR) << "Error-Cannot parse ip_port_str='" << ip_port_str
<< "' for <ip>:<port> - missing port!";
return false;
}
if (ip_port_str.find_first_of(col_pos + 1, ':') != std::string::npos) {
LOG(ERROR) << "Error-Cannot parse ip_port_str='" << ip_port_str
<< "' for <ip>:<port> - too many colons ':',"
<< " use '[]' brackets around IPv6 addresses!";
return false;
}
ip_str = ip_port_str.substr(0, col_pos);
}
if (!addr_.Set(ip_str)) {
return false;
}
std::string port_str = ip_port_str.substr(col_pos + 1);
if (port_str.empty()) {
LOG(ERROR) << "Error-Cannot parse ip_port_str='" << ip_port_str
<< "' for <ip>:<port> - port_str is empty!";
}
int port;
if (!str2int(port_str, &port) || port < 0) {
LOG(ERROR) << "Error-Cannot parse port='" << port_str << "'!";
return false;
}
port_ = port;
return true;
}
bool SockAddrIn::Set(const struct sockaddr* addr, socklen_t addr_len) {
if (addr->sa_family == AF_INET && addr_len == sizeof(struct sockaddr_in)) {
auto* addr_in = reinterpret_cast<const struct sockaddr_in*>(addr);
if (!addr_.Set(&addr_in->sin_addr, sizeof(struct in_addr))) {
return false;
}
port_ = ntohs(addr_in->sin_port);
} else if (addr->sa_family == AF_INET && addr_len == sizeof(struct sockaddr_in6)) {
auto* addr_in = reinterpret_cast<const struct sockaddr_in6*>(addr);
if (!addr_.Set(&addr_in->sin6_addr, sizeof(struct in6_addr))) {
return false;
}
port_ = ntohs(addr_in->sin6_port);
} else if (addr->sa_family == AF_UNSPEC) {
port_ = 0;
addr_.Reset();
} else {
LOG(ERROR) << "Error-Invalid sockaddr provided!";
return false;
}
return true;
}
bool SockAddrIn::Fill(struct sockaddr* sockaddr, int* sockaddr_len, bool allow_unspec) {
int actual_len;
if (addr_.IsAddr4()) {
actual_len = sizeof(struct sockaddr_in);
} else if (addr_.IsAddr6()) {
actual_len = sizeof(struct sockaddr_in6);
} else if (allow_unspec && !addr_.IsSet()) {
actual_len = sizeof(sa_family_t);
} else {
LOG(ERROR) << "Error-SockAddress object is not initialized!";
return false;
}
if (*sockaddr_len < actual_len) {
LOG(ERROR) << "Error-Sockaddr given is too small, need " << actual_len << " have "
<< *sockaddr_len;
return false;
}
memset(sockaddr, 0, actual_len);
if (addr_.IsAddr4()) {
auto* addr_in = reinterpret_cast<struct sockaddr_in*>(sockaddr);
addr_in->sin_family = addr_.GetFamily();
addr_in->sin_port = htons(port_);
addr_in->sin_addr = addr_.GetAddr4();
} else if (addr_.IsAddr6()) {
auto* addr_in = reinterpret_cast<struct sockaddr_in6*>(sockaddr);
addr_in->sin6_family = addr_.GetFamily();
addr_in->sin6_port = htons(port_);
// addr->sin6_flowinfo
addr_in->sin6_addr = addr_.GetAddr6();
// addr->sin6_scope_id
} else {
sockaddr->sa_family = AF_UNSPEC;
}
*sockaddr_len = actual_len;
return true;
}