diff --git a/.vscode/settings.json b/.vscode/settings.json index 5c9c832..42e5956 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -72,7 +72,8 @@ "thread": "cpp", "typeindex": "cpp", "variant": "cpp", - "iostream": "cpp" + "iostream": "cpp", + "fstream": "cpp" }, "editor.tokenColorCustomizations": { "textMateRules": [ diff --git a/AMBEData.cpp b/AMBEData.cpp index 1c2bf87..2cef8c0 100644 --- a/AMBEData.cpp +++ b/AMBEData.cpp @@ -24,6 +24,7 @@ #include "AMBEData.h" #include "DStarDefines.h" #include "Utils.h" +#include "NetUtils.h" CAMBEData::CAMBEData() : m_rptSeq(0U), @@ -582,6 +583,17 @@ unsigned int CAMBEData::getMyPort() const return m_myPort; } +struct sockaddr_storage CAMBEData::getDestination() const +{ + struct sockaddr_storage dest; + ::memset(&dest, 0, sizeof(sockaddr_storage)); + dest.ss_family = AF_INET; + TOIPV4(dest)->sin_addr = m_yourAddress; + TOIPV4(dest)->sin_port = htons(m_yourPort); + + return dest; +} + CHeaderData& CAMBEData::getHeader() { return m_header; diff --git a/AMBEData.h b/AMBEData.h index 8282852..695a5c2 100644 --- a/AMBEData.h +++ b/AMBEData.h @@ -77,6 +77,7 @@ public: in_addr getYourAddress() const; unsigned int getYourPort() const; + struct sockaddr_storage getDestination() const; unsigned int getMyPort() const; unsigned int getErrors() const; diff --git a/DStarGatewayThread.cpp b/DStarGatewayThread.cpp index 6b8d810..693075d 100644 --- a/DStarGatewayThread.cpp +++ b/DStarGatewayThread.cpp @@ -75,7 +75,7 @@ m_dummyRepeaterHandler(NULL), m_dextraPool(NULL), m_dplusPool(NULL), m_dcsPool(NULL), -m_g2Handler(NULL), +m_g2HandlerPool(NULL), m_aprsWriter(NULL), m_irc(NULL), m_cache(), @@ -198,16 +198,16 @@ void* CDStarGatewayThread::Entry() CLog::logError("Failed to allocate incoming DCS handler\n"); } - m_g2Handler = new CG2ProtocolHandler(G2_DV_PORT, m_gatewayAddress); - bool ret = m_g2Handler->open(); + m_g2HandlerPool = new CG2ProtocolHandlerPool(G2_DV_PORT, m_gatewayAddress); + bool ret = m_g2HandlerPool->open(); if (!ret) { CLog::logError("Could not open the G2 protocol handler"); - delete m_g2Handler; - m_g2Handler = NULL; + delete m_g2HandlerPool; + m_g2HandlerPool = NULL; } // Wait here until we have the essentials to run - while (!m_killed && (m_dextraPool == NULL || m_dplusPool == NULL || m_dcsPool == NULL || m_g2Handler == NULL || (m_icomRepeaterHandler == NULL && m_hbRepeaterHandler == NULL && m_dummyRepeaterHandler == NULL) || m_gatewayCallsign.empty())) + while (!m_killed && (m_dextraPool == NULL || m_dplusPool == NULL || m_dcsPool == NULL || m_g2HandlerPool == NULL || (m_icomRepeaterHandler == NULL && m_hbRepeaterHandler == NULL && m_dummyRepeaterHandler == NULL) || m_gatewayCallsign.empty())) ::std::this_thread::sleep_for(std::chrono::milliseconds(500UL)); // 1/2 sec if (m_killed) @@ -232,7 +232,7 @@ void* CDStarGatewayThread::Entry() loadGateways(); loadAllReflectors(); - CG2Handler::setG2ProtocolHandler(m_g2Handler); + CG2Handler::setG2ProtocolHandlerPool(m_g2HandlerPool); CG2Handler::setHeaderLogger(headerLogger); CDExtraHandler::setCallsign(m_gatewayCallsign); @@ -250,7 +250,7 @@ void* CDStarGatewayThread::Entry() CDCSHandler::setHeaderLogger(headerLogger); CRepeaterHandler::setLocalAddress(m_gatewayAddress); - CRepeaterHandler::setG2Handler(m_g2Handler); + CRepeaterHandler::setG2HandlerPool(m_g2HandlerPool); if (m_irc != NULL) CRepeaterHandler::setIRC(m_irc); @@ -284,7 +284,7 @@ void* CDStarGatewayThread::Entry() #ifdef USE_STARNET CStarNetHandler::setCache(&m_cache); CStarNetHandler::setGateway(m_gatewayCallsign); - CStarNetHandler::setG2Handler(m_g2Handler); + CStarNetHandler::setG2HandlerPool(m_g2HandlerPool); if (m_irc != NULL) CStarNetHandler::setIRC(m_irc); @@ -453,8 +453,8 @@ void* CDStarGatewayThread::Entry() m_dcsPool->close(); delete m_dcsPool; - m_g2Handler->close(); - delete m_g2Handler; + m_g2HandlerPool->close(); + delete m_g2HandlerPool; if (m_irc != NULL) { m_irc->close(); @@ -788,9 +788,9 @@ void CDStarGatewayThread::processIrcDDB() if(!res) return; - if(m_g2Handler != nullptr) { + if(m_g2HandlerPool != nullptr) { CLog::logInfo("%s wants to G2 route to us, punching UDP Holes through NAT", address.c_str()); - m_g2Handler->traverseNat(address); + m_g2HandlerPool->traverseNat(address); } else { CLog::logInfo("%s wants to G2 route to us, but G2 is disabled", address.c_str()); @@ -1096,13 +1096,13 @@ void CDStarGatewayThread::processDCS() void CDStarGatewayThread::processG2() { for (;;) { - G2_TYPE type = m_g2Handler->read(); + G2_TYPE type = m_g2HandlerPool->read(); switch (type) { case GT_HEADER: { - CHeaderData* header = m_g2Handler->readHeader(); + CHeaderData* header = m_g2HandlerPool->readHeader(); if (header != NULL) { - // CLog::logInfo("G2 header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); + CLog::logDebug("G2 header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3()); CG2Handler::process(*header); delete header; } @@ -1110,7 +1110,7 @@ void CDStarGatewayThread::processG2() break; case GT_AMBE: { - CAMBEData* data = m_g2Handler->readAMBE(); + CAMBEData* data = m_g2HandlerPool->readAMBE(); if (data != NULL) { CG2Handler::process(*data); delete data; diff --git a/DStarGatewayThread.h b/DStarGatewayThread.h index e81aaf1..5b4fe3c 100644 --- a/DStarGatewayThread.h +++ b/DStarGatewayThread.h @@ -28,7 +28,7 @@ #include "RepeaterProtocolHandler.h" #include "DStarGatewayStatusData.h" #include "DCSProtocolHandlerPool.h" -#include "G2ProtocolHandler.h" +#include "G2ProtocolHandlerPool.h" #include "RemoteHandler.h" #include "CacheManager.h" #include "CallsignList.h" @@ -102,7 +102,7 @@ private: CDExtraProtocolHandlerPool* m_dextraPool; CDPlusProtocolHandlerPool* m_dplusPool; CDCSProtocolHandlerPool* m_dcsPool; - CG2ProtocolHandler* m_g2Handler; + CG2ProtocolHandlerPool* m_g2HandlerPool; CAPRSHandler* m_aprsWriter; CIRCDDB* m_irc; CCacheManager m_cache; diff --git a/G2Handler.cpp b/G2Handler.cpp index 8cbede5..1aaf362 100644 --- a/G2Handler.cpp +++ b/G2Handler.cpp @@ -31,7 +31,7 @@ unsigned int CG2Handler::m_maxRoutes = 0U; CG2Handler** CG2Handler::m_routes = NULL; -CG2ProtocolHandler* CG2Handler::m_handler = NULL; +CG2ProtocolHandlerPool* CG2Handler::m_handler = NULL; CHeaderLogger* CG2Handler::m_headerLogger = NULL; @@ -60,7 +60,7 @@ void CG2Handler::initialise(unsigned int maxRoutes) m_routes[i] = NULL; } -void CG2Handler::setG2ProtocolHandler(CG2ProtocolHandler* handler) +void CG2Handler::setG2ProtocolHandlerPool(CG2ProtocolHandlerPool* handler) { assert(handler != NULL); @@ -101,16 +101,7 @@ void CG2Handler::process(CHeaderData& header) in_addr address = header.getYourAddress(); unsigned int id = header.getId(); - - for (unsigned int i = 0U; i < m_maxRoutes; i++) { - CG2Handler* route = m_routes[i]; - if (route != NULL) { - // Is this a duplicate header, ignore it - if (route->m_id == id) - return; - } - } - + // Find the destination repeater CRepeaterHandler* repeater = CRepeaterHandler::findDVRepeater(header.getRptCall2()); if (repeater == NULL) { @@ -175,6 +166,8 @@ void CG2Handler::process(CAMBEData& data) void CG2Handler::clock(unsigned int ms) { + m_handler->clock(ms); + for (unsigned int i = 0U; i < m_maxRoutes; i++) { CG2Handler* route = m_routes[i]; if (route != NULL) { diff --git a/G2Handler.h b/G2Handler.h index d2b14cd..89f3291 100644 --- a/G2Handler.h +++ b/G2Handler.h @@ -22,7 +22,7 @@ #include -#include "G2ProtocolHandler.h" +#include "G2ProtocolHandlerPool.h" #include "RepeaterHandler.h" #include "DStarDefines.h" #include "HeaderLogger.h" @@ -34,7 +34,7 @@ class CG2Handler { public: static void initialise(unsigned int maxRoutes); - static void setG2ProtocolHandler(CG2ProtocolHandler* handler); + static void setG2ProtocolHandlerPool(CG2ProtocolHandlerPool* handler); static void setHeaderLogger(CHeaderLogger* logger); static void process(CHeaderData& header); @@ -54,7 +54,7 @@ private: static unsigned int m_maxRoutes; static CG2Handler** m_routes; - static CG2ProtocolHandler* m_handler; + static CG2ProtocolHandlerPool* m_handler; static CHeaderLogger* m_headerLogger; diff --git a/G2ProtocolHandler.cpp b/G2ProtocolHandler.cpp index 60e1b30..5592acd 100644 --- a/G2ProtocolHandler.cpp +++ b/G2ProtocolHandler.cpp @@ -18,6 +18,7 @@ */ #include +#include #include "G2ProtocolHandler.h" #include "Utils.h" @@ -27,30 +28,28 @@ const unsigned int BUFFER_LENGTH = 255U; -CG2ProtocolHandler::CG2ProtocolHandler(unsigned int port, const std::string& addr) : -m_socket(addr, port), +CG2ProtocolHandler::CG2ProtocolHandler(CUDPReaderWriter* socket, const struct sockaddr_storage& destination, unsigned int bufferSize) : +m_socket(socket), m_type(GT_NONE), -m_buffer(NULL), +m_buffer(nullptr), m_length(0U), -m_address(), -m_port(0U) +m_address(destination), +m_inactivityTimer(1000U, 29U), +m_id(0U) { - m_buffer = new unsigned char[BUFFER_LENGTH]; + m_inactivityTimer.start(); + m_buffer = new unsigned char[bufferSize]; + ::memset(m_buffer, 0, bufferSize); } CG2ProtocolHandler::~CG2ProtocolHandler() { delete[] m_buffer; - m_portmap.clear(); -} - -bool CG2ProtocolHandler::open() -{ - return m_socket.open(); } bool CG2ProtocolHandler::writeHeader(const CHeaderData& header) { + m_inactivityTimer.start(); unsigned char buffer[60U]; unsigned int length = header.getG2Data(buffer, 60U, true); @@ -58,12 +57,12 @@ bool CG2ProtocolHandler::writeHeader(const CHeaderData& header) CUtils::dump("Sending Header", buffer, length); #endif - in_addr addr = header.getYourAddress(); - auto found = m_portmap.find(addr.s_addr); - unsigned int port = (m_portmap.end()==found) ? header.getYourPort() : found->second; + assert(CNetUtils::match(header.getDestination(), m_address, IMT_ADDRESS_ONLY)); + + //CLog::logTrace("Write header to %s:%u", inet_ntoa(addr), ntohs(TOIPV4(m_address)->sin_port)); for (unsigned int i = 0U; i < 5U; i++) { - bool res = m_socket.write(buffer, length, addr, port); + bool res = m_socket->write(buffer, length, m_address); if (!res) return false; } @@ -73,6 +72,7 @@ bool CG2ProtocolHandler::writeHeader(const CHeaderData& header) bool CG2ProtocolHandler::writeAMBE(const CAMBEData& data) { + m_inactivityTimer.start(); unsigned char buffer[40U]; unsigned int length = data.getG2Data(buffer, 40U); @@ -80,58 +80,34 @@ bool CG2ProtocolHandler::writeAMBE(const CAMBEData& data) CUtils::dump("Sending Data", buffer, length); #endif - in_addr addr = data.getYourAddress(); - auto found = m_portmap.find(addr.s_addr); - unsigned int port = (m_portmap.end()==found) ? data.getYourPort() : found->second; - - return m_socket.write(buffer, length, addr, port); + assert(CNetUtils::match(data.getDestination(), m_address, IMT_ADDRESS_ONLY)); + //CLog::logTrace("Write ambe to %s:%u", inet_ntoa(addr), ntohs(TOIPV4(m_address)->sin_port)); + return m_socket->write(buffer, length, m_address); } -G2_TYPE CG2ProtocolHandler::read() +bool CG2ProtocolHandler::setBuffer(unsigned char * buffer, int length) { - bool res = true; - - // Loop until we have no more data from the socket or we have data for the higher layers - while (res) - res = readPackets(); - - return m_type; -} + assert(buffer != nullptr); -bool CG2ProtocolHandler::readPackets() -{ m_type = GT_NONE; + ::memcpy(m_buffer, buffer, length); - // No more data? - int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_address, m_port); - if (length <= 0) + if(length <= 0) return false; - if(length == 1) { - CLog::logDebug("G2 Nat traversal packet received"); - } - m_length = length; - // save the incoming port (this is to enable mobile hotspots) - if (m_portmap.end() == m_portmap.find(m_address.s_addr)) { - CLog::logInfo("G2 new address %s on port %u\n", inet_ntoa(m_address), m_port); - m_portmap[m_address.s_addr] = m_port; - } else { - if (m_portmap[m_address.s_addr] != m_port) { - CLog::logInfo("G2 new port for %s is %u, was %u\n", inet_ntoa(m_address), m_port, m_portmap[m_address.s_addr]); - m_portmap[m_address.s_addr] = m_port; - } - } - if (m_buffer[0] != 'D' || m_buffer[1] != 'S' || m_buffer[2] != 'V' || m_buffer[3] != 'T') { + CLog::logTrace("DSVT"); return true; } else { // Header or data packet type? - if ((m_buffer[14] & 0x80) == 0x80) + if ((m_buffer[14] & 0x80) == 0x80) { m_type = GT_HEADER; - else + } + else { m_type = GT_AMBE; + } return false; } @@ -139,49 +115,43 @@ bool CG2ProtocolHandler::readPackets() CHeaderData* CG2ProtocolHandler::readHeader() { - if (m_type != GT_HEADER) - return NULL; + m_inactivityTimer.start(); + if (m_type != GT_HEADER || m_id != 0U) + return nullptr; + m_type = GT_NONE; // Header data has been consumed, reset our status CHeaderData* header = new CHeaderData; // G2 checksums are unreliable - bool res = header->setG2Data(m_buffer, m_length, false, m_address, m_port); + bool res = header->setG2Data(m_buffer, m_length, false, TOIPV4(m_address)->sin_addr, ntohs(GETPORT(m_address))); if (!res) { delete header; - return NULL; + return nullptr; } + m_id = header->getId();// remember the id so we do not read it duplicate + return header; } CAMBEData* CG2ProtocolHandler::readAMBE() { + m_inactivityTimer.start(); if (m_type != GT_AMBE) return NULL; + m_type = GT_NONE; // Ambe data has been consumed, reset our status CAMBEData* data = new CAMBEData; - bool res = data->setG2Data(m_buffer, m_length, m_address, m_port); + bool res = data->setG2Data(m_buffer, m_length, TOIPV4(m_address)->sin_addr, ntohs(GETPORT(m_address))); if (!res) { delete data; return NULL; } - return data; -} + if(data->isEnd()) + m_id = 0U; -void CG2ProtocolHandler::close() -{ - m_socket.close(); + return data; } -void CG2ProtocolHandler::traverseNat(const std::string& address) -{ - unsigned char buffer = 0x00U; - - in_addr addr = CUDPReaderWriter::lookup(address); - - CLog::logInfo("G2 Punching hole to %s", address.c_str()); - - m_socket.write(&buffer, 1U, addr, G2_DV_PORT); -} diff --git a/G2ProtocolHandler.h b/G2ProtocolHandler.h index 14eb0b3..92b42a0 100644 --- a/G2ProtocolHandler.h +++ b/G2ProtocolHandler.h @@ -20,11 +20,14 @@ #pragma once #include +#include #include "UDPReaderWriter.h" #include "DStarDefines.h" #include "HeaderData.h" #include "AMBEData.h" +#include "NetUtils.h" +#include "Timer.h" enum G2_TYPE { GT_NONE, @@ -34,7 +37,7 @@ enum G2_TYPE { class CG2ProtocolHandler { public: - CG2ProtocolHandler(unsigned int port, const std::string& addr = std::string("")); + CG2ProtocolHandler(CUDPReaderWriter* socket, const struct sockaddr_storage& destination, unsigned int bufferSize); ~CG2ProtocolHandler(); bool open(); @@ -42,22 +45,25 @@ public: bool writeHeader(const CHeaderData& header); bool writeAMBE(const CAMBEData& data); - G2_TYPE read(); CHeaderData* readHeader(); CAMBEData* readAMBE(); - void close(); - void traverseNat(const std::string& address); + struct sockaddr_storage getDestination() { return m_address; } + G2_TYPE getType() { return m_type; } -private: - std::unordered_map m_portmap; + bool setBuffer(unsigned char * buffer, int length); + + void clock(unsigned int ms) { m_inactivityTimer.clock(ms); } + bool isInactive() { return m_inactivityTimer.hasExpired(); } - CUDPReaderWriter m_socket; +private: + CUDPReaderWriter * m_socket; G2_TYPE m_type; unsigned char* m_buffer; unsigned int m_length; - in_addr m_address; - unsigned int m_port; + struct sockaddr_storage m_address; + CTimer m_inactivityTimer; + unsigned int m_id; bool readPackets(); }; diff --git a/G2ProtocolHandlerPool.cpp b/G2ProtocolHandlerPool.cpp new file mode 100644 index 0000000..7db90d8 --- /dev/null +++ b/G2ProtocolHandlerPool.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2021-2022 by Geoffrey Merck F4FXL / KC3FRA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include "Log.h" +#include "G2ProtocolHandlerPool.h" + +const unsigned int G2_BUFFER_LENGTH = 255U; + +CG2ProtocolHandlerPool::CG2ProtocolHandlerPool(unsigned short port, const std::string& address) : +m_address(address), +m_basePort(port), +m_socket(address, port) +{ + assert(port > 0U); + m_index = m_pool.end(); +} + +CG2ProtocolHandlerPool::~CG2ProtocolHandlerPool() +{ + +} + +bool CG2ProtocolHandlerPool::open() +{ + bool res = m_socket.open(); + return res; +} + +void CG2ProtocolHandlerPool::close() +{ + for(auto handler : m_pool) { + delete handler; + } + m_pool.clear(); + m_index = m_pool.end(); + m_socket.close(); +} + +G2_TYPE CG2ProtocolHandlerPool::read() +{ + bool res = true; + while(res) + res = readPackets(); + + if(m_index == m_pool.end()) + m_index = m_pool.begin(); + + while(m_index != m_pool.end()) { + if((*m_index)->getType() != GT_NONE) { + return (*m_index)->getType(); + } + m_index++; + } + + return GT_NONE; +} + +CAMBEData * CG2ProtocolHandlerPool::readAMBE() +{ + if(m_index == m_pool.end() || (*m_index)->getType() != GT_AMBE) + return nullptr; + + return (*m_index)->readAMBE(); +} + +CHeaderData * CG2ProtocolHandlerPool::readHeader() +{ + if(m_index == m_pool.end() || (*m_index)->getType() != GT_HEADER) + return nullptr; + + return (*m_index)->readHeader(); +} + +bool CG2ProtocolHandlerPool::readPackets() +{ + unsigned char buffer[G2_BUFFER_LENGTH]; + struct sockaddr_storage addr; + ::memset(&addr, 0, sizeof(sockaddr_storage)); + + // No more data? + int length = m_socket.read(buffer, G2_BUFFER_LENGTH, addr); + if(length <= 0) return false; + + if(length == 1 && buffer[0] == 0U) { + CLog::logDebug("G2 Nat traversal packet received"); + } + + CG2ProtocolHandler * handler = findHandler(addr, IMT_ADDRESS_AND_PORT); + if(handler == nullptr) { + CLog::logTrace("new incoming G2 %s:%u", inet_ntoa(TOIPV4(addr)->sin_addr), ntohs(TOIPV4(addr)->sin_port)); + handler = new CG2ProtocolHandler(&m_socket, addr, G2_BUFFER_LENGTH); + m_pool.push_back(handler); + m_index = m_pool.end(); + } + + bool res = handler->setBuffer(buffer, length); + return res; +} + +void CG2ProtocolHandlerPool::traverseNat(const std::string& address) +{ + unsigned char buffer = 0x00U; + + in_addr addr = CUDPReaderWriter::lookup(address); + + CLog::logInfo("G2 Punching hole to %s", address.c_str()); + + m_socket.write(&buffer, 1U, addr, G2_DV_PORT); +} + +bool CG2ProtocolHandlerPool::writeHeader(const CHeaderData& header) +{ + auto handler = findHandler(header.getDestination(), IMT_ADDRESS_AND_PORT); + if(handler == nullptr) + handler = findHandler(header.getDestination(), IMT_ADDRESS_ONLY); + + if(handler == nullptr) { + handler = new CG2ProtocolHandler(&m_socket, header.getDestination(), G2_BUFFER_LENGTH); + m_pool.push_back(handler); + m_index = m_pool.end(); + } + return handler->writeHeader(header); +} + +bool CG2ProtocolHandlerPool::writeAMBE(const CAMBEData& data) +{ + auto handler = findHandler(data.getDestination(), IMT_ADDRESS_AND_PORT); + if(handler == nullptr) + handler = findHandler(data.getDestination(), IMT_ADDRESS_ONLY); + + if(handler == nullptr) { + handler = new CG2ProtocolHandler(&m_socket, data.getDestination(), G2_BUFFER_LENGTH); + m_pool.push_back(handler); + m_index = m_pool.end(); + } + + return handler->writeAMBE(data); +} + +CG2ProtocolHandler * CG2ProtocolHandlerPool::findHandler(const struct sockaddr_storage& addr, IPMATCHTYPE matchType) const +{ + for(auto handler : m_pool) { + if(handler != nullptr && CNetUtils::match(addr, handler->getDestination(), matchType)) + return handler; + } + + return nullptr; +} + +CG2ProtocolHandler * CG2ProtocolHandlerPool::findHandler(in_addr addr, unsigned int port, IPMATCHTYPE matchType) const +{ + struct sockaddr_storage addrStorage; + addrStorage.ss_family = AF_INET; + TOIPV4(addrStorage)->sin_addr = addr; + TOIPV4(addrStorage)->sin_port = port; + + return findHandler(addrStorage, matchType); +} + +void CG2ProtocolHandlerPool::clock(unsigned int ms) +{ + for(auto it = m_pool.begin(); it != m_pool.end();) { + (*it)->clock(ms); + if((*it)->isInactive()) { + delete (*it); + it = m_pool.erase(it); + m_index = m_pool.end(); + } + else { + it++; + } + } +} \ No newline at end of file diff --git a/G2ProtocolHandlerPool.h b/G2ProtocolHandlerPool.h new file mode 100644 index 0000000..9926fc3 --- /dev/null +++ b/G2ProtocolHandlerPool.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021-2022 by Geoffrey Merck F4FXL / KC3FRA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#pragma once + +#include +#include +#include +#include + +#include "G2ProtocolHandler.h" +#include "NetUtils.h" + +struct sockaddr_storage_map { + struct compAddrAndPort { + bool operator() (const struct sockaddr_storage& a, const struct sockaddr_storage& b) const { + return CNetUtils::match(a, b, IMT_ADDRESS_AND_PORT); + } + }; + struct hash { + std::size_t operator() (const sockaddr_storage& a) const { + switch(a.ss_family) + { + case AF_INET: { + auto ptr4 = ((struct sockaddr_in *)&a); + size_t res = AF_INET; + boost::hash_combine(res, ptr4->sin_port); + boost::hash_combine(res, ptr4->sin_addr.s_addr); + return res; + } + case AF_INET6: { + auto ptr6 = ((struct sockaddr_in6 *)&a); + size_t res = AF_INET6; + boost::hash_combine(res, ptr6->sin6_port); + auto in6Ptr = (unsigned int *)&(ptr6->sin6_addr); + boost::hash_combine(res, in6Ptr[0]); + boost::hash_combine(res, in6Ptr[1]); + boost::hash_combine(res, in6Ptr[2]); + boost::hash_combine(res, in6Ptr[3]); + return res; + } + default: + return 0U; + } + } + }; +}; + +class CG2ProtocolHandlerPool +{ +public: + CG2ProtocolHandlerPool(unsigned short g2Port, const std::string& address = ""); + ~CG2ProtocolHandlerPool(); + + bool open(); + void close(); + G2_TYPE read(); + CAMBEData * readAMBE(); + CHeaderData * readHeader(); + + bool writeAMBE(const CAMBEData& data); + bool writeHeader(const CHeaderData& header); + + void traverseNat(const std::string& address); + + void clock(unsigned int ms); + +private: + bool readPackets(); + CG2ProtocolHandler * findHandler(const struct sockaddr_storage& addr, IPMATCHTYPE matchType) const; + CG2ProtocolHandler * findHandler(in_addr addr, unsigned int port, IPMATCHTYPE matchType) const; + + std::string m_address; + unsigned int m_basePort; + CUDPReaderWriter m_socket; + std::vector m_pool; + std::vector::iterator m_index; +}; \ No newline at end of file diff --git a/HeaderData.cpp b/HeaderData.cpp index b29ac0a..6747571 100644 --- a/HeaderData.cpp +++ b/HeaderData.cpp @@ -23,7 +23,7 @@ #include #include #include "HeaderData.h" - +#include "NetUtils.h" #include "CCITTChecksum.h" #include "DStarDefines.h" #include "Utils.h" @@ -922,6 +922,17 @@ unsigned int CHeaderData::getYourPort() const return m_yourPort; } +struct sockaddr_storage CHeaderData::getDestination() const +{ + struct sockaddr_storage dest; + ::memset(&dest, 0, sizeof(sockaddr_storage)); + dest.ss_family = AF_INET; + TOIPV4(dest)->sin_addr = m_yourAddress; + TOIPV4(dest)->sin_port = htons(m_yourPort); + + return dest; +} + unsigned int CHeaderData::getMyPort() const { return m_myPort; diff --git a/HeaderData.h b/HeaderData.h index c4ced7c..d004503 100644 --- a/HeaderData.h +++ b/HeaderData.h @@ -94,6 +94,7 @@ public: in_addr getYourAddress() const; unsigned int getYourPort() const; + struct sockaddr_storage getDestination() const; unsigned int getMyPort() const; unsigned int getErrors() const; diff --git a/IRCDDBApp.cpp b/IRCDDBApp.cpp index 5416d31..58909de 100644 --- a/IRCDDBApp.cpp +++ b/IRCDDBApp.cpp @@ -848,7 +848,7 @@ void IRCDDBApp::doUpdate(std::string& msg) nick = sm1[1]; if (1 == m_d->m_rptrMap.count(value)) { - CLog::logTrace("doUptate RPTR already present"); + // CLog::logTrace("doUptate RPTR already present"); IRCDDBAppRptrObject o = m_d->m_rptrMap[value]; zonerp_cs = o.m_zonerp_cs; CUtils::ReplaceChar(zonerp_cs, '_', ' '); @@ -857,7 +857,7 @@ void IRCDDBApp::doUpdate(std::string& msg) zonerp_cs.push_back('G'); } else { - CLog::logTrace("doUptate RPTR not present"); + // CLog::logTrace("doUptate RPTR not present"); zonerp_cs = arearp_cs.substr(0, arearp_cs.length() - 1U); ip_addr = nick.empty() ? getIPAddressFromCall(zonerp_cs) : getIPAddressFromNick(nick); zonerp_cs.push_back('G'); diff --git a/Makefile b/Makefile index 0174975..86bc984 100644 --- a/Makefile +++ b/Makefile @@ -137,9 +137,8 @@ removehostfiles : .PHONY tests: tests : GitVersion.h -# remove these to force tests makefile to rebuild them with -DUNIT_TESTS, otherwise we end up with 2 mains - @$(RM) -f DStarGatewayApp.o - @$(RM) -f DStarGatewayApp.d +# force tests makefile to rebuild them with -DUNIT_TESTS, otherwise we end up with 2 mains + @touch DStarGatewayApp.cpp @$(MAKE) -C Tests dstargateway_tests .PHONY run-tests: diff --git a/NetUtils.cpp b/NetUtils.cpp new file mode 100644 index 0000000..e0d3ac9 --- /dev/null +++ b/NetUtils.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2022 by Geoffrey Merck F4FXL / KC3FRA + * Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include "NetUtils.h" + +bool CNetUtils::lookupV4(const std::string& hostname, sockaddr_storage& addr) +{ + struct addrinfo hints; + ::memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + + return lookup(hostname, addr, hints); +} + +bool CNetUtils::lookupV6(const std::string& hostname, sockaddr_storage& addr) +{ + struct addrinfo hints; + ::memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + + return lookup(hostname, addr, hints); +} + +bool CNetUtils::lookup(const std::string& hostname, sockaddr_storage& addr) +{ + struct addrinfo hints; + ::memset(&hints, 0, sizeof(hints)); + return lookup(hostname, addr, hints); +} + +bool CNetUtils::lookup(const std::string& hostname, sockaddr_storage& addr, struct addrinfo& hints) +{ + struct addrinfo *res; + + int err = getaddrinfo(hostname.c_str(), nullptr, &hints, &res); + if(err != 0) { + ::memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + lookup("255.255.255.255", addr, hints); + return false; + } + + ::memcpy(&addr, res->ai_addr, res->ai_addrlen); + + ::freeaddrinfo(res); + + return true; +} + +bool CNetUtils::match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type) +{ + if (addr1.ss_family != addr2.ss_family) + return false; + + if (type == IMT_ADDRESS_AND_PORT) { + switch (addr1.ss_family) { + case AF_INET: + struct sockaddr_in *in_1, *in_2; + in_1 = (struct sockaddr_in*)&addr1; + in_2 = (struct sockaddr_in*)&addr2; + return (in_1->sin_addr.s_addr == in_2->sin_addr.s_addr) && (in_1->sin_port == in_2->sin_port); + case AF_INET6: + struct sockaddr_in6 *in6_1, *in6_2; + in6_1 = (struct sockaddr_in6*)&addr1; + in6_2 = (struct sockaddr_in6*)&addr2; + return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr) && (in6_1->sin6_port == in6_2->sin6_port); + default: + return false; + } + } else if (type == IMT_ADDRESS_ONLY) { + switch (addr1.ss_family) { + case AF_INET: + struct sockaddr_in *in_1, *in_2; + in_1 = (struct sockaddr_in*)&addr1; + in_2 = (struct sockaddr_in*)&addr2; + return in_1->sin_addr.s_addr == in_2->sin_addr.s_addr; + case AF_INET6: + struct sockaddr_in6 *in6_1, *in6_2; + in6_1 = (struct sockaddr_in6*)&addr1; + in6_2 = (struct sockaddr_in6*)&addr2; + return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr); + default: + return false; + } + } else { + return false; + } +} + +void CNetUtils::setPort(struct sockaddr_storage& addr, in_port_t port) +{ + switch (addr.ss_family) + { + case AF_INET: + TOIPV4(addr)->sin_port = port; + break; + case AF_INET6: + TOIPV6(addr)->sin6_port = port; + default: + break; + } +} diff --git a/NetUtils.h b/NetUtils.h new file mode 100644 index 0000000..b5c795b --- /dev/null +++ b/NetUtils.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 by Geoffrey Merck F4FXL / KC3FRA + * Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#pragma once + +#include +#include +#include + +#define TOIPV6(s) ((struct sockaddr_in6*)&s) +#define TOIPV4(s) (((struct sockaddr_in*)&s)) +#define GETPORT(s) (s.ss_family == AF_INET6 ? TOIPV6(s)->sin6_port : TOIPV4(s)->sin_port) +#define SETPORT(s, p) (if(s.ss_family == AF_INET6)TOIPV6(s)->sin6_port = p;else TOIPV4(s)->sin_port = p;) + +enum IPMATCHTYPE { + IMT_ADDRESS_AND_PORT, + IMT_ADDRESS_ONLY +}; + +class CNetUtils +{ +public: + static bool lookupV6(const std::string& hostname, sockaddr_storage& addr); + static bool lookupV4(const std::string& hostname, sockaddr_storage& addr); + static bool lookup(const std::string& hostname, sockaddr_storage& addr); + static bool lookup(const std::string& hostname, sockaddr_storage& addr, struct addrinfo& hints); + static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type); + static void setPort(struct sockaddr_storage& addr, in_port_t port); +}; \ No newline at end of file diff --git a/README.md b/README.md index e938cb4..755b04f 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ the testing framwework used is Google Test. # 5. Version History ## 5.1. Version 0.5 +- [Bugfix] Two simultaneous incoming G2 streams would fail to be transmitted on dual band repeaters ([#16](https://github.com/F4FXL/DStarGateway/issues/16)) - [Improvement] Add NAT Traversal for G2 and DExtra, using IRCDDB as a Rendez Vous server ([#5](https://github.com/F4FXL/DStarGateway/issues/5)) - [Improvement] Add forwarding of RS-MS1A messages to APRS-IS ([#9](https://github.com/F4FXL/DStarGateway/issues/9)) - [Bugfix] Failed to download XLX Hosts when URL contains a = sign ([#14](https://github.com/F4FXL/DStarGateway/issues/14)) diff --git a/RepeaterHandler.cpp b/RepeaterHandler.cpp index b88bea2..c55ca20 100644 --- a/RepeaterHandler.cpp +++ b/RepeaterHandler.cpp @@ -45,7 +45,7 @@ unsigned int CRepeaterHandler::m_maxRepeaters = 0U; CRepeaterHandler** CRepeaterHandler::m_repeaters = NULL; std::string CRepeaterHandler::m_localAddress; -CG2ProtocolHandler* CRepeaterHandler::m_g2Handler = NULL; +CG2ProtocolHandlerPool* CRepeaterHandler::m_g2HandlerPool = NULL; CIRCDDB* CRepeaterHandler::m_irc = NULL; CCacheManager* CRepeaterHandler::m_cache = NULL; std::string CRepeaterHandler::m_gateway; @@ -301,11 +301,11 @@ void CRepeaterHandler::add(const std::string& callsign, const std::string& band, delete repeater; } -void CRepeaterHandler::setG2Handler(CG2ProtocolHandler* handler) +void CRepeaterHandler::setG2HandlerPool(CG2ProtocolHandlerPool* handler) { assert(handler != NULL); - m_g2Handler = handler; + m_g2HandlerPool = handler; } void CRepeaterHandler::setCache(CCacheManager* cache) @@ -883,7 +883,7 @@ void CRepeaterHandler::processRepeater(CAMBEData& data) case G2_OK: data.setDestination(m_g2Address, G2_DV_PORT); - m_g2Handler->writeAMBE(data); + m_g2HandlerPool->writeAMBE(data); if (data.isEnd()) { m_repeaterId = 0x00U; @@ -1283,7 +1283,7 @@ void CRepeaterHandler::resolveUserInt(const std::string& user, const std::string m_g2Header->setDestination(m_g2Address, G2_DV_PORT); m_g2Header->setRepeaters(m_g2Gateway, m_g2Repeater); - m_g2Handler->writeHeader(*m_g2Header); + m_g2HandlerPool->writeHeader(*m_g2Header); delete m_g2Header; m_g2Status = G2_OK; @@ -1315,7 +1315,7 @@ void CRepeaterHandler::resolveRepeaterInt(const std::string& repeater, const std m_g2Header->setDestination(m_g2Address, G2_DV_PORT); m_g2Header->setRepeaters(m_g2Gateway, m_g2Repeater); - m_g2Handler->writeHeader(*m_g2Header); + m_g2HandlerPool->writeHeader(*m_g2Header); delete m_g2Header; m_g2Status = G2_OK; @@ -2072,7 +2072,7 @@ void CRepeaterHandler::g2CommandHandler(const std::string& callsign, const std:: m_g2Gateway = data->getGateway(); header.setDestination(m_g2Address, G2_DV_PORT); header.setRepeaters(m_g2Gateway, m_g2Repeater); - m_g2Handler->writeHeader(header); + m_g2HandlerPool->writeHeader(header); delete data; } } else if (string_right(callsign, 1) != "L" && string_right(callsign, 1) != "U") { @@ -2115,7 +2115,7 @@ void CRepeaterHandler::g2CommandHandler(const std::string& callsign, const std:: m_g2Gateway = data->getGateway(); header.setDestination(m_g2Address, G2_DV_PORT); header.setRepeaters(m_g2Gateway, m_g2Repeater); - m_g2Handler->writeHeader(header); + m_g2HandlerPool->writeHeader(header); delete data; } diff --git a/RepeaterHandler.h b/RepeaterHandler.h index 678e383..0b29b85 100644 --- a/RepeaterHandler.h +++ b/RepeaterHandler.h @@ -28,7 +28,7 @@ #include "DExtraProtocolHandler.h" #include "DPlusProtocolHandler.h" #include "RemoteRepeaterData.h" -#include "G2ProtocolHandler.h" +#include "G2ProtocolHandlerPool.h" #include "ReflectorCallback.h" #include "RepeaterCallback.h" #include "AnnouncementUnit.h" @@ -75,7 +75,7 @@ public: #endif static void setLocalAddress(const std::string& address); - static void setG2Handler(CG2ProtocolHandler* handler); + static void setG2HandlerPool(CG2ProtocolHandlerPool* handler); static void setIRC(CIRCDDB* irc); static void setCache(CCacheManager* cache); static void setGateway(const std::string& gateway); @@ -168,7 +168,7 @@ private: static CRepeaterHandler** m_repeaters; static std::string m_localAddress; - static CG2ProtocolHandler* m_g2Handler; + static CG2ProtocolHandlerPool* m_g2HandlerPool; static CCacheManager* m_cache; static std::string m_gateway; static CIRCDDB* m_irc; diff --git a/Tests/NetUtils/lookup.cpp b/Tests/NetUtils/lookup.cpp new file mode 100644 index 0000000..f55f23c --- /dev/null +++ b/Tests/NetUtils/lookup.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021-2022 by Geoffrey Merck F4FXL / KC3FRA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include + +#include "../../NetUtils.h" + +namespace NetUtilsTests +{ + class NetUtils_lookup: public ::testing::Test { + + }; + + TEST_F(NetUtils_lookup, googleShallAlwaysSucceed) + { + sockaddr_storage addr; + struct addrinfo hints; + ::memset(&hints, 0, sizeof(hints)); + + bool res = CNetUtils::lookup("google.fr", addr, hints); + + bool familyOk = addr.ss_family == AF_INET6 || addr.ss_family == AF_INET; + EXPECT_TRUE(familyOk); + EXPECT_TRUE(res); + } + + TEST_F(NetUtils_lookup, erroneousAddress) + { + sockaddr_storage addr; + struct addrinfo hints; + ::memset(&hints, 0, sizeof(hints)); + + bool res = CNetUtils::lookup("gfilufgclqsegfuligyhfcguyhfguilfguils4df64sdw46fcq6sfgvd6f6d7f67d6f7c6sd7f6s7gfv6fc7d6f76tf.fr", addr, hints); + + EXPECT_EQ(addr.ss_family, AF_INET); + + auto ptr = (sockaddr_in*)(&addr); + + EXPECT_EQ((uint32_t)(ptr->sin_addr.s_addr), (uint32_t)INADDR_NONE); + EXPECT_FALSE(res); + } +} \ No newline at end of file diff --git a/Tests/NetUtils/lookupV4.cpp b/Tests/NetUtils/lookupV4.cpp new file mode 100644 index 0000000..5f554fb --- /dev/null +++ b/Tests/NetUtils/lookupV4.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021-2022 by Geoffrey Merck F4FXL / KC3FRA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include + +#include "../../NetUtils.h" + +namespace NetUtilsTests +{ + class NetUtils_lookupV4 : public ::testing::Test { + + }; + + TEST_F(NetUtils_lookupV4, googleShallAlwaysSucceed) + { + sockaddr_storage addr; + + bool res = CNetUtils::lookupV4("google.fr", addr); + + EXPECT_EQ(addr.ss_family, AF_INET); + EXPECT_TRUE(res); + } + + TEST_F(NetUtils_lookupV4, erroneousAddress) + { + sockaddr_storage addr; + + bool res = CNetUtils::lookupV4("gfilufgclqsegfuligyhfcguyhfguilfguils4df64sdw46fcq6sfgvd6f6d7f67d6f7c6sd7f6s7gfv6fc7d6f76tf.fr", addr); + + EXPECT_EQ(addr.ss_family, AF_INET); + + auto ptr = (sockaddr_in*)(&addr); + + EXPECT_EQ((uint32_t)(ptr->sin_addr.s_addr), (uint32_t)INADDR_NONE); + EXPECT_FALSE(res); + } + + TEST_F(NetUtils_lookupV4, addressWithNoIPV4) + { + sockaddr_storage addr; + + bool res = CNetUtils::lookupV4("ircv6.openquad.net", addr); + + EXPECT_EQ(addr.ss_family, AF_INET); + + auto ptr = (sockaddr_in*)(&addr); + + EXPECT_EQ((uint32_t)(ptr->sin_addr.s_addr), (uint32_t)INADDR_NONE); + EXPECT_FALSE(res); + } +} \ No newline at end of file diff --git a/Tests/NetUtils/lookupV6.cpp b/Tests/NetUtils/lookupV6.cpp new file mode 100644 index 0000000..1a6c9bf --- /dev/null +++ b/Tests/NetUtils/lookupV6.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021-2022 by Geoffrey Merck F4FXL / KC3FRA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include + +#include "../../NetUtils.h" + +namespace NetUtilsTests +{ + class NetUtils_lookupV6 : public ::testing::Test { + + }; + + TEST_F(NetUtils_lookupV6, googleShallAlwaysSucceed) + { + sockaddr_storage addr; + + bool res = CNetUtils::lookupV6("google.fr", addr); + + EXPECT_EQ(addr.ss_family, AF_INET6); + EXPECT_TRUE(res); + } + + TEST_F(NetUtils_lookupV6, erroneousAddress) + { + sockaddr_storage addr; + + bool res = CNetUtils::lookupV6("gfilufgclqsegfuligyhfcguyhfguilfguils4df64sdw46fcq6sfgvd6f6d7f67d6f7c6sd7f6s7gfv6fc7d6f76tf.fr", addr); + + EXPECT_EQ(addr.ss_family, AF_INET); + + auto ptr = (sockaddr_in*)(&addr); + + EXPECT_EQ((uint32_t)(ptr->sin_addr.s_addr), (uint32_t)INADDR_NONE); + EXPECT_FALSE(res); + } + + TEST_F(NetUtils_lookupV6, addressWithNoIPV6) + { + sockaddr_storage addr; + + bool res = CNetUtils::lookupV6("ircv4.openquad.net", addr); + + EXPECT_EQ(addr.ss_family, AF_INET); + + auto ptr = (sockaddr_in*)(&addr); + + EXPECT_EQ((uint32_t)(ptr->sin_addr.s_addr), (uint32_t)INADDR_NONE); + EXPECT_FALSE(res); + } +} \ No newline at end of file diff --git a/Tests/NetUtils/match.cpp b/Tests/NetUtils/match.cpp new file mode 100644 index 0000000..0f58d12 --- /dev/null +++ b/Tests/NetUtils/match.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021-2022 by Geoffrey Merck F4FXL / KC3FRA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include + +#include "../../NetUtils.h" + +namespace NetUtilsTests +{ + class NetUtils_match: public ::testing::Test { + + }; + + TEST_F(NetUtils_match, MatchIPAndPort_differentFamilySamePort) + { + struct sockaddr_storage addr1, addr2; + addr1.ss_family = AF_INET6; + addr2.ss_family = AF_INET; + + ((struct sockaddr_in6 *)&addr1)->sin6_addr = IN6ADDR_LOOPBACK_INIT; + ((struct sockaddr_in6 *)&addr1)->sin6_port = 123; + + ((struct sockaddr_in *)&addr2)->sin_addr.s_addr = INADDR_LOOPBACK; + ((struct sockaddr_in *)&addr2)->sin_port = 123; + + EXPECT_FALSE(CNetUtils::match(addr1, addr2, IMT_ADDRESS_AND_PORT)); + } + + TEST_F(NetUtils_match, MatchIPAndPort_SameFamilySamePort) + { + struct sockaddr_storage addr1, addr2; + addr1.ss_family = AF_INET6; + addr2.ss_family = AF_INET6; + + ((struct sockaddr_in6 *)&addr1)->sin6_addr = IN6ADDR_LOOPBACK_INIT; + ((struct sockaddr_in6 *)&addr1)->sin6_port = 123; + + ((struct sockaddr_in6 *)&addr2)->sin6_addr = IN6ADDR_LOOPBACK_INIT; + ((struct sockaddr_in6 *)&addr2)->sin6_port = 123; + + EXPECT_TRUE(CNetUtils::match(addr1, addr2, IMT_ADDRESS_AND_PORT)); + } + + TEST_F(NetUtils_match, MatchIPAndPort_SameFamilyDifferentPort) + { + struct sockaddr_storage addr1, addr2; + addr1.ss_family = AF_INET6; + addr2.ss_family = AF_INET6; + + ((struct sockaddr_in6 *)&addr1)->sin6_addr = IN6ADDR_LOOPBACK_INIT; + ((struct sockaddr_in6 *)&addr1)->sin6_port = 123; + + ((struct sockaddr_in6 *)&addr2)->sin6_addr = IN6ADDR_LOOPBACK_INIT; + ((struct sockaddr_in6 *)&addr2)->sin6_port = 456; + + EXPECT_FALSE(CNetUtils::match(addr1, addr2, IMT_ADDRESS_AND_PORT)); + } + + TEST_F(NetUtils_match, MatchIP_SameFamilyDifferentPort) + { + struct sockaddr_storage addr1, addr2; + addr1.ss_family = AF_INET6; + addr2.ss_family = AF_INET6; + + ((struct sockaddr_in6 *)&addr1)->sin6_addr = IN6ADDR_LOOPBACK_INIT; + ((struct sockaddr_in6 *)&addr1)->sin6_port = 123; + + ((struct sockaddr_in6 *)&addr2)->sin6_addr = IN6ADDR_LOOPBACK_INIT; + ((struct sockaddr_in6 *)&addr2)->sin6_port = 456; + + EXPECT_TRUE(CNetUtils::match(addr1, addr2, IMT_ADDRESS_ONLY)); + } +} \ No newline at end of file diff --git a/UDPReaderWriter.cpp b/UDPReaderWriter.cpp index d89beea..d0b826f 100644 --- a/UDPReaderWriter.cpp +++ b/UDPReaderWriter.cpp @@ -21,6 +21,7 @@ #include #include "UDPReaderWriter.h" #include "Log.h" +#include "NetUtils.h" CUDPReaderWriter::CUDPReaderWriter(const std::string& address, unsigned int port) : m_address(address), @@ -101,7 +102,7 @@ bool CUDPReaderWriter::open() return true; } -int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port) +int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, struct sockaddr_storage& addr) { // Check that the readfrom() won't block fd_set readFds; @@ -122,8 +123,7 @@ int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr& if (ret == 0) return 0; - sockaddr_in addr; - socklen_t size = sizeof(sockaddr_in); + socklen_t size = sizeof(addr); ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&addr, &size); if (len <= 0) { @@ -131,22 +131,54 @@ int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr& return -1; } - address = addr.sin_addr; - port = ntohs(addr.sin_port); - return len; } +int CUDPReaderWriter::read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port) +{ + struct sockaddr_storage addr; + auto res = read(buffer, length, addr); + + if(res >= 0 && addr.ss_family == AF_INET) { + address = TOIPV4(addr)->sin_addr; + port = ntohs(TOIPV4(addr)->sin_port); + } + + return res; +} + bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port) { - sockaddr_in addr; - ::memset(&addr, 0x00, sizeof(sockaddr_in)); + struct sockaddr_storage addr; + ::memset(&addr, 0, sizeof(sockaddr_storage)); + + addr.ss_family = AF_INET; + TOIPV4(addr)->sin_addr = address; + TOIPV4(addr)->sin_port = htons(port); - addr.sin_family = AF_INET; - addr.sin_addr = address; - addr.sin_port = htons(port); + return write(buffer, length, addr); + // sockaddr_in addr; + // ::memset(&addr, 0x00, sizeof(sockaddr_in)); - ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); + // addr.sin_family = AF_INET; + // addr.sin_addr = address; + // addr.sin_port = htons(port); + + // ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(sockaddr_in)); + // if (ret < 0) { + // CLog::logError("Error returned from sendto (port: %u), err: %s\n", m_port, strerror(errno)); + // return false; + // } + + // if (ret != ssize_t(length)) + // return false; + + // return true; +} + +bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, const struct sockaddr_storage& addr) +{ + ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&addr, sizeof(addr)); if (ret < 0) { CLog::logError("Error returned from sendto (port: %u), err: %s\n", m_port, strerror(errno)); return false; @@ -158,6 +190,8 @@ bool CUDPReaderWriter::write(const unsigned char* buffer, unsigned int length, c return true; } + + void CUDPReaderWriter::close() { ::close(m_fd); diff --git a/UDPReaderWriter.h b/UDPReaderWriter.h index 25cfb08..0cddfac 100644 --- a/UDPReaderWriter.h +++ b/UDPReaderWriter.h @@ -41,8 +41,10 @@ public: bool open(); - int read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port); + int read(unsigned char* buffer, unsigned int length, struct sockaddr_storage& addr); + int read(unsigned char* buffer, unsigned int length, in_addr& address, unsigned int& port); bool write(const unsigned char* buffer, unsigned int length, const in_addr& address, unsigned int port); + bool write(const unsigned char* buffer, unsigned int length, const struct sockaddr_storage& addr); void close();