From 7c4d0bb6fd62b48c3b7c5bee38b31a8fec0899e6 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Tue, 27 Oct 2015 08:47:07 -0700 Subject: [PATCH] Implement base Socket class Fix derp in IPAddress::resolve --- CMakeLists.txt | 2 + include/Athena/Socket.hpp | 28 ++++++++++++ include/sockwrap.h | 61 +++++++++++++++++++++++++ src/Athena/IPAddress.cpp | 56 +++++++++++++---------- src/Athena/Socket.cpp | 52 ++++++++++++++++++++++ src/sockwrap.c | 93 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 269 insertions(+), 23 deletions(-) create mode 100644 include/sockwrap.h create mode 100644 src/sockwrap.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f10297..3fc819b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,10 +154,12 @@ add_library(AthenaZelda EXCLUDE_FROM_ALL ) add_library(AthenaNet + src/sockwrap.c src/Athena/Socket.cpp src/Athena/OAuth.cpp src/Athena/IPAddress.cpp + include/sockwrap.h include/Athena/Socket.hpp include/Athena/OAuth.hpp include/Athena/IPAddress.hpp) diff --git a/include/Athena/Socket.hpp b/include/Athena/Socket.hpp index 1f7bf89..7ffd78e 100644 --- a/include/Athena/Socket.hpp +++ b/include/Athena/Socket.hpp @@ -1,15 +1,43 @@ #ifndef SOCKET_HPP #define SOCKET_HPP +#include "Athena/Global.hpp" +#include "sockwrap.h" + namespace Athena { namespace net { + class Socket { + // Disable copying public: + enum Type + { + TCP, + UDP + }; + + explicit Socket(Type type = TCP); + + virtual ~Socket() { close() ; } + + void setBlocking(bool blocking); + bool isBlocking() const { return m_isBlocking; } +protected: + sockhandle_t handle() { return m_handle; } + void create(); + void close(); private: + // Disable copying + Socket(const Socket&)=delete; + Socket& operator=(const Socket&)=delete; + Type m_type; + sockhandle_t m_handle; + bool m_isBlocking; }; + } } diff --git a/include/sockwrap.h b/include/sockwrap.h new file mode 100644 index 0000000..885eb8a --- /dev/null +++ b/include/sockwrap.h @@ -0,0 +1,61 @@ +#ifndef SOCKWRAP_H +#define SOCKWRAP_H + +#include +#include + +#ifdef _WIN32 +#ifdef _WIN32_WINDOWS +#undef _WIN32_WINDOWS +#endif +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINDOWS 0x0501 +#define _WIN32_WINNT 0x0501 +#include +#include +#include + +typedef UINT_PTR sockhandle_t; +typedef int addrlen_t; +#else +#include +#include +#include +#include +#include +#include +#include + +typedef int32_t sockhandle_t; +typedef socklen_t addrlen_t; +#endif + +#define ANY_PORT 0 + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: More granular errors +typedef enum +{ + SS_Done, + SS_NotReady, + SS_Partial, + SS_Disconnected, + SS_Error +} sockstatus_t; + +struct sockaddr_in sock_create_address(uint32_t address, uint16_t port); +void sock_close_socket(sockhandle_t sock); +void sock_set_blocking(sockhandle_t sock, bool block); +sockstatus_t sock_error_status(); +sockhandle_t sock_invalid_socket(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/Athena/IPAddress.cpp b/src/Athena/IPAddress.cpp index a75f932..ff913cc 100644 --- a/src/Athena/IPAddress.cpp +++ b/src/Athena/IPAddress.cpp @@ -1,25 +1,5 @@ #include "Athena/IPAddress.hpp" - -#ifdef __unix__ -#include -#include -#include -#include -#include -#include -#include -#elif defined(_WIN32) -#ifdef _WIN32_WINDOWS -#undef _WIN32_WINDOWS -#endif -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINDOWS 0x0501 -#define _WIN32_WINNT 0x0501 -#include -#include -#endif +#include "sockwrap.h" // To resolve local IP namespace Athena { @@ -55,6 +35,35 @@ const std::string IPAddress::toString() const return inet_ntoa(address); } +const atUint32 IPAddress::toInt() const +{ + return ntohl(m_address); +} + +IPAddress IPAddress::localAddress() +{ + sockhandle_t sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock == sock_invalid_socket()) + return IPAddress(); + + struct sockaddr_in address = sock_create_address(ntohl(INADDR_LOOPBACK), 9); + if (connect(sock, reinterpret_cast(&address), sizeof(struct sockaddr_in)) == -1) + { + sock_close_socket(sock); + return IPAddress(); + } + + addrlen_t size = sizeof(address); + if (getsockname(sock, reinterpret_cast(&address), &size) == -1) + { + sock_close_socket(sock); + return IPAddress(); + } + + sock_close_socket(sock); + return IPAddress(ntohl(address.sin_addr.s_addr)); +} + void IPAddress::resolve(const std::string& address) { if (address == "0.0.0.0") @@ -72,10 +81,11 @@ void IPAddress::resolve(const std::string& address) atUint32 ip = inet_addr(address.c_str()); if (ip == INADDR_NONE) { - addrinfo hints = {0}; + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); hints.ai_family = AF_INET; addrinfo* result = nullptr; - if (getaddrinfo(address.c_str(), NULL, &hints, &result)) + if (getaddrinfo(address.c_str(), nullptr, &hints, &result) == 0) { if (result) { diff --git a/src/Athena/Socket.cpp b/src/Athena/Socket.cpp index 1bd5f24..df60f9d 100644 --- a/src/Athena/Socket.cpp +++ b/src/Athena/Socket.cpp @@ -1 +1,53 @@ #include "Athena/Socket.hpp" + +namespace Athena +{ +namespace net +{ + +Socket::Socket(Socket::Type type) + : m_type(type), + m_handle(sock_invalid_socket()), + m_isBlocking(true) +{} + +void Socket::create() +{ + if (m_handle == sock_invalid_socket()) + { + m_handle = socket(PF_INET, m_type == TCP ? SOCK_STREAM : SOCK_DGRAM, 0); + setBlocking(m_isBlocking); + + int yes = 1; + if (m_type == TCP) + { + // Disable Nagle algorithm + if (setsockopt(m_handle, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(int)) == -1) + atWarning("Failed to set socket option \"TCP_NODELAY\", TCP packets will be buffered"); + +#ifdef __APPLE__ + if (setsockopt(m_handle, SOL_SOCKET, SO_NOSIGPIPE, &yes, sizeof(int)) == -1) + atWarning("Failed to set socket option \"SO_NOSIGPIPE\""); +#endif + } + else + { + if (setsockopt(m_handle, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(int)) == -1) + atWarning("Failed to enable broadcast on UDP socket"); + } + } +} + +void Socket::close() +{ + sock_close_socket(m_handle); + m_handle = sock_invalid_socket(); +} + +void Socket::setBlocking(bool blocking) +{ + sock_set_blocking(m_handle, blocking); +} + +} +} diff --git a/src/sockwrap.c b/src/sockwrap.c new file mode 100644 index 0000000..c6dc169 --- /dev/null +++ b/src/sockwrap.c @@ -0,0 +1,93 @@ +#include "sockwrap.h" + +#ifndef _WIN32 +#include +#include +#endif + +struct sockaddr_in sock_create_address(uint32_t address, uint16_t port) +{ + struct sockaddr_in addr = {0}; + 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; +} + +void sock_close_socket(sockhandle_t sock) +{ +#ifdef _WIN32 + closesocket(sock); +#else + close(sock); +#endif +} + +void sock_set_blocking(sockhandle_t sock, bool block) +{ +#ifdef _WIN32 + u_long blocking = block ? 0 : 1; + ioctlsocket(sock, FIONBIO, &blocking); +#else + int status = fcntl(sock, F_GETFL); + if (block) + status &= ~O_NONBLOCK; + else + status |= O_NONBLOCK; + + fcntl(sock, F_SETFL, status); +#endif +} + +sockstatus_t sock_error_status() +{ +#ifdef _WIN32 + switch(WSAGetLastError()) + { + case WSAEWOULDBLOCK: + case WSAEALREADY: + return SS_NotReady; + case WSAECONNABORTED: + case WSAECONNRESET: + case WSATIMEDOUT: + case WSAENETRESET: + case WSAENOTCONN: + return SS_Disconnected; + case WSAEISCONN: + return SS_Done; + default: + return SS_Error; + } +#else + if (errno == EAGAIN || errno == EINPROGRESS) + return SS_NotReady; + + switch(errno) + { + case EWOULDBLOCK: return SS_NotReady; + case ECONNABORTED: + case ECONNRESET: + case ETIMEDOUT: + case ENETRESET: + case ENOTCONN: + case EPIPE: + return SS_Disconnected; + default: + return SS_Error; + } +#endif +} + +sockhandle_t sock_invalid_socket() +{ +#if _WIN32 + return INVALID_SOCKET; +#else + return -1; +#endif +}