This commit is contained in:
2022-11-12 21:49:50 +00:00
commit 3e50a7c132
37 changed files with 3564 additions and 0 deletions

161
src/network/address.cpp Normal file
View File

@ -0,0 +1,161 @@
#include <std_include.hpp>
#include "network/address.hpp"
using namespace std::literals;
namespace network
{
address::address()
{
std::memset(&this->address_, 0, sizeof(this->address_));
}
address::address(const std::string& addr)
: address()
{
this->parse(addr);
}
address::address(sockaddr_in& addr)
{
this->address_ = addr;
}
bool address::operator==(const address& obj) const
{
return this->address_.sin_family == obj.address_.sin_family //
&& this->address_.sin_addr.s_addr == obj.address_.sin_addr.s_addr //
&& this->address_.sin_port == obj.address_.sin_port;
}
void address::set_ipv4(const in_addr addr)
{
this->address_.sin_family = AF_INET;
this->address_.sin_addr = addr;
}
void address::set_port(const unsigned short port)
{
this->address_.sin_port = htons(port);
}
unsigned short address::get_port() const
{
return ntohs(this->address_.sin_port);
}
std::string address::to_string(bool with_port) const
{
char buffer[1000]{};
inet_ntop(this->address_.sin_family, &this->address_.sin_addr, buffer, sizeof(buffer));
auto address = std::string(buffer);
if (with_port)
{
address += ":"s + std::to_string(this->get_port());
}
return address;
}
bool address::is_local() const
{
// According to: https://en.wikipedia.org/wiki/Private_network
std::uint8_t bytes[4];
*reinterpret_cast<uint32_t*>(&bytes) = this->address_.sin_addr.s_addr;
// 10.X.X.X
if (bytes[0] == 10)
{
return true;
}
// 192.168.X.X
if (bytes[0] == 192
&& bytes[1] == 168)
{
return true;
}
// 172.16.X.X - 172.31.X.X
if (bytes[0] == 172
&& bytes[1] >= 16
&& bytes[1] < 32)
{
return true;
}
// 127.0.0.1
if (this->address_.sin_addr.s_addr == 0x0100007F)
{
return true;
}
return false;
}
sockaddr& address::get_addr()
{
return reinterpret_cast<sockaddr&>(this->get_in_addr());
}
const sockaddr& address::get_addr() const
{
return reinterpret_cast<const sockaddr&>(this->get_in_addr());
}
sockaddr_in& address::get_in_addr()
{
return this->address_;
}
const sockaddr_in& address::get_in_addr() const
{
return this->address_;
}
void address::parse(std::string addr)
{
const auto pos = addr.find_last_of(':');
if (pos != std::string::npos)
{
auto port = addr.substr(pos + 1);
this->set_port(static_cast<std::uint16_t>(std::strtol(port.data(), nullptr, 10)));
addr = addr.substr(0, pos);
}
this->resolve(addr);
}
void address::resolve(const std::string& hostname)
{
addrinfo* result = nullptr;
if (!getaddrinfo(hostname.data(), nullptr, nullptr, &result))
{
for (auto* i = result; i; i = i->ai_next)
{
if (i->ai_addr->sa_family == AF_INET)
{
const auto port = this->get_port();
std::memcpy(&this->address_, i->ai_addr, sizeof(this->address_));
this->set_port(port);
break;
}
}
freeaddrinfo(result);
}
}
}
std::size_t std::hash<network::address>::operator()(const network::address& a) const noexcept
{
const auto h1 = std::hash<decltype(a.get_in_addr().sin_family)>{}(a.get_in_addr().sin_family);
const auto h2 = std::hash<decltype(a.get_in_addr().sin_addr.s_addr)>{}(a.get_in_addr().sin_addr.s_addr);
const auto h3 = std::hash<decltype(a.get_in_addr().sin_port)>{}(a.get_in_addr().sin_port);
return h1 ^ (h2 << 1) ^ (h3 << 2);
}

46
src/network/address.hpp Normal file
View File

