Reactor/lib/InetAddress.hpp
2025-06-27 18:01:00 -05:00

171 lines
4.0 KiB
C++

#pragma once
#include "Utilities.hpp"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <cstring>
namespace reactor {
class InetAddress
{
private:
union
{
sockaddr_in addr4_;
sockaddr_in6 addr6_;
};
bool isIpV6_;
public:
explicit InetAddress(uint16_t port = 0, bool ipv6 = false, bool loopback = false)
: isIpV6_(ipv6)
{
if (ipv6) {
memset(&addr6_, 0, sizeof(addr6_));
addr6_.sin6_family = AF_INET6;
addr6_.sin6_addr = loopback ? in6addr_loopback : in6addr_any;
addr6_.sin6_port = htons(port);
} else {
memset(&addr4_, 0, sizeof(addr4_));
addr4_.sin_family = AF_INET;
addr4_.sin_addr.s_addr = htonl(loopback ? INADDR_LOOPBACK : INADDR_ANY);
addr4_.sin_port = htons(port);
}
LOG_TRACE << "InetAddress created: " << toIpPort();
}
InetAddress(const std::string& ip, uint16_t port)
{
if (ip.find(':') != std::string::npos) {
isIpV6_ = true;
memset(&addr6_, 0, sizeof(addr6_));
addr6_.sin6_family = AF_INET6;
addr6_.sin6_port = htons(port);
if (inet_pton(AF_INET6, ip.c_str(), &addr6_.sin6_addr) <= 0) {
LOG_ERROR << "Invalid IPv6 address: " << ip;
}
} else {
isIpV6_ = false;
memset(&addr4_, 0, sizeof(addr4_));
addr4_.sin_family = AF_INET;
addr4_.sin_port = htons(port);
if (inet_pton(AF_INET, ip.c_str(), &addr4_.sin_addr) <= 0) {
LOG_ERROR << "Invalid IPv4 address: " << ip;
}
}
LOG_TRACE << "InetAddress created from ip:port: " << toIpPort();
}
explicit InetAddress(const sockaddr_in& addr) : addr4_(addr), isIpV6_(false)
{
LOG_TRACE << "InetAddress created from sockaddr_in: " << toIpPort();
}
explicit InetAddress(const sockaddr_in6& addr) : addr6_(addr), isIpV6_(true)
{
LOG_TRACE << "InetAddress created from sockaddr_in6: " << toIpPort();
}
const sockaddr* getSockAddr() const
{
if (isIpV6_) {
return reinterpret_cast<const sockaddr*>(&addr6_);
} else {
return reinterpret_cast<const sockaddr*>(&addr4_);
}
}
socklen_t getSockLen() const { return isIpV6_ ? sizeof(addr6_) : sizeof(addr4_); }
bool isIpV6() const { return isIpV6_; }
uint16_t port() const { return ntohs(isIpV6_ ? addr6_.sin6_port : addr4_.sin_port); }
std::string toIp() const
{
char buf[INET6_ADDRSTRLEN];
if (isIpV6_) {
inet_ntop(AF_INET6, &addr6_.sin6_addr, buf, sizeof(buf));
} else {
inet_ntop(AF_INET, &addr4_.sin_addr, buf, sizeof(buf));
}
return std::string(buf);
}
std::string toIpPort() const
{
return isIpV6_ ? "[" + toIp() + "]:" + std::to_string(port())
: toIp() + ":" + std::to_string(port());
}
bool operator==(const InetAddress& other) const
{
if (isIpV6_ != other.isIpV6_) return false;
if (isIpV6_) {
return memcmp(&addr6_, &other.addr6_, sizeof(addr6_)) == 0;
} else {
return memcmp(&addr4_, &other.addr4_, sizeof(addr4_)) == 0;
}
}
bool operator!=(const InetAddress& other) const
{
return !(*this == other);
}
bool operator<(const InetAddress& other) const
{
if (isIpV6_ != other.isIpV6_) {
return !isIpV6_;
}
if (isIpV6_) {
return memcmp(&addr6_, &other.addr6_, sizeof(addr6_)) < 0;
} else {
return memcmp(&addr4_, &other.addr4_, sizeof(addr4_)) < 0;
}
}
std::string familyToString() const
{
return isIpV6_ ? "IPv6" : "IPv4";
}
static bool resolve(const std::string& hostname, InetAddress& result)
{
// Simple resolution - in a real implementation you'd use getaddrinfo
if (hostname == "localhost") {
result = InetAddress(0, false, true);
return true;
}
// Try to parse as IP address directly
InetAddress addr(hostname, 0);
if (addr.toIp() != "0.0.0.0" && addr.toIp() != "::") {
result = addr;
return true;
}
LOG_WARN << "Could not resolve hostname: " << hostname;
return false;
}
};
} // namespace reactor
namespace std {
template<>
struct hash<reactor::InetAddress>
{
size_t operator()(const reactor::InetAddress& addr) const
{
size_t seed = 0;
reactor::hashCombine(seed, addr.toIp());
reactor::hashCombine(seed, addr.port());
reactor::hashCombine(seed, addr.isIpV6());
return seed;
}
};
} // namespace std