#pragma once #include "Utilities.hpp" #include #include #include #include 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(&addr6_); } else { return reinterpret_cast(&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 { 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