This commit is contained in:
Phillip Stephens 2022-12-08 12:06:12 -05:00 committed by GitHub
commit e7c21070b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 449 additions and 0 deletions

View File

@ -311,6 +311,17 @@ add_library(athena-zelda EXCLUDE_FROM_ALL
include/athena/ZQuestFileWriter.hpp
)
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)
# Icon
set(ATHENA_ICO ${CMAKE_CURRENT_SOURCE_DIR}/Athena.ico)

View File

@ -0,0 +1,44 @@
#ifndef IPADDRESS_HPP
#define IPADDRESS_HPP
#include "Athena/Global.hpp"
#include <string.h>
#include <cstring>
#include <utility>
namespace Athena
{
namespace net
{
class IPAddress
{
public:
static const IPAddress None;
static const IPAddress Any;
static const IPAddress Localhost;
static const IPAddress Broadcast;
IPAddress() : m_address(~0u), m_valid(false) {}
IPAddress(const std::string& address);
IPAddress(atUint8 a, atUint8 b, atUint8 c, atUint8 d);
IPAddress(atUint32 address);
const std::string toString() const;
const atUint32 toInt() const;
static IPAddress localAddress();
private:
atUint32 m_address;
bool m_valid;
void resolve(const std::string& address);
};
}
}
#endif

17
include/Athena/OAuth.hpp Normal file
View File

@ -0,0 +1,17 @@
#ifndef OAUTH_HPP
#define OAUTH_HPP
#include <Athena/Global.hpp>
namespace Athena
{
namespace net
{
class OAuth
{
};
}
}
#endif

44
include/Athena/Socket.hpp Normal file
View File

@ -0,0 +1,44 @@
#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;
};
}
}
#endif

61
include/sockwrap.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef SOCKWRAP_H
#define SOCKWRAP_H
#include <stdint.h>
#include <stdbool.h>
#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 <winsock2.h>
#include <ws2tcpip.h>
#include <basetsd>
typedef UINT_PTR sockhandle_t;
typedef int addrlen_t;
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
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

107
src/Athena/IPAddress.cpp Normal file
View File

@ -0,0 +1,107 @@
#include "Athena/IPAddress.hpp"
#include "sockwrap.h" // To resolve local IP
namespace Athena
{
namespace net
{
const IPAddress IPAddress::Any;
const IPAddress IPAddress::None = IPAddress( 0, 0, 0, 0);
const IPAddress IPAddress::Localhost = IPAddress(127, 0, 0, 1);
const IPAddress IPAddress::Broadcast = IPAddress(255, 255, 255, 255);
IPAddress::IPAddress(const std::string& address)
: m_valid(false)
{
resolve(address);
}
IPAddress::IPAddress(atUint8 a, atUint8 b, atUint8 c, atUint8 d)
: m_address(htonl((a << 24)| (b << 16) | (c << 8) | d)),
m_valid(true)
{
}
IPAddress::IPAddress(atUint32 address)
: m_address(htonl(address)),
m_valid(true)
{
}
const std::string IPAddress::toString() const
{
in_addr address;
address.s_addr = m_address;
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<sockaddr*>(&address), sizeof(struct sockaddr_in)) == -1)
{
sock_close_socket(sock);
return IPAddress();
}
addrlen_t size = sizeof(address);
if (getsockname(sock, reinterpret_cast<sockaddr*>(&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")
{
m_address = 0;
m_valid = true;
}
else if(address == "255.255.255.255")
{
m_address = ~0u;
m_valid = true;
}
else
{
atUint32 ip = inet_addr(address.c_str());
if (ip == INADDR_NONE)
{
addrinfo hints;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_family = AF_INET;
addrinfo* result = nullptr;
if (getaddrinfo(address.c_str(), nullptr, &hints, &result) == 0)
{
if (result)
{
ip = reinterpret_cast<sockaddr_in*>(result->ai_addr)->sin_addr.s_addr;
freeaddrinfo(result);
m_address = ip;
m_valid = true;
}
}
}
else
{
m_address = ip;
m_valid = true;
}
}
}
}
}

1
src/Athena/OAuth.cpp Normal file
View File

@ -0,0 +1 @@
#include "Athena/OAuth.hpp"

71
src/Athena/Socket.cpp Normal file
View File

@ -0,0 +1,71 @@
#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);
}
#if _WIN32
struct WSADerpHandler
{
WSADerpHandler()
{
WSADATA init;
WSAStartup(MAKEWORD(2, 2), &init);
}
~WSADerpHandler()
{
WSACleanup();
}
};
static const WSADerpHandler __wsaderp__;
#endif
}
}

93
src/sockwrap.c Normal file
View File

@ -0,0 +1,93 @@
#include "sockwrap.h"
#ifndef _WIN32
#include <errno.h>
#include <fcntl.h>
#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
}