diff --git a/README.md b/README.md index 95dbb5c..aaa502f 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,29 @@ sudo apt install build-essential sudo apt install nlohmann-json3-dev ``` +### DVIN support (optional, but highly recommended) + +**DVIN**, the Digital Voice Information Network, is implemented using a distributed hash table provided by OpenDHT. + +OpenDHT is available [here](https://github./com/savoirfairelinux/opendht.git). Building and installing instructions are in the [OpenDHT Wiki](https://github.com/savoirfairelinux/opendht/wiki/Build-the-library). Pascal support and proxy-server support (RESTinio) is not required for urfd and so can be considered optional. With this in mind, this should work on Debian/Ubuntu-based systems: + +```bash +# Install OpenDHT dependencies +sudo apt install libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libargon2-0-dev libmsgpack-dev libssl-dev libfmt-dev libjsoncpp-dev libhttp-parser-dev libasio-dev cmake pkg-config libcppunit-dev + +# clone the repo +git clone https://github.com/savoirfairelinux/opendht.git + +# build and install +cd opendht +mkdir build && cd build +cmake -DOPENDHT_PYTHON=OFF -DCMAKE_INSTALL_PREFIX=/usr .. +make +sudo make install +``` + +Please note that there is no easy way to uninstall OpenDHT once it's been installed. + ### Download and build the repository and ```bash diff --git a/config/urfd.ini b/config/urfd.ini index e476ab6..f16d228 100644 --- a/config/urfd.ini +++ b/config/urfd.ini @@ -12,6 +12,14 @@ Country = GB Sponsor = My Home Club +# DashboardURL is where your dashboard for this reflector is avaiable on the WWW. +DashboardUrl = https://YourDashboard.net + +# Bootstrap is the internet address of any existing DHT client. +# Usually, choose a frequented, or close, reflector if it's already a DHT +# client. If that's not possible, you can use xrf757.openquad.net +Bootstrap = xrf757.openquad.net + [IP Addresses] # Binding addresses are usually the 'any' address IPv4Binding = 0.0.0.0 diff --git a/config/urfd.interlink b/config/urfd.interlink index 3232cc2..b0c49a7 100644 --- a/config/urfd.interlink +++ b/config/urfd.interlink @@ -1,14 +1,20 @@ ############################################################################## # URFD interlink file # -# one line per entry -# each entry specifies a remote XLX or XRF to peer with -# format: -# -# example: -# URF270 158.64.26.132 ACD +# One line per entry. +# Each entry specifies a remote URF to peer with. +# If no Port is specified, 10017 will be used. +# If DHT is enabled and the target is also DHT-enabled, then you only +# need to specify the URF-Callsign and the Shared-Modules. +# Format: +# +# Examples: +# URF270 158.64.26.132 EF +# URF280 ABC # -# note: the remote URFD must list this in its interlink file -# for the link to be established +# Brandmeister links use three params, no port is specified. Example: +# BM3104 162.248.88.117 E # +# note: Remote URFD must list this in its interlink file +# for the link to be established. ############################################################################# diff --git a/config/urfd.mk b/config/urfd.mk index 641bc68..3e4ad1a 100644 --- a/config/urfd.mk +++ b/config/urfd.mk @@ -4,3 +4,6 @@ BINDIR = /usr/local/bin # besides making an executable that gdb can use, # this will also provide some additional log messsage debug = false + +# To disable DHT support, set DHT to false. +DHT = true diff --git a/reflector/BMProtocol.cpp b/reflector/BMProtocol.cpp index f16752d..bc0663a 100644 --- a/reflector/BMProtocol.cpp +++ b/reflector/BMProtocol.cpp @@ -288,16 +288,16 @@ void CBMProtocol::HandlePeerLinks(void) CBuffer buffer; // get the list of peers - CPeerCallsignList *list = g_GateKeeper.GetPeerList(); + auto ilmap = g_GateKeeper.GetInterlinkMap(); CPeers *peers = g_Reflector.GetPeers(); // check if all our connected peers are still listed by gatekeeper // if not, disconnect auto pit = peers->begin(); std::shared_ptrpeer = nullptr; - while ( (peer = peers->FindNextPeer(EProtocol::bm, pit)) != nullptr ) + while ( nullptr != (peer = peers->FindNextPeer(EProtocol::bm, pit)) ) { - if ( list->FindListItem(peer->GetCallsign()) == nullptr ) + if ( nullptr == ilmap->FindMapItem(peer->GetCallsign().GetBase()) ) { // send disconnect packet EncodeDisconnectPacket(&buffer); @@ -310,23 +310,21 @@ void CBMProtocol::HandlePeerLinks(void) // check if all ours peers listed by gatekeeper are connected // if not, connect or reconnect - for ( auto it=list->begin(); it!=list->end(); it++ ) + for ( auto it=ilmap->begin(); it!=ilmap->end(); it++ ) { - CCallsign cs = it->GetCallsign(); - if (cs.HasSameCallsignWithWildcard(CCallsign("BM*")) && (nullptr==peers->FindPeer(cs, EProtocol::bm))) + const auto cs = it->first; + if (0 == cs.substr(0, 2).compare("BM") && (nullptr==peers->FindPeer(CCallsign(cs), EProtocol::bm))) { - // resolve again peer's IP in case it's a dynamic IP - it->ResolveIp(); // send connect packet to re-initiate peer link - EncodeConnectPacket(&buffer, it->GetModules()); - Send(buffer, it->GetIp(), m_Port); - std::cout << "Sending connect packet to BM peer " << cs << " @ " << it->GetIp() << " for modules " << it->GetModules() << std::endl; + EncodeConnectPacket(&buffer, it->second.GetModules().c_str()); + Send(buffer, it->second.GetIp(), m_Port); + std::cout << "Sending connect packet to BM peer " << cs << " @ " << it->second.GetIp() << " for modules " << it->second.GetModules() << std::endl; } } // done g_Reflector.ReleasePeers(); - g_GateKeeper.ReleasePeerList(); + g_GateKeeper.ReleaseInterlinkMap(); } diff --git a/reflector/BlackWhiteSet.cpp b/reflector/BlackWhiteSet.cpp new file mode 100644 index 0000000..6359c25 --- /dev/null +++ b/reflector/BlackWhiteSet.cpp @@ -0,0 +1,193 @@ +// +// ccallsignlist.cpp +// m17ref +// +// Created by Jean-Luc Deltombe (LX3JL) on 30/12/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of m17ref. +// +// m17ref 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 3 of the License, or +// (at your option) any later version. +// +// m17ref 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 +// with this software. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "BlackWhiteSet.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// file io + +bool CBlackWhiteSet::LoadFromFile(const std::string &filename) +{ + bool ok = false; + char sz[256]; + + // and load + std::ifstream file(filename); + if ( file.is_open() ) + { + Lock(); + + // empty list + m_Callsigns.clear(); + // fill with file content + while ( file.getline(sz, sizeof(sz)).good() ) + { + // remove leading & trailing spaces + char *szt = TrimWhiteSpaces(sz); + + // crack it + if ( (strlen(szt) > 0) && (szt[0] != '#') ) + { + // 1st token is callsign + if ( (szt = strtok(szt, " ,\t")) != nullptr ) + { + std::string cs(ToUpper(szt)); + if (m_Callsigns.end() == m_Callsigns.find(cs)) + { + m_Callsigns.insert(cs); + } + else + { + std::cerr << "Duplicate ," << cs << " in " << filename << " will be ignored." << std::endl; + } + } + } + } + // close file + file.close(); + + // keep file path + m_Filename = filename; + + // update time + GetLastModTime(&m_LastModTime); + + // and done + Unlock(); + ok = true; + std::cout << "Gatekeeper loaded " << m_Callsigns.size() << " lines from " << filename << std::endl; + } + else + { + std::cout << "Gatekeeper cannot find " << filename << std::endl; + } + + return ok; +} + +bool CBlackWhiteSet::ReloadFromFile(void) +{ + bool ok = false; + + if ( ! m_Filename.empty() ) + { + ok = LoadFromFile(m_Filename); + } + return ok; +} + +bool CBlackWhiteSet::NeedReload(void) +{ + bool needReload = false; + + time_t time; + if ( GetLastModTime(&time) ) + { + needReload = time != m_LastModTime; + } + return needReload; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// compare + +bool CBlackWhiteSet::IsMatched(const std::string &cs) const +{ + for ( const auto &item : m_Callsigns ) + { + auto pos = item.find('*'); + switch (pos) + { + case 0: + return true; + case std::string::npos: + if (0 == item.compare(cs)) + return true; + break; + default: + if (0 == item.compare(0, pos, cs, 0, pos)) + return true; + break; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// helpers + +char *CBlackWhiteSet::TrimWhiteSpaces(char *str) +{ + char *end; + + // Trim leading space & tabs + while((*str == ' ') || (*str == '\t')) str++; + + // All spaces? + if(*str == 0) + return str; + + // Trim trailing space, tab or lf + end = str + ::strlen(str) - 1; + while((end > str) && ((*end == ' ') || (*end == '\t') || (*end == '\r'))) end--; + + // Write new null terminator + *(end+1) = 0; + + return str; +} + +bool CBlackWhiteSet::GetLastModTime(time_t *time) +{ + bool ok = false; + + if ( !m_Filename.empty() ) + { + struct stat fileStat; + if( ::stat(m_Filename.c_str(), &fileStat) != -1 ) + { + *time = fileStat.st_mtime; + ok = true; + } + } + return ok; +} + +char *CBlackWhiteSet::ToUpper(char *str) +{ + constexpr auto diff = 'a' - 'A'; + for (char *p=str; *p; p++) + { + if (*p >= 'a' && *p <= 'z') + *p -= diff; + } + return str; +} diff --git a/reflector/BlackWhiteSet.h b/reflector/BlackWhiteSet.h new file mode 100644 index 0000000..cf3efa7 --- /dev/null +++ b/reflector/BlackWhiteSet.h @@ -0,0 +1,61 @@ +// +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of m17ref. +// +// m17ref 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 3 of the License, or +// (at your option) any later version. +// +// m17ref 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 +// with this software. If not, see . +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CBlackWhiteSet +{ +public: + // constructor + CBlackWhiteSet() : m_LastModTime(0) {} + + // locks + void Lock(void) const { m_Mutex.lock(); } + void Unlock(void) const { m_Mutex.unlock(); } + + // file io + bool LoadFromFile(const std::string &filename); + bool ReloadFromFile(void); + bool NeedReload(void); + + // pass-through + bool empty() const { return m_Callsigns.empty(); } + + // compare + bool IsMatched(const std::string &) const; + +protected: + bool GetLastModTime(time_t *); + char *TrimWhiteSpaces(char *); + char *ToUpper(char *str); + + // data + mutable std::mutex m_Mutex; + std::string m_Filename; + time_t m_LastModTime; + std::set m_Callsigns; +}; diff --git a/reflector/CNotificationQueue.h b/reflector/CNotificationQueue.h deleted file mode 100644 index f0bc210..0000000 --- a/reflector/CNotificationQueue.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2015 Jean-Luc. All rights reserved. - -// urfd -- The universal reflector -// Copyright © 2021 Thomas A. Early N7TAE -// -// 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 3 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, see . - -#pragma once - -#include -#include - -#include "Notification.h" - -class CNotificationQueue -{ -public: - // constructor - CNotificationQueue() {} - - // destructor - ~CNotificationQueue() {} - - // lock - void Lock() { m_Mutex.lock(); } - void Unlock() { m_Mutex.unlock(); } - - // pass thru - CNotification front() { return queue.front(); } - void pop() { queue.pop(); } - void push(CNotification note) { queue.push(note); } - bool empty() const { return queue.empty(); } - -protected: - // data - std::mutex m_Mutex; - std::queue queue; -}; diff --git a/reflector/Callsign.cpp b/reflector/Callsign.cpp index 765b6cb..b4b9666 100644 --- a/reflector/Callsign.cpp +++ b/reflector/Callsign.cpp @@ -349,6 +349,16 @@ UCallsign CCallsign::GetKey() const return rval; } +std::string CCallsign::GetBase() const +{ + auto u = GetKey(); + std::string rval(u.c, CALLSIGN_LEN); + auto pos = rval.find(' '); + if (std::string::npos != pos) + rval.resize(pos); + return rval; +} + void CCallsign::GetCallsign(uint8_t *buffer) const { memcpy(buffer, m_Callsign.c, CALLSIGN_LEN); @@ -404,16 +414,6 @@ bool CCallsign::HasSameCallsignWithWildcard(const CCallsign &cs) const return same; } -bool CCallsign::HasLowerCallsign(const CCallsign &cs) const -{ - return (memcmp(m_Callsign.c, cs.m_Callsign.c, CALLSIGN_LEN) < 0); -} - -bool CCallsign::HasSameModule(const CCallsign &Callsign) const -{ - return (m_Module == Callsign.m_Module); -} - //////////////////////////////////////////////////////////////////////////////////////// // operators diff --git a/reflector/Callsign.h b/reflector/Callsign.h index 49bb6d9..99ca10c 100644 --- a/reflector/Callsign.h +++ b/reflector/Callsign.h @@ -39,6 +39,23 @@ union USuffix uint32_t u; }; +// functions for unordered containers +struct CCallsignHash +{ + std::size_t operator() (const UCallsign &ucs) const + { + std::hash hash; + return hash(ucs.l); + } +}; + +struct CCallsignEqual +{ + bool operator() (const UCallsign &ucs1, const UCallsign &ucs2) const + { + return ucs1.l == ucs2.l; + } +}; //////////////////////////////////////////////////////////////////////////////////////// // class @@ -46,7 +63,7 @@ union USuffix class CCallsign { public: - // contructors + // constructors CCallsign(); CCallsign(const UCallsign &cs); // no id lookup CCallsign(const CCallsign &cs); @@ -73,6 +90,7 @@ public: // get UCallsign GetKey() const; + std::string GetBase() const; void GetCallsign(uint8_t *) const; void GetCallsignString(char *) const; const std::string GetCS() const; @@ -84,8 +102,6 @@ public: // compare bool HasSameCallsign(const CCallsign &) const; bool HasSameCallsignWithWildcard(const CCallsign &) const; - bool HasLowerCallsign(const CCallsign &) const; - bool HasSameModule(const CCallsign &) const; // operators CCallsign &operator = (const CCallsign &cs); diff --git a/reflector/CallsignList.cpp b/reflector/CallsignList.cpp deleted file mode 100644 index 954bb5a..0000000 --- a/reflector/CallsignList.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. - -// urfd -- The universal reflector -// Copyright © 2021 Thomas A. Early N7TAE -// -// 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 3 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, see . - -#include -#include -#include -#include - -#include "CallsignList.h" - -//////////////////////////////////////////////////////////////////////////////////////// -// constructor - -CCallsignList::CCallsignList() -{ - memset(&m_LastModTime, 0, sizeof(time_t)); -} - -//////////////////////////////////////////////////////////////////////////////////////// -// file io - -bool CCallsignList::LoadFromFile(const std::string &filename) -{ - bool ok = false; - char sz[256]; - char szStar[2] = "*"; - - // and load - std::ifstream file (filename); - if ( file.is_open() ) - { - Lock(); - - // empty list - m_Callsigns.clear(); - // fill with file content - while ( file.getline(sz, sizeof(sz)).good() ) - { - // remove leading & trailing spaces - char *szt = TrimWhiteSpaces(sz); - - // crack it - if ( (::strlen(szt) > 0) && (szt[0] != '#') ) - { - // 1st token is callsign - if ( (szt = ::strtok(szt, " ,\t")) != nullptr ) - { - CCallsign callsign(szt); - // 2nd token is modules list - szt = ::strtok(nullptr, " ,\t"); - // if token absent, use wildcard - if ( szt == nullptr ) - { - szt = szStar; - } - // and add to list - m_Callsigns.push_back(CCallsignListItem(callsign, CIp(), szt)); - } - } - } - // close file - file.close(); - - // keep file path - m_Filename.assign(filename); - - // update time - GetLastModTime(&m_LastModTime); - - // and done - Unlock(); - ok = true; - std::cout << "Gatekeeper loaded " << m_Callsigns.size() << " lines from " << filename << std::endl; - } - else - { - std::cout << "Gatekeeper cannot find " << filename << std::endl; - } - - return ok; -} - -bool CCallsignList::ReloadFromFile(void) -{ - bool ok = false; - - if (! m_Filename.empty()) - { - ok = LoadFromFile(m_Filename); - } - return ok; -} - -bool CCallsignList::NeedReload(void) -{ - bool needReload = false; - - time_t time; - if ( GetLastModTime(&time) ) - { - needReload = time != m_LastModTime; - } - return needReload; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// compare - -bool CCallsignList::IsCallsignListedWithWildcard(const CCallsign &callsign) const -{ - for ( const auto &item : m_Callsigns ) - { - if (item.HasSameCallsignWithWildcard(callsign)) - return true; - } - - return false; -} - -bool CCallsignList::IsCallsignListedWithWildcard(const CCallsign &callsign, char module) const -{ - for ( const auto &item : m_Callsigns ) - { - if (item.HasSameCallsignWithWildcard(callsign) && ((module == ' ') || item.HasModuleListed(module)) ) - return true; - } - - return false; -} - -bool CCallsignList::IsCallsignListed(const CCallsign &callsign, char module) const -{ - for ( const auto &item : m_Callsigns ) - { - if (item.HasSameCallsign(callsign) && item.HasModuleListed(module)) - return true; - } - - return false; -} - -bool CCallsignList::IsCallsignListed(const CCallsign &callsign, char *modules) const -{ - for ( const auto &item : m_Callsigns ) - { - if (item.HasSameCallsign(callsign) && item.CheckListedModules(modules)) - return true; - } - - return false; -} - -//////////////////////////////////////////////////////////////////////////////////////// -// find - -CCallsignListItem *CCallsignList::FindListItem(const CCallsign &Callsign) -{ - for ( auto &item : m_Callsigns ) - { - if ( item.GetCallsign().HasSameCallsign(Callsign) ) - { - return &item; - } - } - - return nullptr; - -} - -//////////////////////////////////////////////////////////////////////////////////////// -// helpers - -char *CCallsignList::TrimWhiteSpaces(char *str) -{ - char *end; - - // Trim leading space & tabs - while((*str == ' ') || (*str == '\t')) str++; - - // All spaces? - if(*str == 0) - return str; - - // Trim trailing space, tab or lf - end = str + ::strlen(str) - 1; - while((end > str) && ((*end == ' ') || (*end == '\t') || (*end == '\r'))) end--; - - // Write new null terminator - *(end+1) = 0; - - return str; -} - -bool CCallsignList::GetLastModTime(time_t *time) -{ - bool ok = false; - - if (! m_Filename.empty()) - { - struct stat fileStat; - if( ::stat(m_Filename.c_str(), &fileStat) != -1 ) - { - *time = fileStat.st_mtime; - ok = true; - } - } - return ok; -} diff --git a/reflector/CallsignList.h b/reflector/CallsignList.h deleted file mode 100644 index 3a221a1..0000000 --- a/reflector/CallsignList.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. - -// urfd -- The universal reflector -// Copyright © 2021 Thomas A. Early N7TAE -// -// 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 3 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, see . - -#pragma once - -#include -#include - -#include "CallsignListItem.h" - -//////////////////////////////////////////////////////////////////////////////////////// -// class - -class CCallsignList -{ -public: - // constructor - CCallsignList(); - - // locks - void Lock(void) const { m_Mutex.lock(); } - void Unlock(void) const { m_Mutex.unlock(); } - - // file io - virtual bool LoadFromFile(const std::string &str); - bool ReloadFromFile(void); - bool NeedReload(void); - - // compare - bool IsCallsignListedWithWildcard(const CCallsign &) const; - bool IsCallsignListedWithWildcard(const CCallsign &, char) const; - bool IsCallsignListed(const CCallsign &, char) const; - bool IsCallsignListed(const CCallsign &, char*) const; - - // pass-thru - bool empty() const { return m_Callsigns.empty(); } - std::list::iterator begin() { return m_Callsigns.begin(); } - std::list::iterator end() { return m_Callsigns.end(); } - - // find - CCallsignListItem *FindListItem(const CCallsign &); - -protected: - bool GetLastModTime(time_t *); - char *TrimWhiteSpaces(char *); - - // data - mutable std::mutex m_Mutex; - std::string m_Filename; - time_t m_LastModTime; - std::list m_Callsigns; -}; diff --git a/reflector/CallsignListItem.cpp b/reflector/CallsignListItem.cpp deleted file mode 100644 index 40328bf..0000000 --- a/reflector/CallsignListItem.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. - -// urfd -- The universal reflector -// Copyright © 2021 Thomas A. Early N7TAE -// -// 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 3 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, see . - -#include - -#include "Global.h" - -#include "CallsignListItem.h" - -//////////////////////////////////////////////////////////////////////////////////////// -// constructor - -CCallsignListItem::CCallsignListItem() -{ - memset(m_Modules, 0, sizeof(m_Modules)); - memset(m_szUrl, 0, sizeof(m_szUrl)); -} - -CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const CIp &ip, const char *modules) -{ - const std::string mods(g_Configure.GetString(g_Keys.modules.modules)); - m_Callsign = callsign; - memset(m_szUrl, 0, sizeof(m_szUrl)); - m_Ip = ip; - if ( modules != nullptr ) - { - memset(m_Modules, 0, sizeof(m_Modules)); - if ( modules[0] == '*' ) - { - memcpy(m_Modules, mods.c_str(), mods.size()); - } - else - { - int n = MIN(::strlen(modules), sizeof(m_Modules)-1); - for (int i=0, j=0; i. - -#pragma once - - -#include "Callsign.h" -#include "IP.h" - -//////////////////////////////////////////////////////////////////////////////////////// -// define - -#define URL_MAXLEN 256 - -//////////////////////////////////////////////////////////////////////////////////////// -// class - -class CCallsignListItem -{ -public: - // constructor - CCallsignListItem(); - CCallsignListItem(const CCallsign &, const CIp &, const char *); - CCallsignListItem(const CCallsign &, const char *, const char *); - CCallsignListItem(const CCallsignListItem &); - - // destructor - virtual ~CCallsignListItem() {} - - // compare - bool HasSameCallsign(const CCallsign &) const; - bool HasSameCallsignWithWildcard(const CCallsign &) const; - bool HasModuleListed(char) const; - bool CheckListedModules(char*) const; - - // get - const CCallsign &GetCallsign(void) const { return m_Callsign; } - const CIp &GetIp(void) const { return m_Ip; } - const char *GetModules(void) { return m_Modules; } - - // update - void ResolveIp(void) { m_Ip = CIp(m_szUrl); } - -protected: - // data - CCallsign m_Callsign; - char m_szUrl[URL_MAXLEN+1]; - CIp m_Ip; - char m_Modules[27]; -}; diff --git a/reflector/Client.h b/reflector/Client.h index 48fb022..75dd416 100644 --- a/reflector/Client.h +++ b/reflector/Client.h @@ -50,6 +50,8 @@ public: char GetCSModule(void) const { return m_Callsign.GetCSModule(); } bool HasReflectorModule(void) const { return m_ReflectorModule != ' '; } char GetReflectorModule(void) const { return m_ReflectorModule; } + std::time_t GetConnectTime(void) const { return m_ConnectTime; } + std::time_t GetLastHeardTime(void) const { return m_LastHeardTime; } // set void SetCSModule(char c) { m_Callsign.SetCSModule(c); } diff --git a/reflector/Clients.cpp b/reflector/Clients.cpp index fcddd63..4e0bca4 100644 --- a/reflector/Clients.cpp +++ b/reflector/Clients.cpp @@ -73,7 +73,7 @@ void CClients::RemoveClient(std::shared_ptr client) bool found = false; for ( auto it=begin(); it!=end(); it++ ) { - // compare objetc pointers + // compare object pointers if ( *it == client ) { // found it ! diff --git a/reflector/Clients.h b/reflector/Clients.h index e596076..322c3ef 100644 --- a/reflector/Clients.h +++ b/reflector/Clients.h @@ -47,7 +47,7 @@ public: void RemoveClient(std::shared_ptr); bool IsClient(std::shared_ptr) const; - // pass-thru + // pass-through std::list>::iterator begin() { return m_Clients.begin(); } std::list>::iterator end() { return m_Clients.end(); } std::list>::const_iterator cbegin() const { return m_Clients.cbegin(); } diff --git a/reflector/CodecStream.h b/reflector/CodecStream.h index 74db476..1576f1d 100644 --- a/reflector/CodecStream.h +++ b/reflector/CodecStream.h @@ -50,7 +50,7 @@ public: void Thread(void); void Task(void); - // pass-thru + // pass-through void Push(std::unique_ptr p) { m_Queue.Push(std::move(p)); } protected: diff --git a/reflector/Configure.cpp b/reflector/Configure.cpp index 35dfa3a..21b837e 100644 --- a/reflector/Configure.cpp +++ b/reflector/Configure.cpp @@ -33,9 +33,11 @@ // ini file keywords #define JAUTOLINKMODULE "AutoLinkModule" #define JBLACKLISTPATH "BlacklistPath" +#define JBOOTSTRAP "Bootstrap" #define JBRANDMEISTER "Brandmeister" #define JCALLSIGN "Callsign" #define JCOUNTRY "Country" +#define JDASHBOARDURL "DashboardUrl" #define JDCS "DCS" #define JDEFAULTID "DefaultId" #define JDEFAULTRXFREQ "DefaultRxFreq" @@ -250,6 +252,10 @@ bool CConfigure::ReadData(const std::string &path) case ESection::names: if (0 == key.compare(JCALLSIGN)) data[g_Keys.names.callsign] = value; + else if (0 == key.compare(JBOOTSTRAP)) + data[g_Keys.names.bootstrap] = value; + else if (0 == key.compare(JDASHBOARDURL)) + data[g_Keys.names.url] = value; else if (0 == key.compare(JSYSOPEMAIL)) data[g_Keys.names.email] = value; else if (0 == key.compare(JCOUNTRY)) @@ -487,7 +493,7 @@ bool CConfigure::ReadData(const std::string &path) badParam(key); break; default: - std::cout << "WARNING: parameter '" << line << "' defined befor any [section]" << std::endl; + std::cout << "WARNING: parameter '" << line << "' defined before any [section]" << std::endl; } } @@ -506,6 +512,10 @@ bool CConfigure::ReadData(const std::string &path) } } +#ifndef NO_DHT + isDefined(ErrorLevel::fatal, JNAMES, JBOOTSTRAP, g_Keys.names.bootstrap, rval); +#endif + isDefined(ErrorLevel::fatal, JNAMES, JDASHBOARDURL, g_Keys.names.url, rval); isDefined(ErrorLevel::mild, JNAMES, JSYSOPEMAIL, g_Keys.names.email, rval); isDefined(ErrorLevel::mild, JNAMES, JCOUNTRY, g_Keys.names.country, rval); isDefined(ErrorLevel::mild, JNAMES, JSPONSOR, g_Keys.names.sponsor, rval); @@ -526,7 +536,7 @@ bool CConfigure::ReadData(const std::string &path) } else { - std::cerr << "ERROR: specifed IPv4 external address, " << v4 << ", is malformed" << std::endl; + std::cerr << "ERROR: specified IPv4 external address, " << v4 << ", is malformed" << std::endl; rval = true; } } @@ -564,7 +574,7 @@ bool CConfigure::ReadData(const std::string &path) } else { - std::cerr << "ERROR: the specifed IPv6 address [" << v6 << "] is malformed" << std::endl; + std::cerr << "ERROR: the specified IPv6 address [" << v6 << "] is malformed" << std::endl; rval = true; } } diff --git a/reflector/DMRPlusProtocol.cpp b/reflector/DMRPlusProtocol.cpp index 4de295f..fefc957 100644 --- a/reflector/DMRPlusProtocol.cpp +++ b/reflector/DMRPlusProtocol.cpp @@ -449,7 +449,7 @@ bool CDmrplusProtocol::IsValidDvFramePacket(const CIp &Ip, const CBuffer &Buffer uint8_t dmrsync[7]; // get the 33 bytes ambe memcpy(dmrframe, &(Buffer.data()[26]), 33); - // handle endianess + // handle endianness SwapEndianess(dmrframe, sizeof(dmrframe)); // extract the 3 ambe frames memcpy(dmr3ambe, dmrframe, 14); diff --git a/reflector/G3Protocol.cpp b/reflector/G3Protocol.cpp index 7c15ca8..8d5e24b 100644 --- a/reflector/G3Protocol.cpp +++ b/reflector/G3Protocol.cpp @@ -390,7 +390,7 @@ void CG3Protocol::Task(void) { BaseIp = &ClIp; client->Alive(); - // supress host checks - no ping needed to trigger potential ICMPs + // suppress host checks - no ping needed to trigger potential ICMPs // the regular data flow will do it m_LastKeepaliveTime.start(); break; @@ -444,7 +444,7 @@ void CG3Protocol::HandleQueue(void) { while (! m_Queue.IsEmpty()) { - // supress host checks + // suppress host checks m_LastKeepaliveTime.start(); // get the packet diff --git a/reflector/GateKeeper.cpp b/reflector/GateKeeper.cpp index 8afb9f3..b663592 100644 --- a/reflector/GateKeeper.cpp +++ b/reflector/GateKeeper.cpp @@ -44,9 +44,9 @@ bool CGateKeeper::Init(void) { // load lists from files - m_NodeWhiteList.LoadFromFile(g_Configure.GetString(g_Keys.files.white)); - m_NodeBlackList.LoadFromFile(g_Configure.GetString(g_Keys.files.black)); - m_PeerList.LoadFromFile(g_Configure.GetString(g_Keys.files.interlink)); + m_WhiteSet.LoadFromFile(g_Configure.GetString(g_Keys.files.white)); + m_BlackSet.LoadFromFile(g_Configure.GetString(g_Keys.files.black)); + m_InterlinkMap.LoadFromFile(g_Configure.GetString(g_Keys.files.interlink)); // reset run flag keep_running = true; @@ -72,7 +72,8 @@ void CGateKeeper::Close(void) bool CGateKeeper::MayLink(const CCallsign &callsign, const CIp &ip, EProtocol protocol, char *modules) const { - bool ok = true; + bool ok; + const std::string base(callsign.GetBase()); switch (protocol) { @@ -88,26 +89,24 @@ bool CGateKeeper::MayLink(const CCallsign &callsign, const CIp &ip, EProtocol pr case EProtocol::usrp: case EProtocol::nxdn: case EProtocol::g3: - // first check is IP & callsigned listed OK - ok &= IsNodeListedOk(callsign, ip); - // todo: then apply any protocol specific authorisation for the operation + // is callsign listed OK + ok = IsNodeListedOk(base); break; // URF and BM interlinks case EProtocol::bm: case EProtocol::urf: - ok &= IsPeerListedOk(callsign, ip, modules); + ok = IsPeerListedOk(base, ip, modules); break; // unsupported - case EProtocol::none: default: ok = false; break; } // report - if ( !ok ) + if ( ! ok ) { std::cout << "Gatekeeper blocking linking of " << callsign << " @ " << ip << " using protocol " << ProtocolName(protocol) << std::endl; } @@ -118,7 +117,9 @@ bool CGateKeeper::MayLink(const CCallsign &callsign, const CIp &ip, EProtocol pr bool CGateKeeper::MayTransmit(const CCallsign &callsign, const CIp &ip, const EProtocol protocol, char module) const { - bool ok = true; + bool ok; + + const std::string base(callsign.GetBase()); switch (protocol) { @@ -136,14 +137,14 @@ bool CGateKeeper::MayTransmit(const CCallsign &callsign, const CIp &ip, const EP case EProtocol::usrp: case EProtocol::g3: // first check is IP & callsigned listed OK - ok = ok && IsNodeListedOk(callsign, ip, module); + ok = IsNodeListedOk(base); // todo: then apply any protocol specific authorisation for the operation break; // URF interlinks case EProtocol::urf: case EProtocol::bm: - ok = ok && IsPeerListedOk(callsign, ip, module); + ok = IsPeerListedOk(base, module); break; // unsupported @@ -175,17 +176,17 @@ void CGateKeeper::Thread() std::this_thread::sleep_for(std::chrono::milliseconds(2000)); // have lists files changed ? - if ( m_NodeWhiteList.NeedReload() ) + if ( m_WhiteSet.NeedReload() ) { - m_NodeWhiteList.ReloadFromFile(); + m_WhiteSet.ReloadFromFile(); } - if ( m_NodeBlackList.NeedReload() ) + if ( m_BlackSet.NeedReload() ) { - m_NodeBlackList.ReloadFromFile(); + m_BlackSet.ReloadFromFile(); } - if ( m_PeerList.NeedReload() ) + if ( m_InterlinkMap.NeedReload() ) { - m_PeerList.ReloadFromFile(); + m_InterlinkMap.ReloadFromFile(); } } } @@ -193,30 +194,28 @@ void CGateKeeper::Thread() //////////////////////////////////////////////////////////////////////////////////////// // operation helpers -bool CGateKeeper::IsNodeListedOk(const CCallsign &callsign, const CIp &ip, char module) const +bool CGateKeeper::IsNodeListedOk(const std::string &callsign) const { bool ok = true; - // first check IP - // next, check callsign if ( ok ) { // first check if callsign is in white list // note if white list is empty, everybody is authorized - m_NodeWhiteList.Lock(); - if ( !m_NodeWhiteList.empty() ) + m_WhiteSet.Lock(); + if ( ! m_WhiteSet.empty() ) { - ok = m_NodeWhiteList.IsCallsignListedWithWildcard(callsign, module); + ok = m_WhiteSet.IsMatched(callsign); } - m_NodeWhiteList.Unlock(); + m_WhiteSet.Unlock(); // then check if not blacklisted if (ok) { - m_NodeBlackList.Lock(); - ok = !m_NodeBlackList.IsCallsignListedWithWildcard(callsign); - m_NodeBlackList.Unlock(); + m_BlackSet.Lock(); + ok = ! m_BlackSet.IsMatched(callsign); + m_BlackSet.Unlock(); } } @@ -225,7 +224,7 @@ bool CGateKeeper::IsNodeListedOk(const CCallsign &callsign, const CIp &ip, char } -bool CGateKeeper::IsPeerListedOk(const CCallsign &callsign, const CIp &ip, char module) const +bool CGateKeeper::IsPeerListedOk(const std::string &callsign, char module) const { bool ok = true; @@ -235,19 +234,19 @@ bool CGateKeeper::IsPeerListedOk(const CCallsign &callsign, const CIp &ip, char if ( ok ) { // look for an exact match in the list - m_PeerList.Lock(); - if ( !m_PeerList.empty() ) + m_InterlinkMap.Lock(); + if ( !m_InterlinkMap.empty() ) { - ok = m_PeerList.IsCallsignListed(callsign, module); + ok = m_InterlinkMap.IsCallsignListed(callsign, module); } - m_PeerList.Unlock(); + m_InterlinkMap.Unlock(); } // done return ok; } -bool CGateKeeper::IsPeerListedOk(const CCallsign &callsign, const CIp &ip, char *modules) const +bool CGateKeeper::IsPeerListedOk(const std::string &callsign, const CIp &ip, char *modules) const { bool ok = true; @@ -257,12 +256,12 @@ bool CGateKeeper::IsPeerListedOk(const CCallsign &callsign, const CIp &ip, char if ( ok ) { // look for an exact match in the list - m_PeerList.Lock(); - if ( !m_PeerList.empty() ) + m_InterlinkMap.Lock(); + if ( ! m_InterlinkMap.empty() ) { - ok = m_PeerList.IsCallsignListed(callsign, modules); + ok = m_InterlinkMap.IsCallsignListed(callsign, ip, modules); } - m_PeerList.Unlock(); + m_InterlinkMap.Unlock(); } // done diff --git a/reflector/GateKeeper.h b/reflector/GateKeeper.h index 4b5814d..619dd86 100644 --- a/reflector/GateKeeper.h +++ b/reflector/GateKeeper.h @@ -22,8 +22,8 @@ #include "Defines.h" #include "Callsign.h" #include "IP.h" -#include "CallsignList.h" -#include "PeerCallsignList.h" +#include "BlackWhiteSet.h" +#include "InterlinkMap.h" //////////////////////////////////////////////////////////////////////////////////////// // class @@ -41,29 +41,27 @@ public: bool Init(void); void Close(void); + CInterlinkMap *GetInterlinkMap(void) { m_InterlinkMap.Lock(); return &m_InterlinkMap; } + void ReleaseInterlinkMap(void) { m_InterlinkMap.Unlock(); } + // authorizations bool MayLink(const CCallsign &, const CIp &, const EProtocol, char * = nullptr) const; bool MayTransmit(const CCallsign &, const CIp &, EProtocol = EProtocol::any, char = ' ') const; - // peer list handeling - CPeerCallsignList *GetPeerList(void) { m_PeerList.Lock(); return &m_PeerList; } - void ReleasePeerList(void) { m_PeerList.Unlock(); } - protected: // thread void Thread(); // operation helpers - bool IsNodeListedOk(const CCallsign &, const CIp &, char = ' ') const; - bool IsPeerListedOk(const CCallsign &, const CIp &, char) const; - bool IsPeerListedOk(const CCallsign &, const CIp &, char *) const; + bool IsNodeListedOk(const std::string &) const; + bool IsPeerListedOk(const std::string &, char) const; + bool IsPeerListedOk(const std::string &, const CIp &, char *) const; const std::string ProtocolName(EProtocol) const; protected: // data - CCallsignList m_NodeWhiteList; - CCallsignList m_NodeBlackList; - CPeerCallsignList m_PeerList; + CBlackWhiteSet m_WhiteSet, m_BlackSet; + CInterlinkMap m_InterlinkMap; // thread std::atomic keep_running; diff --git a/reflector/Golay2087.cpp b/reflector/Golay2087.cpp index 3e34543..c43e660 100644 --- a/reflector/Golay2087.cpp +++ b/reflector/Golay2087.cpp @@ -219,7 +219,7 @@ unsigned int CGolay2087::getSyndrome1987(unsigned int pattern) * Compute the syndrome corresponding to the given pattern, i.e., the * remainder after dividing the pattern (when considering it as the vector * representation of a polynomial) by the generator polynomial, GENPOL. - * In the program this pattern has several meanings: (1) pattern = infomation + * In the program this pattern has several meanings: (1) pattern = information * bits, when constructing the encoding table; (2) pattern = error pattern, * when constructing the decoding table; and (3) pattern = received vector, to * obtain its syndrome in decoding. diff --git a/reflector/Golay24128.cpp b/reflector/Golay24128.cpp index 6f810e6..fc1ed18 100644 --- a/reflector/Golay24128.cpp +++ b/reflector/Golay24128.cpp @@ -1055,7 +1055,7 @@ static unsigned int get_syndrome_23127(unsigned int pattern) * Compute the syndrome corresponding to the given pattern, i.e., the * remainder after dividing the pattern (when considering it as the vector * representation of a polynomial) by the generator polynomial, GENPOL. - * In the program this pattern has several meanings: (1) pattern = infomation + * In the program this pattern has several meanings: (1) pattern = information * bits, when constructing the encoding table; (2) pattern = error pattern, * when constructing the decoding table; and (3) pattern = received vector, to * obtain its syndrome in decoding. diff --git a/reflector/Hamming.cpp b/reflector/Hamming.cpp index 42636f0..1980a1f 100644 --- a/reflector/Hamming.cpp +++ b/reflector/Hamming.cpp @@ -224,7 +224,7 @@ bool CHamming::decode1393(bool* d) d[12] = !d[12]; return true; - // Data bit erros + // Data bit errors case 0x0FU: d[0] = !d[0]; return true; @@ -303,7 +303,7 @@ bool CHamming::decode1063(bool* d) d[9] = !d[9]; return true; - // Data bit erros + // Data bit errors case 0x07U: d[0] = !d[0]; return true; diff --git a/reflector/InterlinkMap.cpp b/reflector/InterlinkMap.cpp new file mode 100644 index 0000000..4e32600 --- /dev/null +++ b/reflector/InterlinkMap.cpp @@ -0,0 +1,261 @@ +// +// ccallsignlist.cpp +// m17ref +// +// Created by Jean-Luc Deltombe (LX3JL) on 30/12/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020,2022 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of m17ref. +// +// m17ref 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 3 of the License, or +// (at your option) any later version. +// +// m17ref 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 +// with this software. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include + +#include "Global.h" +#include "InterlinkMap.h" + +CInterlinkMap::CInterlinkMap() +{ + m_Filename.clear(); + ::memset(&m_LastModTime, 0, sizeof(time_t)); +} + +bool CInterlinkMap::LoadFromFile(const std::string &filename) +{ + bool ok = false; + char line[256]; + + // and load + std::ifstream file(filename); + if ( file.is_open() ) + { + Lock(); + + // empty list + m_InterlinkMap.clear(); + // fill with file content + while ( file.getline(line, sizeof(line)).good() ) + { + char *token[4]; + // remove leading & trailing spaces + token[0] = ToUpper(TrimWhiteSpaces(line)); + // crack it + if ( (strlen(token[0]) > 0) && (token[0][0] != '#') ) + { + const char *delim = " \t\r"; + // 1st token is callsign + if ( (token[0] = strtok(token[0], delim)) != nullptr ) + { + if (strcmp(token[0], g_Configure.GetString(g_Keys.names.callsign).c_str())) + { + // the default port depends on the protocol type (URF or BM) + int default_port = (0 == memcmp(token[0], "URF", 3)) ? 10017 : 10002; + if (m_InterlinkMap.end() == m_InterlinkMap.find(token[0])) + { + // read remaining tokens + // 1=IP 2=Modules 3=Port Port is optional and defaults to 10017 + // OR... 1=Modules and the dht will be used + for (int i=1; i<4; i++) + { + token[i] = strtok(nullptr, delim); + } + + if (token[2]) + { + int port = default_port; + if (token[3]) + { + port = std::atoi(token[3]); + if (port < 1024 || port > 49000) + { + std::cout << token[0] << " Port " << port << " is out of range, resetting to " << default_port << std::endl; + port = default_port; + } + } + m_InterlinkMap[token[0]] = CInterlinkMapItem(token[1], token[2], (uint16_t)port); + } +#ifndef NO_DHT + else if (token[1]) + { + m_InterlinkMap[token[0]] = CInterlinkMapItem(token[1]); + } +#endif + else + { + std::cout << token[0] << " has insufficient parameters!" << std::endl; + } + } + else + { + std::cerr << "Duplicate found: " << token[0] << " in " << filename << std::endl; + } + } + else + { + std::cerr << "Self linking is not allowed! You cannot use " << token[0] << " in " << filename << std::endl; + } + } + } + } + // close file + file.close(); + + // keep file path + m_Filename.assign(filename); + + // update time + GetLastModTime(&m_LastModTime); + + // and done + Unlock(); + ok = true; + std::cout << "Gatekeeper loaded " << m_InterlinkMap.size() << " lines from " << filename << std::endl; + } + else + { + std::cout << "Gatekeeper cannot find " << filename << std::endl; + } + + return ok; +} + +bool CInterlinkMap::ReloadFromFile(void) +{ + bool ok = false; + + if ( ! m_Filename.empty() ) + { + ok = LoadFromFile(m_Filename); + } + return ok; +} + +bool CInterlinkMap::NeedReload(void) +{ + bool needReload = false; + + time_t time; + if ( GetLastModTime(&time) ) + { + needReload = time != m_LastModTime; + } + return needReload; +} + +bool CInterlinkMap::IsCallsignListed(const std::string &callsign, char module) const +{ + const auto item = m_InterlinkMap.find(callsign); + if (m_InterlinkMap.cend() == item) + return false; + else + return item->second.HasModuleListed(module); +} + +bool CInterlinkMap::IsCallsignListed(const std::string &callsign, const CIp &ip, const char *modules) const +{ + const auto item = m_InterlinkMap.find(callsign); + if (m_InterlinkMap.cend() != item) + { + if ( item->second.CheckListedModules(modules) ) + { + if ( ip == item->second.GetIp() ) + { + return true; + } + } + } + else + { + std::cout << "'" << callsign << "' not found in interlink map\n"; + } + + return false; +} + +CInterlinkMapItem *CInterlinkMap::FindMapItem(const std::string &cs) +{ + auto it = m_InterlinkMap.find(cs); + if (m_InterlinkMap.end() == it) + return nullptr; + return &it->second; +} + +char *CInterlinkMap::TrimWhiteSpaces(char *str) +{ + char *end; + + // Trim leading space & tabs + while((*str == ' ') || (*str == '\t')) str++; + + // All spaces? + if(*str == 0) + return str; + + // Trim trailing space, tab or lf + end = str + ::strlen(str) - 1; + while((end > str) && ((*end == ' ') || (*end == '\t') || (*end == '\r'))) end--; + + // Write new null terminator + *(end+1) = 0; + + return str; +} + +bool CInterlinkMap::GetLastModTime(time_t *time) +{ + bool ok = false; + + if ( ! m_Filename.empty() ) + { + struct stat fileStat; + if( ::stat(m_Filename.c_str(), &fileStat) != -1 ) + { + *time = fileStat.st_mtime; + ok = true; + } + } + return ok; +} + +char *CInterlinkMap::ToUpper(char *str) +{ + constexpr auto diff = 'a' - 'A'; + for (char *p=str; *p; p++) + { + if (*p >= 'a' && *p <= 'z') + *p -= diff; + } + return str; +} + +#ifndef NO_DHT +void CInterlinkMap::Update(const std::string &cs, const std::string &cmods, const std::string &ipv4, const std::string &ipv6, uint16_t port, const std::string &emods) +{ + auto it = m_InterlinkMap.find(cs); + if (m_InterlinkMap.end() == it) + { + std::cerr << "Can't Update CInterlinkMap item '" << cs << "' because it doesn't exist!"; + } + else + { + it->second.UpdateItem(cmods, ipv4, ipv6, port, emods); + } +} +#endif diff --git a/reflector/InterlinkMap.h b/reflector/InterlinkMap.h new file mode 100644 index 0000000..3e78650 --- /dev/null +++ b/reflector/InterlinkMap.h @@ -0,0 +1,79 @@ +// +// Created by Jean-Luc Deltombe (LX3JL) on 30/12/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of m17ref. +// +// m17ref 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 3 of the License, or +// (at your option) any later version. +// +// m17ref 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 +// with this software. If not, see . +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include + +#include "InterlinkMapItem.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CInterlinkMap +{ +public: + // constructor + CInterlinkMap(); + + // destructor + virtual ~CInterlinkMap() {} + + // locks + void Lock(void) const { m_Mutex.lock(); } + void Unlock(void) const { m_Mutex.unlock(); } + + // file io + virtual bool LoadFromFile(const std::string &filename); + bool ReloadFromFile(void); + bool NeedReload(void); + +#ifndef NO_DHT + void Update(const std::string &cs, const std::string &mods, const std::string &ipv4, const std::string &ipv6, uint16_t port, const std::string &tcmods); +#endif + + // compare + bool IsCallsignListed(const std::string &, const char) const; + bool IsCallsignListed(const std::string &, const CIp &ip, const char*) const; + + // pass-through + bool empty() const { return m_InterlinkMap.empty(); } + std::map::iterator begin() { return m_InterlinkMap.begin(); } + std::map::iterator end() { return m_InterlinkMap.end(); } + std::map::const_iterator cbegin() { return m_InterlinkMap.cbegin(); } + std::map::const_iterator cend() { return m_InterlinkMap.cend(); } + + // find + CInterlinkMapItem *FindMapItem(const std::string &); + +protected: + bool GetLastModTime(time_t *); + char *TrimWhiteSpaces(char *); + char *ToUpper(char *str); + + // data + mutable std::mutex m_Mutex; + std::string m_Filename; + time_t m_LastModTime; + std::map m_InterlinkMap; +}; diff --git a/reflector/InterlinkMapItem.cpp b/reflector/InterlinkMapItem.cpp new file mode 100644 index 0000000..237bff3 --- /dev/null +++ b/reflector/InterlinkMapItem.cpp @@ -0,0 +1,143 @@ +// +// ccallsignlistitem.cpp +// m17ref +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020,2022 Thomas A. Early N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of m17ref. +// +// m17ref 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 3 of the License, or +// (at your option) any later version. +// +// m17ref 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 +// with this software. If not, see . +// ---------------------------------------------------------------------------- + +#include + +#include "Configure.h" +#include "InterlinkMapItem.h" +#include "Reflector.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor +#ifdef NO_DHT +CInterlinkMapItem::CInterlinkMapItem() +{ + m_UsesDHT = false; +} +#else +CInterlinkMapItem::CInterlinkMapItem() +{ + m_UsesDHT = false; + m_Updated = false; +} +#endif + +#ifndef NO_DHT +CInterlinkMapItem::CInterlinkMapItem(const char *mods) +{ + m_UsesDHT = true; + m_Updated = false; + m_Mods.assign(mods); +} +#endif + +CInterlinkMapItem::CInterlinkMapItem(const char *addr, const char *mods, uint16_t port) : CInterlinkMapItem() +{ + m_Mods.assign(mods); + m_Ip.Initialize(strchr(addr, ':') ? AF_INET6 : AF_INET, port, addr); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// compare + +bool CInterlinkMapItem::HasModuleListed(char module) const +{ + return m_Mods.npos != m_Mods.find(module); +} + +bool CInterlinkMapItem::HasSameIp(const CIp &ip) +{ + return ip == m_Ip; +} + +bool CInterlinkMapItem::CheckListedModules(const char *mods) const +{ + if (mods == nullptr) + return false; + + // make sure every mods character is matched in m_Mods + const auto count = m_Mods.size(); + bool found[count]; + for (unsigned i=0; i. +// ---------------------------------------------------------------------------- + +#pragma once + +#include + +#include "Callsign.h" +#include "IP.h" + +class CInterlinkMapItem +{ +public: + // constructor + CInterlinkMapItem(); +#ifndef NO_DHT + CInterlinkMapItem(const char *mods); +#endif + CInterlinkMapItem(const char *addr, const char *mods, uint16_t port); + + // Update things +#ifndef NO_DHT + void UpdateIP(bool IPv6NotConfigured); + void UpdateItem(const std::string &cmods, const std::string &ipv4, const std::string &ipv6, uint16_t port, const std::string &tcmods); +#endif + + // compare + bool HasSameIp(const CIp &ip); + bool HasModuleListed(char) const; + bool CheckListedModules(const char*) const; + + // get + const CIp &GetIp(void) const { return m_Ip; } + const std::string &GetModules(void) const { return m_Mods; } + bool UsesDHT(void) const { return m_UsesDHT; } + uint16_t GetPort(void) const { return m_Port; } +#ifndef NO_DHT + const std::string &GetIPv4(void) const { return m_IPv4; } + const std::string &GetIPv6(void) const { return m_IPv6; } + const std::string &GetTCMods(void) const { return m_TCMods; } + const std::string &GetCMods(void) const { return m_CMods; } +#endif + +private: + // data + CIp m_Ip; + std::string m_Mods; + uint16_t m_Port; + bool m_UsesDHT; + +#ifndef NO_DHT + bool m_Updated; + std::string m_CMods, m_TCMods, m_IPv4, m_IPv6; +#endif +}; diff --git a/reflector/JsonKeys.h b/reflector/JsonKeys.h index f604e30..ab19407 100644 --- a/reflector/JsonKeys.h +++ b/reflector/JsonKeys.h @@ -39,8 +39,8 @@ struct SJsonKeys { struct MMDVM { const std::string port, defaultid; } mmdvm { "MMDVMPort", "mmdvmdefaultid" }; - struct NAMES { const std::string callsign, email, country, sponsor; } - names { "Callsign", "SysopEmail", "Country", "Sponsor" }; + struct NAMES { const std::string callsign, bootstrap, url, email, country, sponsor; } + names { "Callsign", "bootstrap", "DashboardUrl", "SysopEmail", "Country", "Sponsor" }; struct IP { const std::string ipv4bind, ipv4address, ipv6bind, ipv6address, transcoder; } ip { "ipv4bind", "IPv4Address", "ipv6bind", "IPv6Address", "tcaddress" }; diff --git a/reflector/Lookup.h b/reflector/Lookup.h index e7edd78..3f9a46e 100644 --- a/reflector/Lookup.h +++ b/reflector/Lookup.h @@ -27,26 +27,6 @@ enum class Eaction { normal, parse, error_only }; enum class Esource { http, file }; -// compare function for std::map::find - -struct CCallsignHash -{ - std::size_t operator() (const UCallsign &ucs) const - { - std::hash hash; - return hash(ucs.l); - } -}; - -struct CCallsignEqual -{ - bool operator() (const UCallsign &ucs1, const UCallsign &ucs2) const - { - return ucs1.l == ucs2.l; - } -}; - - //////////////////////////////////////////////////////////////////////////////////////// class CLookup diff --git a/reflector/M17CRC.cpp b/reflector/M17CRC.cpp index 6e4587a..fa46504 100644 --- a/reflector/M17CRC.cpp +++ b/reflector/M17CRC.cpp @@ -29,7 +29,7 @@ * Description * ----------- * The source file contains routines which calculate the CCITT CRC - * values for an incomming byte string. + * values for an incoming byte string. */ #include diff --git a/reflector/M17CRC.h b/reflector/M17CRC.h index c6113fb..adc3d9c 100644 --- a/reflector/M17CRC.h +++ b/reflector/M17CRC.h @@ -29,7 +29,7 @@ * Description * ----------- * The source file contains routines which calculate the CCITT CRC - * values for an incomming byte string. + * values for an incoming byte string. */ #pragma once diff --git a/reflector/Main.cpp b/reflector/Main.cpp index 098d35a..2ebd59b 100644 --- a/reflector/Main.cpp +++ b/reflector/Main.cpp @@ -28,7 +28,7 @@ SJsonKeys g_Keys; CReflector g_Reflector; CGateKeeper g_GateKeeper; CConfigure g_Configure; -CVersion g_Version(3,0,1); // The major byte should only change if the interlink packet changes! +CVersion g_Version(3,1,0); // The major byte should only change if the interlink packet changes! CLookupDmr g_LDid; CLookupNxdn g_LNid; CLookupYsf g_LYtr; @@ -39,7 +39,7 @@ int main(int argc, char *argv[]) { if (argc != 2) { - std::cerr << "No configuration file specifed! Usage: " << argv[0] << " /pathname/to/configuration/file" << std::endl; + std::cerr << "No configuration file specified! Usage: " << argv[0] << " /pathname/to/configuration/file" << std::endl; return EXIT_FAILURE; } @@ -98,13 +98,13 @@ static void usage(std::ostream &os, const char *name) " ysf : The Callsign => Tx/Rx frequency database.\n" "SOURCE (choose one)\n" " file : The file specified by the FilePath ini parameter.\n" - " http : The URL specified by the URL ini paramater.\n" + " http : The URL specified by the URL ini parameter.\n" "ACTION (choose one)\n" " print : Print all lines from the SOURCE that are syntactically correct.\n" " error : Print only the lines with failed syntax.\n" "INIFILE : an error-free urfd ini file (check it first with inicheck).\n\n" "Only the first character of DATABASE, SOURCE and ACTION is read.\n" - "Example: " << name << " y f e urfd.ini # Check your YSF Tx/Rx database file specifed in urfd.ini for syntax errors.\n\n"; + "Example: " << name << " y f e urfd.ini # Check your YSF Tx/Rx database file specified in urfd.ini for syntax errors.\n\n"; } enum class Edb { none, dmr, nxdn, ysf }; diff --git a/reflector/Makefile b/reflector/Makefile index c9c8272..3116642 100644 --- a/reflector/Makefile +++ b/reflector/Makefile @@ -27,13 +27,19 @@ DBUTIL = dbutil include urfd.mk ifeq ($(debug), true) -CFLAGS = -ggdb3 -DDEBUG -W -Werror -std=c++11 -MMD -MD +CFLAGS = -ggdb3 -DDEBUG -W -Werror -std=c++17 -MMD -MD else -CFLAGS = -W -Werror -std=c++11 -MMD -MD +CFLAGS = -W -Werror -std=c++17 -MMD -MD endif LDFLAGS=-pthread -lcurl +ifeq ($(DHT), true) +LDFLAGS += -lopendht +else +CFLAGS += -DNO_DHT +endif + SRCS = $(wildcard *.cpp) OBJS = $(SRCS:.cpp=.o) DEPS = $(SRCS:.cpp=.d) diff --git a/reflector/Notification.cpp b/reflector/Notification.cpp deleted file mode 100644 index 6808a02..0000000 --- a/reflector/Notification.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © 2015 Jean-Luc. All rights reserved. - -// urfd -- The universal reflector -// Copyright © 2021 Thomas A. Early N7TAE -// -// 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 3 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, see . - - -#include "Notification.h" - -CNotification::CNotification() -{ - // init variables - m_iId = NOTIFICATION_NONE; -} - -CNotification::CNotification(int iId) -{ - m_iId = iId; -} - -CNotification::CNotification(int iId, const CCallsign &Callsign) -{ - m_iId = iId; - m_Callsign = Callsign; -} diff --git a/reflector/Notification.h b/reflector/Notification.h deleted file mode 100644 index 3db4e0b..0000000 --- a/reflector/Notification.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright © 2015 Jean-Luc. All rights reserved. - -// urfd -- The universal reflector -// Copyright © 2021 Thomas A. Early N7TAE -// -// 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 3 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, see . - -#pragma once - -#include "Callsign.h" - -//////////////////////////////////////////////////////////////////////////////////////// - -// Id -#define NOTIFICATION_NONE 0 -#define NOTIFICATION_CLIENTS 1 -#define NOTIFICATION_USERS 2 -#define NOTIFICATION_STREAM_OPEN 3 -#define NOTIFICATION_STREAM_CLOSE 4 -#define NOTIFICATION_PEERS 5 - -//////////////////////////////////////////////////////////////////////////////////////// -// class - -class CNotification -{ -public: - // constructor - CNotification(); - CNotification(int); - CNotification(int, const CCallsign &); - - // get - int GetId(void) const { return m_iId; } - const CCallsign &GetCallsign(void) const { return m_Callsign; } - -protected: - // data - int m_iId; - CCallsign m_Callsign; - -}; diff --git a/reflector/NotificationQueue.h b/reflector/NotificationQueue.h deleted file mode 100644 index 70b894a..0000000 --- a/reflector/NotificationQueue.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2015 Jean-Luc. All rights reserved. - -// urfd -- The universal reflector -// Copyright © 2021 Thomas A. Early N7TAE -// -// 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 3 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, see . - -#pragma once - -#include -#include - -#include "Notification.h" - - -//////////////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////////////// -// class - -class CNotificationQueue -{ -public: - // lock - void Lock() { m_Mutex.lock(); } - void Unlock() { m_Mutex.unlock(); } - - // pass thru - CNotification front() { return queue.front(); } - void pop() { queue.pop(); } - void push(CNotification note) { queue.push(note); } - bool empty() const { return queue.empty(); } - -protected: - // data - std::mutex m_Mutex; - std::queue queue; -}; diff --git a/reflector/Packet.cpp b/reflector/Packet.cpp index ed4d3a1..2700369 100644 --- a/reflector/Packet.cpp +++ b/reflector/Packet.cpp @@ -88,7 +88,7 @@ void CPacket::EncodeInterlinkPacket(const char *magic, CBuffer &buf) const data[19] = m_uiYsfPacketFrameId; } -// dstar contstructor +// dstar constructor CPacket::CPacket(uint16_t sid, uint8_t dstarpid) { m_uiStreamId = sid; diff --git a/reflector/Peer.h b/reflector/Peer.h index bc1ddf9..4565493 100644 --- a/reflector/Peer.h +++ b/reflector/Peer.h @@ -42,6 +42,7 @@ public: const CCallsign &GetCallsign(void) const { return m_Callsign; } const CIp &GetIp(void) const { return m_Ip; } char *GetReflectorModules(void) { return m_ReflectorModules; } + std::time_t GetConnectTime(void) const { return m_ConnectTime; } // set @@ -60,7 +61,7 @@ public: int GetNbClients(void) const { return (int)m_Clients.size(); } void ClearClients(void) { m_Clients.clear(); } - // pass-thru + // pass-through std::list>::iterator begin() { return m_Clients.begin(); } std::list>::iterator end() { return m_Clients.end(); } std::list>::const_iterator cbegin() const { return m_Clients.cbegin(); } diff --git a/reflector/PeerCallsignList.cpp b/reflector/PeerCallsignList.cpp deleted file mode 100644 index 5729d0a..0000000 --- a/reflector/PeerCallsignList.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. - -// urfd -- The universal reflector -// Copyright © 2021 Thomas A. Early N7TAE -// -// 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 3 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, see . - -#include -#include - -#include "PeerCallsignList.h" - -bool CPeerCallsignList::LoadFromFile(const std::string &filename) -{ - bool ok = false; - char sz[256]; - - // and load - std::ifstream file (filename); - if ( file.is_open() ) - { - Lock(); - - // empty list - m_Callsigns.clear(); - // fill with file content - while ( file.getline(sz, sizeof(sz)).good() ) - { - // remove leading & trailing spaces - char *szt = TrimWhiteSpaces(sz); - - // crack it - if ( (::strlen(szt) > 0) && (szt[0] != '#') ) - { - // 1st token is callsign - if ( (szt = ::strtok(szt, " ,\t")) != nullptr ) - { - CCallsign callsign(szt); - // 2nd token is ip - char *szip; - if ( (szip = ::strtok(nullptr, " ,\t")) != nullptr ) - { - // 3rd token is modules list - if ( (szt = ::strtok(nullptr, " ,\t")) != nullptr ) - { - // and load - m_Callsigns.push_back(CCallsignListItem(callsign, szip, szt)); - } - } - } - } - } - // close file - file.close(); - - // keep file path - m_Filename = filename; - - // update time - GetLastModTime(&m_LastModTime); - - // and done - Unlock(); - ok = true; - std::cout << "Gatekeeper loaded " << m_Callsigns.size() << " lines from " << filename << std::endl; - } - else - { - std::cout << "Gatekeeper cannot find " << filename << std::endl; - } - - return ok; -} diff --git a/reflector/PeerCallsignList.h b/reflector/PeerCallsignList.h deleted file mode 100644 index b4cd2b9..0000000 --- a/reflector/PeerCallsignList.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. - -// urfd -- The universal reflector -// Copyright © 2021 Thomas A. Early N7TAE -// -// 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 3 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, see . - -#pragma once - - -#include "CallsignList.h" - -//////////////////////////////////////////////////////////////////////////////////////// -// class - -class CPeerCallsignList : public CCallsignList -{ -public: - // constructor - CPeerCallsignList() {} - - // destructor - virtual ~CPeerCallsignList() {} - - // file io - bool LoadFromFile(const std::string &filename); -}; diff --git a/reflector/Peers.h b/reflector/Peers.h index 99512ca..81ff5fd 100644 --- a/reflector/Peers.h +++ b/reflector/Peers.h @@ -38,7 +38,7 @@ public: void AddPeer(std::shared_ptr); void RemovePeer(std::shared_ptr); - // pass-thru + // pass-through std::list>::iterator begin() { return m_Peers.begin(); } std::list>::iterator end() { return m_Peers.end(); } std::list>::const_iterator cbegin() const { return m_Peers.cbegin(); } diff --git a/reflector/Protocols.h b/reflector/Protocols.h index 32bbf1e..8de96ee 100644 --- a/reflector/Protocols.h +++ b/reflector/Protocols.h @@ -32,7 +32,7 @@ public: void Lock(void) { m_Mutex.lock(); } void Unlock(void) { m_Mutex.unlock(); } - // pass-thru + // pass-through std::list>::iterator begin() { return m_Protocols.begin(); } std::list>::iterator end() { return m_Protocols.end(); } diff --git a/reflector/QR1676.cpp b/reflector/QR1676.cpp index fe76c90..8f67a2f 100644 --- a/reflector/QR1676.cpp +++ b/reflector/QR1676.cpp @@ -72,7 +72,7 @@ unsigned int CQR1676::getSyndrome1576(unsigned int pattern) * Compute the syndrome corresponding to the given pattern, i.e., the * remainder after dividing the pattern (when considering it as the vector * representation of a polynomial) by the generator polynomial, GENPOL. - * In the program this pattern has several meanings: (1) pattern = infomation + * In the program this pattern has several meanings: (1) pattern = information * bits, when constructing the encoding table; (2) pattern = error pattern, * when constructing the decoding table; and (3) pattern = received vector, to * obtain its syndrome in decoding. diff --git a/reflector/Reflector.cpp b/reflector/Reflector.cpp index fac7dd7..afe9505 100644 --- a/reflector/Reflector.cpp +++ b/reflector/Reflector.cpp @@ -20,8 +20,13 @@ #include #include "Global.h" -//////////////////////////////////////////////////////////////////////////////////////// -// destructor + +CReflector::CReflector() +{ +#ifndef NO_DHT + peers_put_count = clients_put_count = users_put_count = 0; +#endif +} CReflector::~CReflector() { @@ -47,10 +52,18 @@ CReflector::~CReflector() bool CReflector::Start(void) { // get config stuff - m_Callsign = CCallsign(g_Configure.GetString(g_Keys.names.callsign).c_str(), false); + const auto cs(g_Configure.GetString(g_Keys.names.callsign)); + m_Callsign.SetCallsign(cs, false); m_Modules.assign(g_Configure.GetString(g_Keys.modules.modules)); std::string tcmods(g_Configure.GetString(g_Keys.modules.tcmodules)); +#ifndef NO_DHT + // start the dht instance + refhash = dht::InfoHash::get(cs); + node.run(17171, dht::crypto::generateIdentity(cs), true); + node.bootstrap(g_Configure.GetString(g_Keys.names.bootstrap), "17171"); +#endif + // let's go! keep_running = true; @@ -107,13 +120,17 @@ bool CReflector::Start(void) // start the reporting thread try { - m_XmlReportFuture = std::async(std::launch::async, &CReflector::XmlReportThread, this); + m_XmlReportFuture = std::async(std::launch::async, &CReflector::StateReportThread, this); } catch(const std::exception& e) { std::cerr << "Cannot start the dashboard data report thread: " << e.what() << '\n'; } +#ifndef NO_DHT + PutDHTConfig(); +#endif + return false; } @@ -145,6 +162,16 @@ void CReflector::Stop(void) g_LDid.LookupClose(); g_LNid.LookupClose(); g_LYtr.LookupClose(); + +#ifndef NO_DHT + // kill the DHT + node.cancelPut(refhash, toUType(EUrfdValueID::Config)); + node.cancelPut(refhash, toUType(EUrfdValueID::Peers)); + node.cancelPut(refhash, toUType(EUrfdValueID::Clients)); + node.cancelPut(refhash, toUType(EUrfdValueID::Users)); + node.shutdown({}, true); + node.join(); +#endif } //////////////////////////////////////////////////////////////////////////////////////// @@ -155,7 +182,7 @@ bool CReflector::IsStreaming(char module) return false; } -// clients MUST have bee locked by the caller so we can freely access it within the fuction +// clients MUST have bee locked by the caller so we can freely access it within the function std::shared_ptr CReflector::OpenStream(std::unique_ptr &DvHeader, std::shared_ptrclient) { // check sid is not zero @@ -209,7 +236,7 @@ std::shared_ptr CReflector::OpenStream(std::unique_ptrPush(std::move(DvHeader)); // notify - OnStreamOpen(stream->GetUserCallsign()); + //OnStreamOpen(stream->GetUserCallsign()); } return stream; @@ -237,7 +264,7 @@ void CReflector::CloseStream(std::shared_ptr stream) client->NotAMaster(); // notify - OnStreamClose(stream->GetUserCallsign()); + //OnStreamClose(stream->GetUserCallsign()); std::cout << "Closing stream of module " << GetStreamModule(stream) << std::endl; } @@ -296,10 +323,12 @@ void CReflector::RouterThread(const char ThisModule) #define XML_UPDATE_PERIOD 10 -void CReflector::XmlReportThread() +void CReflector::StateReportThread() { std::string xmlpath, jsonpath; - +#ifndef NO_DHT + peers_changed = clients_changed = users_changed = true; +#endif if (g_Configure.Contains(g_Keys.files.xml)) xmlpath.assign(g_Configure.GetString(g_Keys.files.xml)); if (g_Configure.Contains(g_Keys.files.json)) @@ -343,9 +372,29 @@ void CReflector::XmlReportThread() } } - // and wait a bit + // and wait a bit and do something useful at the same time for (int i=0; i< XML_UPDATE_PERIOD && keep_running; i++) + { +#ifndef NO_DHT + // update the dht data, if needed + if (peers_changed) + { + PutDHTPeers(); + peers_changed = false; + } + if (clients_changed) + { + PutDHTClients(); + clients_changed = false; + } + if (users_changed) + { + PutDHTUsers(); + users_changed = false; + } +#endif std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } } } @@ -354,47 +403,23 @@ void CReflector::XmlReportThread() void CReflector::OnPeersChanged(void) { - CNotification notification(NOTIFICATION_PEERS); - - m_Notifications.Lock(); - m_Notifications.push(notification); - m_Notifications.Unlock(); +#ifndef NO_DHT + peers_changed = true; +#endif } void CReflector::OnClientsChanged(void) { - CNotification notification(NOTIFICATION_CLIENTS); - - m_Notifications.Lock(); - m_Notifications.push(notification); - m_Notifications.Unlock(); +#ifndef NO_DHT + clients_changed = true; +#endif } void CReflector::OnUsersChanged(void) { - CNotification notification(NOTIFICATION_USERS); - - m_Notifications.Lock(); - m_Notifications.push(notification); - m_Notifications.Unlock(); -} - -void CReflector::OnStreamOpen(const CCallsign &callsign) -{ - CNotification notification(NOTIFICATION_STREAM_OPEN, callsign); - - m_Notifications.Lock(); - m_Notifications.push(notification); - m_Notifications.Unlock(); -} - -void CReflector::OnStreamClose(const CCallsign &callsign) -{ - CNotification notification(NOTIFICATION_STREAM_CLOSE, callsign); - - m_Notifications.Lock(); - m_Notifications.push(notification); - m_Notifications.Unlock(); +#ifndef NO_DHT + users_changed = true; +#endif } //////////////////////////////////////////////////////////////////////////////////////// @@ -512,3 +537,202 @@ void CReflector::WriteXmlFile(std::ofstream &xmlFile) ReleaseUsers(); xmlFile << "" << std::endl; } + +#ifndef NO_DHT +// DHT put() and get() +void CReflector::PutDHTPeers() +{ + const std::string cs(g_Configure.GetString(g_Keys.names.callsign)); + // load it up + SUrfdPeers1 p; + time(&p.timestamp); + p.sequence = peers_put_count++; + auto peers = GetPeers(); + for (auto pit=peers->cbegin(); pit!=peers->cend(); pit++) + { + p.list.emplace_back((*pit)->GetCallsign().GetCS(), (*pit)->GetReflectorModules(), (*pit)->GetConnectTime()); + } + ReleasePeers(); + + auto nv = std::make_shared(p); + nv->user_type.assign("urfd-peers-1"); + nv->id = toUType(EUrfdValueID::Peers); + + node.putSigned( + refhash, + nv, +#ifdef DEBUG + [](bool success){ std::cout << "PutDHTPeers() " << (success ? "successful" : "unsuccessful") << std::endl; }, +#else + [](bool success){ if (! success) std::cout << "PutDHTPeers() unsuccessful" << std::endl; }, +#endif + true // permanent! + ); +} + +void CReflector::PutDHTClients() +{ + const std::string cs(g_Configure.GetString(g_Keys.names.callsign)); + SUrfdClients1 c; + time(&c.timestamp); + c.sequence = clients_put_count++; + auto clients = GetClients(); + for (auto cit=clients->cbegin(); cit!=clients->cend(); cit++) + { + c.list.emplace_back((*cit)->GetCallsign().GetCS(), std::string((*cit)->GetIp().GetAddress()), (*cit)->GetReflectorModule(), (*cit)->GetConnectTime(), (*cit)->GetLastHeardTime()); + } + ReleaseClients(); + + auto nv = std::make_shared(c); + nv->user_type.assign("urfd-clients-1"); + nv->id = toUType(EUrfdValueID::Clients); + + node.putSigned( + refhash, + nv, +#ifdef DEBUG + [](bool success){ std::cout << "PutDHTClients() " << (success ? "successful" : "unsuccessful") << std::endl; }, +#else + [](bool success){ if (! success) std::cout << "PutDHTClients() unsuccessful" << std::endl; }, +#endif + true // permanent! + ); +} + +void CReflector::PutDHTUsers() +{ + const std::string cs(g_Configure.GetString(g_Keys.names.callsign)); + SUrfdUsers1 u; + time(&u.timestamp); + u.sequence = users_put_count++; + auto users = GetUsers(); + for (auto uit=users->cbegin(); uit!=users->cend(); uit++) + { + u.list.emplace_back((*uit).GetCallsign(), std::string((*uit).GetViaNode()), (*uit).GetOnModule(), (*uit).GetViaPeer(), (*uit).GetLastHeardTime()); + } + ReleaseUsers(); + + auto nv = std::make_shared(u); + nv->user_type.assign("urfd-users-1"); + nv->id = toUType(EUrfdValueID::Users); + + node.putSigned( + refhash, + nv, +#ifdef DEBUG + [](bool success){ std::cout << "PutDHTUsers() " << (success ? "successful" : "unsuccessful") << std::endl; }, +#else + [](bool success){ if (! success) std::cout << "PutDHTUsers() unsuccessful" << std::endl; }, +#endif + true // permanent! + ); +} + +void CReflector::PutDHTConfig() +{ + const std::string cs(g_Configure.GetString(g_Keys.names.callsign)); + SUrfdConfig1 cfg; + time(&cfg.timestamp); + cfg.cs.assign(cs); + cfg.ipv4.assign(g_Configure.GetString(g_Keys.ip.ipv4address)); + cfg.ipv6.assign(g_Configure.GetString(g_Keys.ip.ipv6address)); + cfg.mods.assign(g_Configure.GetString(g_Keys.modules.modules)); + cfg.tcmods.assign(g_Configure.GetString(g_Keys.modules.tcmodules)); + cfg.url.assign(g_Configure.GetString(g_Keys.names.url)); + cfg.email.assign(g_Configure.GetString(g_Keys.names.email)); + cfg.country.assign(g_Configure.GetString(g_Keys.names.country)); + cfg.sponsor.assign(g_Configure.GetString(g_Keys.names.sponsor)); + std::ostringstream ss; + ss << g_Version; + cfg.version.assign(ss.str()); + cfg.almod[toUType(EUrfdAlMod::nxdn)] = g_Configure.GetString(g_Keys.nxdn.autolinkmod).at(0); + cfg.almod[toUType(EUrfdAlMod::p25)] = g_Configure.GetString(g_Keys.p25.autolinkmod).at(0); + cfg.almod[toUType(EUrfdAlMod::ysf)] = g_Configure.GetString(g_Keys.ysf.autolinkmod).at(0); + cfg.ysffreq[toUType(EUrfdTxRx::rx)] = g_Configure.GetUnsigned(g_Keys.ysf.defaultrxfreq); + cfg.ysffreq[toUType(EUrfdTxRx::tx)] = g_Configure.GetUnsigned(g_Keys.ysf.defaulttxfreq); + cfg.refid[toUType(EUrfdRefId::nxdn)] = g_Configure.GetUnsigned(g_Keys.nxdn.reflectorid); + cfg.refid[toUType(EUrfdRefId::p25)] = g_Configure.GetUnsigned(g_Keys.p25.reflectorid); + cfg.port[toUType(EUrfdPorts::dcs)] = (uint16_t)g_Configure.GetUnsigned(g_Keys.dcs.port); + cfg.port[toUType(EUrfdPorts::dextra)] = (uint16_t)g_Configure.GetUnsigned(g_Keys.dextra.port); + cfg.port[toUType(EUrfdPorts::dmrplus)] = (uint16_t)g_Configure.GetUnsigned(g_Keys.dmrplus.port); + cfg.port[toUType(EUrfdPorts::dplus)] = (uint16_t)g_Configure.GetUnsigned(g_Keys.dplus.port); + cfg.port[toUType(EUrfdPorts::m17)] = (uint16_t)g_Configure.GetUnsigned(g_Keys.m17.port); + cfg.port[toUType(EUrfdPorts::mmdvm)] = (uint16_t)g_Configure.GetUnsigned(g_Keys.mmdvm.port); + cfg.port[toUType(EUrfdPorts::nxdn)] = (uint16_t)g_Configure.GetUnsigned(g_Keys.nxdn.port); + cfg.port[toUType(EUrfdPorts::p25)] = (uint16_t)g_Configure.GetUnsigned(g_Keys.p25.port); + cfg.port[toUType(EUrfdPorts::urf)] = (uint16_t)g_Configure.GetUnsigned(g_Keys.urf.port); + cfg.port[toUType(EUrfdPorts::ysf)] = (uint16_t)g_Configure.GetUnsigned(g_Keys.ysf.port); + cfg.g3enabled = g_Configure.GetBoolean(g_Keys.g3.enable); + for (const auto m : cfg.mods) + cfg.description[m] = g_Configure.GetString(g_Keys.modules.descriptor[m-'A']); + + auto nv = std::make_shared(cfg); + nv->user_type.assign("urfd-config-1"); + nv->id = toUType(EUrfdValueID::Config); + + node.putSigned( + refhash, + nv, +#ifdef DEBUG + [](bool success){ std::cout << "PutDHTConfig() " << (success ? "successful" : "unsuccessful") << std::endl; }, +#else + [](bool success){ if(! success) std::cout << "PutDHTConfig() unsuccessful" << std::endl; }, +#endif + true + ); +} + +void CReflector::GetDHTConfig(const std::string &cs) +{ + static SUrfdConfig1 cfg; + cfg.timestamp = 0; // every time this is called, zero the timestamp + + std::cout << "Getting " << cs << " connection info..." << std::endl; + + // we only want the configuration section of the reflector's document + dht::Where w; + w.id(toUType(EUrfdValueID::Config)); + + node.get( + dht::InfoHash::get(cs), + [](const std::shared_ptr &v) { + if (0 == v->user_type.compare("urfd-config-1")) + { + auto rdat = dht::Value::unpack(*v); + if (rdat.timestamp > cfg.timestamp) + { + // the time stamp is the newest so far, so put it in the static cfg struct + cfg = dht::Value::unpack(*v); + } + } + else + { + std::cerr << "Get() returned unknown user_type: '" << v->user_type << "'" << std::endl; + } + return true; // check all the values returned + }, + [](bool success) { + if (success) + { + if (cfg.timestamp) + { + // if the get() call was successful and there is a nonzero timestamp, then do the update + g_GateKeeper.GetInterlinkMap()->Update(cfg.cs, cfg.mods, cfg.ipv4, cfg.ipv6, cfg.port[toUType(EUrfdPorts::urf)], cfg.tcmods); + g_GateKeeper.ReleaseInterlinkMap(); + } + else + { + std::cerr << "node.Get() was successful, but the timestamp was zero" << std::endl; + } + } + else + { + std::cout << "Get() was unsuccessful" << std::endl; + } + }, + {}, // empty filter + w // just the configuration section + ); +} + +#endif diff --git a/reflector/Reflector.h b/reflector/Reflector.h index efe22ea..9f3c925 100644 --- a/reflector/Reflector.h +++ b/reflector/Reflector.h @@ -25,7 +25,10 @@ #include "Peers.h" #include "Protocols.h" #include "PacketStream.h" -#include "NotificationQueue.h" + +#ifndef NO_DHT +#include "urfd-dht-values.h" +#endif //////////////////////////////////////////////////////////////////////////////////////// @@ -42,6 +45,7 @@ class CReflector { public: + CReflector(); // destructor ~CReflector(); @@ -75,13 +79,22 @@ public: void OnPeersChanged(void); void OnClientsChanged(void); void OnUsersChanged(void); - void OnStreamOpen(const CCallsign &); - void OnStreamClose(const CCallsign &); +#ifndef NO_DHT + void GetDHTConfig(const std::string &cs); +#endif protected: +#ifndef NO_DHT + // Publish DHT + void PutDHTConfig(); + void PutDHTPeers(); + void PutDHTClients(); + void PutDHTUsers(); +#endif + // threads void RouterThread(const char); - void XmlReportThread(void); + void StateReportThread(void); // streams std::shared_ptr GetStream(char); @@ -92,7 +105,6 @@ protected: void WriteXmlFile(std::ofstream &); void JsonReport(nlohmann::json &report); -protected: // identity CCallsign m_Callsign; std::string m_Modules, m_TCmodules; @@ -111,6 +123,11 @@ protected: std::unordered_map> m_RouterFuture; std::future m_XmlReportFuture; - // notifications - CNotificationQueue m_Notifications; +#ifndef NO_DHT + // Distributed Hash Table + dht::DhtRunner node; + dht::InfoHash refhash; + unsigned int peers_put_count, clients_put_count, users_put_count; + std::atomic peers_changed, clients_changed, users_changed; +#endif }; diff --git a/reflector/SafePacketQueue.h b/reflector/SafePacketQueue.h index fd51b40..5b5ec60 100644 --- a/reflector/SafePacketQueue.h +++ b/reflector/SafePacketQueue.h @@ -22,7 +22,7 @@ /************************************************************ * THIS IS IMPORTANT - * This template is primarly designed for std::unique_ptr! + * This template is primarily designed for std::unique_ptr! * If you are going to use it for std::shared_ptr, then * please consider that when you Push(), what you pushed * from will be nullptr after the Push()! @@ -56,13 +56,13 @@ public: } } - // If the queue is empty, wait until an element is avaiable. + // If the queue is empty, wait until an element is available. T PopWait(void) { std::unique_lock lock(m); while(q.empty()) { - // release lock as long as the wait and reaquire it afterwards. + // release lock as long as the wait and reacquire it afterwards. c.wait(lock); } T val = std::move(q.front()); diff --git a/reflector/UDPSocket.cpp b/reflector/UDPSocket.cpp index 1b078b6..45774ab 100644 --- a/reflector/UDPSocket.cpp +++ b/reflector/UDPSocket.cpp @@ -40,7 +40,7 @@ CUdpSocket::~CUdpSocket() // returns true on error bool CUdpSocket::Open(const CIp &Ip) { - // check for a vaild family + // check for a valid family if (AF_UNSPEC == Ip.GetFamily()) return true; diff --git a/reflector/URFProtocol.cpp b/reflector/URFProtocol.cpp index 673f21d..d14d6a0 100644 --- a/reflector/URFProtocol.cpp +++ b/reflector/URFProtocol.cpp @@ -287,16 +287,16 @@ void CURFProtocol::HandlePeerLinks(void) CBuffer buffer; // get the list of peers - CPeerCallsignList *list = g_GateKeeper.GetPeerList(); + auto ilmap = g_GateKeeper.GetInterlinkMap(); CPeers *peers = g_Reflector.GetPeers(); // check if all our connected peers are still listed by gatekeeper // if not, disconnect auto pit = peers->begin(); std::shared_ptrpeer = nullptr; - while ( (peer = peers->FindNextPeer(EProtocol::urf, pit)) != nullptr ) + while (nullptr != (peer = peers->FindNextPeer(EProtocol::urf, pit))) { - if ( list->FindListItem(peer->GetCallsign()) == nullptr ) + if (nullptr == ilmap->FindMapItem(peer->GetCallsign().GetBase())) { // send disconnect packet EncodeDisconnectPacket(&buffer); @@ -307,29 +307,64 @@ void CURFProtocol::HandlePeerLinks(void) } } - // check if all ours peers listed by gatekeeper are connected + // check if all ours peers listed by interlink file are connected // if not, connect or reconnect - for ( auto it=list->begin(); it!=list->end(); it++ ) + for ( auto it=ilmap->begin(); it!=ilmap->end(); it++ ) { - if ( it->GetCallsign().HasSameCallsignWithWildcard(CCallsign("XRF*")) ) - continue; - if ( it->GetCallsign().HasSameCallsignWithWildcard(CCallsign("BM*")) ) - continue; - CCallsign cs = it->GetCallsign(); - if (cs.HasSameCallsignWithWildcard(CCallsign("URF*")) && (nullptr==peers->FindPeer(cs, EProtocol::urf))) + const auto cs = it->first; + CCallsign callsign; + callsign.SetCallsign(cs, false); + if ((0 == cs.substr(0, 3).compare("URF")) && (nullptr==peers->FindPeer(callsign, EProtocol::urf))) { - // resolve again peer's IP in case it's a dynamic IP - it->ResolveIp(); - // send connect packet to re-initiate peer link - EncodeConnectPacket(&buffer, it->GetModules()); - Send(buffer, it->GetIp(), m_Port); - std::cout << "Sending connect packet to URF peer " << cs << " @ " << it->GetIp() << " for modules " << it->GetModules() << std::endl; +#ifndef NO_DHT + it->second.UpdateIP(g_Configure.GetString(g_Keys.ip.ipv6address).empty()); + if (it->second.GetIp().IsSet()) + { + bool ok = true; + // does everything match up? + for (const auto c : it->second.GetModules()) + { + if (std::string::npos == g_Configure.GetString(g_Keys.modules.modules).find(c)) + { // is the local module not config'ed? + ok = false; + std::cerr << "This reflector has no module '" << c << "', so it can't interlink with " << it->first << std::endl; + } + else if (it->second.UsesDHT()) + { + if (std::string::npos == it->second.GetCMods().find(c)) + { // the remote module not config'ed! + ok = false; + std::cerr << it->first << " has no module '" << c << "'" << std::endl; + } + else if ((std::string::npos == it->second.GetTCMods().find(c)) != (std::string::npos == g_Configure.GetString(g_Keys.modules.tcmodules).find(c))) + { // are the transcoding states on both sides mismatched? + ok = false; + std::cerr << "The transcode states for module '" << c << "' don't match for this reflector and " << it->first << std::endl; + } + } + } + if (ok) + { + +#endif + // send connect packet to re-initiate peer link + EncodeConnectPacket(&buffer, it->second.GetModules().c_str()); + Send(buffer, it->second.GetIp()); + std::cout << "Sent connect packet to URF peer " << cs << " @ " << it->second.GetIp() << " for modules " << it->second.GetModules() << std::endl; +#ifndef NO_DHT + } + } + else // m_Ip is not set! + { + g_Reflector.GetDHTConfig(it->first); + } +#endif } } // done g_Reflector.ReleasePeers(); - g_GateKeeper.ReleasePeerList(); + g_GateKeeper.ReleaseInterlinkMap(); } @@ -415,10 +450,18 @@ bool CURFProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsi callsign->CodeIn(Buffer.data()+4); valid = callsign->IsValid(); *version = CVersion(Buffer.at(37), Buffer.at(38), Buffer.at(39)); - memcpy(modules, Buffer.data()+10, 27); - for ( unsigned i = 0; i < strlen(modules); i++ ) + if (valid) { - valid = valid && (g_Reflector.IsValidModule (modules[i])); + memcpy(modules, Buffer.data()+10, 27); + for ( unsigned i = 0; i < strlen(modules); i++ ) + { + auto moduleok = g_Reflector.IsValidModule(modules[i]); + if (! moduleok) + { + valid = false; + std::cout << "Requested module '" << modules[i] << "' is not confgured\n"; + } + } } } return valid; diff --git a/reflector/User.h b/reflector/User.h index d5ed1d2..7207660 100644 --- a/reflector/User.h +++ b/reflector/User.h @@ -34,6 +34,13 @@ public: // destructor ~CUser() {} + // get + const std::string GetCallsign(void) const { return m_My.GetCS(); } + const std::string GetViaNode(void) const { return m_Rpt1.GetCS(); } + char GetOnModule(void) const { return m_Rpt2.GetCSModule(); } + const std::string GetViaPeer(void) const { return m_Xlx.GetCS(); } + std::time_t GetLastHeardTime(void) const { return m_LastHeardTime; } + // operation void HeardNow(void) { m_LastHeardTime = time(nullptr); } diff --git a/reflector/Users.h b/reflector/Users.h index f4e3343..da8a680 100644 --- a/reflector/Users.h +++ b/reflector/Users.h @@ -40,9 +40,11 @@ public: int GetSize(void) const { return (int)m_Users.size(); } void AddUser(const CUser &); - // pass-thru + // pass-through std::list::iterator begin() { return m_Users.begin(); } std::list::iterator end() { return m_Users.end(); } + std::list::const_iterator cbegin() { return m_Users.cbegin(); } + std::list::const_iterator cend() { return m_Users.cend(); } // operation void Hearing(const CCallsign &, const CCallsign &, const CCallsign &); diff --git a/reflector/Version.cpp b/reflector/Version.cpp index 35d3c9d..4187f81 100644 --- a/reflector/Version.cpp +++ b/reflector/Version.cpp @@ -85,5 +85,8 @@ bool CVersion::operator <(const CVersion &v) const std::ostream &operator <<(std::ostream &os, const CVersion &v) { os << v.GetMajor() << '.' << v.GetMinor() << '.' << v.GetRevision(); +#ifndef NO_DHT + os << "-dht"; +#endif return os; }; diff --git a/reflector/Version.h b/reflector/Version.h index 84aa8d4..ef04078 100644 --- a/reflector/Version.h +++ b/reflector/Version.h @@ -38,7 +38,7 @@ public: // set void Set(uint8_t, uint8_t, uint8_t); - // comparaison operators + // comparison operators bool operator ==(const CVersion &v) const; bool operator !=(const CVersion &v) const; bool operator >=(const CVersion &v) const; diff --git a/reflector/urfd-dht-values.h b/reflector/urfd-dht-values.h new file mode 100644 index 0000000..ee147e6 --- /dev/null +++ b/reflector/urfd-dht-values.h @@ -0,0 +1,89 @@ +// Copyright © 2023 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of urfd. +// +// M17Refd 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 3 of the License, or +// (at your option) any later version. +// +// M17Refd 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 +// with this software. If not, see . +// ---------------------------------------------------------------------------- + +#pragma once + +#include + +/* HELPERS */ +#ifndef TO_U_TYPE_DEF +#define TO_U_TYPE_DEF +template constexpr auto toUType(E enumerator) noexcept +{ + return static_cast>(enumerator); +} // Item #10 in "Effective Modern C++", by Scott Meyers, O'REILLY +#endif + +enum class EUrfdValueID : uint64_t { Config=1, Peers=2, Clients=3, Users=4 }; + +/* PEERS */ +using UrfdPeerTuple = std::tuple; +enum class EUrfdPeerFields { Callsign, Modules, ConnectTime }; +struct SUrfdPeers1 +{ + std::time_t timestamp; + unsigned int sequence; + std::list list; + + MSGPACK_DEFINE(timestamp, sequence, list) +}; + +/* CLIENTS */ +using UrfdClientTuple = std::tuple; +enum class EUrfdClientFields { Callsign, Ip, Module, ConnectTime, LastHeardTime }; +struct SUrfdClients1 +{ + std::time_t timestamp; + unsigned int sequence; + std::list list; + + MSGPACK_DEFINE(timestamp, sequence, list) +}; + +/* USERS */ +using UrfdUserTuple = std::tuple; +enum class EUrfdUserFields { Callsign, ViaNode, OnModule, ViaPeer, LastHeardTime }; +struct SUrfdUsers1 +{ + std::time_t timestamp; + unsigned int sequence; + std::list list; + + MSGPACK_DEFINE(timestamp, sequence, list) +}; + +/* CONFIGURATION */ +// 'SIZE' has to be last for these scoped enums +enum class EUrfdPorts : unsigned { dcs, dextra, dmrplus, dplus, m17, mmdvm, nxdn, p25, urf, ysf, SIZE }; +enum class EUrfdAlMod : unsigned { nxdn, p25, ysf, SIZE }; +enum class EUrfdTxRx : unsigned { rx, tx, SIZE }; +enum class EUrfdRefId : unsigned { nxdn, p25, SIZE }; +struct SUrfdConfig1 +{ + std::time_t timestamp; + std::string cs, ipv4, ipv6, mods, tcmods, url, email, sponsor, country, version; + std::array port; + std::array almod; + std::array ysffreq; + std::array refid; + std::unordered_map description; + bool g3enabled; + + MSGPACK_DEFINE(timestamp, cs, ipv4, ipv6, mods, tcmods, url, email, sponsor, country, version, almod, ysffreq, refid, g3enabled, port, description) +};