in folly/io/async/AsyncServerSocket.cpp [440:569]
void AsyncServerSocket::bind(uint16_t port) {
struct addrinfo hints, *res0;
char sport[sizeof("65536")];
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
snprintf(sport, sizeof(sport), "%u", port);
// On Windows the value we need to pass to bind to all available
// addresses is an empty string. Everywhere else, it's nullptr.
constexpr const char* kWildcardNode = kIsWindows ? "" : nullptr;
if (getaddrinfo(kWildcardNode, sport, &hints, &res0)) {
throw std::invalid_argument(
"Attempted to bind address to socket with "
"bad getaddrinfo");
}
SCOPE_EXIT { freeaddrinfo(res0); };
auto setupAddress = [&](struct addrinfo* res) {
auto s = netops::socket(res->ai_family, res->ai_socktype, res->ai_protocol);
// IPv6/IPv4 may not be supported by the kernel
if (s == NetworkSocket() && errno == EAFNOSUPPORT) {
return;
}
CHECK_NE(s, NetworkSocket());
try {
setupSocket(s, res->ai_family);
} catch (...) {
closeNoInt(s);
throw;
}
if (res->ai_family == AF_INET6) {
int v6only = 1;
CHECK(
0 ==
netops::setsockopt(
s, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)));
}
// Bind to the socket
if (netops::bind(s, res->ai_addr, socklen_t(res->ai_addrlen)) != 0) {
folly::throwSystemError(
errno,
"failed to bind to async server socket for port ",
SocketAddress::getPortFrom(res->ai_addr),
" family ",
SocketAddress::getFamilyNameFrom(res->ai_addr, "<unknown>"));
}
#if defined(__linux__)
if (noTransparentTls_) {
// Ignore return value, errors are ok
netops::setsockopt(s, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0);
}
#endif
SocketAddress address;
address.setFromLocalAddress(s);
sockets_.emplace_back(eventBase_, s, this, address.getFamily());
};
const int kNumTries = 25;
for (int tries = 1; true; tries++) {
// Prefer AF_INET6 addresses. RFC 3484 mandates that getaddrinfo
// should return IPv6 first and then IPv4 addresses, but glibc's
// getaddrinfo(nullptr) with AI_PASSIVE returns:
// - 0.0.0.0 (IPv4-only)
// - :: (IPv6+IPv4) in this order
// See: https://sourceware.org/bugzilla/show_bug.cgi?id=9981
for (struct addrinfo* res = res0; res; res = res->ai_next) {
if (res->ai_family == AF_INET6) {
setupAddress(res);
}
}
// If port == 0, then we should try to bind to the same port on ipv4 and
// ipv6. So if we did bind to ipv6, figure out that port and use it.
if (sockets_.size() == 1 && port == 0) {
SocketAddress address;
address.setFromLocalAddress(sockets_.back().socket_);
snprintf(sport, sizeof(sport), "%u", address.getPort());
freeaddrinfo(res0);
CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0));
}
try {
for (struct addrinfo* res = res0; res; res = res->ai_next) {
if (res->ai_family != AF_INET6) {
setupAddress(res);
}
}
} catch (const std::system_error&) {
// If we can't bind to the same port on ipv4 as ipv6 when using
// port=0 then we will retry again before giving up after
// kNumTries attempts. We do this by closing the sockets that
// were opened, then restarting from scratch.
if (port == 0 && !sockets_.empty() && tries != kNumTries) {
for (const auto& socket : sockets_) {
if (socket.socket_ == NetworkSocket()) {
continue;
} else if (
const auto shutdownSocketSet = wShutdownSocketSet_.lock()) {
shutdownSocketSet->close(socket.socket_);
} else {
closeNoInt(socket.socket_);
}
}
sockets_.clear();
snprintf(sport, sizeof(sport), "%u", port);
freeaddrinfo(res0);
CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0));
continue;
}
throw;
}
break;
}
if (sockets_.empty()) {
throw std::runtime_error("did not bind any async server socket for port");
}
}