blob: 75173bbfbbf2b29704025b288a9fd424f5b462ca [file] [log] [blame]
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Microsoft Corporation
*
* -=- Robust Distributed System Nucleus (rDSN) -=-
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "runtime/rpc/rpc_address.h"
#include <arpa/inet.h>
#include <errno.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include "absl/strings/string_view.h"
#include "runtime/rpc/group_address.h"
#include "utils/error_code.h"
#include "utils/fixed_size_buffer_pool.h"
#include "utils/fmt_logging.h"
#include "utils/ports.h"
#include "utils/safe_strerror_posix.h"
#include "utils/string_conv.h"
#include "utils/strings.h"
namespace dsn {
/*static*/
error_s rpc_address::GetAddrInfo(const std::string &hostname, const addrinfo &hints, AddrInfo *info)
{
addrinfo *res = nullptr;
const int rc = getaddrinfo(hostname.c_str(), nullptr, &hints, &res);
const int err = errno; // preserving the errno from the getaddrinfo() call
AddrInfo result(res, ::freeaddrinfo);
if (dsn_unlikely(rc != 0)) {
if (rc == EAI_SYSTEM) {
const auto &err_msg = utils::safe_strerror(err);
LOG_ERROR("getaddrinfo failed, name = {}, err = {}", hostname, err_msg);
return error_s::make(ERR_NETWORK_FAILURE, err_msg);
}
LOG_ERROR("getaddrinfo failed, name = {}, err = {}", hostname, gai_strerror(rc));
return error_s::make(ERR_NETWORK_FAILURE, gai_strerror(rc));
}
if (info != nullptr) {
info->swap(result);
}
return error_s::ok();
}
const rpc_address rpc_address::s_invalid_address;
/*static*/
uint32_t rpc_address::ipv4_from_host(const char *name)
{
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
AddrInfo result;
if (dsn_unlikely(!GetAddrInfo(name, hints, &result).is_ok())) {
return 0;
}
CHECK_EQ(result.get()->ai_family, AF_INET);
auto *ipv4 = reinterpret_cast<struct sockaddr_in *>(result.get()->ai_addr);
// converts from network byte order to host byte order
return ntohl(ipv4->sin_addr.s_addr);
}
/*static*/
bool rpc_address::is_site_local_address(uint32_t ip_net)
{
uint32_t iphost = ntohl(ip_net);
return (iphost >= 0x0A000000 && iphost <= 0x0AFFFFFF) || // 10.0.0.0-10.255.255.255
(iphost >= 0xAC100000 && iphost <= 0xAC1FFFFF) || // 172.16.0.0-172.31.255.255
(iphost >= 0xC0A80000 && iphost <= 0xC0A8FFFF) || // 192.168.0.0-192.168.255.255
false;
}
/*static*/
bool rpc_address::is_docker_netcard(const char *netcard_interface, uint32_t ip_net)
{
if (absl::string_view(netcard_interface).find("docker") != absl::string_view::npos) {
return true;
}
uint32_t iphost = ntohl(ip_net);
return iphost == 0xAC112A01; // 172.17.42.1
}
/*static*/
uint32_t rpc_address::ipv4_from_network_interface(const char *network_interface)
{
uint32_t ret = 0;
struct ifaddrs *ifa = nullptr;
if (getifaddrs(&ifa) == 0) {
struct ifaddrs *i = ifa;
while (i != nullptr) {
if (i->ifa_name != nullptr && i->ifa_addr != nullptr &&
i->ifa_addr->sa_family == AF_INET) {
uint32_t ip_val = ((struct sockaddr_in *)i->ifa_addr)->sin_addr.s_addr;
if (utils::equals(i->ifa_name, network_interface) ||
(network_interface[0] == '\0' && !is_docker_netcard(i->ifa_name, ip_val) &&
is_site_local_address(ip_val))) {
ret = (uint32_t)ntohl(ip_val);
break;
}
LOG_DEBUG("skip interface({}), address({})",
i->ifa_name,
rpc_address(ip_val, 0).ipv4_str());
}
i = i->ifa_next;
}
if (i == nullptr) {
LOG_ERROR("get local ip from network interfaces failed, network_interface = {}",
network_interface);
} else {
LOG_INFO("get ip address from network interface({}), addr({}), input interface({})",
i->ifa_name,
rpc_address(ret, 0).ipv4_str(),
network_interface);
}
if (ifa != nullptr) {
// remember to free it
freeifaddrs(ifa);
}
}
return ret;
}
rpc_address::~rpc_address() { set_invalid(); }
rpc_address::rpc_address(const rpc_address &another) { *this = another; }
rpc_address &rpc_address::operator=(const rpc_address &another)
{
if (this == &another) {
// avoid memory leak
return *this;
}
set_invalid();
_addr = another._addr;
switch (another.type()) {
case HOST_TYPE_GROUP:
group_address()->add_ref();
break;
default:
break;
}
return *this;
}
void rpc_address::assign_group(const char *name)
{
set_invalid();
_addr.group.type = HOST_TYPE_GROUP;
rpc_group_address *addr = new rpc_group_address(name);
// take the lifetime of rpc_uri_address, release_ref when change value or call destructor
addr->add_ref();
_addr.group.group = (uint64_t)addr;
}
void rpc_address::set_invalid()
{
switch (type()) {
case HOST_TYPE_GROUP:
group_address()->release_ref();
break;
default:
break;
}
_addr.value = 0;
}
static __thread fixed_size_buffer_pool<8, 256> bf;
const char *rpc_address::ipv4_str() const
{
char *p = bf.next();
auto sz = bf.get_chunk_size();
struct in_addr net_addr;
if (_addr.v4.type == HOST_TYPE_IPV4) {
net_addr.s_addr = htonl(ip());
inet_ntop(AF_INET, &net_addr, p, sz);
} else {
p = (char *)"invalid_ipv4";
}
return p;
}
bool rpc_address::from_string_ipv4(const char *s)
{
set_invalid();
std::string ip_port(s);
auto pos = ip_port.find_last_of(':');
if (pos == std::string::npos) {
return false;
}
std::string ip = ip_port.substr(0, pos);
std::string port = ip_port.substr(pos + 1);
// check port
unsigned int port_num;
if (!internal::buf2unsigned(port, port_num) || port_num > UINT16_MAX) {
return false;
}
// check localhost & IP
uint32_t ip_addr;
if (ip == "localhost" || inet_pton(AF_INET, ip.c_str(), &ip_addr)) {
assign_ipv4(ip.c_str(), (uint16_t)port_num);
return true;
}
return false;
}
const char *rpc_address::to_string() const
{
char *p = bf.next();
auto sz = bf.get_chunk_size();
struct in_addr net_addr;
int ip_len;
switch (_addr.v4.type) {
case HOST_TYPE_IPV4:
net_addr.s_addr = htonl(ip());
inet_ntop(AF_INET, &net_addr, p, sz);
ip_len = strlen(p);
snprintf_p(p + ip_len, sz - ip_len, ":%hu", port());
break;
case HOST_TYPE_GROUP:
p = (char *)group_address()->name();
break;
default:
p = (char *)"invalid address";
break;
}
return (const char *)p;
}
rpc_address::rpc_address(const struct sockaddr_in &addr)
{
set_invalid();
_addr.v4.type = HOST_TYPE_IPV4;
_addr.v4.ip = static_cast<uint32_t>(ntohl(addr.sin_addr.s_addr));
_addr.v4.port = ntohs(addr.sin_port);
}
} // namespace dsn