diff --git a/DPlusAuthenticator.cpp b/DPlusAuthenticator.cpp new file mode 100644 index 0000000..1c7a0f6 --- /dev/null +++ b/DPlusAuthenticator.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX + * Copyright (c) 2021 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 "DPlusAuthenticator.h" +#include "UDPReaderWriter.h" +#include "DStarDefines.h" +#include "Utils.h" +#include "Defs.h" + +const wxString OPENDSTAR_HOSTNAME = wxT("auth.dstargateway.org"); +const unsigned int OPENDSTAR_PORT = 20001U; + +const unsigned int TCP_TIMEOUT = 10U; + +CDPlusAuthenticator::CDPlusAuthenticator(const wxString& loginCallsign, const wxString& gatewayCallsign, const wxString& address, CCacheManager* cache) : +wxThread(wxTHREAD_JOINABLE), +m_loginCallsign(loginCallsign), +m_gatewayCallsign(gatewayCallsign), +m_address(address), +m_cache(cache), +m_timer(1U, 6U * 3600U), // 6 hours +m_killed(false) +{ + wxASSERT(!loginCallsign.IsEmpty()); + wxASSERT(!gatewayCallsign.IsEmpty()); + wxASSERT(cache != NULL); + + m_gatewayCallsign.Truncate(LONG_CALLSIGN_LENGTH - 1U); + + m_loginCallsign.Trim(); + m_gatewayCallsign.Trim(); + + if (m_loginCallsign.IsEmpty()) + m_loginCallsign = m_gatewayCallsign; +} + +CDPlusAuthenticator::~CDPlusAuthenticator() +{ +} + +void CDPlusAuthenticator::start() +{ + Create(); + Run(); +} + +void* CDPlusAuthenticator::Entry() +{ + wxLogMessage(wxT("Starting the D-Plus Authenticator thread")); + + authenticate(m_loginCallsign, OPENDSTAR_HOSTNAME, OPENDSTAR_PORT, '2', true); + + m_timer.start(); + + try { + while (!m_killed) { + if (m_timer.hasExpired()) { + authenticate(m_loginCallsign, OPENDSTAR_HOSTNAME, OPENDSTAR_PORT, '2', true); + m_timer.start(); + } + + Sleep(1000UL); + + m_timer.clock(); + } + } + catch (std::exception& e) { + wxString message(e.what(), wxConvLocal); + wxLogError(wxT("Exception raised in the D-Plus Authenticator thread - \"%s\""), message.c_str()); + } + catch (...) { + wxLogError(wxT("Unknown exception raised in the D-Plus Authenticator thread")); + } + + wxLogMessage(wxT("Stopping the D-Plus Authenticator thread")); + + return NULL; +} + +void CDPlusAuthenticator::stop() +{ + m_killed = true; + + Wait(); +} + +bool CDPlusAuthenticator::authenticate(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id, bool writeToCache) +{ + CTCPReaderWriterClient socket(hostname, port, m_address); + + bool ret = socket.open(); + if (!ret) + return false; + + unsigned char* buffer = new unsigned char[4096U]; + ::memset(buffer, ' ', 56U); + + buffer[0U] = 0x38U; + buffer[1U] = 0xC0U; + buffer[2U] = 0x01U; + buffer[3U] = 0x00U; + + for (unsigned int i = 0U; i < callsign.Len(); i++) + buffer[i + 4U] = callsign.GetChar(i); + + buffer[12U] = 'D'; + buffer[13U] = 'V'; + buffer[14U] = '0'; + buffer[15U] = '1'; + buffer[16U] = '9'; + buffer[17U] = '9'; + buffer[18U] = '9'; + buffer[19U] = '9'; + + buffer[28U] = 'W'; + buffer[29U] = '7'; + buffer[30U] = 'I'; + buffer[31U] = 'B'; + buffer[32U] = id; + + buffer[40U] = 'D'; + buffer[41U] = 'H'; + buffer[42U] = 'S'; + buffer[43U] = '0'; + buffer[44U] = '2'; + buffer[45U] = '5'; + buffer[46U] = '7'; + + ret = socket.write(buffer, 56U); + if (!ret) { + socket.close(); + delete[] buffer; + return false; + } + + ret = read(socket, buffer + 0U, 2U); + + while (ret) { + unsigned int len = (buffer[1U] & 0x0FU) * 256U + buffer[0U]; + + // Ensure that we get exactly len - 2U bytes from the TCP stream + ret = read(socket, buffer + 2U, len - 2U); + if (!ret) { + wxLogError(wxT("Short read from %s:%u"), hostname.c_str(), port); + break; + } + + if ((buffer[1U] & 0xC0U) != 0xC0U || buffer[2U] != 0x01U) { + wxLogError(wxT("Invalid packet received from %s:%u"), hostname.c_str(), port); + CUtils::dump(wxT("Details:"), buffer, len); + break; + } + + for (unsigned int i = 8U; (i + 25U) < len; i += 26U) { + wxString address = wxString((char*)(buffer + i + 0U), wxConvLocal, 16U); + wxString name = wxString((char*)(buffer + i + 16U), wxConvLocal, LONG_CALLSIGN_LENGTH); + + address.Trim(); + name.Trim(); + + // Get the active flag + bool active = (buffer[i + 25U] & 0x80U) == 0x80U; + + // An empty name or IP address or an inactive gateway/reflector is not written out + if (address.Len() > 0U && name.Len() > 0U && !name.Left(3U).IsSameAs(wxT("XRF")) && active && writeToCache){ + if (name.Left(3U).IsSameAs(wxT("REF"))) + wxLogMessage(wxT("D-Plus: %s\t%s"), name.c_str(), address.c_str()); + + name.Append(wxT(" ")); + name.Truncate(LONG_CALLSIGN_LENGTH - 1U); + name.Append(wxT("G")); + m_cache->updateGateway(name, address, DP_DPLUS, false, true); + } + } + + ret = read(socket, buffer + 0U, 2U); + } + + wxLogMessage(wxT("Registered with %s using callsign %s"), hostname.c_str(), callsign.c_str()); + + socket.close(); + + delete[] buffer; + + return true; +} + +bool CDPlusAuthenticator::poll(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id) +{ + CUDPReaderWriter socket(m_address, 0U); + bool ret = socket.open(); + if (!ret) + return false; + + unsigned char buffer[56U]; + ::memset(buffer, ' ', 56U); + + buffer[0U] = 0x38U; + buffer[1U] = 0x20U; + buffer[2U] = 0x01U; + buffer[3U] = 0x01U; + + for (unsigned int i = 0U; i < callsign.Len(); i++) + buffer[i + 4U] = callsign.GetChar(i); + + buffer[12U] = 'D'; + buffer[13U] = 'V'; + buffer[14U] = '0'; + buffer[15U] = '1'; + buffer[16U] = '9'; + buffer[17U] = '9'; + buffer[18U] = '9'; + buffer[19U] = '9'; + + for (unsigned int i = 0U; i < callsign.Len(); i++) + buffer[i + 20U] = callsign.GetChar(i); + + buffer[28U] = 'W'; + buffer[29U] = '7'; + buffer[30U] = 'I'; + buffer[31U] = 'B'; + buffer[32U] = id; + + buffer[40U] = 'D'; + buffer[41U] = 'H'; + buffer[42U] = 'S'; + buffer[43U] = '0'; + buffer[44U] = '2'; + buffer[45U] = '5'; + buffer[46U] = '7'; + + in_addr address = socket.lookup(hostname); + if (address.s_addr == INADDR_NONE) { + socket.close(); + return false; + } + + ret = socket.write(buffer, 56U, address, port); + + socket.close(); + + return ret; +} + +bool CDPlusAuthenticator::read(CTCPReaderWriterClient &socket, unsigned char *buffer, unsigned int len) const +{ + unsigned int offset = 0U; + + do { + int n = socket.read(buffer + offset, len - offset, TCP_TIMEOUT); + if (n < 0) + return false; + + offset += n; + } while ((len - offset) > 0U); + + return true; +} diff --git a/DPlusAuthenticator.h b/DPlusAuthenticator.h new file mode 100644 index 0000000..b4dc759 --- /dev/null +++ b/DPlusAuthenticator.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX + * Copyright (c) 2021 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. + */ + +#ifndef DPlusAuthenticator_H +#define DPlusAuthenticator_H + +#include "TCPReaderWriterClient.h" +#include "CacheManager.h" +#include "Timer.h" + + +#include + +class CDPlusAuthenticator : public wxThread { +public: + CDPlusAuthenticator(const wxString& loginCallsign, const wxString& gatewayCallsign, const wxString& address, CCacheManager* cache); + virtual ~CDPlusAuthenticator(); + + virtual void start(); + + virtual void* Entry(); + + virtual void stop(); + +private: + wxString m_loginCallsign; + wxString m_gatewayCallsign; + wxString m_address; + CCacheManager* m_cache; + CTimer m_timer; + bool m_killed; + + bool poll(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id); + bool authenticate(const wxString& callsign, const wxString& hostname, unsigned int port, unsigned char id, bool writeToCache); + bool read(CTCPReaderWriterClient& socket, unsigned char* buffer, unsigned int len) const; +}; + +#endif diff --git a/DPlusHandler.cpp b/DPlusHandler.cpp new file mode 100644 index 0000000..780a374 --- /dev/null +++ b/DPlusHandler.cpp @@ -0,0 +1,954 @@ +/* + * Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX + * Copyright (c) 2021 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 "RepeaterHandler.h" +#include "DPlusHandler.h" +#include "DStarDefines.h" +#include "Utils.h" + +#include + +unsigned int CDPlusHandler::m_maxReflectors = 0U; +unsigned int CDPlusHandler::m_maxDongles = 0U; +CDPlusHandler** CDPlusHandler::m_reflectors = NULL; + +wxString CDPlusHandler::m_gatewayCallsign; +wxString CDPlusHandler::m_dplusLogin; +CDPlusProtocolHandlerPool* CDPlusHandler::m_pool = NULL; +CDPlusProtocolHandler* CDPlusHandler::m_incoming = NULL; + +bool CDPlusHandler::m_stateChange = false; + +CDPlusAuthenticator* CDPlusHandler::m_authenticator = NULL; +CHeaderLogger* CDPlusHandler::m_headerLogger = NULL; + +CCallsignList* CDPlusHandler::m_whiteList = NULL; +CCallsignList* CDPlusHandler::m_blackList = NULL; + + +CDPlusHandler::CDPlusHandler(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port) : +m_repeater(repeater.Clone()), +m_callsign(m_dplusLogin.Clone()), +m_reflector(reflector.Clone()), +m_handler(protoHandler), +m_yourAddress(address), +m_yourPort(port), +m_myPort(0U), +m_direction(DIR_OUTGOING), +m_linkState(DPLUS_LINKING), +m_destination(handler), +m_time(), +m_pollTimer(1000U, 1U), // 1s +m_pollInactivityTimer(1000U, 30U), +m_tryTimer(1000U, 1U), +m_tryCount(0U), +m_dPlusId(0x00U), +m_dPlusSeq(0x00U), +m_inactivityTimer(1000U, NETWORK_TIMEOUT), +m_header(NULL) +{ + wxASSERT(protoHandler != NULL); + wxASSERT(handler != NULL); + wxASSERT(port > 0U); + + m_myPort = protoHandler->getPort(); + + m_pollInactivityTimer.start(); + m_tryTimer.start(); + + m_time = ::time(NULL); + + m_callsign.resize(LONG_CALLSIGN_LENGTH, ' '); + wxChar band = m_repeater.GetChar(LONG_CALLSIGN_LENGTH - 1U); + m_callsign.SetChar(LONG_CALLSIGN_LENGTH - 1U, band); +} + +CDPlusHandler::CDPlusHandler(CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port) : +m_repeater(), +m_callsign(), +m_reflector(), +m_handler(protoHandler), +m_yourAddress(address), +m_yourPort(port), +m_myPort(0U), +m_direction(DIR_INCOMING), +m_linkState(DPLUS_LINKING), +m_destination(NULL), +m_time(), +m_pollTimer(1000U, 1U), // 1s +m_pollInactivityTimer(1000U, 10U), // 10s +m_tryTimer(1000U), +m_tryCount(0U), +m_dPlusId(0x00U), +m_dPlusSeq(0x00U), +m_inactivityTimer(1000U, NETWORK_TIMEOUT), +m_header(NULL) +{ + wxASSERT(protoHandler != NULL); + wxASSERT(port > 0U); + + m_myPort = protoHandler->getPort(); + + m_pollTimer.start(); + m_pollInactivityTimer.start(); + + m_time = ::time(NULL); +} + +CDPlusHandler::~CDPlusHandler() +{ + if (m_direction == DIR_OUTGOING) + m_pool->release(m_handler); + + delete m_header; +} + +void CDPlusHandler::initialise(unsigned int maxReflectors) +{ + wxASSERT(maxReflectors > 0U); + + m_maxReflectors = maxReflectors; + + m_reflectors = new CDPlusHandler*[m_maxReflectors]; + for (unsigned int i = 0U; i < m_maxReflectors; i++) + m_reflectors[i] = NULL; +} + +void CDPlusHandler::startAuthenticator(const wxString& address, CCacheManager* cache) +{ + wxASSERT(cache != NULL); + + m_authenticator = new CDPlusAuthenticator(m_dplusLogin, m_gatewayCallsign, address, cache); + m_authenticator->start(); +} + +void CDPlusHandler::setCallsign(const wxString& callsign) +{ + m_gatewayCallsign = callsign; +} + +void CDPlusHandler::setDPlusProtocolHandlerPool(CDPlusProtocolHandlerPool* pool) +{ + wxASSERT(pool != NULL); + + m_pool = pool; +} + +void CDPlusHandler::setDPlusProtocolIncoming(CDPlusProtocolHandler* handler) +{ + wxASSERT(handler != NULL); + + m_incoming = handler; +} + +void CDPlusHandler::setDPlusLogin(const wxString& dplusLogin) +{ + m_dplusLogin = dplusLogin; +} + +void CDPlusHandler::setHeaderLogger(CHeaderLogger* logger) +{ + m_headerLogger = logger; +} + +void CDPlusHandler::setMaxDongles(unsigned int maxDongles) +{ + m_maxDongles = maxDongles; +} + +void CDPlusHandler::setWhiteList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_whiteList = list; +} + +void CDPlusHandler::setBlackList(CCallsignList* list) +{ + wxASSERT(list != NULL); + + m_blackList = list; +} + +void CDPlusHandler::getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data) +{ + wxASSERT(handler != NULL); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (reflector->m_destination == handler && reflector->m_linkState != DPLUS_UNLINKING) + data.addLink(reflector->m_reflector, PROTO_DPLUS, reflector->m_linkState == DPLUS_LINKED, reflector->m_direction, true); + } + } +} + +wxString CDPlusHandler::getDongles() +{ + wxString dongles; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL && reflector->m_direction == DIR_INCOMING) { + dongles.Append(wxT("P:")); + dongles.Append(reflector->m_reflector); + dongles.Append(wxT(" ")); + } + } + + return dongles; +} + +void CDPlusHandler::process(CHeaderData& header) +{ + in_addr yourAddress = header.getYourAddress(); + unsigned int yourPort = header.getYourPort(); + unsigned int myPort = header.getMyPort(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort && + reflector->m_myPort == myPort) { + reflector->processInt(header); + return; + } + } + } +} + +void CDPlusHandler::process(CAMBEData& data) +{ + in_addr yourAddress = data.getYourAddress(); + unsigned int yourPort = data.getYourPort(); + unsigned int myPort = data.getMyPort(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort && + reflector->m_myPort == myPort) { + reflector->processInt(data); + return; + } + } + } +} + +void CDPlusHandler::process(const CPollData& poll) +{ + in_addr yourAddress = poll.getYourAddress(); + unsigned int yourPort = poll.getYourPort(); + unsigned int myPort = poll.getMyPort(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort && + reflector->m_myPort == myPort) { + reflector->m_pollInactivityTimer.start(); + return; + } + } + } + + // If we cannot find an existing link, we ignore the poll + wxLogMessage(wxT("Incoming poll from unknown D-Plus dongle")); +} + +void CDPlusHandler::process(CConnectData& connect) +{ + CD_TYPE type = connect.getType(); + in_addr yourAddress = connect.getYourAddress(); + unsigned int yourPort = connect.getYourPort(); + unsigned int myPort = connect.getMyPort(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort && + reflector->m_myPort == myPort) { + bool res = m_reflectors[i]->processInt(connect, type); + if (res) { + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } + } + + // Check that it isn't a duplicate + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + if (reflector->m_yourAddress.s_addr == yourAddress.s_addr && + reflector->m_yourPort == yourPort && + reflector->m_myPort == myPort) + return; + } + } + + if (type == CT_UNLINK) + return; + + if (type != CT_LINK1) { + wxLogMessage(wxT("Incoming D-Plus message from unknown source")); + return; + } + + // Check to see if we are allowed to accept it + unsigned int count = 0U; + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL && + m_reflectors[i]->m_direction == DIR_INCOMING) + count++; + } + + if (count >= m_maxDongles) + return; + + CDPlusHandler* dplus = new CDPlusHandler(m_incoming, yourAddress, yourPort); + + bool found = false; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] == NULL) { + m_reflectors[i] = dplus; + found = true; + break; + } + } + + if (found) { + CConnectData connect(CT_LINK1, yourAddress, yourPort); + m_incoming->writeConnect(connect); + } else { + wxLogError(wxT("No space to add new D-Plus dongle, ignoring")); + delete dplus; + } +} + +void CDPlusHandler::link(IReflectorCallback* handler, const wxString& repeater, const wxString &gateway, const in_addr& address) +{ + CDPlusProtocolHandler* protoHandler = m_pool->getHandler(); + if (protoHandler == NULL) + return; + + CDPlusHandler* dplus = new CDPlusHandler(handler, repeater, gateway, protoHandler, address, DPLUS_PORT); + + bool found = false; + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] == NULL) { + m_reflectors[i] = dplus; + found = true; + break; + } + } + + if (found) { + CConnectData connect(CT_LINK1, address, DPLUS_PORT); + protoHandler->writeConnect(connect); + m_stateChange = true; + } else { + wxLogError(wxT("No space to add new D-Plus reflector, ignoring")); + delete dplus; + } +} + +void CDPlusHandler::relink(IReflectorCallback* handler, const wxString &gateway) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL && m_reflectors[i]->m_direction == DIR_OUTGOING) { + if (m_reflectors[i]->m_destination == handler) { + m_reflectors[i]->m_reflector = gateway; + m_reflectors[i]->m_dPlusId = 0x00U; + m_reflectors[i]->m_dPlusSeq = 0x00U; + m_stateChange = true; + return; + } + } + } +} + +void CDPlusHandler::unlink(IReflectorCallback* handler, const wxString& callsign, bool exclude) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + + if (reflector != NULL) { + bool found = false; + + if (exclude) { + if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination == handler && !reflector->m_reflector.IsSameAs(callsign)) { + wxLogMessage(wxT("Removing outgoing D-Plus link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + + if (reflector->m_linkState == DPLUS_LINKING || reflector->m_linkState == DPLUS_LINKED) { + CConnectData connect(CT_UNLINK, reflector->m_yourAddress, DPLUS_PORT); + reflector->m_handler->writeConnect(connect); + reflector->m_handler->writeConnect(connect); + + reflector->m_linkState = DPLUS_UNLINKING; + reflector->m_tryTimer.start(1U); + reflector->m_pollTimer.stop(); + reflector->m_pollInactivityTimer.stop(); + reflector->m_tryCount = 0U; + } + + found = true; + } + } else { + if (reflector->m_destination == handler && reflector->m_reflector.IsSameAs(callsign)) { + wxLogMessage(wxT("Removing D-Plus link %s, %s"), reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + + if (reflector->m_linkState == DPLUS_LINKING || reflector->m_linkState == DPLUS_LINKED) { + CConnectData connect(CT_UNLINK, reflector->m_yourAddress, DPLUS_PORT); + reflector->m_handler->writeConnect(connect); + reflector->m_handler->writeConnect(connect); + + reflector->m_linkState = DPLUS_UNLINKING; + reflector->m_tryTimer.start(1U); + reflector->m_pollTimer.stop(); + reflector->m_pollInactivityTimer.stop(); + reflector->m_tryCount = 0U; + } + + found = true; + } + } + + // If an active link with incoming traffic, send an EOT to the repeater + if (found) { + if (reflector->m_dPlusId != 0x00U) { + unsigned int seq = reflector->m_dPlusSeq + 1U; + if (seq == 21U) + seq = 0U; + + CAMBEData data; + data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES); + data.setSeq(seq); + data.setEnd(true); + data.setId(reflector->m_dPlusId); + + reflector->m_destination->process(data, reflector->m_direction, AS_DPLUS); + } + + m_stateChange = true; + } + } + } +} + +void CDPlusHandler::unlink() +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (!reflector->m_reflector.IsEmpty()) + wxLogMessage(wxT("Unlinking from D-Plus reflector or dongle %s"), reflector->m_reflector.c_str()); + + CConnectData connect(CT_UNLINK, reflector->m_yourAddress, reflector->m_yourPort); + reflector->m_handler->writeConnect(connect); + reflector->m_handler->writeConnect(connect); + reflector->m_tryTimer.start(1U); + reflector->m_pollTimer.stop(); + reflector->m_pollInactivityTimer.stop(); + reflector->m_tryCount = 0U; + } + } +} + +void CDPlusHandler::writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) + m_reflectors[i]->writeHeaderInt(handler, header, direction); + } +} + +void CDPlusHandler::writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) + m_reflectors[i]->writeAMBEInt(handler, data, direction); + } +} + +void CDPlusHandler::gatewayUpdate(const wxString& gateway, const wxString& address) +{ + wxString gatewayBase = gateway; + gatewayBase.Truncate(LONG_CALLSIGN_LENGTH - 1U); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + if (!reflector->m_reflector.IsEmpty() && reflector->m_reflector.Left(LONG_CALLSIGN_LENGTH - 1U).IsSameAs(gatewayBase)) { + if (!address.IsEmpty()) { + // A new address, change the value + wxLogMessage(wxT("Changing IP address of D-Plus gateway or reflector %s to %s"), gatewayBase.c_str(), address.c_str()); + reflector->m_yourAddress.s_addr = ::inet_addr(address.mb_str()); + } else { + wxLogMessage(wxT("IP address for D-Plus gateway or reflector %s has been removed"), gatewayBase.c_str()); + + // No address, this probably shouldn't happen.... + if (reflector->m_direction == DIR_OUTGOING && reflector->m_destination != NULL) + reflector->m_destination->linkFailed(DP_DPLUS, reflector->m_reflector, false); + + m_stateChange = true; + + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } + } +} + +void CDPlusHandler::clock(unsigned int ms) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + if (m_reflectors[i] != NULL) { + bool ret = m_reflectors[i]->clockInt(ms); + if (ret) { + delete m_reflectors[i]; + m_reflectors[i] = NULL; + } + } + } +} + +void CDPlusHandler::finalise() +{ + if (m_authenticator != NULL) + m_authenticator->stop(); + + for (unsigned int i = 0U; i < m_maxReflectors; i++) + delete m_reflectors[i]; + + delete[] m_reflectors; +} + +void CDPlusHandler::processInt(CHeaderData& header) +{ + wxString my = header.getMyCall1(); + wxString rpt1 = header.getRptCall1(); + wxString rpt2 = header.getRptCall2(); + unsigned int id = header.getId(); + + if (m_whiteList != NULL) { + bool res = m_whiteList->isInList(my); + if (!res) { + wxLogMessage(wxT("%s rejected from D-Plus as not found in the white list"), my.c_str()); + m_dPlusId = 0x00U; + return; + } + } + + if (m_blackList != NULL) { + bool res = m_blackList->isInList(my); + if (res) { + wxLogMessage(wxT("%s rejected from D-Plus as found in the black list"), my.c_str()); + m_dPlusId = 0x00U; + return; + } + } + + if (m_linkState != DPLUS_LINKED) + return; + + switch (m_direction) { + case DIR_OUTGOING: + if (m_reflector.IsSameAs(rpt1) || m_reflector.IsSameAs(rpt2)) { + // If we're already processing, ignore the new header + if (m_dPlusId != 0x00U) + return; + + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("DPlus"), header); + + m_dPlusId = id; + m_dPlusSeq = 0x00U; + m_inactivityTimer.start(); + m_pollInactivityTimer.start(); + + delete m_header; + + m_header = new CHeaderData(header); + m_header->setCQCQCQ(); + m_header->setFlags(0x00U, 0x00U, 0x00U); + + m_destination->process(*m_header, m_direction, AS_DPLUS); + } + break; + + case DIR_INCOMING: { + m_destination = CRepeaterHandler::findDVRepeater(rpt1); + if (m_destination == NULL) { + m_destination = CRepeaterHandler::findDVRepeater(rpt2); + if (m_destination == NULL) + return; + } + + if (m_dPlusId != 0x00U) + return; + + // Write to Header.log if it's enabled + if (m_headerLogger != NULL) + m_headerLogger->write(wxT("DPlus"), header); + + m_dPlusId = id; + m_dPlusSeq = 0x00U; + m_inactivityTimer.start(); + m_pollInactivityTimer.start(); + + delete m_header; + + m_header = new CHeaderData(header); + m_header->setCQCQCQ(); + m_header->setFlags(0x00U, 0x00U, 0x00U); + + m_destination->process(*m_header, m_direction, AS_DPLUS); + } + break; + } +} + +void CDPlusHandler::processInt(CAMBEData& data) +{ + unsigned int id = data.getId(); + + if (m_dPlusId != id) + return; + + m_dPlusSeq = data.getSeq(); + + // Send the header every 21 frames, if we have it + if (m_dPlusSeq == 0U && m_header != NULL) + m_destination->process(*m_header, m_direction, AS_DUP); + + m_inactivityTimer.start(); + m_pollInactivityTimer.start(); + + m_destination->process(data, m_direction, AS_DPLUS); + + if (data.isEnd()) { + m_dPlusId = 0x00U; + m_dPlusSeq = 0x00U; + + delete m_header; + m_header = NULL; + + m_inactivityTimer.stop(); + } +} + +bool CDPlusHandler::processInt(CConnectData& connect, CD_TYPE type) +{ + switch (m_direction) { + case DIR_OUTGOING: + switch (type) { + case CT_ACK: + if (m_linkState == DPLUS_LINKING) { + wxLogMessage(wxT("D-Plus ACK message received from %s"), m_reflector.c_str()); + m_destination->linkUp(DP_DPLUS, m_reflector); + m_stateChange = true; + m_linkState = DPLUS_LINKED; + m_tryTimer.stop(); + m_pollTimer.start(); + m_pollInactivityTimer.start(); + } + return false; + + case CT_NAK: + if (m_linkState == DPLUS_LINKING) { + wxLogMessage(wxT("D-Plus NAK message received from %s"), m_reflector.c_str()); + m_destination->linkRefused(DP_DPLUS, m_reflector); + CConnectData reply(CT_UNLINK, connect.getYourAddress(), connect.getYourPort()); + m_handler->writeConnect(reply); + m_tryTimer.stop(); + } + return true; + + case CT_UNLINK: + if (m_linkState == DPLUS_UNLINKING) { + wxLogMessage(wxT("D-Plus disconnect acknowledgement received from %s"), m_reflector.c_str()); + m_destination->linkFailed(DP_DPLUS, m_reflector, false); + m_stateChange = true; + m_tryTimer.stop(); + } + return true; + + case CT_LINK1: { + CConnectData reply(m_dplusLogin, CT_LINK2, connect.getYourAddress(), connect.getYourPort()); + m_handler->writeConnect(reply); + m_tryTimer.stop(); + } + return false; + + default: + return false; + } + break; + + case DIR_INCOMING: + switch (type) { + case CT_LINK2: { + m_reflector = connect.getRepeater(); + wxLogMessage(wxT("D-Plus dongle link to %s has started"), m_reflector.c_str()); + CConnectData reply(CT_ACK, m_yourAddress, m_yourPort); + m_handler->writeConnect(reply); + m_linkState = DPLUS_LINKED; + m_stateChange = true; + } + return false; + + case CT_UNLINK: + if (m_linkState == DPLUS_LINKED) { + wxLogMessage(wxT("D-Plus dongle link to %s has ended (unlinked)"), m_reflector.c_str()); + m_stateChange = true; + m_handler->writeConnect(connect); + } + return true; + + default: + return false; + } + break; + } + + return false; +} + +bool CDPlusHandler::clockInt(unsigned int ms) +{ + m_tryTimer.clock(ms); + m_pollTimer.clock(ms); + m_inactivityTimer.clock(ms); + m_pollInactivityTimer.clock(ms); + + if (m_pollInactivityTimer.isRunning() && m_pollInactivityTimer.hasExpired()) { + m_pollInactivityTimer.start(); + + delete m_header; + m_header = NULL; + + m_stateChange = true; + m_dPlusId = 0x00U; + m_dPlusSeq = 0x00U; + + if (!m_reflector.IsEmpty()) { + switch (m_linkState) { + case DPLUS_LINKING: + wxLogMessage(wxT("D-Plus link to %s has failed to connect"), m_reflector.c_str()); + break; + case DPLUS_LINKED: + wxLogMessage(wxT("D-Plus link to %s has failed (poll inactivity)"), m_reflector.c_str()); + break; + case DPLUS_UNLINKING: + wxLogMessage(wxT("D-Plus link to %s has failed to disconnect cleanly"), m_reflector.c_str()); + break; + default: + break; + } + } + + if (m_direction == DIR_OUTGOING) { + bool reconnect = m_destination->linkFailed(DP_DPLUS, m_reflector, true); + if (reconnect) { + CConnectData connect(CT_LINK1, m_yourAddress, DPLUS_PORT); + m_handler->writeConnect(connect); + m_linkState = DPLUS_LINKING; + m_tryTimer.start(1U); + m_tryCount = 0U; + return false; + } + } + + return true; + } + + if (m_pollTimer.isRunning() && m_pollTimer.hasExpired()) { + CPollData poll(m_yourAddress, m_yourPort); + m_handler->writePoll(poll); + + m_pollTimer.start(); + } + + if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) { + switch (m_linkState) { + case DPLUS_LINKING: { + CConnectData connect(CT_LINK1, m_yourAddress, DPLUS_PORT); + m_handler->writeConnect(connect); + } + break; + + case DPLUS_UNLINKING: { + CConnectData connect(CT_UNLINK, m_yourAddress, m_yourPort); + m_handler->writeConnect(connect); + m_handler->writeConnect(connect); + } + break; + + default: + break; + } + + unsigned int timeout = calcBackoff(); + m_tryTimer.start(timeout); + } + + if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) { + delete m_header; + m_header = NULL; + + m_dPlusId = 0x00U; + m_dPlusSeq = 0x00U; + + m_inactivityTimer.stop(); + } + + return false; +} + +void CDPlusHandler::writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction) +{ + wxASSERT(handler != NULL); + + if (m_linkState != DPLUS_LINKED) + return; + + if (direction != m_direction) + return; + + // Already is use? + if (m_dPlusId != 0x00U) + return; + + switch (m_direction) { + case DIR_OUTGOING: + if (m_destination == handler) { + header.setRepeaters(m_callsign, m_reflector); + header.setDestination(m_yourAddress, m_yourPort); + m_handler->writeHeader(header); + } + break; + + case DIR_INCOMING: + header.setDestination(m_yourAddress, m_yourPort); + m_handler->writeHeader(header); + break; + } +} + +void CDPlusHandler::writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction) +{ + if (m_linkState != DPLUS_LINKED) + return; + + if (direction != m_direction) + return; + + // Already in use? + if (m_dPlusId != 0x00U) + return; + + switch (m_direction) { + case DIR_OUTGOING: + if (m_destination == handler) { + data.setDestination(m_yourAddress, m_yourPort); + m_handler->writeAMBE(data); + } + break; + + case DIR_INCOMING: + data.setDestination(m_yourAddress, m_yourPort); + m_handler->writeAMBE(data); + break; + } +} + +bool CDPlusHandler::stateChange() +{ + bool stateChange = m_stateChange; + + m_stateChange = false; + + return stateChange; +} + +void CDPlusHandler::writeStatus(wxFFile& file) +{ + for (unsigned int i = 0U; i < m_maxReflectors; i++) { + CDPlusHandler* reflector = m_reflectors[i]; + if (reflector != NULL) { + wxString text; + + struct tm* tm = ::gmtime(&reflector->m_time); + + if (reflector->m_linkState == DPLUS_LINKED) { + switch (reflector->m_direction) { + case DIR_OUTGOING: + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DPlus link - Type: Dongle Rptr: %s Refl: %s Dir: Outgoing\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + reflector->m_repeater.c_str(), reflector->m_reflector.c_str()); + break; + + case DIR_INCOMING: + text.Printf(wxT("%04d-%02d-%02d %02d:%02d:%02d: DPlus link - Type: Dongle User: %s Dir: Incoming\n"), + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + reflector->m_reflector.c_str()); + break; + } + + file.Write(text); + } + } + } +} + +unsigned int CDPlusHandler::calcBackoff() +{ + if (m_tryCount >= 7U) { + m_tryCount++; + return 60U; + } + + unsigned int timeout = 1U; + + for (unsigned int i = 0U; i < m_tryCount; i++) + timeout *= 2U; + + m_tryCount++; + + if (timeout > 60U) + return 60U; + else + return timeout; +} diff --git a/DPlusHandler.h b/DPlusHandler.h new file mode 100644 index 0000000..1192f20 --- /dev/null +++ b/DPlusHandler.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX + * Copyright (c) 2021 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. + */ + +#ifndef DPlusHandler_H +#define DPlusHandler_H + +#include "DPlusProtocolHandlerPool.h" +#include "DPlusAuthenticator.h" +#include "ReflectorCallback.h" +#include "CacheManager.h" +#include "DStarDefines.h" +#include "HeaderLogger.h" +#include "CallsignList.h" +#include "ConnectData.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "PollData.h" +#include "Timer.h" +#include "Defs.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include +#include + +enum DPLUS_STATE { + DPLUS_LINKING, + DPLUS_LINKED, + DPLUS_UNLINKING +}; + +class CDPlusHandler { +public: + static void initialise(unsigned int maxReflectors); + + static void setCallsign(const wxString& callsign); + static void setDPlusProtocolHandlerPool(CDPlusProtocolHandlerPool* pool); + static void setDPlusProtocolIncoming(CDPlusProtocolHandler* handler); + static void setDPlusLogin(const wxString& dplusLogin); + static void setHeaderLogger(CHeaderLogger* logger); + static void setMaxDongles(unsigned int maxDongles); + + static void startAuthenticator(const wxString& address, CCacheManager* cache); + + static void link(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, const in_addr& address); + static void relink(IReflectorCallback* handler, const wxString& reflector); + static void unlink(IReflectorCallback* handler, const wxString& reflector = wxEmptyString, bool exclude = true); + static void unlink(); + + static void writeHeader(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction); + static void writeAMBE(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction); + + static void process(CHeaderData& header); + static void process(CAMBEData& header); + static void process(const CPollData& header); + static void process(CConnectData& process); + + static void gatewayUpdate(const wxString& gateway, const wxString& address); + static void clock(unsigned int ms); + + static bool stateChange(); + static void writeStatus(wxFFile& file); + + static void setWhiteList(CCallsignList* list); + static void setBlackList(CCallsignList* list); + + static void finalise(); + + static void getInfo(IReflectorCallback* handler, CRemoteRepeaterData& data); + + static wxString getDongles(); + +protected: + CDPlusHandler(IReflectorCallback* handler, const wxString& repeater, const wxString& reflector, CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port); + CDPlusHandler(CDPlusProtocolHandler* protoHandler, const in_addr& address, unsigned int port); + ~CDPlusHandler(); + + void processInt(CHeaderData& header); + void processInt(CAMBEData& data); + bool processInt(CConnectData& connect, CD_TYPE type); + + void writeHeaderInt(IReflectorCallback* handler, CHeaderData& header, DIRECTION direction); + void writeAMBEInt(IReflectorCallback* handler, CAMBEData& data, DIRECTION direction); + + bool clockInt(unsigned int ms); + +private: + static unsigned int m_maxReflectors; + static unsigned int m_maxDongles; + static CDPlusHandler** m_reflectors; + + static wxString m_gatewayCallsign; + static wxString m_dplusLogin; + static CDPlusProtocolHandlerPool* m_pool; + static CDPlusProtocolHandler* m_incoming; + + static bool m_stateChange; + + static CHeaderLogger* m_headerLogger; + static CDPlusAuthenticator* m_authenticator; + + static CCallsignList* m_whiteList; + static CCallsignList* m_blackList; + + wxString m_repeater; + wxString m_callsign; + wxString m_reflector; + CDPlusProtocolHandler* m_handler; + in_addr m_yourAddress; + unsigned int m_yourPort; + unsigned int m_myPort; + DIRECTION m_direction; + DPLUS_STATE m_linkState; + IReflectorCallback* m_destination; + time_t m_time; + CTimer m_pollTimer; + CTimer m_pollInactivityTimer; + CTimer m_tryTimer; + unsigned int m_tryCount; + unsigned int m_dPlusId; + unsigned int m_dPlusSeq; + CTimer m_inactivityTimer; + CHeaderData* m_header; + + unsigned int calcBackoff(); +}; + +#endif diff --git a/DPlusProtocolHandler.cpp b/DPlusProtocolHandler.cpp new file mode 100644 index 0000000..a2f03ec --- /dev/null +++ b/DPlusProtocolHandler.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX + * Copyright (c) 2021 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 "DPlusProtocolHandler.h" + +#include "DStarDefines.h" +#include "Utils.h" + +// #define DUMP_TX + +const unsigned int BUFFER_LENGTH = 1000U; + +CDPlusProtocolHandler::CDPlusProtocolHandler(unsigned int port, const wxString& addr) : +m_socket(addr, port), +m_type(DP_NONE), +m_buffer(NULL), +m_length(0U), +m_yourAddress(), +m_yourPort(0U), +m_myPort(port) +{ + m_buffer = new unsigned char[BUFFER_LENGTH]; +} + +CDPlusProtocolHandler::~CDPlusProtocolHandler() +{ + delete[] m_buffer; +} + +bool CDPlusProtocolHandler::open() +{ + return m_socket.open(); +} + +unsigned int CDPlusProtocolHandler::getPort() const +{ + return m_myPort; +} + +bool CDPlusProtocolHandler::writeHeader(const CHeaderData& header) +{ + unsigned char buffer[60U]; + unsigned int length = header.getDPlusData(buffer, 60U, true); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Header"), buffer, length); +#endif + + for (unsigned int i = 0U; i < 5U; i++) { + bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort()); + if (!res) + return false; + } + + return true; +} + +bool CDPlusProtocolHandler::writeAMBE(const CAMBEData& data) +{ + unsigned char buffer[40U]; + unsigned int length = data.getDPlusData(buffer, 40U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Data"), buffer, length); +#endif + + return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort()); +} + +bool CDPlusProtocolHandler::writePoll(const CPollData& poll) +{ + unsigned char buffer[10U]; + unsigned int length = poll.getDPlusData(buffer, 10U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Poll"), buffer, length); +#endif + + return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort()); +} + +bool CDPlusProtocolHandler::writeConnect(const CConnectData& connect) +{ + unsigned char buffer[40U]; + unsigned int length = connect.getDPlusData(buffer, 40U); + +#if defined(DUMP_TX) + CUtils::dump(wxT("Sending Connect"), buffer, length); +#endif + + return m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort()); +} + +DPLUS_TYPE CDPlusProtocolHandler::read() +{ + 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; +} + +bool CDPlusProtocolHandler::readPackets() +{ + m_type = DP_NONE; + + // No more data? + int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_yourAddress, m_yourPort); + if (length <= 0) + return false; + + m_length = length; + + if (m_buffer[2] != 'D' || m_buffer[3] != 'S' || m_buffer[4] != 'V' || m_buffer[5] != 'T') { + switch (m_length) { + case 3U: + m_type = DP_POLL; + return false; + case 5U: + case 8U: + case 28U: + m_type = DP_CONNECT; + return false; + default: + // An unknown type + // CUtils::dump(wxT("Unknown packet type from D-Plus"), m_buffer, m_length); + return true; + } + } else { + // Header or data packet type? + if (m_buffer[0] == 0x3A && m_buffer[1] == 0x80) { + m_type = DP_HEADER; + return false; + } else if (m_buffer[0] == 0x1D && m_buffer[1] == 0x80) { + m_type = DP_AMBE; + return false; + } else if (m_buffer[0] == 0x20 && m_buffer[1] == 0x80) { + m_type = DP_AMBE; + return false; + } else { + // An unknown type + CUtils::dump(wxT("Unknown packet type from D-Plus"), m_buffer, m_length); + return true; + } + } +} + +CHeaderData* CDPlusProtocolHandler::readHeader() +{ + if (m_type != DP_HEADER) + return NULL; + + CHeaderData* header = new CHeaderData; + + // DPlus checksums are unreliable + bool res = header->setDPlusData(m_buffer, m_length, false, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete header; + return NULL; + } + + return header; +} + +CAMBEData* CDPlusProtocolHandler::readAMBE() +{ + if (m_type != DP_AMBE) + return NULL; + + CAMBEData* data = new CAMBEData; + + bool res = data->setDPlusData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete data; + return NULL; + } + + return data; +} + +CPollData* CDPlusProtocolHandler::readPoll() +{ + if (m_type != DP_POLL) + return NULL; + + CPollData* poll = new CPollData; + + bool res = poll->setDPlusData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete poll; + return NULL; + } + + return poll; +} + +CConnectData* CDPlusProtocolHandler::readConnect() +{ + if (m_type != DP_CONNECT) + return NULL; + + CConnectData* connect = new CConnectData; + + bool res = connect->setDPlusData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort); + if (!res) { + delete connect; + return NULL; + } + + return connect; +} + +void CDPlusProtocolHandler::close() +{ + m_socket.close(); +} diff --git a/DPlusProtocolHandler.h b/DPlusProtocolHandler.h new file mode 100644 index 0000000..3978f78 --- /dev/null +++ b/DPlusProtocolHandler.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010,2011,2013 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. + */ + +#ifndef DPlusProtocolHandler_H +#define DPlusProtocolHandler_H + +#include "UDPReaderWriter.h" +#include "DStarDefines.h" +#include "ConnectData.h" +#include "HeaderData.h" +#include "AMBEData.h" +#include "PollData.h" + +#if defined(__WINDOWS__) +#include "Inaddr.h" +#else +#include +#endif + +#include + +enum DPLUS_TYPE { + DP_NONE, + DP_HEADER, + DP_AMBE, + DP_POLL, + DP_CONNECT +}; + +class CDPlusProtocolHandler { +public: + CDPlusProtocolHandler(unsigned int port, const wxString& addr = wxEmptyString); + ~CDPlusProtocolHandler(); + + bool open(); + + unsigned int getPort() const; + + bool writeHeader(const CHeaderData& header); + bool writeAMBE(const CAMBEData& data); + bool writeConnect(const CConnectData& connect); + bool writePoll(const CPollData& poll); + + DPLUS_TYPE read(); + CHeaderData* readHeader(); + CAMBEData* readAMBE(); + CPollData* readPoll(); + CConnectData* readConnect(); + + void close(); + +private: + CUDPReaderWriter m_socket; + DPLUS_TYPE m_type; + unsigned char* m_buffer; + unsigned int m_length; + in_addr m_yourAddress; + unsigned int m_yourPort; + unsigned int m_myPort; + + bool readPackets(); +}; + +#endif diff --git a/DPlusProtocolHandlerPool.cpp b/DPlusProtocolHandlerPool.cpp new file mode 100644 index 0000000..1e56488 --- /dev/null +++ b/DPlusProtocolHandlerPool.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX + * Copyright (c) 2021 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 "DPlusProtocolHandlerPool.h" + +CDPlusProtocolHandlerPool::CDPlusProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr) : +m_pool(NULL), +m_n(n), +m_index(0U) +{ + wxASSERT(port > 0U); + wxASSERT(n > 0U); + + m_pool = new CDPlusProtocolHandlerEntry[n]; + + for (unsigned int i = 0U; i < n; i++) { + m_pool[i].m_handler = new CDPlusProtocolHandler(port + i, addr); + m_pool[i].m_port = port + i; + m_pool[i].m_inUse = false; + } + + wxLogMessage(wxT("Allocated UDP ports %u-%u to D-Plus"), port, port + n - 1U); +} + +CDPlusProtocolHandlerPool::~CDPlusProtocolHandlerPool() +{ + for (unsigned int i = 0U; i < m_n; i++) + delete m_pool[i].m_handler; + + delete[] m_pool; +} + +bool CDPlusProtocolHandlerPool::open() +{ + for (unsigned int i = 0U; i < m_n; i++) { + bool ret = m_pool[i].m_handler->open(); + if (!ret) + return false; + } + + return true; +} + +CDPlusProtocolHandler* CDPlusProtocolHandlerPool::getHandler(unsigned int port) +{ + if (port == 0U) { + for (unsigned int i = 0U; i < m_n; i++) { + if (!m_pool[i].m_inUse) { + m_pool[i].m_inUse = true; + return m_pool[i].m_handler; + } + } + } else { + for (unsigned int i = 0U; i < m_n; i++) { + if (m_pool[i].m_port == port) { + m_pool[i].m_inUse = true; + return m_pool[i].m_handler; + } + } + } + + wxLogError(wxT("Cannot find a free D-Plus port in the pool")); + + return NULL; +} + +void CDPlusProtocolHandlerPool::release(CDPlusProtocolHandler* handler) +{ + wxASSERT(handler != NULL); + + for (unsigned int i = 0U; i < m_n; i++) { + if (m_pool[i].m_handler == handler && m_pool[i].m_inUse) { + m_pool[i].m_inUse = false; + return; + } + } + + wxLogError(wxT("Trying to release an unused D-Plus port")); +} + +DPLUS_TYPE CDPlusProtocolHandlerPool::read() +{ + while (m_index < m_n) { + if (m_pool[m_index].m_inUse) { + DPLUS_TYPE type = m_pool[m_index].m_handler->read(); + if (type != DP_NONE) + return type; + } + + m_index++; + } + + m_index = 0U; + + return DP_NONE; +} + +CHeaderData* CDPlusProtocolHandlerPool::readHeader() +{ + return m_pool[m_index].m_handler->readHeader(); +} + +CAMBEData* CDPlusProtocolHandlerPool::readAMBE() +{ + return m_pool[m_index].m_handler->readAMBE(); +} + +CPollData* CDPlusProtocolHandlerPool::readPoll() +{ + return m_pool[m_index].m_handler->readPoll(); +} + +CConnectData* CDPlusProtocolHandlerPool::readConnect() +{ + return m_pool[m_index].m_handler->readConnect(); +} + +void CDPlusProtocolHandlerPool::close() +{ + for (unsigned int i = 0U; i < m_n; i++) + m_pool[i].m_handler->close(); +} diff --git a/DPlusProtocolHandlerPool.h b/DPlusProtocolHandlerPool.h new file mode 100644 index 0000000..fbaede1 --- /dev/null +++ b/DPlusProtocolHandlerPool.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX + * Copyright (c) 2021 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. + */ + +#ifndef DPlusProtocolHandlerPool_H +#define DPlusProtocolHandlerPool_H + +#include + +#include "DPlusProtocolHandler.h" + +class CDPlusProtocolHandlerEntry { +public: + CDPlusProtocolHandler* m_handler; + unsigned int m_port; + bool m_inUse; +}; + +class CDPlusProtocolHandlerPool { +public: + CDPlusProtocolHandlerPool(unsigned int n, unsigned int port, const wxString& addr = wxEmptyString); + ~CDPlusProtocolHandlerPool(); + + bool open(); + + CDPlusProtocolHandler* getHandler(unsigned int port = 0U); + void release(CDPlusProtocolHandler* handler); + + DPLUS_TYPE read(); + CHeaderData* readHeader(); + CAMBEData* readAMBE(); + CPollData* readPoll(); + CConnectData* readConnect(); + + void close(); + +private: + CDPlusProtocolHandlerEntry* m_pool; + unsigned int m_n; + unsigned int m_index; +}; + +#endif