@ -0,0 +1,46 @@
#pragma once
namespace network
{
class address
{
public:
address();
address(const std::string& addr);
address(sockaddr_in& addr);
void set_ipv4(in_addr addr);
void set_port(unsigned short port);
[[nodiscard]] unsigned short get_port() const;
[[nodiscard]] sockaddr& get_addr();
[[nodiscard]] const sockaddr& get_addr() const;
[[nodiscard]] sockaddr_in& get_in_addr();
[[nodiscard]] const sockaddr_in& get_in_addr() const;
[[nodiscard]] bool is_local() const;
[[nodiscard]] std::string to_string(bool with_port = true) const;
bool operator==(const address& obj) const;
bool operator!=(const address& obj) const
{
return !(*this == obj);
}
private:
sockaddr_in address_{};
void parse(std::string addr);
void resolve(const std::string& hostname);
};
}
namespace std
{
template <>
struct hash<network::address>
{
std::size_t operator()(const network::address& a) const noexcept;
};
}

131
src/network/socket.cpp Normal file
View File

@ -0,0 +1,131 @@
#include <std_include.hpp>
#include "address.hpp"
#include "socket.hpp"
using namespace std::literals;
namespace network
{
namespace
{
#ifdef _WIN32
[[maybe_unused]] class wsa_initializer
{
public:
wsa_initializer()
{
WSADATA wsa_data;
if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
{
throw std::runtime_error("Unable to initialize WSA");
}
}
~wsa_initializer()
{
WSACleanup();
}
} _;
#endif
}
socket::socket()
{
this->socket_ = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
}
socket::~socket()
{
if (this->socket_ != INVALID_SOCKET)
{
#ifdef _WIN32
closesocket(this->socket_);
#else
close(this->socket_);
#endif
}
}
socket::socket(socket&& obj) noexcept
{
this->operator=(std::move(obj));
}
socket& socket::operator=(socket&& obj) noexcept
{
if (this != &obj)
{
this->~socket();
this->socket_ = obj.socket_;
obj.socket_ = INVALID_SOCKET;
}
return *this;
}
bool socket::bind(const address& target)
{
return ::bind(this->socket_, &target.get_addr(), sizeof(target.get_addr())) == 0;
}
void socket::send(const address& target, const std::string& data) const
{
::sendto(this->socket_, data.data(), static_cast<int>(data.size()), 0, &target.get_addr(), sizeof(target.get_addr()));
}
bool socket::receive(address& source, std::string& data) const
{
char buffer[0x2000]{};
socklen_t len = sizeof(source.get_in_addr());
const auto result = recvfrom(this->socket_, buffer, sizeof(buffer), 0, &source.get_addr(), &len);
if (result == SOCKET_ERROR) // Probably WSAEWOULDBLOCK
{
return false;
}
data.assign(buffer, buffer + result);
return true;
}
bool socket::set_blocking(const bool blocking)
{
#ifdef _WIN32
unsigned long mode = blocking ? 0 : 1;
return ioctlsocket(this->socket_, FIONBIO, &mode) == 0;
#else
int flags = fcntl(this->socket_, F_GETFL, 0);
if (flags == -1) return false;
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
return fcntl(this->socket_, F_SETFL, flags) == 0;
#endif
}
bool socket::sleep(const std::chrono::milliseconds timeout) const
{
fd_set fdr;
FD_ZERO(&fdr);
FD_SET(this->socket_, &fdr);
const auto msec = timeout.count();
timeval tv{};
tv.tv_sec = static_cast<long>(msec / 1000ll);
tv.tv_usec = static_cast<long>((msec % 1000) * 1000);
const auto ret_val = select(static_cast<int>(this->socket_) + 1, &fdr, nullptr, nullptr, &tv);
if (ret_val == SOCKET_ERROR)
{
std::this_thread::sleep_for(1ms);
return socket_is_ready;
}
if (ret_val > 0)
{
return socket_is_ready;
}
return !socket_is_ready;
}
}

38
src/network/socket.hpp Normal file
View File

@ -0,0 +1,38 @@
#pragma once
namespace network
{
class socket
{
public:
socket();
~socket();
socket(const socket& obj) = delete;
socket& operator=(const socket& obj) = delete;
socket(socket&& obj) noexcept;
socket& operator=(socket&& obj) noexcept;
bool bind(const address& target);
void send(const address& target, const std::string& data) const;
bool receive(address& source, std::string& data) const;
bool set_blocking(bool blocking);
static const bool socket_is_ready = true;
bool sleep(std::chrono::milliseconds timeout) const;
private:
#ifdef _WIN32
using socklen_t = int;
#else
using SOCKET = int;
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR (-1)
#endif
SOCKET socket_ = INVALID_SOCKET;
};
}