blob: bcfc1efbe6d6edc71a8c9638dbcfbb484e7ccc28 [file] [log] [blame]
/** @file
Support class for describing the local machine.
@section license License
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 "tscore/ink_platform.h"
#include "tscore/ink_inet.h"
#include "tscore/ink_assert.h"
#include "tscore/Diags.h"
#include "I_Machine.h"
#if HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif
static void
make_to_lower_case(const char *name, char *lower_case_name, int buf_len)
{
int name_len = strlen(name);
int i;
if (name_len > (buf_len - 1)) {
name_len = buf_len - 1;
}
for (i = 0; i < name_len; i++) {
lower_case_name[i] = ParseRules::ink_tolower(name[i]);
}
lower_case_name[i] = '\0';
}
// Singleton
Machine *Machine::_instance = nullptr;
Machine *
Machine::instance()
{
ink_assert(_instance || !"Machine instance accessed before initialization");
return Machine::_instance;
}
Machine *
Machine::init(char const *name, sockaddr const *ip)
{
ink_assert(!_instance || !"Machine instance initialized twice.");
Machine::_instance = new Machine(name, ip);
return Machine::_instance;
}
Machine::Machine(char const *the_hostname, sockaddr const *addr)
: hostname(nullptr), hostname_len(0), ip_string_len(0), ip_hex_string_len(0)
{
char localhost[1024];
char ip_strbuf[INET6_ADDRSTRLEN];
int status; // return for system calls.
ip_string[0] = 0;
ip_hex_string[0] = 0;
ink_zero(ip);
ink_zero(ip4);
ink_zero(ip6);
uuid.initialize(TS_UUID_V4);
ink_release_assert(nullptr != uuid.getString()); // The Process UUID must be available on startup
localhost[sizeof(localhost) - 1] = 0; // ensure termination.
if (!ats_is_ip(addr)) {
if (!the_hostname) {
ink_release_assert(!gethostname(localhost, sizeof(localhost) - 1));
the_hostname = localhost;
}
hostname = ats_strdup(the_hostname);
#if HAVE_IFADDRS_H
ifaddrs *ifa_addrs = nullptr;
status = getifaddrs(&ifa_addrs);
#else
int s = socket(AF_INET, SOCK_DGRAM, 0);
// This number is hard to determine, but needs to be much larger than
// you would expect. On a normal system with just two interfaces and
// one address / interface the return count is 120. Stack space is
// cheap so it's best to go big.
static const int N_REQ = 1024;
ifconf conf;
ifreq req[N_REQ];
if (0 <= s) {
conf.ifc_len = sizeof(req);
conf.ifc_req = req;
status = ioctl(s, SIOCGIFCONF, &conf);
} else {
status = -1;
}
#endif
if (0 != status) {
Warning("Unable to determine local host '%s' address information - %s", hostname, strerror(errno));
} else {
// Loop through the interface addresses and prefer by type.
enum {
NA, // Not an (IP) Address.
LO, // Loopback.
LL, // Link Local
PR, // Private.
MC, // Multicast.
GL // Global.
} spot_type = NA,
ip4_type = NA, ip6_type = NA;
sockaddr const *ifip;
unsigned int ifflags;
for (
#if HAVE_IFADDRS_H
ifaddrs *spot = ifa_addrs; spot; spot = spot->ifa_next
#else
ifreq *spot = req, *req_limit = req + (conf.ifc_len / sizeof(*req)); spot < req_limit; ++spot
#endif
) {
#if HAVE_IFADDRS_H
ifip = spot->ifa_addr;
ifflags = spot->ifa_flags;
#else
ifip = &spot->ifr_addr;
// get the interface's flags
struct ifreq ifr;
ink_strlcpy(ifr.ifr_name, spot->ifr_name, IFNAMSIZ);
if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) {
ifflags = ifr.ifr_flags;
} else {
ifflags = 0; // flags not available, default to just looking at IP
}
#endif
if (!ats_is_ip(ifip)) {
spot_type = NA;
} else if (ats_is_ip_loopback(ifip) || (IFF_LOOPBACK & ifflags)) {
spot_type = LO;
} else if (ats_is_ip_linklocal(ifip)) {
spot_type = LL;
} else if (ats_is_ip_private(ifip)) {
spot_type = PR;
} else if (ats_is_ip_multicast(ifip)) {
spot_type = MC;
} else {
spot_type = GL;
}
if (spot_type == NA) {
continue; // Next!
}
if (ats_is_ip4(ifip) || ats_is_ip6(ifip)) {
ink_zero(ip_strbuf);
ink_zero(localhost);
ats_ip_ntop(ifip, ip_strbuf, sizeof(ip_strbuf));
insert_id(ip_strbuf);
if (spot_type != LL && getnameinfo(ifip, ats_ip_size(ifip), localhost, sizeof(localhost) - 1, nullptr, 0, 0) == 0) {
insert_id(localhost);
}
IpAddr *ipaddr = new IpAddr(ifip);
insert_id(ipaddr);
if (ats_is_ip4(ifip)) {
if (spot_type > ip4_type) {
ats_ip_copy(&ip4, ifip);
ip4_type = spot_type;
}
} else if (ats_is_ip6(ifip)) {
if (spot_type > ip6_type) {
ats_ip_copy(&ip6, ifip);
ip6_type = spot_type;
}
}
}
}
#if HAVE_IFADDRS_H
freeifaddrs(ifa_addrs);
#endif
// What about the general address? Prefer IPv4?
if (ip4_type >= ip6_type) {
ats_ip_copy(&ip.sa, &ip4.sa);
} else {
ats_ip_copy(&ip.sa, &ip6.sa);
}
}
#if !HAVE_IFADDRS_H
close(s);
#endif
} else { // address provided.
ats_ip_copy(&ip, addr);
if (ats_is_ip4(addr)) {
ats_ip_copy(&ip4, addr);
} else if (ats_is_ip6(addr)) {
ats_ip_copy(&ip6, addr);
}
status = getnameinfo(addr, ats_ip_size(addr), localhost, sizeof(localhost) - 1, nullptr, 0, 0); // no flags
if (0 != status) {
ip_text_buffer ipbuff;
Warning("Failed to find hostname for address '%s' - %s", ats_ip_ntop(addr, ipbuff, sizeof(ipbuff)), gai_strerror(status));
} else {
hostname = ats_strdup(localhost);
}
}
hostname_len = hostname ? strlen(hostname) : 0;
ats_ip_ntop(&ip.sa, ip_string, sizeof(ip_string));
ip_string_len = strlen(ip_string);
ip_hex_string_len = ats_ip_to_hex(&ip.sa, ip_hex_string, sizeof(ip_hex_string));
}
Machine::~Machine()
{
ats_free(hostname);
for (auto &machine_id_ipaddr : machine_id_ipaddrs) {
delete machine_id_ipaddr.second;
}
}
bool
Machine::is_self(const char *name)
{
char lower_case_name[TS_MAX_HOST_NAME_LEN + 1] = {0};
if (name == nullptr) {
return false;
}
make_to_lower_case(name, lower_case_name, sizeof(lower_case_name));
return machine_id_strings.find(lower_case_name) != machine_id_strings.end();
}
bool
Machine::is_self(const IpAddr *ipaddr)
{
char string_value[INET6_ADDRSTRLEN + 1] = {0};
if (ipaddr == nullptr) {
return false;
}
ipaddr->toString(string_value, sizeof(string_value));
return machine_id_ipaddrs.find(string_value) != machine_id_ipaddrs.end();
}
bool
Machine::is_self(struct sockaddr const *addr)
{
char string_value[INET6_ADDRSTRLEN + 1] = {0};
if (addr == nullptr) {
return false;
}
ats_ip_ntop(addr, string_value, sizeof(string_value));
return machine_id_ipaddrs.find(string_value) != machine_id_ipaddrs.end();
}
void
Machine::insert_id(char *id)
{
char lower_case_name[TS_MAX_HOST_NAME_LEN + 1] = {0};
make_to_lower_case(id, lower_case_name, sizeof(lower_case_name));
machine_id_strings.emplace(lower_case_name);
}
void
Machine::insert_id(IpAddr *ipaddr)
{
char string_value[INET6_ADDRSTRLEN + 1] = {0};
if (ipaddr != nullptr) {
ipaddr->toString(string_value, sizeof(string_value));
machine_id_strings.emplace(string_value);
machine_id_ipaddrs.emplace(string_value, ipaddr);
}
}