blob: a00265adc2079f7323df0818c81ff4527e1ceef2 [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 <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <cstring>
#include <memory>
#include <unordered_set>
#include <utility>
#include "fmt/core.h"
#include "runtime/rpc/group_host_port.h"
#include "runtime/rpc/rpc_host_port.h"
#include "utils/api_utilities.h"
#include "utils/error_code.h"
#include "utils/ports.h"
#include "utils/string_conv.h"
#include "utils/timer.h"
#include "utils/utils.h"
namespace dsn {
const host_port host_port::s_invalid_host_port;
host_port::host_port(std::string host, uint16_t port)
: _host(std::move(host)), _port(port), _type(HOST_TYPE_IPV4)
{
// ipv4_from_host may be slow, just call it in DEBUG version.
DCHECK_NE_MSG(rpc_address::ipv4_from_host(_host.c_str()), 0, "invalid hostname: {}", _host);
}
host_port host_port::from_address(rpc_address addr)
{
host_port hp;
SCOPED_LOG_SLOW_EXECUTION(
WARNING, 100, "construct host_port '{}' from rpc_address '{}'", hp, addr);
switch (addr.type()) {
case HOST_TYPE_IPV4: {
CHECK(utils::hostname_from_ip(htonl(addr.ip()), &hp._host),
"invalid host_port {}",
addr.ipv4_str());
hp._port = addr.port();
} break;
case HOST_TYPE_GROUP: {
hp._group_host_port = std::make_shared<rpc_group_host_port>(addr.group_address());
} break;
default:
break;
}
// Now is_invalid() return false.
hp._type = addr.type();
return hp;
}
host_port host_port::from_string(const std::string &host_port_str)
{
host_port hp;
SCOPED_LOG_SLOW_EXECUTION(
WARNING, 100, "construct host_port '{}' from string '{}'", hp, host_port_str);
const auto pos = host_port_str.find_last_of(':');
if (dsn_unlikely(pos == std::string::npos)) {
return hp;
}
hp._host = host_port_str.substr(0, pos);
const auto port_str = host_port_str.substr(pos + 1);
if (dsn_unlikely(!dsn::buf2uint16(port_str, hp._port))) {
return hp;
}
if (dsn_unlikely(rpc_address::ipv4_from_host(hp._host.c_str()) == 0)) {
return hp;
}
// Now is_invalid() return false.
hp._type = HOST_TYPE_IPV4;
return hp;
}
void host_port::reset()
{
switch (type()) {
case HOST_TYPE_IPV4:
_host.clear();
_port = 0;
break;
case HOST_TYPE_GROUP:
_group_host_port = nullptr;
break;
default:
break;
}
_type = HOST_TYPE_INVALID;
}
host_port &host_port::operator=(const host_port &other)
{
if (this == &other) {
return *this;
}
reset();
switch (other.type()) {
case HOST_TYPE_IPV4:
_host = other.host();
_port = other.port();
break;
case HOST_TYPE_GROUP:
_group_host_port = other._group_host_port;
break;
default:
break;
}
_type = other.type();
return *this;
}
std::string host_port::to_string() const
{
switch (type()) {
case HOST_TYPE_IPV4:
return fmt::format("{}:{}", _host, _port);
case HOST_TYPE_GROUP:
return fmt::format("host_port group {}", group_host_port()->name());
default:
return "invalid host_port";
}
}
void host_port::assign_group(const char *name)
{
reset();
_type = HOST_TYPE_GROUP;
_group_host_port = std::make_shared<rpc_group_host_port>(name);
}
error_s host_port::resolve_addresses(std::vector<rpc_address> &addresses) const
{
CHECK(addresses.empty(), "");
switch (type()) {
case HOST_TYPE_INVALID:
return error_s::make(dsn::ERR_INVALID_STATE, "invalid host_port type: HOST_TYPE_INVALID");
case HOST_TYPE_GROUP:
return error_s::make(dsn::ERR_INVALID_STATE, "invalid host_port type: HOST_TYPE_GROUP");
case HOST_TYPE_IPV4:
break;
default:
CHECK(false, "");
__builtin_unreachable();
}
rpc_address rpc_addr;
// Resolve hostname like "localhost:80" or "192.168.0.1:8080".
if (rpc_addr.from_string_ipv4(this->to_string().c_str())) {
addresses.emplace_back(rpc_addr);
return error_s::ok();
}
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
AddrInfo result;
RETURN_NOT_OK(rpc_address::GetAddrInfo(_host, hints, &result));
// DNS may return the same host multiple times. We want to return only the unique
// addresses, but in the same order as DNS returned them. To do so, we keep track
// of the already-inserted elements in a set.
std::unordered_set<rpc_address> inserted;
std::vector<rpc_address> result_addresses;
for (const addrinfo *ai = result.get(); ai != nullptr; ai = ai->ai_next) {
CHECK_EQ(AF_INET, ai->ai_family);
sockaddr_in *addr = reinterpret_cast<sockaddr_in *>(ai->ai_addr);
addr->sin_port = htons(_port);
rpc_address rpc_addr(*addr);
LOG_INFO("resolved address {} for host_port {}", rpc_addr, to_string());
if (inserted.insert(rpc_addr).second) {
result_addresses.emplace_back(rpc_addr);
}
}
if (result_addresses.empty()) {
return error_s::make(dsn::ERR_NETWORK_FAILURE,
fmt::format("can not resolve host_port {}.", to_string()));
}
addresses = std::move(result_addresses);
return error_s::ok();
}
} // namespace dsn