--- a/sources/network/ipaddress.cpp Wed Jan 27 14:16:58 2021 +0200 +++ b/sources/network/ipaddress.cpp Wed Jan 27 18:55:03 2021 +0200 @@ -46,133 +46,139 @@ typedef struct addrinfo AddrInfo; #endif -// ----------------------------------------------------------------------------- -// -std::string IPAddress::to_string (WithPort withport) const +net::octet_t net::ip_octet(const net::ip_address& address, unsigned char n) { - std::string val; + assert(n < 4); + return (address.host >> ((3 - n) * 8)) & 0xFF; +} - if (withport == WITH_PORT) - val = sprintf ("%u.%u.%u.%u:%u", octet (0), octet (1), octet (2), octet (3), port); - else - val = sprintf ("%u.%u.%u.%u", octet (0), octet (1), octet (2), octet (3)); +void net::ip_set_octet(net::ip_address* address, unsigned char n, net::octet_t octet) +{ + // TODO: make a big-endian version + assert(n < 4); + address->host &= ~(0xFF << (3 - n) * 8); + address->host |= octet << ((3 - n) * 8); +} +std::string net::ip_address_to_string(const net::ip_address& address) +{ + const auto octet = [&](unsigned char n) { return ip_octet(address, n); }; + std::string val = sprintf ("%u.%u.%u.%u:%u", octet(0), octet(1), octet(2), octet(3), address.port); return val; } -// ----------------------------------------------------------------------------- -// -unsigned char IPAddress::octet (int n) const +int net::ip_compare(const net::ip_address &one, const net::ip_address &other) { - return (host >> ((3 - n) * 8)) & 0xFF; -} - -// ----------------------------------------------------------------------------- -// -void IPAddress::set_octet (int n, unsigned char oct) -{ - // TODO: make a big-endian version - host &= ~(0xFF << (3 - n) * 8); - host |= oct << ((3 - n) * 8); + if (one.host != other.host) + { + return one.host - other.host; + } + return one.port - other.port; } -// ----------------------------------------------------------------------------- -// -bool IPAddress::compare (const IPAddress& other) const +bool net::ip_address::operator<(const ip_address& other) const { - for (int i = 0; i < 4; i += 1) - { - if (octet (i) != other.octet (i)) - return false; - } - - if (port != 0 - and other.port != 0 - and port != other.port) - { - return false; - } - - return true; + return ip_compare(*this, other) < 0; } -// ----------------------------------------------------------------------------- -// -bool IPAddress::operator< (const IPAddress& other) const +bool net::ip_address::operator==(const net::ip_address &other) const { - for (int i = 0; i < 4; i += 1) - { - if (octet (i) != other.octet (i)) - return octet (i) < other.octet (i); - } - - return port < other.port; + return this->host == other.host and this->port == other.port; } // ----------------------------------------------------------------------------- // -sockaddr_in IPAddress::to_sockaddr_in() const +sockaddr_in net::ip_address_to_sockaddr_in(const net::ip_address& address) { sockaddr_in claddr; memset (&claddr, 0, sizeof claddr); - claddr.sin_addr.s_addr = htonl (host); - claddr.sin_port = htons (port); + claddr.sin_addr.s_addr = htonl(address.host); + claddr.sin_port = htons(address.port); claddr.sin_family = AF_INET; return claddr; } -// ----------------------------------------------------------------------------- -// -IPAddress IPAddress::from_string (std::string input) +std::optional<unsigned short> net::ip_parse_port(const char* port_string, std::ostream& errorStream) { - unsigned int parts[4]; - int colonpos = input.find (":"); - std::string addressString = colonpos == -1 ? input : mid(input, 0, colonpos); - IPAddress value; - - // Try scanf the IPv4 host first - if (sscanf(addressString.data(), "%u.%u.%u.%u", &parts[0], &parts[1], &parts[2], &parts[3])) + std::optional<long> opt = to_int(port_string); + if (opt.has_value()) { - for (int i = 0; i < 4; i += 1) - value.set_octet (i, parts[i]); + if (*opt >= 0 and *opt < 65536) + { + return static_cast<unsigned short>(opt.value()); + } + else + { + return {}; + errorStream << "port is not in range"s; + } } else { - // Possibly a hostname, try resolve it - value = IPAddress::resolve (addressString); + return {}; + errorStream << "could not parse port"s; } +} - if (colonpos != -1) +std::optional<net::ip_address> net::ip_scan_octets(const char* address_string) +{ + std::optional<net::ip_address> value; + net::octet_t parts[4]; + if (std::sscanf(address_string, "%hhu.%hhu.%hhu.%hhu", &parts[0], &parts[1], &parts[2], &parts[3])) { - std::optional<long> opt = to_int(mid(input, colonpos + 1, -1).data()); - if (opt.has_value()) - { - value.port = opt.value(); - } + value = net::ip_address{}; + for (unsigned char i = 0; i < 4; i += 1) + ip_set_octet(&*value, i, parts[i]); } - return value; } -// ----------------------------------------------------------------------------- -// -IPAddress IPAddress::resolve (std::string node) +std::optional<net::ip_address> net::ip_resolve_hostname(const std::string& node, std::ostream& errorStream) { AddrInfo hints; AddrInfo* lookup; - memset (&hints, 0, sizeof hints); + memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; - - if (getaddrinfo(node.data(), nullptr, &hints, &lookup) != 0) - throw StringParseError ("unknown host " + node); - - IPAddress result; - assert (lookup != nullptr); - sockaddr_in* addr = reinterpret_cast<sockaddr_in*> (lookup[0].ai_addr); - result.host = ntohl (addr->sin_addr.s_addr); - result.port = ntohs (addr->sin_port); - freeaddrinfo (lookup); + std::optional<net::ip_address> result = {}; + if (::getaddrinfo(node.data(), nullptr, &hints, &lookup) != 0) + { + errorStream << "unknown host "s + node; + } + else + { + assert (lookup != nullptr); + sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(lookup[0].ai_addr); + result = net::ip_address{ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port)}; + ::freeaddrinfo(lookup); + } return result; } +std::optional<net::ip_address> net::ip_resolve(const std::string& input_string, std::ostream& errorStream) +{ + int colonpos = input_string.find(":"); + std::string addressString = colonpos == -1 ? input_string : mid(input_string, 0, colonpos); + std::optional<net::ip_address> value; + // Try scanf the IPv4 host first + value = ip_scan_octets(addressString.data()); + if (not value.has_value()) + { + // Possibly a hostname, try resolve it + value = ip_resolve_hostname(addressString, errorStream); + } + if (value.has_value() and colonpos != -1) + { + std::optional<unsigned short> port_opt = ip_parse_port(&input_string.data()[colonpos + 1], errorStream); + if (port_opt.has_value()) + { + value->port = *port_opt; + } + else + { + value.reset(); + } + } + return value; +} + END_ZFC_NAMESPACE \ No newline at end of file