refactor and reorganize network socket handling classes to be more consistent;

pull/48/head
Bryan Biedenkapp 2 years ago
parent 8aba76156b
commit 80d38ccf3a

@ -44,6 +44,8 @@ file(GLOB common_SRC
"src/common/network/*.cpp"
"src/common/network/rest/*.cpp"
"src/common/network/rest/http/*.cpp"
"src/common/network/tcp/*.cpp"
"src/common/network/udp/*.cpp"
"src/common/yaml/*.cpp"
"src/common/*.cpp"
)
@ -85,6 +87,7 @@ file(GLOB common_INCLUDE
"src/common/network/rest/*.h"
"src/common/network/rest/http/*.h"
"src/common/network/tcp/*.h"
"src/common/network/udp/*.h"
"src/common/yaml/*.h"
"src/common/*.h"
)

@ -65,7 +65,7 @@ BaseNetwork::BaseNetwork(uint32_t peerId, bool duplex, bool debug, bool slot1, b
{
assert(peerId < 999999999U);
m_socket = new UDPSocket(localPort);
m_socket = new udp::Socket(localPort);
m_frameQueue = new FrameQueue(m_socket, peerId, debug);
std::random_device rd;

@ -23,7 +23,7 @@
#include "common/p25/Audio.h"
#include "common/nxdn/lc/RTCH.h"
#include "common/network/FrameQueue.h"
#include "common/network/UDPSocket.h"
#include "common/network/udp/Socket.h"
#include "common/RingBuffer.h"
#include "common/Utils.h"
@ -253,7 +253,7 @@ namespace network
bool m_debug;
UDPSocket* m_socket;
udp::Socket* m_socket;
FrameQueue* m_frameQueue;
RingBuffer<uint8_t> m_rxDMRData;

@ -35,7 +35,7 @@ using namespace network::frame;
/// </FrameQueue>
/// <param name="socket">Local port used to listen for incoming data.</param>
/// <param name="peerId">Unique ID of this modem on the network.</param>
FrameQueue::FrameQueue(UDPSocket* socket, uint32_t peerId, bool debug) : RawFrameQueue(socket, debug),
FrameQueue::FrameQueue(udp::Socket* socket, uint32_t peerId, bool debug) : RawFrameQueue(socket, debug),
m_peerId(peerId),
m_streamTimestamps()
{
@ -227,7 +227,7 @@ void FrameQueue::enqueueMessage(const uint8_t* message, uint32_t length, uint32_
if (m_debug)
Utils::dump(1U, "FrameQueue::enqueueMessage() Buffered Message", buffer, bufferLen);
UDPDatagram *dgram = new UDPDatagram;
udp::UDPDatagram *dgram = new udp::UDPDatagram;
dgram->buffer = buffer;
dgram->length = bufferLen;
dgram->address = addr;

@ -36,8 +36,12 @@ namespace network
class HOST_SW_API FrameQueue : public RawFrameQueue {
public: typedef std::pair<const uint8_t, const uint8_t> OpcodePair;
public:
auto operator=(FrameQueue&) -> FrameQueue& = delete;
auto operator=(FrameQueue&&) -> FrameQueue& = delete;
FrameQueue(FrameQueue&) = delete;
/// <summary>Initializes a new instance of the FrameQueue class.</summary>
FrameQueue(UDPSocket* socket, uint32_t peerId, bool debug);
FrameQueue(udp::Socket* socket, uint32_t peerId, bool debug);
/// <summary>Read message from the received UDP packet.</summary>
UInt8Array read(int& messageLength, sockaddr_storage& address, uint32_t& addrLen,

@ -12,7 +12,7 @@
*/
#include "Defines.h"
#include "network/RawFrameQueue.h"
#include "network/UDPSocket.h"
#include "network/udp/Socket.h"
#include "Log.h"
#include "Utils.h"
@ -30,7 +30,7 @@ using namespace network;
/// </summary>
/// <param name="socket">Local port used to listen for incoming data.</param>
/// <param name="debug"></param>
RawFrameQueue::RawFrameQueue(UDPSocket* socket, bool debug) :
RawFrameQueue::RawFrameQueue(udp::Socket* socket, bool debug) :
m_socket(socket),
m_buffers(),
m_debug(debug)
@ -101,7 +101,7 @@ void RawFrameQueue::enqueueMessage(const uint8_t* message, uint32_t length, sock
if (m_debug)
Utils::dump(1U, "RawFrameQueue::enqueueMessage() Buffered Message", buffer, length);
UDPDatagram* dgram = new UDPDatagram;
udp::UDPDatagram* dgram = new udp::UDPDatagram;
dgram->buffer = buffer;
dgram->length = length;
dgram->address = addr;

@ -14,7 +14,7 @@
#define __RAW_FRAME_QUEUE_H__
#include "common/Defines.h"
#include "common/network/UDPSocket.h"
#include "common/network/udp/Socket.h"
#include "common/Utils.h"
namespace network
@ -32,8 +32,12 @@ namespace network
class HOST_SW_API RawFrameQueue {
public:
auto operator=(RawFrameQueue&) -> RawFrameQueue& = delete;
auto operator=(RawFrameQueue&&) -> RawFrameQueue& = delete;
RawFrameQueue(RawFrameQueue&) = delete;
/// <summary>Initializes a new instance of the RawFrameQueue class.</summary>
RawFrameQueue(UDPSocket* socket, bool debug);
RawFrameQueue(udp::Socket* socket, bool debug);
/// <summary>Finalizes a instance of the RawFrameQueue class.</summary>
virtual ~RawFrameQueue();
@ -49,9 +53,9 @@ namespace network
protected:
sockaddr_storage m_addr;
uint32_t m_addrLen;
UDPSocket* m_socket;
udp::Socket* m_socket;
BufferVector m_buffers;
udp::BufferVector m_buffers;
bool m_debug;

@ -1,167 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2006-2016,2020 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__UDP_SOCKET_H__)
#define __UDP_SOCKET_H__
#include "common/Defines.h"
#include "common/AESCrypto.h"
#include <string>
#include <vector>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#if !defined(UDP_SOCKET_MAX)
#define UDP_SOCKET_MAX 1
#endif
#define AES_WRAPPED_PCKT_MAGIC 0xC0FEU
#define AES_WRAPPED_PCKT_KEY_LEN 32
enum IPMATCHTYPE {
IMT_ADDRESS_AND_PORT,
IMT_ADDRESS_ONLY
};
namespace network
{
#if defined(HAVE_SENDMSG) && !defined(HAVE_SENDMMSG)
/* For `sendmmsg'. */
struct mmsghdr {
struct msghdr msg_hdr; /* Actual message header. */
unsigned int msg_len; /* Number of received or sent bytes for the entry. */
};
/* Send a VLEN messages as described by VMESSAGES to socket FD.
Returns the number of datagrams successfully written or -1 for errors.
This function is a cancellation point and therefore not marked with
__THROW. */
static inline int sendmmsg(int sockfd, struct mmsghdr* msgvec, unsigned int vlen, int flags)
{
ssize_t n = 0;
for (unsigned int i = 0; i < vlen; i++) {
ssize_t ret = sendmsg(sockfd, &msgvec[i].msg_hdr, flags);
if (ret < 0)
break;
n += ret;
}
if (n == 0)
return -1;
return int(n);
}
#endif
// ---------------------------------------------------------------------------
// Structure Declaration
// This structure represents a container for a network buffer.
// ---------------------------------------------------------------------------
struct UDPDatagram {
uint8_t* buffer;
size_t length;
sockaddr_storage address;
uint32_t addrLen;
};
/* Vector of buffers that contain a full frames */
typedef std::vector<UDPDatagram*> BufferVector;
// ---------------------------------------------------------------------------
// Class Declaration
// This class implements low-level routines to communicate over a UDP
// network socket.
// ---------------------------------------------------------------------------
class HOST_SW_API UDPSocket {
public:
/// <summary>Initializes a new instance of the UDPSocket class.</summary>
UDPSocket(const std::string& address, uint16_t port = 0U);
/// <summary>Initializes a new instance of the UDPSocket class.</summary>
UDPSocket(uint16_t port = 0U);
/// <summary>Finalizes a instance of the UDPSocket class.</summary>
~UDPSocket();
/// <summary>Opens UDP socket connection.</summary>
bool open(uint32_t af = AF_UNSPEC);
/// <summary>Opens UDP socket connection.</summary>
bool open(const sockaddr_storage& address);
/// <summary>Opens UDP socket connection.</summary>
bool open(const uint32_t index, const uint32_t af, const std::string& address, const uint16_t port);
/// <summary>Read data from the UDP socket.</summary>
int read(uint8_t* buffer, uint32_t length, sockaddr_storage& address, uint32_t& addrLen);
/// <summary>Write data to the UDP socket.</summary>
bool write(const uint8_t* buffer, uint32_t length, const sockaddr_storage& address, uint32_t addrLen, int* lenWritten = nullptr);
/// <summary>Write data to the UDP socket.</summary>
bool write(BufferVector& buffers, int* lenWritten = nullptr);
/// <summary>Closes the UDP socket connection.</summary>
void close();
/// <summary>Closes the UDP socket connection.</summary>
void close(const uint32_t index);
/// <summary>Sets the preshared encryption key.</summary>
void setPresharedKey(const uint8_t* presharedKey);
/// <summary>Flag indicating the UDP socket(s) are open.</summary>
bool isOpen() const { return m_isOpen; }
/// <summary>Helper to lookup a hostname and resolve it to an IP address.</summary>
static int lookup(const std::string& hostName, uint16_t port, sockaddr_storage& address, uint32_t& addrLen);
/// <summary>Helper to lookup a hostname and resolve it to an IP address.</summary>
static int lookup(const std::string& hostName, uint16_t port, sockaddr_storage& address, uint32_t& addrLen, struct addrinfo& hints);
/// <summary></summary>
static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT);
/// <summary></summary>
static std::string address(const sockaddr_storage& addr);
/// <summary></summary>
static uint16_t port(const sockaddr_storage& addr);
/// <summary></summary>
static bool isNone(const sockaddr_storage& addr);
private:
std::string m_address_save;
uint16_t m_port_save;
std::string m_address[UDP_SOCKET_MAX];
uint16_t m_port[UDP_SOCKET_MAX];
bool m_isOpen;
uint32_t m_af[UDP_SOCKET_MAX];
int m_fd[UDP_SOCKET_MAX];
crypto::AES* m_aes;
bool m_isCryptoWrapped;
uint8_t* m_presharedKey;
uint32_t m_counter;
};
} // namespace network
#endif // __UDP_SOCKET_H__

@ -0,0 +1,402 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "Defines.h"
#include "network/tcp/Socket.h"
#include "Log.h"
#include "Utils.h"
using namespace network;
using namespace network::tcp;
#include <cassert>
#include <cerrno>
#include <cstring>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the Socket class.
/// </summary>
Socket::Socket() :
m_localAddress(),
m_localPort(0U),
m_fd(-1),
m_counter(0U)
{
/* stub */
}
/// <summary>
/// Initializes a new instance of the Socket class.
/// </summary>
/// <param name="fd"></param>
Socket::Socket(const int fd) noexcept :
m_localAddress(),
m_localPort(0U),
m_fd(fd),
m_counter(0U)
{
/* stub */
}
/// <summary>
/// Initializes a new instance of the Socket class.
/// </summary>
/// <param name="domain"></param>
/// <param name="type"></param>
/// <param name="protocol"></param>
Socket::Socket(const int domain, const int type, const int protocol) : Socket()
{
initSocket(domain, type, protocol);
}
/// <summary>
/// Finalizes a instance of the Socket class.
/// </summary>
Socket::~Socket()
{
static_cast<void>(::shutdown(m_fd, SHUT_RDWR));
static_cast<void>(::close(m_fd));
}
/// <summary>
/// Accepts a pending connection request.
/// </summary>
/// <param name="address"></param>
/// <param name="addrlen"></param>
/// <returns></returns>
int Socket::accept(sockaddr* address, socklen_t* addrlen) noexcept
{
// check that the accept() won't block
int i, n;
struct pollfd pfd[TCP_SOCKET_MAX];
for (i = n = 0; i < TCP_SOCKET_MAX; i++) {
if (m_fd >= 0) {
pfd[n].fd = m_fd;
pfd[n].events = POLLIN;
n++;
}
}
// no socket descriptor to receive
if (n == 0)
return 0;
// Return immediately
int ret = ::poll(pfd, n, 0);
if (ret < 0) {
LogError(LOG_NET, "Error returned from TCP poll, err: %d", errno);
return -1;
}
int index;
for (i = 0; i < n; i++) {
// round robin
index = (i + m_counter) % n;
if (pfd[index].revents & POLLIN)
break;
}
if (i == n)
return -1;
return ::accept(pfd[index].fd, address, addrlen);
}
/// <summary>
/// Connects the client to a remote TCP host using the specified host name and port number.
/// </summary>
/// <param name="ipAddr"></param>
/// <param name="port"></param>
/// <returns></returns>
bool Socket::connect(const std::string& ipAddr, const uint16_t port)
{
sockaddr_in addr = {};
initAddr(ipAddr, port, addr);
m_localAddress = ipAddr;
m_localPort = port;
socklen_t length = sizeof(addr);
bool retval = true;
if (::connect(m_fd, reinterpret_cast<sockaddr*>(&addr), length) < 0)
retval = false;
return retval;
}
/// <summary>
/// Starts listening for incoming connection requests with a maximum number of pending connection.
/// </summary>
/// <param name="ipAddr"></param>
/// <param name="port"></param>
/// <param name="backlog"></param>
/// <returns></returns>
ssize_t Socket::listen(const std::string& ipAddr, const uint16_t port, int backlog) noexcept
{
m_localAddress = ipAddr;
m_localPort = port;
if (!bind(m_localAddress, m_localPort)) {
return -1;
}
LogInfoEx(LOG_NET, "Listening TCP port on %u", m_localPort);
return ::listen(m_fd, backlog);
}
/// <summary>
/// Read data from the socket.
/// </summary>
/// <param name="buffer">Buffer to read data into.</param>
/// <param name="length">Length of data to read.</param>
/// <returns></returns>
[[nodiscard]] ssize_t Socket::read(uint8_t* buffer, size_t length) noexcept
{
assert(buffer != nullptr);
assert(length > 0U);
if (m_fd < 0)
return -1;
// check that the read() won't block
int i, n;
struct pollfd pfd[TCP_SOCKET_MAX];
for (i = n = 0; i < TCP_SOCKET_MAX; i++) {
if (m_fd >= 0) {
pfd[n].fd = m_fd;
pfd[n].events = POLLIN;
n++;
}
}
// no socket descriptor to receive
if (n == 0)
return 0;
// Return immediately
int ret = ::poll(pfd, n, 0);
if (ret < 0) {
LogError(LOG_NET, "Error returned from TCP poll, err: %d", errno);
return -1;
}
int index;
for (i = 0; i < n; i++) {
// round robin
index = (i + m_counter) % n;
if (pfd[index].revents & POLLIN)
break;
}
if (i == n)
return 0;
m_counter++;
return ::read(pfd[index].fd, (char*)buffer, length);
}
/// <summary>
/// Write data to the socket.
/// </summary>
/// <param name="buffer">Buffer containing data to write to socket.</param>
/// <param name="length">Length of data to write.</param>
/// <returns></returns>
ssize_t Socket::write(const uint8_t* buffer, size_t length) noexcept
{
assert(buffer != nullptr);
assert(length > 0U);
if (m_fd < 0)
return -1;
return ::send(m_fd, buffer, length, 0);
}
/// <summary>
///
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
uint32_t Socket::addr(const sockaddr_storage& addr)
{
switch (addr.ss_family) {
case AF_INET:
{
struct sockaddr_in* in;
in = (struct sockaddr_in*)& addr;
return in->sin_addr.s_addr;
}
break;
case AF_INET6:
default:
return -1;
break;
}
}
/// <summary>
///
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
std::string Socket::address(const sockaddr_storage& addr)
{
std::string address = std::string();
char str[INET_ADDRSTRLEN];
switch (addr.ss_family) {
case AF_INET:
{
struct sockaddr_in* in;
in = (struct sockaddr_in*)& addr;
inet_ntop(AF_INET, &(in->sin_addr), str, INET_ADDRSTRLEN);
address = std::string(str);
}
break;
case AF_INET6:
{
struct sockaddr_in6* in6;
in6 = (struct sockaddr_in6*)& addr;
inet_ntop(AF_INET6, &(in6->sin6_addr), str, INET_ADDRSTRLEN);
address = std::string(str);
}
break;
default:
break;
}
return address;
}
/// <summary>
///
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
uint16_t Socket::port(const sockaddr_storage& addr)
{
uint16_t port = 0U;
switch (addr.ss_family) {
case AF_INET:
{
struct sockaddr_in* in;
in = (struct sockaddr_in*)& addr;
port = ntohs(in->sin_port);
}
break;
case AF_INET6:
{
struct sockaddr_in6* in6;
in6 = (struct sockaddr_in6*)& addr;
port = ntohs(in6->sin6_port);
}
break;
default:
break;
}
return port;
}
/// <summary>
///
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
bool Socket::isNone(const sockaddr_storage& addr)
{
struct sockaddr_in* in = (struct sockaddr_in*)& addr;
return ((addr.ss_family == AF_INET) && (in->sin_addr.s_addr == htonl(INADDR_NONE)));
}
// ---------------------------------------------------------------------------
// Protected Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
/// <param name="domain"></param>
/// <param name="type"></param>
/// <param name="protocol"></param>
bool Socket::initSocket(const int domain, const int type, const int protocol)
{
m_fd = ::socket(domain, type, protocol);
if (m_fd < 0) {
LogError(LOG_NET, "Cannot create the TCP socket, err: %d", errno);
return false;
}
return true;
}
/// <summary>
///
/// </summary>
/// <param name="ipAddr"></param>
/// <param name="port"></param>
/// <returns></returns>
bool Socket::bind(const std::string& ipAddr, const uint16_t port)
{
m_localAddress = std::string(ipAddr);
m_localPort = port;
sockaddr_in addr = {};
initAddr(ipAddr, port, addr);
socklen_t length = sizeof(addr);
bool retval = true;
if (::bind(m_fd, reinterpret_cast<sockaddr*>(&addr), length) < 0) {
LogError(LOG_NET, "Cannot bind the TCP address, err: %d", errno);
retval = false;
}
return retval;
}
/// <summary>
/// Helper to lookup a hostname and resolve it to an IP address.
/// </summary>
/// <param name="inaddr">String containing hostname to resolve.</param>
/// <returns></returns>
[[nodiscard]] std::string Socket::getIpAddress(const in_addr inaddr)
{
char* receivedAddr = ::inet_ntoa(inaddr);
if (receivedAddr == reinterpret_cast<char*>(INADDR_NONE))
throw std::runtime_error("Invalid IP address received on readfrom.");
return { receivedAddr };
}
/// <summary>
/// Initialize the sockaddr_in structure with the provided IP and port
/// </summary>
/// <param name="ipAddr">IP address.</param>
/// <param name="port">IP address.</param>
/// <param name="addr"></param>
void Socket::initAddr(const std::string& ipAddr, const int port, sockaddr_in& addr) noexcept(false)
{
addr.sin_family = AF_INET;
if (ipAddr.empty() || ipAddr == "0.0.0.0")
addr.sin_addr.s_addr = INADDR_ANY;
else
{
if (::inet_pton(AF_INET, ipAddr.c_str(), &addr.sin_addr) <= 0)
throw std::runtime_error("Failed to parse IP address");
}
addr.sin_port = ::htons(port);
}

@ -10,8 +10,8 @@
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__SOCKET_H__)
#define __SOCKET_H__
#if !defined(__TCP_SOCKET_H__)
#define __TCP_SOCKET_H__
#include "Defines.h"
#include "common/Log.h"
@ -47,321 +47,56 @@ namespace network
Socket(Socket&) = delete;
/// <summary>Initializes a new instance of the Socket class.</summary>
Socket() noexcept(false) :
m_fd(-1),
m_address(),
m_port(0U),
m_counter(0U)
{
/* stub */
}
Socket();
/// <summary>Initializes a new instance of the Socket class.</summary>
/// <param name="fd"></param>
Socket(const int fd) noexcept :
m_fd(fd),
m_address(),
m_port(0U),
m_counter(0U)
{
/* stub */
}
Socket(const int fd) noexcept;
/// <summary>Initializes a new instance of the Socket class.</summary>
/// <param name="domain"></param>
/// <param name="type"></param>
/// <param name="protocol"></param>
Socket(const int domain, const int type, const int protocol) noexcept(false) : Socket()
{
initSocket(domain, type, protocol);
}
Socket(const int domain, const int type, const int protocol);
/// <summary>Finalizes a instance of the Socket class.</summary>
virtual ~Socket()
{
static_cast<void>(::shutdown(m_fd, SHUT_RDWR));
static_cast<void>(::close(m_fd));
}
virtual ~Socket();
/// <summary>
///
/// </summary>
/// <param name="domain"></param>
/// <param name="type"></param>
/// <param name="protocol"></param>
void initSocket(const int domain, const int type, const int protocol) noexcept(false)
{
m_fd = ::socket(domain, type, protocol);
if (m_fd < 0)
throw std::runtime_error("Failed to create Socket");
}
/// <summary>Accepts a pending connection request.</summary>
int accept(sockaddr* address, socklen_t* addrlen) noexcept;
/// <summary>Connects the client to a remote TCP host using the specified host name and port number.</summary>
virtual bool connect(const std::string& ipAddr, const uint16_t port);
/// <summary>Starts listening for incoming connection requests with a maximum number of pending connection.</summary>
ssize_t listen(const std::string& ipAddr, const uint16_t port, int backlog) noexcept;
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <param name="addrlen"></param>
/// <returns></returns>
int accept(sockaddr* address, socklen_t* addrlen) const noexcept
{
// check that the accept() won't block
int i, n;
struct pollfd pfd[TCP_SOCKET_MAX];
for (i = n = 0; i < TCP_SOCKET_MAX; i++) {
if (m_fd >= 0) {
pfd[n].fd = m_fd;
pfd[n].events = POLLIN;
n++;
}
}
// no socket descriptor to receive
if (n == 0)
return 0;
// Return immediately
int ret = ::poll(pfd, n, 0);
if (ret < 0) {
LogError(LOG_NET, "Error returned from TCP poll, err: %d", errno);
return -1;
}
int index;
for (i = 0; i < n; i++) {
// round robin
index = (i + m_counter) % n;
if (pfd[index].revents & POLLIN)
break;
}
if (i == n)
return -1;
return ::accept(pfd[index].fd, address, addrlen);
}
/// <summary>
///
/// </summary>
/// <param name="ipAddr"></param>
/// <param name="port"></param>
/// <returns></returns>
bool bind(const std::string& ipAddr, const uint16_t port) noexcept(false)
{
m_address = std::string(ipAddr);
m_port = port;
sockaddr_in addr = {};
initAddr(ipAddr, port, addr);
socklen_t length = sizeof(addr);
bool retval = true;
if (::bind(m_fd, reinterpret_cast<sockaddr*>(&addr), length) < 0)
retval = false;
return retval;
}
/// <summary>
///
/// </summary>
/// <param name="ipAddr"></param>
/// <param name="port"></param>
/// <returns></returns>
virtual bool connect(const std::string& ipAddr, const uint16_t port) noexcept(false)
{
sockaddr_in addr = {};
initAddr(ipAddr, port, addr);
socklen_t length = sizeof(addr);
bool retval = true;
if (::connect(m_fd, reinterpret_cast<sockaddr*>(&addr), length) < 0)
retval = false;
return retval;
}
/// <summary>
///
/// </summary>
/// <param name="backlog"></param>
/// <returns></returns>
ssize_t listen(int backlog) const noexcept
{
LogInfoEx(LOG_NET, "Listening TCP port on %u", m_port);
return ::listen(m_fd, backlog);
}
/// <summary>
/// Read data from the socket.
/// </summary>
/// <param name="buffer">Buffer to read data into.</param>
/// <param name="length">Length of data to read.</param>
/// <returns></returns>
[[nodiscard]] ssize_t read(uint8_t* buffer, size_t length) const noexcept
{
// check that the read() won't block
int i, n;
struct pollfd pfd[TCP_SOCKET_MAX];
for (i = n = 0; i < TCP_SOCKET_MAX; i++) {
if (m_fd >= 0) {
pfd[n].fd = m_fd;
pfd[n].events = POLLIN;
n++;
}
}
// no socket descriptor to receive
if (n == 0)
return 0;
// Return immediately
int ret = ::poll(pfd, n, 0);
if (ret < 0) {
LogError(LOG_NET, "Error returned from TCP poll, err: %d", errno);
return -1;
}
int index;
for (i = 0; i < n; i++) {
// round robin
index = (i + m_counter) % n;
if (pfd[index].revents & POLLIN)
break;
}
if (i == n)
return 0;
return ::read(pfd[index].fd, (char*)buffer, length);
}
/// <summary>
/// Write data to the socket.
/// </summary>
/// <param name="buffer">Buffer containing data to write to socket.</param>
/// <param name="length">Length of data to write.</param>
/// <returns></returns>
ssize_t write(const uint8_t* buffer, size_t length) const noexcept
{
return ::send(m_fd, buffer, length, 0);
}
/// <summary>Read data from the socket.</summary>
[[nodiscard]] virtual ssize_t read(uint8_t* buffer, size_t length) noexcept;
/// <summary>Write data to the socket.</summary>
virtual ssize_t write(const uint8_t* buffer, size_t length) noexcept;
/// <summary></summary>
static uint32_t addr(const sockaddr_storage& addr)
{
switch (addr.ss_family) {
case AF_INET:
{
struct sockaddr_in* in;
in = (struct sockaddr_in*)& addr;
return in->sin_addr.s_addr;
}
break;
case AF_INET6:
default:
return -1;
break;
}
}
static uint32_t addr(const sockaddr_storage& addr);
/// <summary></summary>
static std::string address(const sockaddr_storage& addr)
{
std::string address = std::string();
char str[INET_ADDRSTRLEN];
switch (addr.ss_family) {
case AF_INET:
{
struct sockaddr_in* in;
in = (struct sockaddr_in*)& addr;
inet_ntop(AF_INET, &(in->sin_addr), str, INET_ADDRSTRLEN);
address = std::string(str);
}
break;
case AF_INET6:
{
struct sockaddr_in6* in6;
in6 = (struct sockaddr_in6*)& addr;
inet_ntop(AF_INET6, &(in6->sin6_addr), str, INET_ADDRSTRLEN);
address = std::string(str);
}
break;
default:
break;
}
return address;
}
static std::string address(const sockaddr_storage& addr);
/// <summary></summary>
static uint16_t port(const sockaddr_storage& addr)
{
uint16_t port = 0U;
switch (addr.ss_family) {
case AF_INET:
{
struct sockaddr_in* in;
in = (struct sockaddr_in*)& addr;
port = ntohs(in->sin_port);
}
break;
case AF_INET6:
{
struct sockaddr_in6* in6;
in6 = (struct sockaddr_in6*)& addr;
port = ntohs(in6->sin6_port);
}
break;
default:
break;
}
return port;
}
static uint16_t port(const sockaddr_storage& addr);
/// <summary></summary>
static bool isNone(const sockaddr_storage& addr)
{
struct sockaddr_in* in = (struct sockaddr_in*)& addr;
return ((addr.ss_family == AF_INET) && (in->sin_addr.s_addr == htonl(INADDR_NONE)));
}
static bool isNone(const sockaddr_storage& addr);
protected:
std::string m_localAddress;
uint16_t m_localPort;
int m_fd;
std::string m_address;
uint16_t m_port;
uint32_t m_counter;
/// <summary>
/// Helper to lookup a hostname and resolve it to an IP address.
/// </summary>
/// <param name="inaddr">String containing hostname to resolve.</param>
/// <returns></returns>
[[nodiscard]] static std::string getIpAddress(const in_addr inaddr) noexcept(false)
{
char* receivedAddr = ::inet_ntoa(inaddr);
if (receivedAddr == reinterpret_cast<char*>(INADDR_NONE))
throw std::runtime_error("Invalid IP address received on readfrom.");
return { receivedAddr };
}
/// <summary></summary>
bool initSocket(const int domain, const int type, const int protocol);
/// <summary></summary>
bool bind(const std::string& ipAddr, const uint16_t port);
/// <summary>
/// Initialize the sockaddr_in structure with the provided IP and port
/// </summary>
/// <param name="ipAddr">IP address.</param>
/// <param name="port">IP address.</param>
/// <param name="addr"></param>
static void initAddr(const std::string& ipAddr, const int port, sockaddr_in& addr) noexcept(false)
{
addr.sin_family = AF_INET;
if (ipAddr.empty() || ipAddr == "0.0.0.0")
addr.sin_addr.s_addr = INADDR_ANY;
else
{
if (::inet_pton(AF_INET, ipAddr.c_str(), &addr.sin_addr) <= 0)
throw std::runtime_error("Failed to parse IP address");
}
/// <summary>Helper to lookup a hostname and resolve it to an IP address.</summary>
[[nodiscard]] static std::string getIpAddress(const in_addr inaddr);
addr.sin_port = ::htons(port);
}
/// <summary>Initialize the sockaddr_in structure with the provided IP and port.</summary>
static void initAddr(const std::string& ipAddr, const int port, sockaddr_in& addr);
};
} // namespace tcp
} // namespace network
#endif // __SOCKET_H__
#endif // __TCP_SOCKET_H__

@ -13,11 +13,12 @@
*
*/
#include "Defines.h"
#include "network/UDPSocket.h"
#include "network/udp/Socket.h"
#include "Log.h"
#include "Utils.h"
using namespace network;
using namespace network::udp;
#include <cassert>
#include <cerrno>
@ -32,15 +33,17 @@ using namespace network;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the UDPSocket class.
/// Initializes a new instance of the Socket class.
/// </summary>
/// <param name="address">Hostname/IP address to connect to.</param>
/// <param name="port">Port number.</param>
UDPSocket::UDPSocket(const std::string& address, uint16_t port) :
m_address_save(address),
m_port_save(port),
m_isOpen(false),
Socket::Socket(const std::string& address, uint16_t port) :
m_localAddress(address),
m_localPort(port),
m_af(AF_UNSPEC),
m_fd(-1),
m_aes(nullptr),
m_isCryptoWrapped(false),
m_presharedKey(nullptr),
@ -48,22 +51,17 @@ UDPSocket::UDPSocket(const std::string& address, uint16_t port) :
{
m_aes = new crypto::AES(crypto::AESKeyLength::AES_256);
m_presharedKey = new uint8_t[AES_WRAPPED_PCKT_KEY_LEN];
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
m_address[i] = "";
m_port[i] = 0U;
m_af[i] = 0U;
m_fd[i] = -1;
}
}
/// <summary>
/// Initializes a new instance of the UDPSocket class.
/// Initializes a new instance of the Socket class.
/// </summary>
/// <param name="port">Port number.</param>
UDPSocket::UDPSocket(uint16_t port) :
m_address_save(),
m_port_save(port),
m_isOpen(false),
Socket::Socket(uint16_t port) :
m_localAddress(),
m_localPort(port),
m_af(AF_UNSPEC),
m_fd(-1),
m_aes(nullptr),
m_isCryptoWrapped(false),
m_presharedKey(nullptr),
@ -71,18 +69,12 @@ UDPSocket::UDPSocket(uint16_t port) :
{
m_aes = new crypto::AES(crypto::AESKeyLength::AES_256);
m_presharedKey = new uint8_t[AES_WRAPPED_PCKT_KEY_LEN];
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
m_address[i] = "";
m_port[i] = 0U;
m_af[i] = 0U;
m_fd[i] = -1;
}
}
/// <summary>
/// Finalizes a instance of the UDPSocket class.
/// Finalizes a instance of the Socket class.
/// </summary>
UDPSocket::~UDPSocket()
Socket::~Socket()
{
if (m_aes != nullptr)
delete m_aes;
@ -95,7 +87,7 @@ UDPSocket::~UDPSocket()
/// </summary>
/// <param name="address"></param>
/// <returns>True, if UDP socket is opened, otherwise false.</returns>
bool UDPSocket::open(const sockaddr_storage& address)
bool Socket::open(const sockaddr_storage& address) noexcept
{
return open(address.ss_family);
}
@ -105,20 +97,19 @@ bool UDPSocket::open(const sockaddr_storage& address)
/// </summary>
/// <param name="af"></param>
/// <returns>True, if UDP socket is opened, otherwise false.</returns>
bool UDPSocket::open(uint32_t af)
bool Socket::open(uint32_t af) noexcept
{
return open(0, af, m_address_save, m_port_save);
return open(af, m_localAddress, m_localPort);
}
/// <summary>
/// Opens UDP socket connection.
/// </summary>
/// <param name="index"></param>
/// <param name="af"></param>
/// <param name="address"></param>
/// <param name="port"></param>
/// <returns>True, if UDP socket is opened, otherwise false.</returns>
bool UDPSocket::open(const uint32_t index, const uint32_t af, const std::string& address, const uint16_t port)
bool Socket::open(const uint32_t af, const std::string& address, const uint16_t port) noexcept
{
sockaddr_storage addr;
uint32_t addrlen;
@ -132,45 +123,42 @@ bool UDPSocket::open(const uint32_t index, const uint32_t af, const std::string&
int err = lookup(address, port, addr, addrlen, hints);
if (err != 0) {
LogError(LOG_NET, "The local address is invalid - %s", address.c_str());
m_isOpen = false;
return false;
}
close(index);
close();
int fd = ::socket(addr.ss_family, SOCK_DGRAM, 0);
if (fd < 0) {
LogError(LOG_NET, "Cannot create the UDP socket, err: %d", errno);
m_isOpen = false;
if (!initSocket(addr.ss_family, SOCK_DGRAM, 0))
return false;
}
m_address[index] = address;
m_port[index] = port;
m_af[index] = addr.ss_family;
m_fd[index] = fd;
if (port > 0U) {
int reuse = 1;
if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)& reuse, sizeof(reuse)) == -1) {
if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char*)& reuse, sizeof(reuse)) == -1) {
LogError(LOG_NET, "Cannot set the UDP socket option, err: %d", errno);
m_isOpen = false;
return false;
}
if (::bind(fd, (sockaddr*)& addr, addrlen) == -1) {
LogError(LOG_NET, "Cannot bind the UDP address, err: %d", errno);
m_isOpen = false;
if (!bind(address, port)) {
return false;
}
LogInfoEx(LOG_NET, "Opening UDP port on %u", port);
}
m_isOpen = true;
return true;
}
/// <summary>
/// Closes the UDP socket connection.
/// </summary>
void Socket::close()
{
if (m_fd >= 0) {
::close(m_fd);
m_fd = -1;
}
}
/// <summary>
/// Read data from the UDP socket.
/// </summary>
@ -179,17 +167,20 @@ bool UDPSocket::open(const uint32_t index, const uint32_t af, const std::string&
/// <param name="address">IP address data read from.</param>
/// <param name="addrLen"></param>
/// <returns>Actual length of data read from remote UDP socket.</returns>
int UDPSocket::read(uint8_t* buffer, uint32_t length, sockaddr_storage& address, uint32_t& addrLen)
ssize_t Socket::read(uint8_t* buffer, uint32_t length, sockaddr_storage& address, uint32_t& addrLen) noexcept
{
assert(buffer != nullptr);
assert(length > 0U);
if (m_fd < 0)
return -1;
// Check that the readfrom() won't block
int i, n;
struct pollfd pfd[UDP_SOCKET_MAX];
for (i = n = 0; i < UDP_SOCKET_MAX; i++) {
if (m_fd[i] >= 0) {
pfd[n].fd = m_fd[i];
if (m_fd >= 0) {
pfd[n].fd = m_fd;
pfd[n].events = POLLIN;
n++;
}
@ -222,7 +213,7 @@ int UDPSocket::read(uint8_t* buffer, uint32_t length, sockaddr_storage& address,
LogError(LOG_NET, "Error returned from recvfrom, err: %d", errno);
if (len == -1 && errno == ENOTSOCK) {
LogMessage(LOG_NET, "Re-opening UDP port on %u", m_port[index]);
LogMessage(LOG_NET, "Re-opening UDP port on %u", m_localPort);
close();
open();
}
@ -241,12 +232,12 @@ int UDPSocket::read(uint8_t* buffer, uint32_t length, sockaddr_storage& address,
uint16_t magic = __GET_UINT16B(buffer, 0U);
if (magic == AES_WRAPPED_PCKT_MAGIC) {
uint32_t cryptedLen = (len - 2U) * sizeof(uint8_t);
// Utils::dump(1U, "UDPSocket::read() crypted", buffer + 2U, cryptedLen);
// Utils::dump(1U, "Socket::read() crypted", buffer + 2U, cryptedLen);
// decrypt
uint8_t* decrypted = m_aes->decryptECB(buffer + 2U, cryptedLen, m_presharedKey);
// Utils::dump(1U, "UDPSocket::read() decrypted", decrypted, cryptedLen);
// Utils::dump(1U, "Socket::read() decrypted", decrypted, cryptedLen);
// finalize, cleanup buffers and replace with new
if (decrypted != nullptr) {
@ -279,14 +270,22 @@ int UDPSocket::read(uint8_t* buffer, uint32_t length, sockaddr_storage& address,
/// <param name="addrLen"></param>
/// <param name="lenWritten">Total number of bytes written.</param>
/// <returns>True if message was sent, otherwise false.</returns>
bool UDPSocket::write(const uint8_t* buffer, uint32_t length, const sockaddr_storage& address, uint32_t addrLen, int* lenWritten)
bool Socket::write(const uint8_t* buffer, uint32_t length, const sockaddr_storage& address, uint32_t addrLen, ssize_t* lenWritten) noexcept
{
assert(buffer != nullptr);
assert(length > 0U);
bool result = false;
if (m_fd < 0) {
if (lenWritten != nullptr) {
*lenWritten = -1;
}
return false;
}
bool result = false;
UInt8Array out = nullptr;
// are we crypto wrapped?
if (m_isCryptoWrapped) {
if (m_presharedKey == nullptr) {
@ -312,7 +311,7 @@ bool UDPSocket::write(const uint8_t* buffer, uint32_t length, const sockaddr_sto
// encrypt
uint8_t* crypted = m_aes->encryptECB(cryptoBuffer, cryptedLen, m_presharedKey);
// Utils::dump(1U, "UDPSocket::write() crypted", crypted, cryptedLen);
// Utils::dump(1U, "Socket::write() crypted", crypted, cryptedLen);
// finalize, cleanup buffers and replace with new
out = std::unique_ptr<uint8_t[]>(new uint8_t[cryptedLen + 2U]);
@ -334,25 +333,20 @@ bool UDPSocket::write(const uint8_t* buffer, uint32_t length, const sockaddr_sto
::memcpy(out.get(), buffer, length);
}
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
if (m_fd[i] < 0 || m_af[i] != address.ss_family)
continue;
ssize_t sent = ::sendto(m_fd, (char*)out.get(), length, 0, (sockaddr*)& address, addrLen);
if (sent < 0) {
LogError(LOG_NET, "Error returned from sendto, err: %d", errno);
ssize_t sent = ::sendto(m_fd[i], (char*)out.get(), length, 0, (sockaddr*)& address, addrLen);
if (sent < 0) {
LogError(LOG_NET, "Error returned from sendto, err: %d", errno);
if (lenWritten != nullptr) {
*lenWritten = -1;
}
if (lenWritten != nullptr) {
*lenWritten = -1;
}
else {
if (sent == ssize_t(length))
result = true;
}
else {
if (sent == ssize_t(length))
result = true;
if (lenWritten != nullptr) {
*lenWritten = sent;
}
if (lenWritten != nullptr) {
*lenWritten = sent;
}
}
@ -367,16 +361,32 @@ bool UDPSocket::write(const uint8_t* buffer, uint32_t length, const sockaddr_sto
/// <param name="addrLen"></param>
/// <param name="lenWritten">Total number of bytes written.</param>
/// <returns>True if messages were sent, otherwise false.</returns>
bool UDPSocket::write(BufferVector& buffers, int* lenWritten)
bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept
{
bool result = false;
if (m_fd < 0) {
if (lenWritten != nullptr) {
*lenWritten = -1;
}
return false;
}
if (buffers.empty()) {
if (lenWritten != nullptr) {
*lenWritten = -1;
}
return false;
}
// bryanb: this is the same as above -- but for some assinine reason prevents
// weirdness
if (buffers.size() == 0U) {
if (lenWritten != nullptr) {
*lenWritten = -1;
}
return false;
}
@ -384,15 +394,25 @@ bool UDPSocket::write(BufferVector& buffers, int* lenWritten)
if (buffers.size() > UINT16_MAX) {
LogError(LOG_NET, "Trying to send too many buffers?");
if (lenWritten != nullptr) {
*lenWritten = -1;
}
return false;
}
// LogDebug(LOG_NET, "Sending message(s) (to %s:%u) addrLen %u", UDPSocket::address(address).c_str(), UDPSocket::port(address), addrLen);
// LogDebug(LOG_NET, "Sending message(s) (to %s:%u) addrLen %u", Socket::address(address).c_str(), Socket::port(address), addrLen);
// are we crypto wrapped?
if (m_isCryptoWrapped) {
if (m_presharedKey == nullptr) {
LogError(LOG_NET, "tried to write datagram encrypted with no key? this shouldn't happen BUGBUG");
if (lenWritten != nullptr) {
*lenWritten = -1;
}
return false;
}
}
@ -441,7 +461,7 @@ bool UDPSocket::write(BufferVector& buffers, int* lenWritten)
continue;
}
// Utils::dump(1U, "UDPSocket::write() crypted", crypted, cryptedLen);
// Utils::dump(1U, "Socket::write() crypted", crypted, cryptedLen);
// finalize
uint8_t out[cryptedLen + 2U];
@ -475,71 +495,50 @@ bool UDPSocket::write(BufferVector& buffers, int* lenWritten)
headers[i].msg_hdr.msg_controllen = 0;
}
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
if (m_fd[i] < 0)
continue;
bool skip = false;
for (auto& buffer : buffers) {
if (m_af != buffer->address.ss_family) {
skip = true;
break;
}
}
bool skip = false;
for (auto& buffer : buffers) {
if (m_af[i] != buffer->address.ss_family) {
skip = true;
break;
}
if (skip) {
if (lenWritten != nullptr) {
*lenWritten = -1;
}
if (skip)
continue;
if (sendmmsg(m_fd[i], headers, size, 0) < 0) {
LogError(LOG_NET, "Error returned from sendmmsg, err: %d", errno);
if (lenWritten != nullptr) {
*lenWritten = -1;
}
return false;
}
if (sendmmsg(m_fd, headers, size, 0) < 0) {
LogError(LOG_NET, "Error returned from sendmmsg, err: %d", errno);
if (lenWritten != nullptr) {
*lenWritten = -1;
}
}
if (sent < 0) {
LogError(LOG_NET, "Error returned from sendmmsg, err: %d", errno);
if (lenWritten != nullptr) {
*lenWritten = -1;
}
if (sent < 0) {
LogError(LOG_NET, "Error returned from sendmmsg, err: %d", errno);
if (lenWritten != nullptr) {
*lenWritten = -1;
}
else {
result = true;
if (lenWritten != nullptr) {
*lenWritten = sent;
}
}
else {
result = true;
if (lenWritten != nullptr) {
*lenWritten = sent;
}
}
return result;
}
/// <summary>
/// Closes the UDP socket connection.
/// </summary>
void UDPSocket::close()
{
for (int i = 0; i < UDP_SOCKET_MAX; i++)
close(i);
m_isOpen = false;
}
/// <summary>
/// Closes the UDP socket connection.
/// </summary>
/// <param name="index"></param>
void UDPSocket::close(const uint32_t index)
{
if ((index < UDP_SOCKET_MAX) && (m_fd[index] >= 0)) {
::close(m_fd[index]);
m_fd[index] = -1;
}
}
/// <summary>
/// Sets the preshared encryption key.
/// </summary>
/// <param name="presharedKey"></param>
void UDPSocket::setPresharedKey(const uint8_t* presharedKey)
void Socket::setPresharedKey(const uint8_t* presharedKey)
{
if (presharedKey != nullptr) {
::memset(m_presharedKey, 0x00U, AES_WRAPPED_PCKT_KEY_LEN);
@ -559,7 +558,7 @@ void UDPSocket::setPresharedKey(const uint8_t* presharedKey)
/// <param name="addr">Socket address structure.</param>
/// <param name="addrLen"></param>
/// <returns>Zero if no error during lookup, otherwise error.</returns>
int UDPSocket::lookup(const std::string& hostname, uint16_t port, sockaddr_storage& addr, uint32_t& addrLen)
int Socket::lookup(const std::string& hostname, uint16_t port, sockaddr_storage& addr, uint32_t& addrLen)
{
struct addrinfo hints;
::memset(&hints, 0, sizeof(hints));
@ -576,7 +575,7 @@ int UDPSocket::lookup(const std::string& hostname, uint16_t port, sockaddr_stora
/// <param name="addrLen"></param>
/// <param name="hints"></param>
/// <returns>Zero if no error during lookup, otherwise error.</returns>
int UDPSocket::lookup(const std::string& hostname, uint16_t port, sockaddr_storage& addr, uint32_t& addrLen, struct addrinfo& hints)
int Socket::lookup(const std::string& hostname, uint16_t port, sockaddr_storage& addr, uint32_t& addrLen, struct addrinfo& hints)
{
std::string portstr = std::to_string(port);
struct addrinfo* res;
@ -609,7 +608,7 @@ int UDPSocket::lookup(const std::string& hostname, uint16_t port, sockaddr_stora
/// <param name="addr2"></param>
/// <param name="type"></param>
/// <returns></returns>
bool UDPSocket::match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type)
bool Socket::match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type)
{
if (addr1.ss_family != addr2.ss_family)
return false;
@ -656,7 +655,7 @@ bool UDPSocket::match(const sockaddr_storage& addr1, const sockaddr_storage& add
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
std::string UDPSocket::address(const sockaddr_storage& addr)
std::string Socket::address(const sockaddr_storage& addr)
{
std::string address = std::string();
char str[INET_ADDRSTRLEN];
@ -690,7 +689,7 @@ std::string UDPSocket::address(const sockaddr_storage& addr)
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
uint16_t UDPSocket::port(const sockaddr_storage& addr)
uint16_t Socket::port(const sockaddr_storage& addr)
{
uint16_t port = 0U;
@ -721,9 +720,75 @@ uint16_t UDPSocket::port(const sockaddr_storage& addr)
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
bool UDPSocket::isNone(const sockaddr_storage& addr)
bool Socket::isNone(const sockaddr_storage& addr)
{
struct sockaddr_in* in = (struct sockaddr_in*)& addr;
return ((addr.ss_family == AF_INET) && (in->sin_addr.s_addr == htonl(INADDR_NONE)));
}
// ---------------------------------------------------------------------------
// Protected Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
/// <param name="domain"></param>
/// <param name="type"></param>
/// <param name="protocol"></param>
bool Socket::initSocket(const int domain, const int type, const int protocol) noexcept(false)
{
m_fd = ::socket(domain, type, protocol);
if (m_fd < 0) {
LogError(LOG_NET, "Cannot create the UDP socket, err: %d", errno);
return false;
}
m_af = domain;
return true;
}
/// <summary>
///
/// </summary>
/// <param name="ipAddr"></param>
/// <param name="port"></param>
/// <returns></returns>
bool Socket::bind(const std::string& ipAddr, const uint16_t port) noexcept(false)
{
m_localAddress = std::string(ipAddr);
m_localPort = port;
sockaddr_in addr = {};
initAddr(ipAddr, port, addr);
socklen_t length = sizeof(addr);
bool retval = true;
if (::bind(m_fd, reinterpret_cast<sockaddr*>(&addr), length) < 0) {
LogError(LOG_NET, "Cannot bind the UDP address, err: %d", errno);
retval = false;
}
return retval;
}
/// <summary>
/// Initialize the sockaddr_in structure with the provided IP and port
/// </summary>
/// <param name="ipAddr">IP address.</param>
/// <param name="port">IP address.</param>
/// <param name="addr"></param>
void Socket::initAddr(const std::string& ipAddr, const int port, sockaddr_in& addr) noexcept(false)
{
addr.sin_family = AF_INET;
if (ipAddr.empty() || ipAddr == "0.0.0.0")
addr.sin_addr.s_addr = INADDR_ANY;
else
{
if (::inet_pton(AF_INET, ipAddr.c_str(), &addr.sin_addr) <= 0)
throw std::runtime_error("Failed to parse IP address");
}
addr.sin_port = ::htons(port);
}

@ -0,0 +1,176 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @derivedfrom MMDVMHost (https://github.com/g4klx/MMDVMHost)
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2006-2016,2020 Jonathan Naylor, G4KLX
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__UDP_SOCKET_H__)
#define __UDP_SOCKET_H__
#include "common/Defines.h"
#include "common/AESCrypto.h"
#include <string>
#include <vector>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#if !defined(UDP_SOCKET_MAX)
#define UDP_SOCKET_MAX 1
#endif
#define AES_WRAPPED_PCKT_MAGIC 0xC0FEU
#define AES_WRAPPED_PCKT_KEY_LEN 32
enum IPMATCHTYPE {
IMT_ADDRESS_AND_PORT,
IMT_ADDRESS_ONLY
};
namespace network
{
namespace udp
{
#if defined(HAVE_SENDMSG) && !defined(HAVE_SENDMMSG)
/* For `sendmmsg'. */
struct mmsghdr {
struct msghdr msg_hdr; /* Actual message header. */
unsigned int msg_len; /* Number of received or sent bytes for the entry. */
};
/* Send a VLEN messages as described by VMESSAGES to socket FD.
Returns the number of datagrams successfully written or -1 for errors.
This function is a cancellation point and therefore not marked with
__THROW. */
static inline int sendmmsg(int sockfd, struct mmsghdr* msgvec, unsigned int vlen, int flags)
{
ssize_t n = 0;
for (unsigned int i = 0; i < vlen; i++) {
ssize_t ret = sendmsg(sockfd, &msgvec[i].msg_hdr, flags);
if (ret < 0)
break;
n += ret;
}
if (n == 0)
return -1;
return int(n);
}
#endif
// ---------------------------------------------------------------------------
// Structure Declaration
// This structure represents a container for a network buffer.
// ---------------------------------------------------------------------------
struct UDPDatagram {
uint8_t* buffer;
size_t length;
sockaddr_storage address;
uint32_t addrLen;
};
/* Vector of buffers that contain a full frames */
typedef std::vector<UDPDatagram*> BufferVector;
// ---------------------------------------------------------------------------
// Class Declaration
// This class implements low-level routines to communicate over a UDP
// network socket.
// ---------------------------------------------------------------------------
class HOST_SW_API Socket {
public:
auto operator=(Socket&) -> Socket& = delete;
auto operator=(Socket&&) -> Socket& = delete;
Socket(Socket&) = delete;
/// <summary>Initializes a new instance of the Socket class.</summary>
Socket(const std::string& address, uint16_t port = 0U);
/// <summary>Initializes a new instance of the Socket class.</summary>
Socket(uint16_t port = 0U);
/// <summary>Finalizes a instance of the Socket class.</summary>
virtual ~Socket();
/// <summary>Opens UDP socket connection.</summary>
bool open(const sockaddr_storage& address) noexcept;
/// <summary>Opens UDP socket connection.</summary>
bool open(uint32_t af = AF_UNSPEC) noexcept;
/// <summary>Opens UDP socket connection.</summary>
bool open(const uint32_t af, const std::string& address, const uint16_t port) noexcept;
/// <summary>Closes the UDP socket connection.</summary>
void close();
/// <summary>Read data from the UDP socket.</summary>
virtual ssize_t read(uint8_t* buffer, uint32_t length, sockaddr_storage& address, uint32_t& addrLen) noexcept;
/// <summary>Write data to the UDP socket.</summary>
virtual bool write(const uint8_t* buffer, uint32_t length, const sockaddr_storage& address, uint32_t addrLen, ssize_t* lenWritten = nullptr) noexcept;
/// <summary>Write data to the UDP socket.</summary>
virtual bool write(BufferVector& buffers, ssize_t* lenWritten = nullptr) noexcept;
/// <summary>Sets the preshared encryption key.</summary>
void setPresharedKey(const uint8_t* presharedKey);
/// <summary>Helper to lookup a hostname and resolve it to an IP address.</summary>
static int lookup(const std::string& hostName, uint16_t port, sockaddr_storage& address, uint32_t& addrLen);
/// <summary>Helper to lookup a hostname and resolve it to an IP address.</summary>
static int lookup(const std::string& hostName, uint16_t port, sockaddr_storage& address, uint32_t& addrLen, struct addrinfo& hints);
/// <summary></summary>
static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT);
/// <summary></summary>
static uint32_t addr(const sockaddr_storage& addr);
/// <summary></summary>
static std::string address(const sockaddr_storage& addr);
/// <summary></summary>
static uint16_t port(const sockaddr_storage& addr);
/// <summary></summary>
static bool isNone(const sockaddr_storage& addr);
private:
std::string m_localAddress;
uint16_t m_localPort;
uint32_t m_af;
int m_fd;
crypto::AES* m_aes;
bool m_isCryptoWrapped;
uint8_t* m_presharedKey;
uint32_t m_counter;
/// <summary></summary>
bool initSocket(const int domain, const int type, const int protocol);
/// <summary></summary>
bool bind(const std::string& ipAddr, const uint16_t port);
/// <summary>Initialize the sockaddr_in structure with the provided IP and port.</summary>
static void initAddr(const std::string& ipAddr, const int port, sockaddr_in& addr);
};
} // namespace udp
} // namespace network
#endif // __UDP_SOCKET_H__

@ -11,7 +11,7 @@
*
*/
#include "Defines.h"
#include "common/network/UDPSocket.h"
#include "common/network/udp/Socket.h"
#include "common/Log.h"
#include "common/StopWatch.h"
#include "common/Thread.h"

@ -237,7 +237,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
@ -254,7 +254,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
@ -271,7 +271,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
@ -477,7 +477,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
@ -496,7 +496,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
@ -527,7 +527,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
@ -548,7 +548,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
@ -571,7 +571,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
@ -604,7 +604,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
lookups::AffiliationLookup* aff = m_peerAffiliations[peerId];
// validate peer (simple validation really)
@ -624,7 +624,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
lookups::AffiliationLookup* aff = m_peerAffiliations[peerId];
// validate peer (simple validation really)
@ -642,7 +642,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
lookups::AffiliationLookup* aff = m_peerAffiliations[peerId];
// validate peer (simple validation really)
@ -660,7 +660,7 @@ void FNENetwork::clock(uint32_t ms)
if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) {
FNEPeerConnection* connection = m_peers[peerId];
if (connection != nullptr) {
std::string ip = UDPSocket::address(address);
std::string ip = udp::Socket::address(address);
// validate peer (simple validation really)
if (connection->connected() && connection->address() == ip) {
@ -728,7 +728,7 @@ bool FNENetwork::open()
m_status = NET_STAT_MST_RUNNING;
m_maintainenceTimer.start();
m_socket = new UDPSocket(m_address, m_port);
m_socket = new udp::Socket(m_address, m_port);
// reinitialize the frame queue
if (m_frameQueue != nullptr) {

@ -76,8 +76,8 @@ namespace network
m_currStreamId(0U),
m_socketStorage(socketStorage),
m_sockStorageLen(sockStorageLen),
m_address(UDPSocket::address(socketStorage)),
m_port(UDPSocket::port(socketStorage)),
m_address(udp::Socket::address(socketStorage)),
m_port(udp::Socket::port(socketStorage)),
m_salt(0U),
m_connected(false),
m_connectionState(NET_STAT_INVALID),

@ -14,7 +14,6 @@
#define __REST_API_H__
#include "fne/Defines.h"
#include "common/network/UDPSocket.h"
#include "common/network/rest/RequestDispatcher.h"
#include "common/network/rest/http/HTTPServer.h"
#include "common/lookups/RadioIdLookup.h"

@ -11,7 +11,7 @@
*
*/
#include "Defines.h"
#include "common/network/UDPSocket.h"
#include "common/network/udp/Socket.h"
#include "modem/port/ModemNullPort.h"
#include "modem/port/UARTPort.h"
#include "modem/port/PseudoPTYPort.h"

@ -47,11 +47,11 @@ UDPPort::UDPPort(const std::string& address, uint16_t modemPort) :
assert(!address.empty());
assert(modemPort > 0U);
if (UDPSocket::lookup(address, modemPort, m_addr, m_addrLen) != 0)
if (udp::Socket::lookup(address, modemPort, m_addr, m_addrLen) != 0)
m_addrLen = 0U;
if (m_addrLen > 0U) {
std::string addrStr = UDPSocket::address(m_addr);
std::string addrStr = udp::Socket::address(m_addr);
LogWarning(LOG_HOST, "SECURITY: Remote modem expects IP address; %s for remote modem control", addrStr.c_str());
}
}
@ -99,11 +99,11 @@ int UDPPort::read(uint8_t* buffer, uint32_t length)
// Add new data to the ring buffer
if (ret > 0) {
if (UDPSocket::match(addr, m_addr)) {
if (udp::Socket::match(addr, m_addr)) {
m_buffer.addData(data, ret);
}
else {
std::string addrStr = UDPSocket::address(addr);
std::string addrStr = udp::Socket::address(addr);
LogWarning(LOG_HOST, "SECURITY: Remote modem mode encountered invalid IP address; %s", addrStr.c_str());
}
}

@ -16,7 +16,7 @@
#define __UDP_PORT_H__
#include "Defines.h"
#include "common/network/UDPSocket.h"
#include "common/network/udp/Socket.h"
#include "common/RingBuffer.h"
#include "modem/port/IModemPort.h"
@ -50,7 +50,7 @@ namespace modem
void close() override;
protected:
network::UDPSocket m_socket;
network::udp::Socket m_socket;
sockaddr_storage m_addr;
uint32_t m_addrLen;

@ -255,7 +255,7 @@ void Network::clock(uint32_t ms)
// read message
UInt8Array buffer = m_frameQueue->read(length, address, addrLen, &rtpHeader, &fneHeader);
if (length > 0) {
if (!UDPSocket::match(m_addr, address)) {
if (!udp::Socket::match(m_addr, address)) {
LogError(LOG_NET, "Packet received from an invalid source");
return;
}
@ -580,7 +580,7 @@ bool Network::open()
if (m_debug)
LogMessage(LOG_NET, "Opening Network");
if (UDPSocket::lookup(m_address, m_port, m_addr, m_addrLen) != 0) {
if (udp::Socket::lookup(m_address, m_port, m_addr, m_addrLen) != 0) {
LogMessage(LOG_NET, "Could not lookup the address of the master");
return false;
}

@ -14,7 +14,6 @@
#define __REST_API_H__
#include "Defines.h"
#include "common/network/UDPSocket.h"
#include "common/network/rest/RequestDispatcher.h"
#include "common/network/rest/http/HTTPServer.h"
#include "common/lookups/RadioIdLookup.h"

Loading…
Cancel
Save

Powered by TurnKey Linux.