From f414abe5a5d359a77add09061fe1ad5e4413ee32 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Fri, 29 Dec 2017 15:05:53 -1000 Subject: [PATCH] Isolate WinSock2 header to translation unit --- CMakeLists.txt | 1 + include/athena/Socket.hpp | 314 +++----------------------------------- src/athena/Socket.cpp | 304 ++++++++++++++++++++++++++++++++++++ 3 files changed, 327 insertions(+), 292 deletions(-) create mode 100644 src/athena/Socket.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b8041ce..c7960f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ add_library(athena-core src/athena/Global.cpp src/athena/Checksums.cpp src/athena/Compression.cpp + src/athena/Socket.cpp src/LZ77/LZLookupTable.cpp src/LZ77/LZType10.cpp src/LZ77/LZType11.cpp diff --git a/include/athena/Socket.hpp b/include/athena/Socket.hpp index 4b76c88..ca6bbfe 100644 --- a/include/athena/Socket.hpp +++ b/include/athena/Socket.hpp @@ -1,19 +1,6 @@ #ifndef ATHENA_SOCKET_HPP #define ATHENA_SOCKET_HPP -#ifndef _WIN32 -#include -#include -#include -#include -#include -#include -#include -#else -#include -#include -#endif - #include #include #include @@ -21,68 +8,23 @@ #include "Global.hpp" +#ifdef _WIN32 +#include +typedef UINT_PTR SOCKET; +#endif + +struct sockaddr_in; + namespace athena::net { -/* Define the low-level send/receive flags, which depend on the OS */ -#ifdef __linux__ -static const int _flags = MSG_NOSIGNAL; -#else -static const int _flags = 0; -#endif - /** IP address class derived from SFML */ class IPAddress { uint32_t m_address = 0; bool m_valid = false; - void resolve(const std::string& address) - { - m_address = 0; - m_valid = false; - - if (address == "255.255.255.255") - { - /* The broadcast address needs to be handled explicitly, - * because it is also the value returned by inet_addr on error */ - m_address = INADDR_BROADCAST; - m_valid = true; - } - else if (address == "0.0.0.0") - { - m_address = INADDR_ANY; - m_valid = true; - } - else - { - /* Try to convert the address as a byte representation ("xxx.xxx.xxx.xxx") */ - struct in_addr addr; - if (inet_pton(AF_INET, address.c_str(), &addr) == 1) - { - m_address = addr.s_addr; - m_valid = true; - } - else - { - /* Not a valid address, try to convert it as a host name */ - addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - addrinfo* result = NULL; - if (getaddrinfo(address.c_str(), NULL, &hints, &result) == 0) - { - if (result) - { - addr = reinterpret_cast(result->ai_addr)->sin_addr; - freeaddrinfo(result); - m_address = addr.s_addr; - m_valid = true; - } - } - } - } - } + void resolve(const std::string& address); public: IPAddress(const std::string& address) @@ -90,11 +32,7 @@ public: resolve(address); } - uint32_t toInteger() const - { - return ntohl(m_address); - } - + uint32_t toInteger() const; operator bool() const { return m_valid; } }; @@ -109,50 +47,8 @@ class Socket SocketTp m_socket = -1; bool m_isBlocking; - static sockaddr_in createAddress(uint32_t address, unsigned short port) - { - sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_addr.s_addr = htonl(address); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - -#ifdef __APPLE__ - addr.sin_len = sizeof(addr); -#endif - - return addr; - } - - bool openSocket() - { - if (isOpen()) - return false; - - m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_socket == -1) - { - atError("Can't allocate socket"); - return false; - } - - int one = 1; - setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&one), sizeof(one)); -#ifdef __APPLE__ - setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast(&one), sizeof(one)); -#endif - - setBlocking(m_isBlocking); - - return true; - } - - void setRemoteSocket(int remSocket) - { - close(); - m_socket = remSocket; - setBlocking(m_isBlocking); - } + bool openSocket(); + void setRemoteSocket(int remSocket); public: enum class EResult @@ -163,17 +59,7 @@ public: }; #ifdef _WIN32 - static EResult LastWSAError() - { - switch (WSAGetLastError()) - { - case WSAEWOULDBLOCK: - case WSAEALREADY: - return EResult::Busy; - default: - return EResult::Error; - } - } + static EResult LastWSAError(); #endif Socket(bool blocking) @@ -196,173 +82,17 @@ public: return *this; } - void setBlocking(bool blocking) - { - m_isBlocking = blocking; -#ifndef _WIN32 - int status = fcntl(m_socket, F_GETFL); - if (m_isBlocking) - fcntl(m_socket, F_SETFL, status & ~O_NONBLOCK); - else - fcntl(m_socket, F_SETFL, status | O_NONBLOCK); -#else - u_long b = blocking ? 0 : 1; - ioctlsocket(m_socket, FIONBIO, &b); -#endif - } - + void setBlocking(bool blocking); bool isOpen() const { return m_socket != -1; } - bool openAndListen(const IPAddress& address, uint32_t port) - { - if (!openSocket()) - return false; - - sockaddr_in addr = createAddress(address.toInteger(), port); - if (bind(m_socket, reinterpret_cast(&addr), sizeof(addr)) == -1) - { - /* Not likely to happen, but... */ - atError("Failed to bind listener socket to port %d", port); - return false; - } - - if (::listen(m_socket, 0) == -1) - { - /* Oops, socket is deaf */ - atError("Failed to listen to port %d", port); - return false; - } - - return true; - } - - EResult accept(Socket& remoteSocketOut, sockaddr_in& fromAddress) - { - if (!isOpen()) - return EResult::Error; - - /* Accept a new connection */ - socklen_t length = sizeof(sockaddr_in); - int remoteSocket = ::accept(m_socket, reinterpret_cast(&fromAddress), &length); - - /* Check for errors */ - if (remoteSocket == -1) - { -#ifndef _WIN32 - EResult res = (errno == EAGAIN) ? EResult::Busy : EResult::Error; - if (res == EResult::Error) - atError("Failed to accept incoming connection: %s", strerror(errno)); -#else - EResult res = LastWSAError(); - if (res == EResult::Error) - atError("Failed to accept incoming connection"); -#endif - return res; - } - - /* Initialize the new connected socket */ - remoteSocketOut.setRemoteSocket(remoteSocket); - - return EResult::OK; - } - - EResult accept(Socket& remoteSocketOut) - { - sockaddr_in fromAddress; - return accept(remoteSocketOut, fromAddress); - } - - EResult accept(Socket& remoteSocketOut, std::string& fromHostname) - { - sockaddr_in fromAddress; - socklen_t len = sizeof(fromAddress); - char name[NI_MAXHOST]; - EResult res = accept(remoteSocketOut, fromAddress); - if (res == EResult::OK) - if (getnameinfo((sockaddr*)&fromAddress, len, name, NI_MAXHOST, nullptr, 0, 0) == 0) - fromHostname.assign(name); - return res; - } - - void close() - { - if (!isOpen()) - return; -#ifndef _WIN32 - ::close(m_socket); -#else - closesocket(m_socket); -#endif - m_socket = -1; - } - - EResult send(const void* buf, size_t len, size_t& transferred) - { - transferred = 0; - if (!isOpen()) - return EResult::Error; - - if (!buf || !len) - return EResult::Error; - - /* Loop until every byte has been sent */ - int result = 0; - for (size_t sent = 0; sent < len; sent += result) - { - /* Send a chunk of data */ - result = ::send(m_socket, static_cast(buf) + sent, len - sent, _flags); - - /* Check for errors */ - if (result < 0) -#ifndef _WIN32 - return (errno == EAGAIN) ? EResult::Busy : EResult::Error; -#else - return LastWSAError(); -#endif - } - - transferred = len; - return EResult::OK; - } - - EResult send(const void* buf, size_t len) - { - size_t transferred; - return send(buf, len, transferred); - } - - EResult recv(void* buf, size_t len, size_t& transferred) - { - transferred = 0; - if (!isOpen()) - return EResult::Error; - - if (!buf) - return EResult::Error; - - if (!len) - return EResult::OK; - - /* Receive a chunk of bytes */ - int result = ::recv(m_socket, static_cast(buf), static_cast(len), _flags); - - if (result < 0) -#ifndef _WIN32 - return (errno == EAGAIN) ? EResult::Busy : EResult::Error; -#else - return LastWSAError(); -#endif - else if (result == 0) - return EResult::Error; - - transferred = result; - return EResult::OK; - } - - EResult recv(void* buf, size_t len) - { - size_t transferred; - return recv(buf, len, transferred); - } + bool openAndListen(const IPAddress& address, uint32_t port); + EResult accept(Socket& remoteSocketOut, sockaddr_in& fromAddress); + EResult accept(Socket& remoteSocketOut); + EResult accept(Socket& remoteSocketOut, std::string& fromHostname); + void close(); + EResult send(const void* buf, size_t len, size_t& transferred); + EResult send(const void* buf, size_t len); + EResult recv(void* buf, size_t len, size_t& transferred); + EResult recv(void* buf, size_t len); operator bool() const { return isOpen(); } diff --git a/src/athena/Socket.cpp b/src/athena/Socket.cpp new file mode 100644 index 0000000..0b283b8 --- /dev/null +++ b/src/athena/Socket.cpp @@ -0,0 +1,304 @@ +#include "athena/Socket.hpp" + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#endif + +namespace athena::net +{ + +/* Define the low-level send/receive flags, which depend on the OS */ +#ifdef __linux__ +static const int _flags = MSG_NOSIGNAL; +#else +static const int _flags = 0; +#endif + +void IPAddress::resolve(const std::string& address) +{ + m_address = 0; + m_valid = false; + + if (address == "255.255.255.255") + { + /* The broadcast address needs to be handled explicitly, + * because it is also the value returned by inet_addr on error */ + m_address = INADDR_BROADCAST; + m_valid = true; + } + else if (address == "0.0.0.0") + { + m_address = INADDR_ANY; + m_valid = true; + } + else + { + /* Try to convert the address as a byte representation ("xxx.xxx.xxx.xxx") */ + struct in_addr addr; + if (inet_pton(AF_INET, address.c_str(), &addr) == 1) + { + m_address = addr.s_addr; + m_valid = true; + } + else + { + /* Not a valid address, try to convert it as a host name */ + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + addrinfo* result = NULL; + if (getaddrinfo(address.c_str(), NULL, &hints, &result) == 0) + { + if (result) + { + addr = reinterpret_cast(result->ai_addr)->sin_addr; + freeaddrinfo(result); + m_address = addr.s_addr; + m_valid = true; + } + } + } + } +} + +uint32_t IPAddress::toInteger() const +{ + return ntohl(m_address); +} + +static sockaddr_in createAddress(uint32_t address, unsigned short port) +{ + sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_addr.s_addr = htonl(address); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + +#ifdef __APPLE__ + addr.sin_len = sizeof(addr); +#endif + + return addr; +} + +bool Socket::openSocket() +{ + if (isOpen()) + return false; + + m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_socket == -1) + { + atError("Can't allocate socket"); + return false; + } + + int one = 1; + setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&one), sizeof(one)); +#ifdef __APPLE__ + setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast(&one), sizeof(one)); +#endif + + setBlocking(m_isBlocking); + + return true; +} + +void Socket::setRemoteSocket(int remSocket) +{ + close(); + m_socket = remSocket; + setBlocking(m_isBlocking); +} + +#ifdef _WIN32 +Socket::EResult Socket::LastWSAError() +{ + switch (WSAGetLastError()) + { + case WSAEWOULDBLOCK: + case WSAEALREADY: + return EResult::Busy; + default: + return EResult::Error; + } +} +#endif + +void Socket::setBlocking(bool blocking) +{ + m_isBlocking = blocking; +#ifndef _WIN32 + int status = fcntl(m_socket, F_GETFL); + if (m_isBlocking) + fcntl(m_socket, F_SETFL, status & ~O_NONBLOCK); + else + fcntl(m_socket, F_SETFL, status | O_NONBLOCK); +#else + u_long b = blocking ? 0 : 1; + ioctlsocket(m_socket, FIONBIO, &b); +#endif +} + +bool Socket::openAndListen(const IPAddress& address, uint32_t port) +{ + if (!openSocket()) + return false; + + sockaddr_in addr = createAddress(address.toInteger(), port); + if (bind(m_socket, reinterpret_cast(&addr), sizeof(addr)) == -1) + { + /* Not likely to happen, but... */ + atError("Failed to bind listener socket to port %d", port); + return false; + } + + if (::listen(m_socket, 0) == -1) + { + /* Oops, socket is deaf */ + atError("Failed to listen to port %d", port); + return false; + } + + return true; +} + +Socket::EResult Socket::accept(Socket& remoteSocketOut, sockaddr_in& fromAddress) +{ + if (!isOpen()) + return EResult::Error; + + /* Accept a new connection */ + socklen_t length = sizeof(sockaddr_in); + int remoteSocket = ::accept(m_socket, reinterpret_cast(&fromAddress), &length); + + /* Check for errors */ + if (remoteSocket == -1) + { +#ifndef _WIN32 + EResult res = (errno == EAGAIN) ? EResult::Busy : EResult::Error; + if (res == EResult::Error) + atError("Failed to accept incoming connection: %s", strerror(errno)); +#else + EResult res = LastWSAError(); + if (res == EResult::Error) + atError("Failed to accept incoming connection"); +#endif + return res; + } + + /* Initialize the new connected socket */ + remoteSocketOut.setRemoteSocket(remoteSocket); + + return EResult::OK; +} + +Socket::EResult Socket::accept(Socket& remoteSocketOut) +{ + sockaddr_in fromAddress; + return accept(remoteSocketOut, fromAddress); +} + +Socket::EResult Socket::accept(Socket& remoteSocketOut, std::string& fromHostname) +{ + sockaddr_in fromAddress; + socklen_t len = sizeof(fromAddress); + char name[NI_MAXHOST]; + EResult res = accept(remoteSocketOut, fromAddress); + if (res == EResult::OK) + if (getnameinfo((sockaddr*)&fromAddress, len, name, NI_MAXHOST, nullptr, 0, 0) == 0) + fromHostname.assign(name); + return res; +} + +void Socket::close() +{ + if (!isOpen()) + return; +#ifndef _WIN32 + ::close(m_socket); +#else + closesocket(m_socket); +#endif + m_socket = -1; +} + +Socket::EResult Socket::send(const void* buf, size_t len, size_t& transferred) +{ + transferred = 0; + if (!isOpen()) + return EResult::Error; + + if (!buf || !len) + return EResult::Error; + + /* Loop until every byte has been sent */ + int result = 0; + for (size_t sent = 0; sent < len; sent += result) + { + /* Send a chunk of data */ + result = ::send(m_socket, static_cast(buf) + sent, len - sent, _flags); + + /* Check for errors */ + if (result < 0) +#ifndef _WIN32 + return (errno == EAGAIN) ? EResult::Busy : EResult::Error; +#else + return LastWSAError(); +#endif + } + + transferred = len; + return EResult::OK; +} + +Socket::EResult Socket::send(const void* buf, size_t len) +{ + size_t transferred; + return send(buf, len, transferred); +} + +Socket::EResult Socket::recv(void* buf, size_t len, size_t& transferred) +{ + transferred = 0; + if (!isOpen()) + return EResult::Error; + + if (!buf) + return EResult::Error; + + if (!len) + return EResult::OK; + + /* Receive a chunk of bytes */ + int result = ::recv(m_socket, static_cast(buf), static_cast(len), _flags); + + if (result < 0) +#ifndef _WIN32 + return (errno == EAGAIN) ? EResult::Busy : EResult::Error; +#else + return LastWSAError(); +#endif + else if (result == 0) + return EResult::Error; + + transferred = result; + return EResult::OK; +} + +Socket::EResult Socket::recv(void* buf, size_t len) +{ + size_t transferred; + return recv(buf, len, transferred); +} + +}