Wed, 27 Jan 2021 18:55:03 +0200
cleanup ip address code
--- a/sources/interface.cpp Wed Jan 27 14:16:58 2021 +0200 +++ b/sources/interface.cpp Wed Jan 27 18:55:03 2021 +0200 @@ -132,7 +132,7 @@ { case INPUTSTATE_ADDRESS: if (m_remoteAddress.host != 0) - getEditableInput() = m_remoteAddress.to_string(IPAddress::WITH_PORT); + getEditableInput() = net::ip_address_to_string(m_remoteAddress); break; default: @@ -488,7 +488,7 @@ case RCON_CONNECTING: case RCON_AUTHENTICATING: - text = "Connecting to " + m_session.address().to_string(IPAddress::WITH_PORT) + "..."; + text = "Connecting to " + net::ip_address_to_string(m_session.address()) + "..."; break; case RCON_CONNECTED: @@ -506,7 +506,7 @@ } text = zfc::sprintf("%s | %s | %s", - m_session.address().to_string(IPAddress::WITH_PORT).data(), + net::ip_address_to_string(m_session.address()).data(), m_session.getLevel().data(), adminText.data()); } @@ -606,6 +606,26 @@ m_needInputRender = true; } +bool Interface::tryResolveAddress(const std::string &address_string, net::ip_address* target) +{ + std::stringstream errors; + const std::optional<net::ip_address> address_opt = net::ip_resolve(address_string, errors); + if (address_opt.has_value()) + { + *target = address_opt.value(); + if (target->port == 0) + { + target->port = 10666; + } + return true; + } + else + { + this->printError("%s\n", errors.str().data()); + return false; + } +} + // ------------------------------------------------------------------------------------------------- // void Interface::handleInput() @@ -794,20 +814,10 @@ break; // handled above case INPUTSTATE_ADDRESS: - try - { - m_remoteAddress = IPAddress::from_string(getCurrentInput()); - } - catch (std::exception& e) + if (this->tryResolveAddress(this->getCurrentInput(), &this->m_remoteAddress)) { - print("%s\n", e.what()); - return; + setInputState(INPUTSTATE_PASSWORD); } - - if (m_remoteAddress.port == 0) - m_remoteAddress.port = 10666; - - setInputState(INPUTSTATE_PASSWORD); break; case INPUTSTATE_PASSWORD: @@ -996,24 +1006,14 @@ // ------------------------------------------------------------------------------------------------- // -void Interface::connect(std::string address, std::string password) +void Interface::connect(std::string address_string, std::string password) { - try - { - m_remoteAddress = IPAddress::from_string(address); - } - catch (std::exception& e) + if (this->tryResolveAddress(address_string, &this->m_remoteAddress)) { - print("%s\n", e.what()); - return; + m_session.disconnect(); + m_session.setPassword(password); + m_session.connect(m_remoteAddress); } - - if (m_remoteAddress.port == 0) - m_remoteAddress.port = 10666; - - m_session.disconnect(); - m_session.setPassword(password); - m_session.connect(m_remoteAddress); } // ------------------------------------------------------------------------------------------------- @@ -1069,24 +1069,7 @@ } else { - IPAddress address; - - try - { - address = IPAddress::from_string(args[0]); - } - catch (std::exception& e) - { - printError("%s\n", e.what()); - return; - } - - if (address.port == 0) - address.port = 10666; - - m_session.setPassword(args[1]); - m_session.disconnect(); - m_session.connect(m_remoteAddress = address); + this->connect(args[0], args[1]); } } else if (command == "disconnect") @@ -1107,7 +1090,7 @@ // void Interface::disconnected() { - print("Disconnected from %s\n", m_session.address().to_string(IPAddress::WITH_PORT).data()); + print("Disconnected from %s\n", net::ip_address_to_string(m_session.address()).data()); resetTitle(); renderFull(); }
--- a/sources/interface.h Wed Jan 27 14:16:58 2021 +0200 +++ b/sources/interface.h Wed Jan 27 18:55:03 2021 +0200 @@ -83,7 +83,7 @@ std::string m_title; InputState m_inputState; std::function<void(bool)> m_disconnectCallback; - IPAddress m_remoteAddress; + net::ip_address m_remoteAddress; std::string m_statusBarText; std::vector<ColoredLine> m_playerNames; std::string m_pasteBuffer; @@ -111,6 +111,7 @@ void safeDisconnect(std::function<void(bool)> afterwards); void setInputState(InputState newstate); void yank(int a, int b); + bool tryResolveAddress(const std::string& address_string, net::ip_address *target); }; END_ZFC_NAMESPACE \ No newline at end of file
--- a/sources/main.h Wed Jan 27 14:16:58 2021 +0200 +++ b/sources/main.h Wed Jan 27 18:55:03 2021 +0200 @@ -31,6 +31,8 @@ #pragma once #include <algorithm> #include <functional> +#include <string> +#include <sstream> #include <cstdio> #include <cstdlib> #include <cstdint>
--- 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
--- a/sources/network/ipaddress.h Wed Jan 27 14:16:58 2021 +0200 +++ b/sources/network/ipaddress.h Wed Jan 27 18:55:03 2021 +0200 @@ -29,6 +29,7 @@ */ #pragma once +#include <ostream> #include "../main.h" struct sockaddr; @@ -36,48 +37,26 @@ BEGIN_ZFC_NAMESPACE -struct IPAddress +namespace net { - enum - { - LOCALHOST = 0x7f000001 - }; - - enum WithPort - { - WITH_PORT, - NO_PORT - }; - - class StringParseError : public std::exception + using octet_t = std::uint8_t; + struct ip_address { - std::string m_message; - - public: - StringParseError (std::string message) : - m_message (message) {} - - const char* what() const throw() - { - return m_message.data(); - } + std::uint32_t host; + std::uint16_t port; + bool operator<(const ip_address& other) const; + bool operator==(const ip_address& other) const; }; - - unsigned long host; - unsigned short port; - - bool compare (const IPAddress& other) const; - unsigned char octet (int n) const; - void set_octet (int n, unsigned char oct); - std::string to_string (WithPort withport = NO_PORT) const; - sockaddr_in to_sockaddr_in() const; - bool operator< (const IPAddress& other) const; - bool operator== (const IPAddress& other) const { return compare (other); } - bool operator!= (const IPAddress& other) const { return not compare (other); } - unsigned char operator[] (int n) const { return octet (n); } - - static IPAddress from_string (std::string input); - static IPAddress resolve (std::string node); -}; + constexpr ip_address localhost = {0x7f000001, 0}; + int ip_compare(const ip_address& one, const ip_address& other); + net::octet_t ip_octet(const ip_address& address, unsigned char n); + void ip_set_octet(ip_address* address, unsigned char n, net::octet_t octet); + std::optional<ip_address> ip_scan_octets(const char* address_string); + std::optional<unsigned short> ip_parse_port(const char* port_string, std::ostream& errorStream); + std::optional<ip_address> ip_resolve_hostname(const std::string& node, std::ostream& errorStream); + std::optional<ip_address> ip_resolve(const std::string& input_string, std::ostream &errorStream); + sockaddr_in ip_address_to_sockaddr_in(const ip_address& address); + std::string ip_address_to_string(const ip_address& address); +} END_ZFC_NAMESPACE
--- a/sources/network/rconsession.cpp Wed Jan 27 14:16:58 2021 +0200 +++ b/sources/network/rconsession.cpp Wed Jan 27 18:55:03 2021 +0200 @@ -56,7 +56,7 @@ // ------------------------------------------------------------------------------------------------- // -void RCONSession::connect(IPAddress address) +void RCONSession::connect(net::ip_address address) { m_address = address; m_state = RCON_CONNECTING; @@ -277,7 +277,7 @@ // void RCONSession::sendHello() { - m_interface->print("Connecting to %s...\n", m_address.to_string(IPAddress::WITH_PORT).data()); + m_interface->print("Connecting to %s...\n", net::ip_address_to_string(m_address).data()); send({CLRC_BEGINCONNECTION, RCON_PROTOCOL_VERSION}); bumpLastPing(); } @@ -344,7 +344,7 @@ // ------------------------------------------------------------------------------------------------- // -const IPAddress& RCONSession::address() const +const net::ip_address& RCONSession::address() const { return m_address; }
--- a/sources/network/rconsession.h Wed Jan 27 14:16:58 2021 +0200 +++ b/sources/network/rconsession.h Wed Jan 27 18:55:03 2021 +0200 @@ -95,9 +95,9 @@ RCONSession(); ~RCONSession(); - const IPAddress& address() const; + const net::ip_address& address() const; void bumpLastPing(); - void connect(IPAddress address); + void connect(net::ip_address address); void disconnect(); int getAdminCount() const; const std::string& getLevel() const; @@ -117,7 +117,7 @@ private: RCONSessionState m_state; - IPAddress m_address; + net::ip_address m_address; UDPSocket m_socket; time_t m_lastPing; std::string m_password;
--- a/sources/network/udpsocket.cpp Wed Jan 27 14:16:58 2021 +0200 +++ b/sources/network/udpsocket.cpp Wed Jan 27 18:55:03 2021 +0200 @@ -141,11 +141,11 @@ // ------------------------------------------------------------------------------------------------- // -bool UDPSocket::send (const IPAddress& address, const ByteArray& data) +bool UDPSocket::send (const net::ip_address& address, const ByteArray& data) { int encodedlength = sizeof HuffmanBuffer; HUFFMAN_Encode (data.data(), reinterpret_cast<unsigned char*> (HuffmanBuffer), data.size(), &encodedlength); - sockaddr_in claddr = address.to_sockaddr_in(); + sockaddr_in claddr = net::ip_address_to_sockaddr_in(address); int res = ::sendto (m_socket, HuffmanBuffer, encodedlength, 0, reinterpret_cast<sockaddr*> (&claddr), sizeof claddr);
--- a/sources/network/udpsocket.h Wed Jan 27 14:16:58 2021 +0200 +++ b/sources/network/udpsocket.h Wed Jan 27 18:55:03 2021 +0200 @@ -39,7 +39,7 @@ struct Datagram { ByteArray message; - IPAddress address; + net::ip_address address; }; // ------------------------------------------------------------------------------------------------- @@ -52,7 +52,7 @@ bool bind (unsigned short port); bool read (Datagram& datagram); - bool send (const IPAddress& address, const ByteArray& data); + bool send (const net::ip_address& address, const ByteArray& data); bool set_blocking (bool a); const std::string& error_string() const { return m_error; } int file_descriptor() const { return m_socket; }