Created
March 6, 2021 22:56
-
-
Save aprehot/86c20b91a64a745650269b28aeec194c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// XXX: Currently only used for testing due to issue with name lookup | |
/// Class that populates a sockaddress_storage with information related to the hostname, service, family and socktype. | |
/// Specifically avoids 0.0.0.0 address for compliance with STIGs | |
/// in Centos7, getaddrinfo doesnt work correctly with hostnames specified within /etc/hosts | |
class AddressInfo { | |
public: | |
AddressInfo() = delete; | |
~AddressInfo() { | |
if (this->ressave != nullptr) { | |
free(this->ressave); | |
this->ressave = nullptr; | |
} | |
}; | |
enum TransportType : int { | |
TCP = SOCK_STREAM, | |
UDP = SOCK_DGRAM | |
}; | |
enum IpDomain : int { | |
IPV4 = PF_INET, | |
IPV6 = PF_INET6, | |
ANY = PF_UNSPEC | |
}; | |
AddressInfo(std::string const &hName, | |
short portNum) : | |
hostname(hName), | |
port(portNum) { | |
if (!isValidPortNumber(port)) { | |
LOG(ERROR) << "Supplied port was invalid"; | |
} | |
this->getAddressFromHostname(); | |
} | |
AddressInfo(std::string const &hName, | |
short portNum, | |
int proto, | |
int ipDomain) : | |
hostname(hName), | |
port(portNum), | |
protocol(TransportType(proto)), | |
domain(IpDomain(ipDomain)) { | |
if (ipDomain == IpDomain::IPV6) { | |
this->isIpv6 = true; | |
} | |
if (!isValidPortNumber(port)) { | |
LOG(ERROR) << "Supplied port was invalid"; | |
} | |
this->getAddressFromHostname(); | |
} | |
/// check for if 0.0.0.0 is returned from current address | |
bool checkForMeta() { | |
std::string addrStr(INET6_ADDRSTRLEN, '\0'); | |
std::variant<sockaddr_in6*, sockaddr_in*> sockInfo; | |
if (this->isIpv6) { | |
sockInfo = reinterpret_cast<sockaddr_in6 *>(res->ai_addr); | |
} else { | |
sockInfo = reinterpret_cast<sockaddr_in *>(res->ai_addr); | |
} | |
std::visit([&](auto &&sock) { | |
using T = std::decay_t<decltype(sock)>; | |
if constexpr (std::is_same_v<T, sockaddr_in6*>) { | |
inet_ntop(AF_INET6, &sock->sin6_addr, addrStr.data(), addrStr.size()); | |
} else { | |
inet_ntop(AF_INET, &sock->sin_addr, addrStr.data(), addrStr.size()); | |
} | |
}, sockInfo); | |
if (addrStr == META_ADDRESS) { | |
LOG(INFO) << "Skipping address " << addrStr; | |
res = res->ai_next; | |
return true; | |
} else { | |
LOG(INFO) << "Got address: " << addrStr; | |
} | |
return false; | |
} | |
/// this is so that a valid address can be identified based on the supplied hostname parameter | |
bool getValidAddress() { | |
int yes{}; | |
ressave = res; | |
std::string addrStr(INET6_ADDRSTRLEN, '\0'); | |
while (res) { | |
sockFd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | |
if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &(yes = 1), sizeof(yes)) < 0) { | |
LOG(ERROR) << "setsockopt(SO_REUSEADDR) failed"; | |
// return false; | |
} | |
if (!(sockFd < 0)) { | |
this->isIpv6 = res->ai_family == AF_INET6; /// set ipv6 here or in ctor | |
if (this->checkForMeta()) continue; /// skip if 0.0.0.0 | |
int err = bind(sockFd, res->ai_addr, res->ai_addrlen); | |
std::stringstream ss; | |
ss << "getaddrinfo error:: [" << gai_strerror(err) << "]"; | |
LOG(ERROR) << ss.str(); | |
if (err == 0) { | |
LOG(INFO) << "Address located was " << addrStr; | |
close(sockFd); | |
sockFd = Socket::INVALID_SOCKET; | |
memcpy(&this->sockStorage, res->ai_addr, sizeof(this->sockStorage)); | |
if (this->ressave != nullptr) { | |
freeaddrinfo(ressave); | |
this->ressave = nullptr; | |
} | |
return true; | |
} | |
close(sockFd); | |
sockFd = Socket::INVALID_SOCKET; | |
} | |
res = res->ai_next; | |
} | |
if (this->ressave != nullptr) { | |
freeaddrinfo(ressave); | |
this->ressave = nullptr; | |
} | |
return false; | |
} | |
/// Fetch ipv4 or ipv6 address info from the supplied hostname | |
void getAddressFromHostname() { | |
this->hints = {}; | |
memset(&hints, 0, sizeof(hints)); | |
/// TODO: change these back to class members | |
hints.ai_family = AF_UNSPEC; | |
hints.ai_socktype = 0; | |
hints.ai_protocol = 0; | |
hints.ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG); | |
int err{}; | |
const char * portNum = std::to_string(port).c_str(); | |
err = getaddrinfo(hostname.c_str(), port == 0 ? nullptr : portNum, &hints, &res); | |
if (err != 0) { | |
std::stringstream ss; | |
ss << "getaddrinfo error:: [" << gai_strerror(err) << "]"; | |
auto lookupErr = ss.str().c_str(); | |
throw lookup_error(lookupErr); | |
} | |
// addrinfo *reslocal; | |
// /// Get the local wildcard address to bind to (i.e. "::") | |
// int wildcardAddrFd = socket(AF_INET6, SOCK_DGRAM, 0); | |
// sockaddr_in6 addr = {}; | |
// addr.sin6_addr = in6addr_any; | |
// addrinfo * inAddrAny = reinterpret_cast<addrinfo *>(&addr); | |
// if (0 != (bind(wildcardAddrFd, inAddrAny->ai_addr, inAddrAny->ai_addrlen))) { | |
// std::stringstream ss; | |
// ss << "bind error:: could not bind to ip address=[::]"; | |
// auto lookupErr = ss.str().c_str(); | |
// throw lookup_error(lookupErr); | |
// } | |
// getaddrinfo(nullptr, nullptr, &hints, &reslocal); | |
if (!this->getValidAddress()) { | |
std::stringstream ss; | |
ss << "get_addr error:: could not find ip address=[" | |
<< hostname << "] port=[" << std::to_string(port) << "]"; | |
auto lookupErr = ss.str().c_str(); | |
throw lookup_error(lookupErr); | |
} | |
// freeaddrinfo(reslocal); | |
} | |
sockaddr_storage getAddressInfo() const { | |
return sockStorage; | |
} | |
private: | |
int sockFd{ Socket::INVALID_SOCKET }; | |
addrinfo hints = {}; | |
addrinfo *res{ nullptr }; | |
addrinfo *ressave{ nullptr }; | |
static inline constexpr std::string_view META_ADDRESS{ "0.0.0.0" }; | |
bool isIpv6{ false }; | |
std::string hostname; | |
short port; | |
TransportType protocol{ TransportType::TCP }; | |
IpDomain domain{ IpDomain::ANY }; | |
sockaddr_storage sockStorage = {}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment