406 lines
8.8 KiB
C++
406 lines
8.8 KiB
C++
#include "ehs/io/socket/UDP_BSD.h"
|
|
#include "ehs/Log.h"
|
|
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <cerrno>
|
|
|
|
namespace ehs
|
|
{
|
|
UDP::~UDP()
|
|
{
|
|
if (hdl == EHS_INVALID_SOCKET)
|
|
return;
|
|
|
|
Int_32 code = close(hdl);
|
|
if (code == -1)
|
|
EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket with error #" + Str_8::FromNum(errno) + ".");
|
|
}
|
|
|
|
UDP::UDP()
|
|
: hdl(EHS_INVALID_SOCKET)
|
|
{
|
|
}
|
|
|
|
UDP::UDP(const IP version)
|
|
: BaseUDP(version), hdl(EHS_INVALID_SOCKET)
|
|
{
|
|
if (version == IP::V6)
|
|
hdl = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
|
else if (version == IP::V4)
|
|
hdl = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
else
|
|
return;
|
|
|
|
if (hdl == EHS_INVALID_SOCKET)
|
|
{
|
|
UInt_32 code = errno;
|
|
|
|
EHS_LOG_INT(LogType::ERR, 1, "Failed to create socket with error #" + Str_8::FromNum(code) + ".");
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
UDP::UDP(UDP&& udp) noexcept
|
|
: BaseUDP(std::move(udp)), hdl(udp.hdl)
|
|
{
|
|
udp.hdl = EHS_INVALID_SOCKET;
|
|
}
|
|
|
|
UDP::UDP(const UDP& udp)
|
|
: BaseUDP(udp), hdl(EHS_INVALID_SOCKET)
|
|
{
|
|
}
|
|
|
|
UDP& UDP::operator=(UDP&& udp) noexcept
|
|
{
|
|
if (this == &udp)
|
|
return *this;
|
|
|
|
BaseUDP::operator=(std::move(udp));
|
|
|
|
hdl = udp.hdl;
|
|
|
|
udp.hdl = EHS_INVALID_SOCKET;
|
|
|
|
return *this;
|
|
}
|
|
|
|
UDP& UDP::operator=(const UDP& udp)
|
|
{
|
|
if (this == &udp)
|
|
return *this;
|
|
|
|
BaseUDP::operator=(udp);
|
|
|
|
hdl = EHS_INVALID_SOCKET;
|
|
|
|
return *this;
|
|
}
|
|
|
|
void UDP::Release()
|
|
{
|
|
if (!IsValid())
|
|
return;
|
|
|
|
const Int_32 code = close(hdl);
|
|
if (code == -1)
|
|
EHS_LOG_INT(LogType::ERR, 0, "Failed to close socket with error #" + Str_8::FromNum(errno) + ".");
|
|
|
|
hdl = EHS_INVALID_SOCKET;
|
|
|
|
bound = false;
|
|
}
|
|
|
|
void UDP::Bind(const Endpoint &endpoint)
|
|
{
|
|
if (!IsValid() || bound)
|
|
return;
|
|
|
|
if (endpoint.version == IP::V6)
|
|
Bind_v6(endpoint.address, endpoint.port);
|
|
else if (endpoint.version == IP::V4)
|
|
Bind_v4(endpoint.address, endpoint.port);
|
|
|
|
localEndpoint = endpoint;
|
|
|
|
bound = true;
|
|
}
|
|
|
|
UInt_64 UDP::Send(const Endpoint &endpoint, const Byte *const data, const UInt_64 size)
|
|
{
|
|
if (endpoint.version == IP::V6)
|
|
return Send_v6(endpoint.address, endpoint.port, data, size);
|
|
else if (endpoint.version == IP::V4)
|
|
return Send_v4(endpoint.address, endpoint.port, data, size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
UInt_64 UDP::Receive(Endpoint *endpoint, Byte* const data, const UInt_64 size)
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
EHS_LOG_INT(LogType::ERR, 0, "Attempted to receive while socket is not initialized.");
|
|
return 0;
|
|
}
|
|
|
|
sockaddr_in6 remote = {};
|
|
UInt_32 addrLen = sizeof(sockaddr_in6);
|
|
SInt_64 received;
|
|
|
|
received = recvfrom(hdl, data, size, 0, (sockaddr*)&remote, &addrLen);
|
|
if (received == -1)
|
|
{
|
|
int code = errno;
|
|
if (code != ECONNRESET && code != EWOULDBLOCK)
|
|
{
|
|
Release();
|
|
|
|
EHS_LOG_INT(LogType::ERR, 1, "Failed to receive with error #" + Str_8::FromNum(code) + ".");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (addrLen == sizeof(sockaddr_in6))
|
|
{
|
|
char tmpAddr[INET6_ADDRSTRLEN];
|
|
|
|
if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN))
|
|
{
|
|
Int_32 code = errno;
|
|
|
|
EHS_LOG_INT(LogType::ERR, 2, "Failed to convert IPv6 address with error #" + Str_8::FromNum(code) + ".");
|
|
|
|
return received;
|
|
}
|
|
|
|
*endpoint = {IP::V6, tmpAddr, remote.sin6_port};
|
|
}
|
|
else if (addrLen == sizeof(sockaddr_in))
|
|
{
|
|
char tmpAddr[INET_ADDRSTRLEN];
|
|
|
|
if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN))
|
|
{
|
|
Int_32 code = errno;
|
|
|
|
EHS_LOG_INT(LogType::ERR, 2, "Failed to convert IPv4 address with error #" + Str_8::FromNum(code) + ".");
|
|
|
|
return received;
|
|
}
|
|
|
|
*endpoint = {IP::V4, tmpAddr, ntohs(((sockaddr_in*)&remote)->sin_port)};
|
|
}
|
|
|
|
return received;
|
|
}
|
|
|
|
void UDP::SetBlocking(const bool blocking)
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
EHS_LOG_INT(LogType::ERR, 0, "Attempted to toggle blocking while socket is not initialized.");
|
|
return;
|
|
}
|
|
|
|
if (fcntl(hdl, F_SETFL, O_NONBLOCK, blocking) == -1)
|
|
EHS_LOG_INT(LogType::ERR, 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(errno) + ".");
|
|
}
|
|
|
|
bool UDP::IsBlocking() const
|
|
{
|
|
return (bool)fcntl(hdl, F_GETFL, O_NONBLOCK);
|
|
}
|
|
|
|
void UDP::SetIPv6Only(const bool value)
|
|
{
|
|
if (localEndpoint.version != IP::V6)
|
|
{
|
|
EHS_LOG_INT(LogType::WARN, 0, "Cannot set IPv6 only mode while socket is not using IPv6.");
|
|
return;
|
|
}
|
|
|
|
if (!IsValid())
|
|
{
|
|
EHS_LOG_INT(LogType::WARN, 1, "Attempted to set IPv6 only mode while socket is not initialized.");
|
|
return;
|
|
}
|
|
|
|
const int result = (int)value;
|
|
if (setsockopt(hdl, IPPROTO_IPV6, IPV6_V6ONLY, &result, sizeof(int)) == -1)
|
|
{
|
|
EHS_LOG_INT(LogType::ERR, 2, "Failed to set IPv6 only mode with error #" + Str_8::FromNum(errno) + ".");
|
|
return;
|
|
}
|
|
|
|
EHS_LOG_SUCCESS();
|
|
}
|
|
|
|
bool UDP::IsIPv6Only() const
|
|
{
|
|
if (localEndpoint.version != IP::V6)
|
|
return false;
|
|
|
|
if (!IsValid())
|
|
{
|
|
EHS_LOG_INT(LogType::WARN, 1, "Attempted to set IPv6 only mode while socket is not initialized.");
|
|
return false;
|
|
}
|
|
|
|
int result;
|
|
socklen_t len = sizeof(int);
|
|
|
|
if (getsockopt(hdl, IPPROTO_IPV6, IPV6_V6ONLY, &result, &len) == -1)
|
|
{
|
|
EHS_LOG_INT(LogType::ERR, 2, "Failed to set IPv6 only mode with error #" + Str_8::FromNum(errno) + ".");
|
|
return false;
|
|
}
|
|
|
|
EHS_LOG_SUCCESS();
|
|
|
|
return result;
|
|
}
|
|
|
|
bool UDP::IsValid() const
|
|
{
|
|
return hdl != EHS_INVALID_SOCKET;
|
|
}
|
|
|
|
void UDP::Bind_v6(const Str_8& address, const UInt_16 port) const
|
|
{
|
|
sockaddr_in6 result = {};
|
|
result.sin6_family = AF_INET6;
|
|
result.sin6_port = htons(port);
|
|
|
|
if (address.Size())
|
|
{
|
|
Int_32 code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
|
if (!code)
|
|
{
|
|
EHS_LOG_INT(LogType::ERR, 0, "The given address, \"" + address + "\" is not valid.");
|
|
return;
|
|
}
|
|
else if (code == -1)
|
|
{
|
|
Int_32 dCode = errno;
|
|
|
|
EHS_LOG_INT(LogType::ERR, 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.sin6_addr = in6addr_any;
|
|
}
|
|
|
|
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6));
|
|
if (code == -1)
|
|
{
|
|
EHS_LOG_INT(LogType::ERR, 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + ".");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void UDP::Bind_v4(const Str_8& address, const UInt_16 port) const
|
|
{
|
|
sockaddr_in result = {};
|
|
result.sin_family = AF_INET;
|
|
result.sin_port = htons(port);
|
|
|
|
if (address.Size())
|
|
{
|
|
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
|
if (!code)
|
|
{
|
|
EHS_LOG_INT(LogType::ERR, 0, "The given address, \"" + address + "\" is not valid.");
|
|
return;
|
|
}
|
|
else if (code == -1)
|
|
{
|
|
Int_32 dCode = errno;
|
|
|
|
EHS_LOG_INT(LogType::ERR, 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.sin_addr.s_addr = INADDR_ANY;
|
|
}
|
|
|
|
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in));
|
|
if (code == -1)
|
|
{
|
|
EHS_LOG_INT(LogType::ERR, 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + ".");
|
|
return;
|
|
}
|
|
}
|
|
|
|
UInt_64 UDP::Send_v6(const Str_8& address, const UInt_16 port, const Byte* const data, const UInt_64 size)
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
EHS_LOG_INT(LogType::INFO, 0, "Attempted to send while socket is not initialized.");
|
|
return 0;
|
|
}
|
|
|
|
sockaddr_in6 result = {};
|
|
result.sin6_family = AF_INET6;
|
|
result.sin6_port = htons(port);
|
|
|
|
Int_32 code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
|
if (!code)
|
|
{
|
|
EHS_LOG_INT(LogType::ERR, 1, "The given address, \"" + address + "\" is not valid.");
|
|
return 0;
|
|
}
|
|
else if (code == -1)
|
|
{
|
|
Int_32 dCode = errno;
|
|
|
|
EHS_LOG_INT(LogType::ERR, 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
|
return 0;
|
|
}
|
|
|
|
SInt_64 sent = sendto(hdl, (char*)&data[0], (int)size, 0, (sockaddr*)&result, sizeof(sockaddr_in6));
|
|
if (sent == -1)
|
|
{
|
|
Int_32 dCode = errno;
|
|
|
|
EHS_LOG_INT(LogType::ERR, 3, "Failed to send with error #" + Str_8::FromNum(dCode) + ".");
|
|
|
|
Release();
|
|
|
|
return 0;
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
|
|
UInt_64 UDP::Send_v4(const Str_8& address, const UInt_16 port, const Byte* const data, const UInt_64 size)
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
EHS_LOG_INT(LogType::INFO, 0, "Attempted to send while socket is not initialized.");
|
|
return 0;
|
|
}
|
|
|
|
sockaddr_in result = {};
|
|
result.sin_family = AF_INET;
|
|
result.sin_port = htons(port);
|
|
|
|
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
|
if (!code)
|
|
{
|
|
EHS_LOG_INT(LogType::ERR, 1, "The given address, \"" + address + "\" is not valid.");
|
|
return 0;
|
|
}
|
|
else if (code == -1)
|
|
{
|
|
Int_32 dCode = errno;
|
|
|
|
EHS_LOG_INT(LogType::ERR, 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
|
return 0;
|
|
}
|
|
|
|
SInt_64 sent = sendto(hdl, (char*)&data[0], (int)size, 0, (sockaddr*)&result, sizeof(sockaddr_in));
|
|
if (sent == -1)
|
|
{
|
|
Int_32 dCode = errno;
|
|
|
|
EHS_LOG_INT(LogType::ERR, 3, "Failed to send with error #" + Str_8::FromNum(dCode) + ".");
|
|
|
|
Release();
|
|
|
|
return 0;
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
} |