Wed, 27 Jan 2021 19:27:23 +0200
various changes, better network error handling
--- a/sources/basics.h Wed Jan 27 19:01:37 2021 +0200 +++ b/sources/basics.h Wed Jan 27 19:27:23 2021 +0200 @@ -125,4 +125,10 @@ struct Exitception {}; +#ifdef __GNUC__ +# define GNUATTRIBUTE(X) __attribute__(X) +#else +# define GNUATTRIBUTE(X) +#endif + END_ZFC_NAMESPACE
--- a/sources/interface.h Wed Jan 27 19:01:37 2021 +0200 +++ b/sources/interface.h Wed Jan 27 19:27:23 2021 +0200 @@ -55,10 +55,10 @@ void handleCommand(const std::string& input); void handleInput(); void needRefresh(); - void __cdecl print(const char* fmtstr, ...); - void __cdecl printWarning(const char* fmtstr, ...); - void __cdecl printError(const char* fmtstr, ...); - void __cdecl printText(const char* fmtstr, ...); + void __cdecl print(const char* fmtstr, ...) GNUATTRIBUTE((format(printf, 2, 3))); + void __cdecl printWarning(const char* fmtstr, ...) GNUATTRIBUTE((format(printf, 2, 3))); + void __cdecl printError(const char* fmtstr, ...) GNUATTRIBUTE((format(printf, 2, 3))); + void __cdecl printText(const char* fmtstr, ...) GNUATTRIBUTE((format(printf, 2, 3))); void render(); void renderFull(); void setPlayerNames(const std::vector<std::string>& names);
--- a/sources/main.cpp Wed Jan 27 19:01:37 2021 +0200 +++ b/sources/main.cpp Wed Jan 27 19:27:23 2021 +0200 @@ -84,7 +84,7 @@ FD_ZERO (&fdset); FD_SET (0, &fdset); - int fd = iface.getSession()->getSocket()->file_descriptor(); + int fd = iface.getSession()->getSocket()->file_descriptor; highest = zfc::max (highest, fd); FD_SET (fd, &fdset);
--- a/sources/mystring.h Wed Jan 27 19:01:37 2021 +0200 +++ b/sources/mystring.h Wed Jan 27 19:27:23 2021 +0200 @@ -46,7 +46,7 @@ std::string mid(const std::string& str, int rangeBegin, int rangeEnd); std::string right(const std::string& str, int length); std::string vsprintf(const char* formatString, va_list args); -std::string __cdecl sprintf(const char* formatString, ...); +std::string __cdecl sprintf(const char* formatString, ...) GNUATTRIBUTE((format(printf, 1, 2))); std::string remove_range(const std::string& string, int start, int end); void replace_all(std::string& str, const char* text, const char* replacement); bool starts_with(const std::string& str, const std::string& other);
--- a/sources/network/ipaddress.h Wed Jan 27 19:01:37 2021 +0200 +++ b/sources/network/ipaddress.h Wed Jan 27 19:27:23 2021 +0200 @@ -40,10 +40,12 @@ namespace net { using octet_t = std::uint8_t; + using port_t = std::uint16_t; + using host_t = std::uint32_t; struct ip_address { - std::uint32_t host; - std::uint16_t port; + net::host_t host = 0; + net::port_t port = 0; bool operator<(const ip_address& other) const; bool operator==(const ip_address& other) const; };
--- a/sources/network/rconsession.cpp Wed Jan 27 19:01:37 2021 +0200 +++ b/sources/network/rconsession.cpp Wed Jan 27 19:27:23 2021 +0200 @@ -42,10 +42,10 @@ m_adminCount(0), m_interface(nullptr) { - if (not m_socket.set_blocking(false)) + std::stringstream errors; + if (not m_socket.set_blocking(false, errors)) { - fprintf(stderr, "unable to set socket as non-blocking: %s\n", - m_socket.error_string().data()); + fprintf(stderr, "unable to set socket as non-blocking: %s\n", errors.str().data()); exit(EXIT_FAILURE); } } @@ -80,9 +80,15 @@ // ------------------------------------------------------------------------------------------------- // -void RCONSession::send(const ByteArray& packet) +bool RCONSession::send(const ByteArray& packet) { - m_socket.send(m_address, packet); + std::stringstream errors; + const bool result = m_socket.send(m_address, packet, errors); + if (not result) + { + this->m_interface->printError("Network error: %s\n", errors.str().data()); + } + return result; } // ------------------------------------------------------------------------------------------------- @@ -112,8 +118,14 @@ } } - for (Datagram datagram; m_socket.read(datagram);) + std::stringstream errors; + for (net::Datagram datagram; m_socket.read(datagram, errors);) { + if (errors.tellp() > 0) + { + m_interface->printError("Network error: %s\n", errors.str().data()); + errors = {}; + } // Only process packets that originate from the game server. if (datagram.address == m_address) handlePacket(datagram.message); @@ -268,7 +280,7 @@ // ------------------------------------------------------------------------------------------------- // -UDPSocket* RCONSession::getSocket() +net::UDPSocket* RCONSession::getSocket() { return &m_socket; } @@ -379,7 +391,7 @@ } else { - m_interface->print("This server does not support tab-completion\n", m_serverProtocol); + m_interface->print("This server does not support tab-completion\n"); } }
--- a/sources/network/rconsession.h Wed Jan 27 19:01:37 2021 +0200 +++ b/sources/network/rconsession.h Wed Jan 27 19:27:23 2021 +0200 @@ -101,13 +101,13 @@ void disconnect(); int getAdminCount() const; const std::string& getLevel() const; - UDPSocket* getSocket(); + net::UDPSocket* getSocket(); RCONSessionState getState() const; void handlePacket(ByteArray& message); bool isActive() const; void processServerUpdates(Bytestream& packet); void requestTabCompletion(const std::string& part); - void send(const ByteArray& packet); + bool send(const ByteArray& packet); bool sendCommand(const std::string& commandString); void sendHello(); void sendPassword(); @@ -118,7 +118,7 @@ private: RCONSessionState m_state; net::ip_address m_address; - UDPSocket m_socket; + net::UDPSocket m_socket; time_t m_lastPing; std::string m_password; std::string m_salt;
--- a/sources/network/udpsocket.cpp Wed Jan 27 19:01:37 2021 +0200 +++ b/sources/network/udpsocket.cpp Wed Jan 27 19:27:23 2021 +0200 @@ -47,115 +47,119 @@ BEGIN_ZFC_NAMESPACE -char UDPSocket::HuffmanBuffer[131072]; +static char HuffmanBuffer[131072]; -// ----------------------------------------------------------------------------- -// -UDPSocket::UDPSocket() : - m_socket (socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) {} +net::UDPSocket::UDPSocket() : + file_descriptor{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)} {} -// ----------------------------------------------------------------------------- -// -UDPSocket::~UDPSocket() +net::UDPSocket::~UDPSocket() { #ifdef _WIN32 - closesocket (m_socket); + ::closesocket(m_socket); #else - close (m_socket); + ::close(this->file_descriptor); #endif } -// ------------------------------------------------------------------------------------------------- -// -bool UDPSocket::set_blocking (bool a) +bool net::UDPSocket::set_blocking(bool a, std::ostream& errors) { #ifndef _WIN32 - int flags = fcntl (m_socket, F_GETFL, 0); - int newflags = a ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); - - if (flags < 0 || fcntl (m_socket, F_SETFL, newflags) != 0) + int flags = ::fcntl(this->file_descriptor, F_GETFL, 0); + int newflags = a ?(flags & ~O_NONBLOCK) :(flags | O_NONBLOCK); + if (flags < 0 || ::fcntl(this->file_descriptor, F_SETFL, newflags) != 0) { - m_error = "Unable to set socket as non-blocking"; + errors << "Unable to set the UDP socket as non-blocking"; return false; } - - return true; + else + { + return true; + } #else unsigned long mode = a ? 0 : 1; - if (ioctlsocket (m_socket, FIONBIO, &mode) != 0) + if (::ioctlsocket(m_socket, FIONBIO, &mode) != 0) { - m_error = strerror (errno); + errors << strerror(errno); return false; } - - return true; + else + { + return true; + } #endif } -// ------------------------------------------------------------------------------------------------- -// -bool UDPSocket::bind (unsigned short port) +bool net::UDPSocket::bind(const net::port_t port, std::ostream& errors) { sockaddr_in svaddr; - memset (&svaddr, 0, sizeof svaddr); + std::memset(&svaddr, 0, sizeof svaddr); svaddr.sin_family = AF_INET; - svaddr.sin_port = htons (port); - svaddr.sin_addr.s_addr = htonl (INADDR_ANY); - - if (::bind (m_socket, reinterpret_cast<sockaddr*> (&svaddr), sizeof svaddr) == -1) + svaddr.sin_port = htons(port); + svaddr.sin_addr.s_addr = htonl(INADDR_ANY); + if (::bind(this->file_descriptor, reinterpret_cast<sockaddr*>(&svaddr), sizeof svaddr) == -1) { - m_error = "Couldn't bind to port "s + std::to_string(port); + errors << "Couldn't bind to port "s + std::to_string(port); return false; } - - return true; + else + { + return true; + } } -// ------------------------------------------------------------------------------------------------- -// -bool UDPSocket::read (Datagram& datagram) +bool net::UDPSocket::read(Datagram& datagram, std::ostream& errors) { sockaddr_in claddr; socklen_t socklen = sizeof claddr; - int length = ::recvfrom (m_socket, HuffmanBuffer, sizeof HuffmanBuffer, 0, - reinterpret_cast<sockaddr*> (&claddr), &socklen); - + const int length = ::recvfrom( + this->file_descriptor, + zfc::HuffmanBuffer, + sizeof zfc::HuffmanBuffer, + 0, + reinterpret_cast<sockaddr*>(&claddr), + &socklen + ); if (length == -1) { if (errno != EWOULDBLOCK) - m_error = std::string ("recvfrom error: ") + strerror (errno); - + { + errors << std::string("recvfrom error: ") + std::strerror(errno); + } return false; } - unsigned char decodedPacket[MAX_DATAGRAM_LENGTH]; int decodedLength = sizeof decodedPacket; - HUFFMAN_Decode (reinterpret_cast<unsigned char*> (HuffmanBuffer), + ::HUFFMAN_Decode(reinterpret_cast<unsigned char*>(HuffmanBuffer), decodedPacket, length, &decodedLength); - datagram.address.host = ntohl (claddr.sin_addr.s_addr); - datagram.address.port = ntohs (claddr.sin_port); + datagram.address.host = ntohl(claddr.sin_addr.s_addr); + datagram.address.port = ntohs(claddr.sin_port); datagram.message = ByteArray{&decodedPacket[0], &decodedPacket[decodedLength]}; return true; } -// ------------------------------------------------------------------------------------------------- -// -bool UDPSocket::send (const net::ip_address& address, const ByteArray& data) +bool net::UDPSocket::send(const net::ip_address& address, const ByteArray& data, std::ostream& errors) { int encodedlength = sizeof HuffmanBuffer; - HUFFMAN_Encode (data.data(), reinterpret_cast<unsigned char*> (HuffmanBuffer), data.size(), &encodedlength); + ::HUFFMAN_Encode(data.data(), reinterpret_cast<unsigned char*>(HuffmanBuffer), data.size(), &encodedlength); sockaddr_in claddr = net::ip_address_to_sockaddr_in(address); - int res = ::sendto (m_socket, HuffmanBuffer, encodedlength, 0, - reinterpret_cast<sockaddr*> (&claddr), sizeof claddr); - - if (res == -1) + const int send_result = ::sendto( + this->file_descriptor, + HuffmanBuffer, + encodedlength, + 0, + reinterpret_cast<sockaddr*>(&claddr), + sizeof claddr + ); + if (send_result == -1) { - m_error = std::string ("Unable to launch packet: ") + strerror (errno); + errors << "Unable to launch packet: "s + std::strerror(errno); return false; } - - return true; + else + { + return true; + } } END_ZFC_NAMESPACE \ No newline at end of file
--- a/sources/network/udpsocket.h Wed Jan 27 19:01:37 2021 +0200 +++ b/sources/network/udpsocket.h Wed Jan 27 19:27:23 2021 +0200 @@ -34,9 +34,14 @@ #include "bytestream.h" BEGIN_ZFC_NAMESPACE -enum { MAX_DATAGRAM_LENGTH = 5120 }; +namespace net +{ + constexpr int MAX_DATAGRAM_LENGTH = 5120; + struct Datagram; + class UDPSocket; +} -struct Datagram +struct net::Datagram { ByteArray message; net::ip_address address; @@ -44,24 +49,16 @@ // ------------------------------------------------------------------------------------------------- // -class UDPSocket +class net::UDPSocket { public: UDPSocket(); virtual ~UDPSocket(); - - bool bind (unsigned short port); - bool read (Datagram& datagram); - 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; } - -private: - static char HuffmanBuffer[131072]; - - std::string m_error; - int m_socket; + [[nodiscard]] bool bind(port_t port, std::ostream &errors); + [[nodiscard]] bool read(Datagram& datagram, std::ostream& errors); + [[nodiscard]] bool send(const ip_address& address, const ByteArray& data, std::ostream &errors); + [[nodiscard]] bool set_blocking(bool a, std::ostream &errors); + const int file_descriptor; }; END_ZFC_NAMESPACE \ No newline at end of file