blob: 61b499eabf9e56e426fef2042a4b39d7814132ec [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 "util/network-util.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <limits.h>
#include <algorithm>
#include <sstream>
#include <random>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include "exec/kudu/kudu-util.h"
#include "kudu/util/net/sockaddr.h"
#include "util/debug-util.h"
#include "util/error-util.h"
#include "util/uid-util.h"
#include <util/string-parser.h>
#include "common/names.h"
using boost::algorithm::is_any_of;
using boost::algorithm::split;
using std::find;
using std::random_device;
#ifdef __APPLE__
// OS X does not seem to have a similar limitation as Linux and thus the
// macro is not defined.
#define HOST_NAME_MAX 64
#endif
namespace impala {
const string LOCALHOST_IP_STR("127.0.0.1");
const string LOCALHOST_IP_V6_STR("::1");
Status GetHostname(string* hostname) {
char name[HOST_NAME_MAX];
int ret = gethostname(name, HOST_NAME_MAX);
if (ret != 0) {
string error_msg = GetStrErrMsg();
stringstream ss;
ss << "Could not get hostname: " << error_msg;
return Status(ss.str());
}
*hostname = string(name);
return Status::OK();
}
Status HostnameToIpAddr(const Hostname& hostname, IpAddr* ip, bool ipv6){
// Try to resolve via the operating system.
vector<IpAddr> addresses;
addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = ipv6 ? AF_INET6 : AF_INET;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo* addr_info;
if (getaddrinfo(hostname.c_str(), NULL, &hints, &addr_info) != 0) {
stringstream ss;
ss << "Could not find IPv" << (ipv6 ? 6 : 4) << " address for: " << hostname;
return Status(ss.str());
}
addrinfo* it = addr_info;
while (it != NULL) {
char addr_buf[INET6_ADDRSTRLEN];
const char* result = nullptr;
bool is_ipv6_addr = it->ai_family == AF_INET6;
if (is_ipv6_addr) {
result = inet_ntop(AF_INET6, &((sockaddr_in6*)it->ai_addr)->sin6_addr,
addr_buf, sizeof(addr_buf));
} else {
result = inet_ntop(AF_INET, &((sockaddr_in*)it->ai_addr)->sin_addr,
addr_buf, sizeof(addr_buf));
}
if (result == NULL) {
stringstream ss;
ss << "Could not convert IPv" << (is_ipv6_addr ? 6: 4)
<< "address for: " << hostname;
freeaddrinfo(addr_info);
return Status(ss.str());
}
if (is_ipv6_addr == ipv6) {
addresses.push_back(string(addr_buf));
}
it = it->ai_next;
}
freeaddrinfo(addr_info);
if (addresses.empty()) {
stringstream ss;
ss << "Could not convert IPv" << (ipv6 ? 6 : 4) << " address for: " << hostname;
return Status(ss.str());
}
// RFC 3484 only specifies a partial order for the result of getaddrinfo() so we need to
// sort the addresses before picking the first non-localhost one.
sort(addresses.begin(), addresses.end());
// Try to find a non-localhost address, otherwise just use the first IP address
// returned.
*ip = addresses[0];
if (!FindFirstNonLocalhost(addresses, ip)) {
VLOG(3) << "Only localhost addresses found for " << hostname;
}
return Status::OK();
}
bool IsResolvedAddress(const TNetworkAddress& addr) {
kudu::Sockaddr sock;
return sock.ParseString(addr.hostname, addr.port).ok();
}
bool IsResolvedAddress(const NetworkAddressPB& addr) {
kudu::Sockaddr sock;
return sock.ParseString(addr.hostname(), addr.port()).ok();
}
bool FindFirstNonLocalhost(const vector<string>& addresses, string* addr) {
for (const string& candidate: addresses) {
if (candidate != LOCALHOST_IP_STR && candidate != LOCALHOST_IP_V6_STR) {
*addr = candidate;
return true;
}
}
return false;
}
TNetworkAddress MakeNetworkAddress(const string& hostname, int port) {
TNetworkAddress ret;
ret.__set_hostname(hostname);
ret.__set_port(port);
return ret;
}
TNetworkAddress MakeNetworkAddress(const string& address) {
vector<string> tokens;
split(tokens, address, is_any_of(":"));
TNetworkAddress ret;
if (tokens.size() == 1) {
ret.__set_hostname(tokens[0]);
ret.port = 0;
return ret;
}
if (tokens.size() != 2) return ret;
ret.__set_hostname(tokens[0]);
StringParser::ParseResult parse_result;
int32_t port = StringParser::StringToInt<int32_t>(
tokens[1].data(), tokens[1].length(), &parse_result);
if (parse_result != StringParser::PARSE_SUCCESS) return ret;
ret.__set_port(port);
return ret;
}
NetworkAddressPB MakeNetworkAddressPB(const string& hostname, int port) {
NetworkAddressPB ret;
ret.set_hostname(hostname);
ret.set_port(port);
return ret;
}
string GetUDSAddress(const std::string& hostname, int port, const UniqueIdPB& backend_id,
const UdsAddressUniqueIdPB& uds_addr_unique_id) {
stringstream ss;
switch (uds_addr_unique_id) {
case UdsAddressUniqueIdPB::IP_ADDRESS: {
string ip_addr = hostname;
kudu::Sockaddr sock;
// Check if the hostname is resolved IP address.
if (!sock.ParseString(hostname, port).ok()) {
IpAddr ip;
Status status = HostnameToIpAddr(hostname, &ip);
if (status.ok()) ip_addr = ip;
}
ss << "@impala-krpc:" << ip_addr << ":" << port;
break;
}
case UdsAddressUniqueIdPB::BACKEND_ID:
ss << "@impala-krpc:" << PrintId(backend_id);
break;
case UdsAddressUniqueIdPB::NO_UNIQUE_ID:
ss << "@impala-krpc";
break;
}
return ss.str();
}
NetworkAddressPB MakeNetworkAddressPB(const std::string& hostname, int port,
const UniqueIdPB& backend_id, const UdsAddressUniqueIdPB& uds_addr_unique_id) {
NetworkAddressPB ret;
ret.set_hostname(hostname);
ret.set_port(port);
ret.set_uds_address(GetUDSAddress(hostname, port, backend_id, uds_addr_unique_id));
return ret;
}
NetworkAddressPB MakeNetworkAddressPB(const std::string& hostname, int port,
const UdsAddressUniqueIdPB& uds_addr_unique_id) {
NetworkAddressPB ret;
UniqueIdPB backend_id;
if (uds_addr_unique_id == UdsAddressUniqueIdPB::BACKEND_ID) {
UUIDToUniqueIdPB(boost::uuids::random_generator()(), &backend_id);
}
ret.set_hostname(hostname);
ret.set_port(port);
ret.set_uds_address(GetUDSAddress(hostname, port, backend_id, uds_addr_unique_id));
return ret;
}
bool IsWildcardAddress(const string& ipaddress) {
return ipaddress == "0.0.0.0";
}
string TNetworkAddressToString(const TNetworkAddress& address) {
stringstream ss;
if (address.hostname.find(':') == string::npos) {
// IPv4
ss << address.hostname << ":" << dec << address.port;
} else {
// IPv6
ss << "[" << address.hostname << "]:" << dec << address.port;
}
return ss.str();
}
string NetworkAddressPBToString(const NetworkAddressPB& address) {
stringstream ss;
ss << address.hostname() << ":" << dec << address.port();
return ss.str();
}
TNetworkAddress FromNetworkAddressPB(const NetworkAddressPB& address) {
TNetworkAddress t_address;
t_address.__set_hostname(address.hostname());
t_address.__set_port(address.port());
if (address.has_uds_address()) t_address.__set_uds_address(address.uds_address());
return t_address;
}
NetworkAddressPB FromTNetworkAddress(const TNetworkAddress& address) {
NetworkAddressPB address_pb;
address_pb.set_hostname(address.hostname);
address_pb.set_port(address.port);
if (address.__isset.uds_address) address_pb.set_uds_address(address.uds_address);
return address_pb;
}
bool TNetworkAddressComparator::operator()(const TNetworkAddress& a,
const TNetworkAddress& b) const {
const int host_compare = a.hostname.compare(b.hostname);
if (host_compare < 0) {
return true;
} else if(host_compare > 0) {
return false;
}
// Hostnames were the same, compare on port
if (a.port < b.port) {
return true;
} else if (a.port > b.port) {
return false;
}
// Hostnames and ports were the same, compare on uds address.
if (a.__isset.uds_address) {
if (b.__isset.uds_address) {
return a.uds_address.compare(b.uds_address) < 0;
} else {
return false;
}
} else if (b.__isset.uds_address) {
return true;
}
return false;
}
/// Pick a random port in the range of ephemeral ports
/// https://tools.ietf.org/html/rfc6335
int FindUnusedEphemeralPort() {
static uint32_t LOWER = 49152, UPPER = 65000;
random_device rd;
srand(rd());
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) return -1;
struct sockaddr_in server_address;
bzero(reinterpret_cast<char*>(&server_address), sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
for (int tries = 0; tries < 100; ++tries) {
int port = LOWER + rand() % (UPPER - LOWER);
server_address.sin_port = htons(port);
if (bind(sockfd, reinterpret_cast<struct sockaddr*>(&server_address),
sizeof(server_address)) == 0) {
close(sockfd);
return port;
}
}
close(sockfd);
return -1;
}
Status NetworkAddressPBToSockaddr(
const NetworkAddressPB& address, bool use_uds, kudu::Sockaddr* sockaddr) {
if (use_uds) {
DCHECK(!address.uds_address().empty());
KUDU_RETURN_IF_ERROR(sockaddr->ParseUnixDomainPath(address.uds_address()),
"Invalid UNIX domain socket address.");
} else {
DCHECK(IsResolvedAddress(address));
KUDU_RETURN_IF_ERROR(
sockaddr->ParseString(NetworkAddressPBToString(address), address.port()),
"Failed to parse IP address to Kudu Sockaddr.");
}
return Status::OK();
}
}