diff -r 060a13878ca0 -r be953e1621d9 sources/network/ipaddress.cpp --- a/sources/network/ipaddress.cpp Wed Jan 27 12:41:50 2021 +0200 +++ b/sources/network/ipaddress.cpp Wed Jan 27 19:48:41 2021 +0200 @@ -1,5 +1,5 @@ /* - Copyright 2014 - 2016 Teemu Piippo + Copyright 2014 - 2021 Teemu Piippo All rights reserved. Redistribution and use in source and binary forms, with or without @@ -46,145 +46,143 @@ typedef struct addrinfo AddrInfo; #endif -// ----------------------------------------------------------------------------- -// -IPAddress::IPAddress() : - host (0), - port (0) {} - -// ----------------------------------------------------------------------------- -// -IPAddress::IPAddress (unsigned long host, unsigned short port) : - host (host), - port (port) {} +net::octet_t net::ip_octet(const net::ip_address& address, unsigned char n) +{ + assert(n < 4); + return (address.host >> ((3 - n) * 8)) & 0xFF; +} -// ----------------------------------------------------------------------------- -// -IPAddress::IPAddress (const IPAddress& other) : - host (other.host), - port (other.port) {} +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); +} -// ----------------------------------------------------------------------------- -// -String IPAddress::to_string (WithPort withport) const +std::string net::ip_address_to_string(const net::ip_address& address) { - String val; - - 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)); - + 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 : range(4)) - { - if (octet (i) != other.octet (i)) - return false; - } + return ip_compare(*this, other) < 0; +} - if (port != 0 - and other.port != 0 - and port != other.port) - { - return false; - } - - return true; +bool net::ip_address::operator==(const net::ip_address &other) const +{ + return this->host == other.host and this->port == other.port; } // ----------------------------------------------------------------------------- // -bool IPAddress::operator< (const IPAddress& other) const -{ - for (int i : range(4)) - { - if (octet (i) != other.octet (i)) - return octet (i) < other.octet (i); - } - - return 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 (String input) +std::optional net::ip_parse_port(const char* port_string, std::ostream& errorStream) +{ + std::optional opt = to_int(port_string); + if (opt.has_value()) + { + if (*opt >= 0 and *opt < 65536) + { + return static_cast(opt.value()); + } + else + { + return {}; + errorStream << "port is not in range"s; + } + } + else + { + return {}; + errorStream << "could not parse port"s; + } +} + +std::optional net::ip_resolve_hostname(const std::string& node, std::ostream& errorStream) { - unsigned int parts[4]; - int colonpos = input.find (":"); - String addressString = colonpos == -1 ? input : input.mid (0, colonpos); - IPAddress value; + AddrInfo hints; + AddrInfo* lookup; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + std::optional result = {}; + if (::getaddrinfo(node.data(), nullptr, &hints, &lookup) != 0) + { + errorStream << "unknown host "s + node; + } + else + { + assert (lookup != nullptr); + sockaddr_in* addr = reinterpret_cast(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_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 value; // Try scanf the IPv4 host first - if (sscanf (addressString, "%u.%u.%u.%u", &parts[0], &parts[1], &parts[2], &parts[3])) + int parts[4]; + if (std::sscanf(addressString.data(), "%d.%d.%d.%d", &parts[0], &parts[1], &parts[2], &parts[3])) { - for (int i : range(4)) - value.set_octet (i, parts[i]); + value = net::ip_address{}; + for (unsigned char i = 0; i < 4; i += 1) + { + if (parts[i] >= 0 and parts[i] < 256) + { + ip_set_octet(&*value, i, parts[i]); + } + else + { + value.reset(); + errorStream << "IP address value out of range"; + break; + } + } } else { // Possibly a hostname, try resolve it - value = IPAddress::resolve (addressString); + value = ip_resolve_hostname(addressString, errorStream); } - - if (colonpos != -1) - value.port = (unsigned short) input.mid (colonpos + 1, -1).toInt(); - + if (value.has_value() and colonpos != -1) + { + std::optional 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; } -// ----------------------------------------------------------------------------- -// -IPAddress IPAddress::resolve (String node) -{ - AddrInfo hints; - AddrInfo* lookup; - memset (&hints, 0, sizeof hints); - hints.ai_family = AF_INET; - - if (getaddrinfo (node, nullptr, &hints, &lookup) != 0) - throw StringParseError ("unknown host " + node); - - IPAddress result; - assert (lookup != nullptr); - sockaddr_in* addr = reinterpret_cast (lookup[0].ai_addr); - result.host = ntohl (addr->sin_addr.s_addr); - result.port = ntohs (addr->sin_port); - freeaddrinfo (lookup); - return result; -} - END_ZFC_NAMESPACE \ No newline at end of file