Commit 2fb69431 authored by Joseph Noir's avatar Joseph Noir

Add local address lookup with getifaddrs for unix

parent f582df13
...@@ -30,6 +30,10 @@ namespace ip { ...@@ -30,6 +30,10 @@ namespace ip {
/// Returns all IP addresses of to `host` (if any). /// Returns all IP addresses of to `host` (if any).
std::vector<ip_address> resolve(string_view host); std::vector<ip_address> resolve(string_view host);
/// Returns the IP addresses for a local endpoint, which is either an address,
/// an interface, or localhost.
std::vector<ip_address> local_addresses(string_view host);
/// Returns the hostname of this device. /// Returns the hostname of this device.
std::string hostname(); std::string hostname();
......
...@@ -25,7 +25,9 @@ ...@@ -25,7 +25,9 @@
#include "caf/detail/socket_sys_includes.hpp" #include "caf/detail/socket_sys_includes.hpp"
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/ip_address.hpp" #include "caf/ip_address.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/logger.hpp" #include "caf/logger.hpp"
#include "caf/string_algorithms.hpp"
#include "caf/string_view.hpp" #include "caf/string_view.hpp"
#ifdef CAF_WINDOWS #ifdef CAF_WINDOWS
...@@ -57,6 +59,9 @@ namespace { ...@@ -57,6 +59,9 @@ namespace {
// Dummy port to resolve empty string with getaddrinfo. // Dummy port to resolve empty string with getaddrinfo.
constexpr auto dummy_port = "42"; constexpr auto dummy_port = "42";
constexpr auto v4_any_addr = "0.0.0.0";
constexpr auto v6_any_addr = "::";
constexpr auto localhost = "localhost";
void* fetch_in_addr(int family, sockaddr* addr) { void* fetch_in_addr(int family, sockaddr* addr) {
if (family == AF_INET) if (family == AF_INET)
...@@ -75,6 +80,20 @@ int fetch_addr_str(char (&buf)[INET6_ADDRSTRLEN], sockaddr* addr) { ...@@ -75,6 +80,20 @@ int fetch_addr_str(char (&buf)[INET6_ADDRSTRLEN], sockaddr* addr) {
: AF_UNSPEC; : AF_UNSPEC;
} }
void find_by_name(const vector<pair<string, ip_address>>& interfaces,
string_view name, vector<ip_address>& results) {
for (auto& p : interfaces)
if (p.first == name)
results.push_back(p.second);
}
void find_by_addr(const vector<pair<string, ip_address>>& interfaces,
ip_address addr, vector<ip_address>& results) {
for (auto& p : interfaces)
if (p.second == addr)
results.push_back(p.second);
}
} // namespace } // namespace
std::vector<ip_address> resolve(string_view host) { std::vector<ip_address> resolve(string_view host) {
...@@ -118,6 +137,58 @@ std::string hostname() { ...@@ -118,6 +137,58 @@ std::string hostname() {
return buf; return buf;
} }
#ifdef CAF_WINDOWS
std::vector<ip_address> local_addresses(string_view host) {
std::vector<ip_address> results;
return results;
}
#else // CAF_WINDOWS
std::vector<ip_address> local_addresses(string_view host) {
std::vector<ip_address> results;
// The string is not returned by getifaddrs, let's just do that ourselves.
if (host.compare(localhost) == 0)
return {ip_address{{0}, {0x1}},
ipv6_address{make_ipv4_address(127, 0, 0, 1)}};
if (host.compare(v4_any_addr) == 0 || host.compare(v6_any_addr) == 0)
return {ip_address{}};
ifaddrs* tmp = nullptr;
if (getifaddrs(&tmp) != 0)
return {};
std::unique_ptr<ifaddrs, decltype(freeifaddrs)*> addrs{tmp, freeifaddrs};
char buffer[INET6_ADDRSTRLEN];
// Unless explicitly specified we are going to skip link-local addresses.
auto is_link_local = starts_with(host, "fe80:");
std::vector<std::pair<std::string, ip_address>> interfaces;
for (auto i = addrs.get(); i != nullptr; i = i->ifa_next) {
auto family = fetch_addr_str(buffer, i->ifa_addr);
if (family != AF_UNSPEC) {
ip_address ip;
if (!is_link_local && starts_with(buffer, "fe80:")) {
CAF_LOG_DEBUG("skipping link-local address: " << buffer);
continue;
} else if (auto err = parse(buffer, ip)) {
CAF_LOG_ERROR("could not parse into ip address " << buffer);
continue;
}
interfaces.emplace_back(std::string{i->ifa_name}, ip);
}
}
ip_address host_addr;
if (host.empty())
for (auto& p : interfaces)
results.push_back(std::move(p.second));
else if (auto err = parse(host, host_addr))
find_by_name(interfaces, host, results);
else
find_by_addr(interfaces, host_addr, results);
return results;
}
#endif // CAF_WINDOWS
} // namespace ip } // namespace ip
} // namespace net } // namespace net
} // namespace caf } // namespace caf
...@@ -30,16 +30,31 @@ ...@@ -30,16 +30,31 @@
using namespace caf; using namespace caf;
using namespace caf::net; using namespace caf::net;
CAF_TEST_FIXTURE_SCOPE(ip_tests, host_fixture) namespace {
struct fixture : host_fixture {
fixture() : v6_local{{0}, {0x1}} {
v4_local = ip_address{make_ipv4_address(127, 0, 0, 1)};
}
bool contains(ip_address x) {
return std::count(addrs.begin(), addrs.end(), x) > 0;
}
ip_address any_addr;
ip_address v4_local;
ip_address v6_local;
std::vector<ip_address> addrs;
};
} // namespace
CAF_TEST_FIXTURE_SCOPE(ip_tests, fixture)
CAF_TEST(resolve localhost) { CAF_TEST(resolve localhost) {
ip_address v4_local{make_ipv4_address(127, 0, 0, 1)}; addrs = ip::resolve("localhost");
ip_address v6_local{{0}, {0x1}};
auto addrs = ip::resolve("localhost");
CAF_CHECK(!addrs.empty()); CAF_CHECK(!addrs.empty());
auto contains = [&](ip_address x) {
return std::count(addrs.begin(), addrs.end(), x) > 0;
};
CAF_CHECK(contains(v4_local) || contains(v6_local)); CAF_CHECK(contains(v4_local) || contains(v6_local));
} }
...@@ -54,4 +69,21 @@ CAF_TEST(resolve any) { ...@@ -54,4 +69,21 @@ CAF_TEST(resolve any) {
CAF_CHECK(contains(v4_any) || contains(v6_any)); CAF_CHECK(contains(v4_any) || contains(v6_any));
} }
CAF_TEST(local addresses) {
// This is not that easy to test. There are few hard-coded addresses we can
// check.
CAF_MESSAGE("check: v6");
addrs = ip::local_addresses("::");
CAF_CHECK(!addrs.empty());
CAF_CHECK(contains(any_addr));
CAF_MESSAGE("check: v4");
addrs = ip::local_addresses("0.0.0.0");
CAF_CHECK(!addrs.empty());
CAF_CHECK(contains(any_addr));
CAF_MESSAGE("check: localhost");
addrs = ip::local_addresses("localhost");
CAF_CHECK(!addrs.empty());
CAF_CHECK(contains(v4_local) && contains(v6_local));
}
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment