Commit cf0e7e2c authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #554

Allow socket creation on IPv4 only hosts
parents 926a55c5 5ddd1bdd
...@@ -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