Commit 5ddd1bdd authored by Joseph Noir's avatar Joseph Noir

Fix server creation on IPv4-only hosts

Creation of tcp acceptors now uses getaddrinfo to query the local
address and available IP version. Preferring v6 over v4 if available,
the framework tries all available addrs before failing.
parent abf66536
...@@ -72,6 +72,11 @@ public: ...@@ -72,6 +72,11 @@ public:
/// Returns a native IPv4 or IPv6 translation of `host`. /// Returns a native IPv4 or IPv6 translation of `host`.
static optional<std::pair<std::string, protocol>> static optional<std::pair<std::string, protocol>>
native_address(const std::string& host, optional<protocol> preferred = none); native_address(const std::string& host, optional<protocol> preferred = none);
/// Returns the host and protocol available for a local server socket
static std::vector<std::pair<std::string, protocol>>
server_address(uint16_t port, const char* host,
optional<protocol> preferred = none);
}; };
} // namespace network } // namespace network
......
...@@ -1316,10 +1316,20 @@ expected<void> set_inaddr_any(native_socket fd, sockaddr_in6& sa) { ...@@ -1316,10 +1316,20 @@ expected<void> set_inaddr_any(native_socket fd, sockaddr_in6& sa) {
} }
template <int Family> template <int Family>
expected<void> new_ip_acceptor_impl(native_socket fd, uint16_t port, expected<native_socket> new_ip_acceptor_impl(uint16_t port, const char* addr,
const char* addr) { bool reuse_addr) {
static_assert(Family == AF_INET || Family == AF_INET6, "invalid family"); static_assert(Family == AF_INET || Family == AF_INET6, "invalid family");
CAF_LOG_TRACE(CAF_ARG(port) << ", addr = " << (addr ? addr : "nullptr")); CAF_LOG_TRACE(CAF_ARG(port) << ", addr = " << (addr ? addr : "nullptr"));
CALL_CFUN(fd, cc_valid_socket, "socket", socket(Family, SOCK_STREAM, 0));
// sguard closes the socket in case of exception
socket_guard sguard{fd};
if (reuse_addr) {
int on = 1;
CALL_CFUN(tmp1, cc_zero, "setsockopt",
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<setsockopt_ptr>(&on),
static_cast<socklen_t>(sizeof(on))));
}
using sockaddr_type = using sockaddr_type =
typename std::conditional< typename std::conditional<
Family == AF_INET, Family == AF_INET,
...@@ -1339,38 +1349,39 @@ expected<void> new_ip_acceptor_impl(native_socket fd, uint16_t port, ...@@ -1339,38 +1349,39 @@ expected<void> new_ip_acceptor_impl(native_socket fd, uint16_t port,
CALL_CFUN(res, cc_zero, "bind", CALL_CFUN(res, cc_zero, "bind",
bind(fd, reinterpret_cast<sockaddr*>(&sa), bind(fd, reinterpret_cast<sockaddr*>(&sa),
static_cast<socklen_t>(sizeof(sa)))); static_cast<socklen_t>(sizeof(sa))));
return unit; return sguard.release();
} }
expected<native_socket> new_tcp_acceptor_impl(uint16_t port, const char* addr, expected<native_socket> new_tcp_acceptor_impl(uint16_t port, const char* addr,
bool reuse_addr) { bool reuse_addr) {
CAF_LOG_TRACE(CAF_ARG(port) << ", addr = " << (addr ? addr : "nullptr")); CAF_LOG_TRACE(CAF_ARG(port) << ", addr = " << (addr ? addr : "nullptr"));
protocol proto = ipv6; auto addrs = interfaces::server_address(port, addr);
if (addr != nullptr) { if (addrs.empty())
auto addrs = interfaces::native_address(addr); return make_error(sec::cannot_open_port, "No local interface available",
if (!addrs) addr);
return make_error(sec::cannot_open_port, "Invalid ADDR", addr); int fd = invalid_native_socket;
proto = addrs->second; for (auto& elem : addrs) {
CAF_ASSERT(proto == ipv4 || proto == ipv6); auto addr = elem.first.c_str();
auto p = elem.second == ipv4
? new_ip_acceptor_impl<AF_INET>(port, addr, reuse_addr)
: new_ip_acceptor_impl<AF_INET6>(port, addr, reuse_addr);
if (!p) {
CAF_LOG_DEBUG(p.error());
continue;
}
fd = *p;
break;
} }
CALL_CFUN(fd, cc_valid_socket, "socket", if (fd == invalid_native_socket) {
socket(proto == ipv4 ? AF_INET : AF_INET6, SOCK_STREAM, 0)); CAF_LOG_WARNING("could not open tcp socket on:" << CAF_ARG(port)
// sguard closes the socket in case of exception << CAF_ARG(addr));
socket_guard sguard(fd); return make_error(sec::cannot_open_port, "tcp socket creation failed",
if (reuse_addr) { port, addr);
int on = 1;
CALL_CFUN(tmp1, cc_zero, "setsockopt",
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<setsockopt_ptr>(&on),
static_cast<socklen_t>(sizeof(on))));
} }
auto p = proto == ipv4 ? new_ip_acceptor_impl<AF_INET>(fd, port, addr) socket_guard sguard{fd};
: new_ip_acceptor_impl<AF_INET6>(fd, port, addr);
if (!p)
return std::move(p.error());
CALL_CFUN(tmp2, cc_zero, "listen", listen(fd, SOMAXCONN)); CALL_CFUN(tmp2, cc_zero, "listen", listen(fd, SOMAXCONN));
// ok, no errors so far // ok, no errors so far
CAF_LOG_DEBUG(CAF_ARG(fd) << CAF_ARG(p)); CAF_LOG_DEBUG(CAF_ARG(fd));
return sguard.release(); return sguard.release();
} }
......
...@@ -239,6 +239,42 @@ interfaces::native_address(const std::string& host, ...@@ -239,6 +239,42 @@ interfaces::native_address(const std::string& host,
return none; return none;
} }
std::vector<std::pair<std::string, protocol>>
interfaces::server_address(uint16_t port, const char* host,
optional<protocol> preferred) {
using addr_pair = std::pair<std::string, protocol>;
addrinfo hint;
memset(&hint, 0, sizeof(hint));
hint.ai_socktype = SOCK_STREAM;
if (preferred)
hint.ai_family = *preferred == protocol::ipv4 ? AF_INET : AF_INET6;
else
hint.ai_family = AF_UNSPEC;
if (host == nullptr)
hint.ai_flags = AI_PASSIVE;
auto port_str = std::to_string(port);
addrinfo* tmp = nullptr;
if (getaddrinfo(host, port_str.c_str(), &hint, &tmp) != 0)
return {};
std::unique_ptr<addrinfo, decltype(freeaddrinfo)*> addrs{tmp, freeaddrinfo};
char buffer[INET6_ADDRSTRLEN];
// Take the first ipv6 address or the first available address otherwise
std::vector<addr_pair> results;
for (auto i = addrs.get(); i != nullptr; i = i->ai_next) {
auto family = fetch_addr_str(true, true, buffer, i->ai_addr);
if (family != AF_UNSPEC) {
results.emplace_back(std::string{buffer},
family == AF_INET ? protocol::ipv4
: protocol::ipv6);
}
}
std::stable_sort(std::begin(results), std::end(results),
[](const addr_pair& lhs, const addr_pair& rhs) {
return lhs.second > rhs.second;
});
return results;
}
} // namespace network } // namespace network
} // namespace io } // namespace io
} // namespace caf } // namespace caf
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