Merge pull request #2 from n7tae/main

Add the ability to put and get information from a existing distributed hash table
pull/8/head
nostar 3 years ago committed by GitHub
commit 715365f737
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -51,6 +51,29 @@ sudo apt install build-essential
sudo apt install nlohmann-json3-dev 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 ### Download and build the repository and
```bash ```bash

@ -12,6 +12,14 @@ Country = GB
Sponsor = My Home Club 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] [IP Addresses]
# Binding addresses are usually the 'any' address # Binding addresses are usually the 'any' address
IPv4Binding = 0.0.0.0 IPv4Binding = 0.0.0.0

@ -1,14 +1,20 @@
############################################################################## ##############################################################################
# URFD interlink file # URFD interlink file
# #
# one line per entry # One line per entry.
# each entry specifies a remote XLX or XRF to peer with # Each entry specifies a remote URF to peer with.
# format: # If no Port is specified, 10017 will be used.
# <URF callsign> <ip> <list of modules shared> # If DHT is enabled and the target is also DHT-enabled, then you only
# example: # need to specify the URF-Callsign and the Shared-Modules.
# URF270 158.64.26.132 ACD # Format:
# <URF-Callsign> <IP-Address> <Shared-Mdoules> <Port>
# Examples:
# URF270 158.64.26.132 EF
# URF280 ABC
# #
# note: the remote URFD must list this in its interlink file # Brandmeister links use three params, no port is specified. Example:
# for the link to be established # BM3104 162.248.88.117 E
# #
# note: Remote URFD must list this in its interlink file
# for the link to be established.
############################################################################# #############################################################################

@ -4,3 +4,6 @@ BINDIR = /usr/local/bin
# besides making an executable that gdb can use, # besides making an executable that gdb can use,
# this will also provide some additional log messsage # this will also provide some additional log messsage
debug = false debug = false
# To disable DHT support, set DHT to false.
DHT = true

@ -288,16 +288,16 @@ void CBMProtocol::HandlePeerLinks(void)
CBuffer buffer; CBuffer buffer;
// get the list of peers // get the list of peers
CPeerCallsignList *list = g_GateKeeper.GetPeerList(); auto ilmap = g_GateKeeper.GetInterlinkMap();
CPeers *peers = g_Reflector.GetPeers(); CPeers *peers = g_Reflector.GetPeers();
// check if all our connected peers are still listed by gatekeeper // check if all our connected peers are still listed by gatekeeper
// if not, disconnect // if not, disconnect
auto pit = peers->begin(); auto pit = peers->begin();
std::shared_ptr<CPeer>peer = nullptr; std::shared_ptr<CPeer>peer = 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 // send disconnect packet
EncodeDisconnectPacket(&buffer); EncodeDisconnectPacket(&buffer);
@ -310,23 +310,21 @@ void CBMProtocol::HandlePeerLinks(void)
// check if all ours peers listed by gatekeeper are connected // check if all ours peers listed by gatekeeper are connected
// if not, connect or reconnect // 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(); const auto cs = it->first;
if (cs.HasSameCallsignWithWildcard(CCallsign("BM*")) && (nullptr==peers->FindPeer(cs, EProtocol::bm))) 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 // send connect packet to re-initiate peer link
EncodeConnectPacket(&buffer, it->GetModules()); EncodeConnectPacket(&buffer, it->second.GetModules().c_str());
Send(buffer, it->GetIp(), m_Port); Send(buffer, it->second.GetIp(), m_Port);
std::cout << "Sending connect packet to BM peer " << cs << " @ " << it->GetIp() << " for modules " << it->GetModules() << std::endl; std::cout << "Sending connect packet to BM peer " << cs << " @ " << it->second.GetIp() << " for modules " << it->second.GetModules() << std::endl;
} }
} }
// done // done
g_Reflector.ReleasePeers(); g_Reflector.ReleasePeers();
g_GateKeeper.ReleasePeerList(); g_GateKeeper.ReleaseInterlinkMap();
} }

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <iostream>
#include <fstream>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#pragma once
#include <set>
#include <string>
#include <mutex>
////////////////////////////////////////////////////////////////////////////////////////
// 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<std::string> m_Callsigns;
};

@ -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 <https://www.gnu.org/licenses/>.
#pragma once
#include <queue>
#include <mutex>
#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<CNotification> queue;
};

@ -349,6 +349,16 @@ UCallsign CCallsign::GetKey() const
return rval; 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 void CCallsign::GetCallsign(uint8_t *buffer) const
{ {
memcpy(buffer, m_Callsign.c, CALLSIGN_LEN); memcpy(buffer, m_Callsign.c, CALLSIGN_LEN);
@ -404,16 +414,6 @@ bool CCallsign::HasSameCallsignWithWildcard(const CCallsign &cs) const
return same; 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 // operators

@ -39,6 +39,23 @@ union USuffix
uint32_t u; uint32_t u;
}; };
// functions for unordered containers
struct CCallsignHash
{
std::size_t operator() (const UCallsign &ucs) const
{
std::hash<uint64_t> hash;
return hash(ucs.l);
}
};
struct CCallsignEqual
{
bool operator() (const UCallsign &ucs1, const UCallsign &ucs2) const
{
return ucs1.l == ucs2.l;
}
};
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// class // class
@ -46,7 +63,7 @@ union USuffix
class CCallsign class CCallsign
{ {
public: public:
// contructors // constructors
CCallsign(); CCallsign();
CCallsign(const UCallsign &cs); // no id lookup CCallsign(const UCallsign &cs); // no id lookup
CCallsign(const CCallsign &cs); CCallsign(const CCallsign &cs);
@ -73,6 +90,7 @@ public:
// get // get
UCallsign GetKey() const; UCallsign GetKey() const;
std::string GetBase() const;
void GetCallsign(uint8_t *) const; void GetCallsign(uint8_t *) const;
void GetCallsignString(char *) const; void GetCallsignString(char *) const;
const std::string GetCS() const; const std::string GetCS() const;
@ -84,8 +102,6 @@ public:
// compare // compare
bool HasSameCallsign(const CCallsign &) const; bool HasSameCallsign(const CCallsign &) const;
bool HasSameCallsignWithWildcard(const CCallsign &) const; bool HasSameCallsignWithWildcard(const CCallsign &) const;
bool HasLowerCallsign(const CCallsign &) const;
bool HasSameModule(const CCallsign &) const;
// operators // operators
CCallsign &operator = (const CCallsign &cs); CCallsign &operator = (const CCallsign &cs);

@ -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 <https://www.gnu.org/licenses/>.
#include <fstream>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#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;
}

@ -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 <https://www.gnu.org/licenses/>.
#pragma once
#include <list>
#include <mutex>
#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<CCallsignListItem>::iterator begin() { return m_Callsigns.begin(); }
std::list<CCallsignListItem>::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<CCallsignListItem> m_Callsigns;
};

@ -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 <https://www.gnu.org/licenses/>.
#include <string.h>
#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<n; i++)
{
if (std::string::npos != mods.find(modules[i]))
{
m_Modules[j++] = modules[i];
}
}
}
}
}
CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const char *url, const char *modules)
{
const std::string mods(g_Configure.GetString(g_Keys.modules.modules));
m_Callsign = callsign;
::strncpy(m_szUrl, url, URL_MAXLEN);
m_Ip = CIp(m_szUrl);
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<n; i++)
{
if (std::string::npos != mods.find(modules[i]))
{
m_Modules[j++] = modules[i];
}
}
}
}
}
CCallsignListItem::CCallsignListItem(const CCallsignListItem &item)
{
m_Callsign = item.m_Callsign;
memcpy(m_szUrl, item.m_szUrl, sizeof(m_szUrl));
m_Ip = item.m_Ip;
memcpy(m_Modules, item.m_Modules, sizeof(m_Modules));
}
////////////////////////////////////////////////////////////////////////////////////////
// compare
bool CCallsignListItem::HasSameCallsign(const CCallsign &callsign) const
{
return m_Callsign.HasSameCallsign(callsign);
}
bool CCallsignListItem::HasSameCallsignWithWildcard(const CCallsign &callsign) const
{
return m_Callsign.HasSameCallsignWithWildcard(callsign);
}
bool CCallsignListItem::HasModuleListed(char module) const
{
return (::strchr(m_Modules, (int)module) != nullptr);
}
bool CCallsignListItem::CheckListedModules(char *Modules) const
{
bool listed = false;
if ( Modules != nullptr )
{
// build a list of common modules
char list[27];
list[0] = 0;
//
for ( unsigned i = 0; i < ::strlen(Modules); i++ )
{
if ( HasModuleListed(Modules[i]) )
{
::strncat(list, &(Modules[i]), 1);
listed = true;
}
}
::strcpy(Modules, list);
}
return listed;
}

@ -1,65 +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 <https://www.gnu.org/licenses/>.
#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];
};

@ -50,6 +50,8 @@ public:
char GetCSModule(void) const { return m_Callsign.GetCSModule(); } char GetCSModule(void) const { return m_Callsign.GetCSModule(); }
bool HasReflectorModule(void) const { return m_ReflectorModule != ' '; } bool HasReflectorModule(void) const { return m_ReflectorModule != ' '; }
char GetReflectorModule(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 // set
void SetCSModule(char c) { m_Callsign.SetCSModule(c); } void SetCSModule(char c) { m_Callsign.SetCSModule(c); }

@ -73,7 +73,7 @@ void CClients::RemoveClient(std::shared_ptr<CClient> client)
bool found = false; bool found = false;
for ( auto it=begin(); it!=end(); it++ ) for ( auto it=begin(); it!=end(); it++ )
{ {
// compare objetc pointers // compare object pointers
if ( *it == client ) if ( *it == client )
{ {
// found it ! // found it !

@ -47,7 +47,7 @@ public:
void RemoveClient(std::shared_ptr<CClient>); void RemoveClient(std::shared_ptr<CClient>);
bool IsClient(std::shared_ptr<CClient>) const; bool IsClient(std::shared_ptr<CClient>) const;
// pass-thru // pass-through
std::list<std::shared_ptr<CClient>>::iterator begin() { return m_Clients.begin(); } std::list<std::shared_ptr<CClient>>::iterator begin() { return m_Clients.begin(); }
std::list<std::shared_ptr<CClient>>::iterator end() { return m_Clients.end(); } std::list<std::shared_ptr<CClient>>::iterator end() { return m_Clients.end(); }
std::list<std::shared_ptr<CClient>>::const_iterator cbegin() const { return m_Clients.cbegin(); } std::list<std::shared_ptr<CClient>>::const_iterator cbegin() const { return m_Clients.cbegin(); }

@ -50,7 +50,7 @@ public:
void Thread(void); void Thread(void);
void Task(void); void Task(void);
// pass-thru // pass-through
void Push(std::unique_ptr<CPacket> p) { m_Queue.Push(std::move(p)); } void Push(std::unique_ptr<CPacket> p) { m_Queue.Push(std::move(p)); }
protected: protected:

@ -33,9 +33,11 @@
// ini file keywords // ini file keywords
#define JAUTOLINKMODULE "AutoLinkModule" #define JAUTOLINKMODULE "AutoLinkModule"
#define JBLACKLISTPATH "BlacklistPath" #define JBLACKLISTPATH "BlacklistPath"
#define JBOOTSTRAP "Bootstrap"
#define JBRANDMEISTER "Brandmeister" #define JBRANDMEISTER "Brandmeister"
#define JCALLSIGN "Callsign" #define JCALLSIGN "Callsign"
#define JCOUNTRY "Country" #define JCOUNTRY "Country"
#define JDASHBOARDURL "DashboardUrl"
#define JDCS "DCS" #define JDCS "DCS"
#define JDEFAULTID "DefaultId" #define JDEFAULTID "DefaultId"
#define JDEFAULTRXFREQ "DefaultRxFreq" #define JDEFAULTRXFREQ "DefaultRxFreq"
@ -250,6 +252,10 @@ bool CConfigure::ReadData(const std::string &path)
case ESection::names: case ESection::names:
if (0 == key.compare(JCALLSIGN)) if (0 == key.compare(JCALLSIGN))
data[g_Keys.names.callsign] = value; 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)) else if (0 == key.compare(JSYSOPEMAIL))
data[g_Keys.names.email] = value; data[g_Keys.names.email] = value;
else if (0 == key.compare(JCOUNTRY)) else if (0 == key.compare(JCOUNTRY))
@ -487,7 +493,7 @@ bool CConfigure::ReadData(const std::string &path)
badParam(key); badParam(key);
break; break;
default: 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, JSYSOPEMAIL, g_Keys.names.email, rval);
isDefined(ErrorLevel::mild, JNAMES, JCOUNTRY, g_Keys.names.country, rval); isDefined(ErrorLevel::mild, JNAMES, JCOUNTRY, g_Keys.names.country, rval);
isDefined(ErrorLevel::mild, JNAMES, JSPONSOR, g_Keys.names.sponsor, rval); isDefined(ErrorLevel::mild, JNAMES, JSPONSOR, g_Keys.names.sponsor, rval);
@ -526,7 +536,7 @@ bool CConfigure::ReadData(const std::string &path)
} }
else 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; rval = true;
} }
} }
@ -564,7 +574,7 @@ bool CConfigure::ReadData(const std::string &path)
} }
else 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; rval = true;
} }
} }

@ -449,7 +449,7 @@ bool CDmrplusProtocol::IsValidDvFramePacket(const CIp &Ip, const CBuffer &Buffer
uint8_t dmrsync[7]; uint8_t dmrsync[7];
// get the 33 bytes ambe // get the 33 bytes ambe
memcpy(dmrframe, &(Buffer.data()[26]), 33); memcpy(dmrframe, &(Buffer.data()[26]), 33);
// handle endianess // handle endianness
SwapEndianess(dmrframe, sizeof(dmrframe)); SwapEndianess(dmrframe, sizeof(dmrframe));
// extract the 3 ambe frames // extract the 3 ambe frames
memcpy(dmr3ambe, dmrframe, 14); memcpy(dmr3ambe, dmrframe, 14);

@ -390,7 +390,7 @@ void CG3Protocol::Task(void)
{ {
BaseIp = &ClIp; BaseIp = &ClIp;
client->Alive(); 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 // the regular data flow will do it
m_LastKeepaliveTime.start(); m_LastKeepaliveTime.start();
break; break;
@ -444,7 +444,7 @@ void CG3Protocol::HandleQueue(void)
{ {
while (! m_Queue.IsEmpty()) while (! m_Queue.IsEmpty())
{ {
// supress host checks // suppress host checks
m_LastKeepaliveTime.start(); m_LastKeepaliveTime.start();
// get the packet // get the packet

@ -44,9 +44,9 @@ bool CGateKeeper::Init(void)
{ {
// load lists from files // load lists from files
m_NodeWhiteList.LoadFromFile(g_Configure.GetString(g_Keys.files.white)); m_WhiteSet.LoadFromFile(g_Configure.GetString(g_Keys.files.white));
m_NodeBlackList.LoadFromFile(g_Configure.GetString(g_Keys.files.black)); m_BlackSet.LoadFromFile(g_Configure.GetString(g_Keys.files.black));
m_PeerList.LoadFromFile(g_Configure.GetString(g_Keys.files.interlink)); m_InterlinkMap.LoadFromFile(g_Configure.GetString(g_Keys.files.interlink));
// reset run flag // reset run flag
keep_running = true; 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 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) switch (protocol)
{ {
@ -88,26 +89,24 @@ bool CGateKeeper::MayLink(const CCallsign &callsign, const CIp &ip, EProtocol pr
case EProtocol::usrp: case EProtocol::usrp:
case EProtocol::nxdn: case EProtocol::nxdn:
case EProtocol::g3: case EProtocol::g3:
// first check is IP & callsigned listed OK // is callsign listed OK
ok &= IsNodeListedOk(callsign, ip); ok = IsNodeListedOk(base);
// todo: then apply any protocol specific authorisation for the operation
break; break;
// URF and BM interlinks // URF and BM interlinks
case EProtocol::bm: case EProtocol::bm:
case EProtocol::urf: case EProtocol::urf:
ok &= IsPeerListedOk(callsign, ip, modules); ok = IsPeerListedOk(base, ip, modules);
break; break;
// unsupported // unsupported
case EProtocol::none:
default: default:
ok = false; ok = false;
break; break;
} }
// report // report
if ( !ok ) if ( ! ok )
{ {
std::cout << "Gatekeeper blocking linking of " << callsign << " @ " << ip << " using protocol " << ProtocolName(protocol) << std::endl; 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 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) switch (protocol)
{ {
@ -136,14 +137,14 @@ bool CGateKeeper::MayTransmit(const CCallsign &callsign, const CIp &ip, const EP
case EProtocol::usrp: case EProtocol::usrp:
case EProtocol::g3: case EProtocol::g3:
// first check is IP & callsigned listed OK // 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 // todo: then apply any protocol specific authorisation for the operation
break; break;
// URF interlinks // URF interlinks
case EProtocol::urf: case EProtocol::urf:
case EProtocol::bm: case EProtocol::bm:
ok = ok && IsPeerListedOk(callsign, ip, module); ok = IsPeerListedOk(base, module);
break; break;
// unsupported // unsupported
@ -175,17 +176,17 @@ void CGateKeeper::Thread()
std::this_thread::sleep_for(std::chrono::milliseconds(2000)); std::this_thread::sleep_for(std::chrono::milliseconds(2000));
// have lists files changed ? // 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 // 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; bool ok = true;
// first check IP
// next, check callsign // next, check callsign
if ( ok ) if ( ok )
{ {
// first check if callsign is in white list // first check if callsign is in white list
// note if white list is empty, everybody is authorized // note if white list is empty, everybody is authorized
m_NodeWhiteList.Lock(); m_WhiteSet.Lock();
if ( !m_NodeWhiteList.empty() ) 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 // then check if not blacklisted
if (ok) if (ok)
{ {
m_NodeBlackList.Lock(); m_BlackSet.Lock();
ok = !m_NodeBlackList.IsCallsignListedWithWildcard(callsign); ok = ! m_BlackSet.IsMatched(callsign);
m_NodeBlackList.Unlock(); 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; bool ok = true;
@ -235,19 +234,19 @@ bool CGateKeeper::IsPeerListedOk(const CCallsign &callsign, const CIp &ip, char
if ( ok ) if ( ok )
{ {
// look for an exact match in the list // look for an exact match in the list
m_PeerList.Lock(); m_InterlinkMap.Lock();
if ( !m_PeerList.empty() ) if ( !m_InterlinkMap.empty() )
{ {
ok = m_PeerList.IsCallsignListed(callsign, module); ok = m_InterlinkMap.IsCallsignListed(callsign, module);
} }
m_PeerList.Unlock(); m_InterlinkMap.Unlock();
} }
// done // done
return ok; 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; bool ok = true;
@ -257,12 +256,12 @@ bool CGateKeeper::IsPeerListedOk(const CCallsign &callsign, const CIp &ip, char
if ( ok ) if ( ok )
{ {
// look for an exact match in the list // look for an exact match in the list
m_PeerList.Lock(); m_InterlinkMap.Lock();
if ( !m_PeerList.empty() ) if ( ! m_InterlinkMap.empty() )
{ {
ok = m_PeerList.IsCallsignListed(callsign, modules); ok = m_InterlinkMap.IsCallsignListed(callsign, ip, modules);
} }
m_PeerList.Unlock(); m_InterlinkMap.Unlock();
} }
// done // done

@ -22,8 +22,8 @@
#include "Defines.h" #include "Defines.h"
#include "Callsign.h" #include "Callsign.h"
#include "IP.h" #include "IP.h"
#include "CallsignList.h" #include "BlackWhiteSet.h"
#include "PeerCallsignList.h" #include "InterlinkMap.h"
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// class // class
@ -41,29 +41,27 @@ public:
bool Init(void); bool Init(void);
void Close(void); void Close(void);
CInterlinkMap *GetInterlinkMap(void) { m_InterlinkMap.Lock(); return &m_InterlinkMap; }
void ReleaseInterlinkMap(void) { m_InterlinkMap.Unlock(); }
// authorizations // authorizations
bool MayLink(const CCallsign &, const CIp &, const EProtocol, char * = nullptr) const; bool MayLink(const CCallsign &, const CIp &, const EProtocol, char * = nullptr) const;
bool MayTransmit(const CCallsign &, const CIp &, EProtocol = EProtocol::any, char = ' ') 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: protected:
// thread // thread
void Thread(); void Thread();
// operation helpers // operation helpers
bool IsNodeListedOk(const CCallsign &, const CIp &, char = ' ') const; bool IsNodeListedOk(const std::string &) const;
bool IsPeerListedOk(const CCallsign &, const CIp &, char) const; bool IsPeerListedOk(const std::string &, char) const;
bool IsPeerListedOk(const CCallsign &, const CIp &, char *) const; bool IsPeerListedOk(const std::string &, const CIp &, char *) const;
const std::string ProtocolName(EProtocol) const; const std::string ProtocolName(EProtocol) const;
protected: protected:
// data // data
CCallsignList m_NodeWhiteList; CBlackWhiteSet m_WhiteSet, m_BlackSet;
CCallsignList m_NodeBlackList; CInterlinkMap m_InterlinkMap;
CPeerCallsignList m_PeerList;
// thread // thread
std::atomic<bool> keep_running; std::atomic<bool> keep_running;

@ -219,7 +219,7 @@ unsigned int CGolay2087::getSyndrome1987(unsigned int pattern)
* Compute the syndrome corresponding to the given pattern, i.e., the * Compute the syndrome corresponding to the given pattern, i.e., the
* remainder after dividing the pattern (when considering it as the vector * remainder after dividing the pattern (when considering it as the vector
* representation of a polynomial) by the generator polynomial, GENPOL. * 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, * bits, when constructing the encoding table; (2) pattern = error pattern,
* when constructing the decoding table; and (3) pattern = received vector, to * when constructing the decoding table; and (3) pattern = received vector, to
* obtain its syndrome in decoding. * obtain its syndrome in decoding.

@ -1055,7 +1055,7 @@ static unsigned int get_syndrome_23127(unsigned int pattern)
* Compute the syndrome corresponding to the given pattern, i.e., the * Compute the syndrome corresponding to the given pattern, i.e., the
* remainder after dividing the pattern (when considering it as the vector * remainder after dividing the pattern (when considering it as the vector
* representation of a polynomial) by the generator polynomial, GENPOL. * 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, * bits, when constructing the encoding table; (2) pattern = error pattern,
* when constructing the decoding table; and (3) pattern = received vector, to * when constructing the decoding table; and (3) pattern = received vector, to
* obtain its syndrome in decoding. * obtain its syndrome in decoding.

@ -224,7 +224,7 @@ bool CHamming::decode1393(bool* d)
d[12] = !d[12]; d[12] = !d[12];
return true; return true;
// Data bit erros // Data bit errors
case 0x0FU: case 0x0FU:
d[0] = !d[0]; d[0] = !d[0];
return true; return true;
@ -303,7 +303,7 @@ bool CHamming::decode1063(bool* d)
d[9] = !d[9]; d[9] = !d[9];
return true; return true;
// Data bit erros // Data bit errors
case 0x07U: case 0x07U:
d[0] = !d[0]; d[0] = !d[0];
return true; return true;

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <fstream>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#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

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#pragma once
#include <mutex>
#include <map>
#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<std::string, CInterlinkMapItem>::iterator begin() { return m_InterlinkMap.begin(); }
std::map<std::string, CInterlinkMapItem>::iterator end() { return m_InterlinkMap.end(); }
std::map<std::string, CInterlinkMapItem>::const_iterator cbegin() { return m_InterlinkMap.cbegin(); }
std::map<std::string, CInterlinkMapItem>::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<std::string, CInterlinkMapItem> m_InterlinkMap;
};

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <string.h>
#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<count; i++)
found[i] = false;
for (auto p=mods; *p; p++)
{
auto pos = m_Mods.find(*p);
if (pos == m_Mods.npos)
return false;
else
found[pos] = true;
}
for (unsigned i=0; i<count; i++)
{
if (! found[i])
return false;
}
return true;
}
#ifndef NO_DHT
void CInterlinkMapItem::UpdateItem(const std::string &cmods, const std::string &ipv4, const std::string &ipv6, uint16_t port, const std::string &tcmods)
{
if (m_CMods.compare(cmods))
{
m_CMods.assign(cmods);
m_Updated = true;
}
if (m_IPv4.compare(ipv4))
{
m_IPv4.assign(ipv4);
m_Updated = true;
}
if (m_IPv6.compare(ipv6))
{
m_IPv6.assign(ipv6);
m_Updated = true;
}
if (m_Port != port)
{
m_Port = port;
m_Updated = true;
}
if (m_TCMods.compare(tcmods))
{
m_TCMods.assign(tcmods);
m_Updated = true;
}
}
void CInterlinkMapItem::UpdateIP(bool IPv6NotConfigured)
{
if (m_Updated)
{
if (IPv6NotConfigured || m_IPv6.empty())
m_Ip.Initialize(AF_INET, m_Port, m_IPv4.c_str());
else
m_Ip.Initialize(AF_INET6, m_Port, m_IPv6.c_str());
m_Updated = false;
}
}
#endif

@ -0,0 +1,74 @@
//
// Created by Jean-Luc Deltombe (LX3JL) on 31/01/2016.
// 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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#pragma once
#include <string>
#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
};

@ -39,8 +39,8 @@ struct SJsonKeys {
struct MMDVM { const std::string port, defaultid; } struct MMDVM { const std::string port, defaultid; }
mmdvm { "MMDVMPort", "mmdvmdefaultid" }; mmdvm { "MMDVMPort", "mmdvmdefaultid" };
struct NAMES { const std::string callsign, email, country, sponsor; } struct NAMES { const std::string callsign, bootstrap, url, email, country, sponsor; }
names { "Callsign", "SysopEmail", "Country", "Sponsor" }; names { "Callsign", "bootstrap", "DashboardUrl", "SysopEmail", "Country", "Sponsor" };
struct IP { const std::string ipv4bind, ipv4address, ipv6bind, ipv6address, transcoder; } struct IP { const std::string ipv4bind, ipv4address, ipv6bind, ipv6address, transcoder; }
ip { "ipv4bind", "IPv4Address", "ipv6bind", "IPv6Address", "tcaddress" }; ip { "ipv4bind", "IPv4Address", "ipv6bind", "IPv6Address", "tcaddress" };

@ -27,26 +27,6 @@
enum class Eaction { normal, parse, error_only }; enum class Eaction { normal, parse, error_only };
enum class Esource { http, file }; enum class Esource { http, file };
// compare function for std::map::find
struct CCallsignHash
{
std::size_t operator() (const UCallsign &ucs) const
{
std::hash<uint64_t> hash;
return hash(ucs.l);
}
};
struct CCallsignEqual
{
bool operator() (const UCallsign &ucs1, const UCallsign &ucs2) const
{
return ucs1.l == ucs2.l;
}
};
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
class CLookup class CLookup

@ -29,7 +29,7 @@
* Description * Description
* ----------- * -----------
* The source file contains routines which calculate the CCITT CRC * The source file contains routines which calculate the CCITT CRC
* values for an incomming byte string. * values for an incoming byte string.
*/ */
#include <stdlib.h> #include <stdlib.h>

@ -29,7 +29,7 @@
* Description * Description
* ----------- * -----------
* The source file contains routines which calculate the CCITT CRC * The source file contains routines which calculate the CCITT CRC
* values for an incomming byte string. * values for an incoming byte string.
*/ */
#pragma once #pragma once

@ -28,7 +28,7 @@ SJsonKeys g_Keys;
CReflector g_Reflector; CReflector g_Reflector;
CGateKeeper g_GateKeeper; CGateKeeper g_GateKeeper;
CConfigure g_Configure; 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; CLookupDmr g_LDid;
CLookupNxdn g_LNid; CLookupNxdn g_LNid;
CLookupYsf g_LYtr; CLookupYsf g_LYtr;
@ -39,7 +39,7 @@ int main(int argc, char *argv[])
{ {
if (argc != 2) 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; return EXIT_FAILURE;
} }
@ -98,13 +98,13 @@ static void usage(std::ostream &os, const char *name)
" ysf : The Callsign => Tx/Rx frequency database.\n" " ysf : The Callsign => Tx/Rx frequency database.\n"
"SOURCE (choose one)\n" "SOURCE (choose one)\n"
" file : The file specified by the FilePath ini parameter.\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" "ACTION (choose one)\n"
" print : Print all lines from the SOURCE that are syntactically correct.\n" " print : Print all lines from the SOURCE that are syntactically correct.\n"
" error : Print only the lines with failed syntax.\n" " error : Print only the lines with failed syntax.\n"
"INIFILE : an error-free urfd ini file (check it first with inicheck).\n\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" "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 }; enum class Edb { none, dmr, nxdn, ysf };

@ -27,13 +27,19 @@ DBUTIL = dbutil
include urfd.mk include urfd.mk
ifeq ($(debug), true) ifeq ($(debug), true)
CFLAGS = -ggdb3 -DDEBUG -W -Werror -std=c++11 -MMD -MD CFLAGS = -ggdb3 -DDEBUG -W -Werror -std=c++17 -MMD -MD
else else
CFLAGS = -W -Werror -std=c++11 -MMD -MD CFLAGS = -W -Werror -std=c++17 -MMD -MD
endif endif
LDFLAGS=-pthread -lcurl LDFLAGS=-pthread -lcurl
ifeq ($(DHT), true)
LDFLAGS += -lopendht
else
CFLAGS += -DNO_DHT
endif
SRCS = $(wildcard *.cpp) SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o) OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d) DEPS = $(SRCS:.cpp=.d)

@ -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 <https://www.gnu.org/licenses/>.
#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;
}

@ -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 <https://www.gnu.org/licenses/>.
#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;
};

@ -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 <https://www.gnu.org/licenses/>.
#pragma once
#include <queue>
#include <mutex>
#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<CNotification> queue;
};

@ -88,7 +88,7 @@ void CPacket::EncodeInterlinkPacket(const char *magic, CBuffer &buf) const
data[19] = m_uiYsfPacketFrameId; data[19] = m_uiYsfPacketFrameId;
} }
// dstar contstructor // dstar constructor
CPacket::CPacket(uint16_t sid, uint8_t dstarpid) CPacket::CPacket(uint16_t sid, uint8_t dstarpid)
{ {
m_uiStreamId = sid; m_uiStreamId = sid;

@ -42,6 +42,7 @@ public:
const CCallsign &GetCallsign(void) const { return m_Callsign; } const CCallsign &GetCallsign(void) const { return m_Callsign; }
const CIp &GetIp(void) const { return m_Ip; } const CIp &GetIp(void) const { return m_Ip; }
char *GetReflectorModules(void) { return m_ReflectorModules; } char *GetReflectorModules(void) { return m_ReflectorModules; }
std::time_t GetConnectTime(void) const { return m_ConnectTime; }
// set // set
@ -60,7 +61,7 @@ public:
int GetNbClients(void) const { return (int)m_Clients.size(); } int GetNbClients(void) const { return (int)m_Clients.size(); }
void ClearClients(void) { m_Clients.clear(); } void ClearClients(void) { m_Clients.clear(); }
// pass-thru // pass-through
std::list<std::shared_ptr<CClient>>::iterator begin() { return m_Clients.begin(); } std::list<std::shared_ptr<CClient>>::iterator begin() { return m_Clients.begin(); }
std::list<std::shared_ptr<CClient>>::iterator end() { return m_Clients.end(); } std::list<std::shared_ptr<CClient>>::iterator end() { return m_Clients.end(); }
std::list<std::shared_ptr<CClient>>::const_iterator cbegin() const { return m_Clients.cbegin(); } std::list<std::shared_ptr<CClient>>::const_iterator cbegin() const { return m_Clients.cbegin(); }

@ -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 <https://www.gnu.org/licenses/>.
#include <fstream>
#include <string.h>
#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;
}

@ -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 <https://www.gnu.org/licenses/>.
#pragma once
#include "CallsignList.h"
////////////////////////////////////////////////////////////////////////////////////////
// class
class CPeerCallsignList : public CCallsignList
{
public:
// constructor
CPeerCallsignList() {}
// destructor
virtual ~CPeerCallsignList() {}
// file io
bool LoadFromFile(const std::string &filename);
};

@ -38,7 +38,7 @@ public:
void AddPeer(std::shared_ptr<CPeer>); void AddPeer(std::shared_ptr<CPeer>);
void RemovePeer(std::shared_ptr<CPeer>); void RemovePeer(std::shared_ptr<CPeer>);
// pass-thru // pass-through
std::list<std::shared_ptr<CPeer>>::iterator begin() { return m_Peers.begin(); } std::list<std::shared_ptr<CPeer>>::iterator begin() { return m_Peers.begin(); }
std::list<std::shared_ptr<CPeer>>::iterator end() { return m_Peers.end(); } std::list<std::shared_ptr<CPeer>>::iterator end() { return m_Peers.end(); }
std::list<std::shared_ptr<CPeer>>::const_iterator cbegin() const { return m_Peers.cbegin(); } std::list<std::shared_ptr<CPeer>>::const_iterator cbegin() const { return m_Peers.cbegin(); }

@ -32,7 +32,7 @@ public:
void Lock(void) { m_Mutex.lock(); } void Lock(void) { m_Mutex.lock(); }
void Unlock(void) { m_Mutex.unlock(); } void Unlock(void) { m_Mutex.unlock(); }
// pass-thru // pass-through
std::list<std::unique_ptr<CProtocol>>::iterator begin() { return m_Protocols.begin(); } std::list<std::unique_ptr<CProtocol>>::iterator begin() { return m_Protocols.begin(); }
std::list<std::unique_ptr<CProtocol>>::iterator end() { return m_Protocols.end(); } std::list<std::unique_ptr<CProtocol>>::iterator end() { return m_Protocols.end(); }

@ -72,7 +72,7 @@ unsigned int CQR1676::getSyndrome1576(unsigned int pattern)
* Compute the syndrome corresponding to the given pattern, i.e., the * Compute the syndrome corresponding to the given pattern, i.e., the
* remainder after dividing the pattern (when considering it as the vector * remainder after dividing the pattern (when considering it as the vector
* representation of a polynomial) by the generator polynomial, GENPOL. * 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, * bits, when constructing the encoding table; (2) pattern = error pattern,
* when constructing the decoding table; and (3) pattern = received vector, to * when constructing the decoding table; and (3) pattern = received vector, to
* obtain its syndrome in decoding. * obtain its syndrome in decoding.

@ -20,8 +20,13 @@
#include <string.h> #include <string.h>
#include "Global.h" #include "Global.h"
////////////////////////////////////////////////////////////////////////////////////////
// destructor CReflector::CReflector()
{
#ifndef NO_DHT
peers_put_count = clients_put_count = users_put_count = 0;
#endif
}
CReflector::~CReflector() CReflector::~CReflector()
{ {
@ -47,10 +52,18 @@ CReflector::~CReflector()
bool CReflector::Start(void) bool CReflector::Start(void)
{ {
// get config stuff // 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)); m_Modules.assign(g_Configure.GetString(g_Keys.modules.modules));
std::string tcmods(g_Configure.GetString(g_Keys.modules.tcmodules)); 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! // let's go!
keep_running = true; keep_running = true;
@ -107,13 +120,17 @@ bool CReflector::Start(void)
// start the reporting thread // start the reporting thread
try 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) catch(const std::exception& e)
{ {
std::cerr << "Cannot start the dashboard data report thread: " << e.what() << '\n'; std::cerr << "Cannot start the dashboard data report thread: " << e.what() << '\n';
} }
#ifndef NO_DHT
PutDHTConfig();
#endif
return false; return false;
} }
@ -145,6 +162,16 @@ void CReflector::Stop(void)
g_LDid.LookupClose(); g_LDid.LookupClose();
g_LNid.LookupClose(); g_LNid.LookupClose();
g_LYtr.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; 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<CPacketStream> CReflector::OpenStream(std::unique_ptr<CDvHeaderPacket> &DvHeader, std::shared_ptr<CClient>client) std::shared_ptr<CPacketStream> CReflector::OpenStream(std::unique_ptr<CDvHeaderPacket> &DvHeader, std::shared_ptr<CClient>client)
{ {
// check sid is not zero // check sid is not zero
@ -209,7 +236,7 @@ std::shared_ptr<CPacketStream> CReflector::OpenStream(std::unique_ptr<CDvHeaderP
stream->Push(std::move(DvHeader)); stream->Push(std::move(DvHeader));
// notify // notify
OnStreamOpen(stream->GetUserCallsign()); //OnStreamOpen(stream->GetUserCallsign());
} }
return stream; return stream;
@ -237,7 +264,7 @@ void CReflector::CloseStream(std::shared_ptr<CPacketStream> stream)
client->NotAMaster(); client->NotAMaster();
// notify // notify
OnStreamClose(stream->GetUserCallsign()); //OnStreamClose(stream->GetUserCallsign());
std::cout << "Closing stream of module " << GetStreamModule(stream) << std::endl; 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 #define XML_UPDATE_PERIOD 10
void CReflector::XmlReportThread() void CReflector::StateReportThread()
{ {
std::string xmlpath, jsonpath; std::string xmlpath, jsonpath;
#ifndef NO_DHT
peers_changed = clients_changed = users_changed = true;
#endif
if (g_Configure.Contains(g_Keys.files.xml)) if (g_Configure.Contains(g_Keys.files.xml))
xmlpath.assign(g_Configure.GetString(g_Keys.files.xml)); xmlpath.assign(g_Configure.GetString(g_Keys.files.xml));
if (g_Configure.Contains(g_Keys.files.json)) 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++) 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)); std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
} }
} }
@ -354,47 +403,23 @@ void CReflector::XmlReportThread()
void CReflector::OnPeersChanged(void) void CReflector::OnPeersChanged(void)
{ {
CNotification notification(NOTIFICATION_PEERS); #ifndef NO_DHT
peers_changed = true;
m_Notifications.Lock(); #endif
m_Notifications.push(notification);
m_Notifications.Unlock();
} }
void CReflector::OnClientsChanged(void) void CReflector::OnClientsChanged(void)
{ {
CNotification notification(NOTIFICATION_CLIENTS); #ifndef NO_DHT
clients_changed = true;
m_Notifications.Lock(); #endif
m_Notifications.push(notification);
m_Notifications.Unlock();
} }
void CReflector::OnUsersChanged(void) void CReflector::OnUsersChanged(void)
{ {
CNotification notification(NOTIFICATION_USERS); #ifndef NO_DHT
users_changed = true;
m_Notifications.Lock(); #endif
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();
} }
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -512,3 +537,202 @@ void CReflector::WriteXmlFile(std::ofstream &xmlFile)
ReleaseUsers(); ReleaseUsers();
xmlFile << "</" << cs << "heard users>" << std::endl; xmlFile << "</" << cs << "heard users>" << 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<dht::Value>(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<dht::Value>(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<dht::Value>(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<dht::Value>(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<dht::Value> &v) {
if (0 == v->user_type.compare("urfd-config-1"))
{
auto rdat = dht::Value::unpack<SUrfdConfig1>(*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<SUrfdConfig1>(*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

@ -25,7 +25,10 @@
#include "Peers.h" #include "Peers.h"
#include "Protocols.h" #include "Protocols.h"
#include "PacketStream.h" #include "PacketStream.h"
#include "NotificationQueue.h"
#ifndef NO_DHT
#include "urfd-dht-values.h"
#endif
//////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@ -42,6 +45,7 @@
class CReflector class CReflector
{ {
public: public:
CReflector();
// destructor // destructor
~CReflector(); ~CReflector();
@ -75,13 +79,22 @@ public:
void OnPeersChanged(void); void OnPeersChanged(void);
void OnClientsChanged(void); void OnClientsChanged(void);
void OnUsersChanged(void); void OnUsersChanged(void);
void OnStreamOpen(const CCallsign &); #ifndef NO_DHT
void OnStreamClose(const CCallsign &); void GetDHTConfig(const std::string &cs);
#endif
protected: protected:
#ifndef NO_DHT
// Publish DHT
void PutDHTConfig();
void PutDHTPeers();
void PutDHTClients();
void PutDHTUsers();
#endif
// threads // threads
void RouterThread(const char); void RouterThread(const char);
void XmlReportThread(void); void StateReportThread(void);
// streams // streams
std::shared_ptr<CPacketStream> GetStream(char); std::shared_ptr<CPacketStream> GetStream(char);
@ -92,7 +105,6 @@ protected:
void WriteXmlFile(std::ofstream &); void WriteXmlFile(std::ofstream &);
void JsonReport(nlohmann::json &report); void JsonReport(nlohmann::json &report);
protected:
// identity // identity
CCallsign m_Callsign; CCallsign m_Callsign;
std::string m_Modules, m_TCmodules; std::string m_Modules, m_TCmodules;
@ -111,6 +123,11 @@ protected:
std::unordered_map<char, std::future<void>> m_RouterFuture; std::unordered_map<char, std::future<void>> m_RouterFuture;
std::future<void> m_XmlReportFuture; std::future<void> m_XmlReportFuture;
// notifications #ifndef NO_DHT
CNotificationQueue m_Notifications; // Distributed Hash Table
dht::DhtRunner node;
dht::InfoHash refhash;
unsigned int peers_put_count, clients_put_count, users_put_count;
std::atomic<bool> peers_changed, clients_changed, users_changed;
#endif
}; };

@ -22,7 +22,7 @@
/************************************************************ /************************************************************
* THIS IS IMPORTANT * 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 * If you are going to use it for std::shared_ptr, then
* please consider that when you Push(), what you pushed * please consider that when you Push(), what you pushed
* from will be nullptr after the Push()! * 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) T PopWait(void)
{ {
std::unique_lock<std::mutex> lock(m); std::unique_lock<std::mutex> lock(m);
while(q.empty()) 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); c.wait(lock);
} }
T val = std::move(q.front()); T val = std::move(q.front());

@ -40,7 +40,7 @@ CUdpSocket::~CUdpSocket()
// returns true on error // returns true on error
bool CUdpSocket::Open(const CIp &Ip) bool CUdpSocket::Open(const CIp &Ip)
{ {
// check for a vaild family // check for a valid family
if (AF_UNSPEC == Ip.GetFamily()) if (AF_UNSPEC == Ip.GetFamily())
return true; return true;

@ -287,16 +287,16 @@ void CURFProtocol::HandlePeerLinks(void)
CBuffer buffer; CBuffer buffer;
// get the list of peers // get the list of peers
CPeerCallsignList *list = g_GateKeeper.GetPeerList(); auto ilmap = g_GateKeeper.GetInterlinkMap();
CPeers *peers = g_Reflector.GetPeers(); CPeers *peers = g_Reflector.GetPeers();
// check if all our connected peers are still listed by gatekeeper // check if all our connected peers are still listed by gatekeeper
// if not, disconnect // if not, disconnect
auto pit = peers->begin(); auto pit = peers->begin();
std::shared_ptr<CPeer>peer = nullptr; std::shared_ptr<CPeer>peer = 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 // send disconnect packet
EncodeDisconnectPacket(&buffer); 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 // 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*")) ) const auto cs = it->first;
continue; CCallsign callsign;
if ( it->GetCallsign().HasSameCallsignWithWildcard(CCallsign("BM*")) ) callsign.SetCallsign(cs, false);
continue; if ((0 == cs.substr(0, 3).compare("URF")) && (nullptr==peers->FindPeer(callsign, EProtocol::urf)))
CCallsign cs = it->GetCallsign();
if (cs.HasSameCallsignWithWildcard(CCallsign("URF*")) && (nullptr==peers->FindPeer(cs, EProtocol::urf)))
{ {
// resolve again peer's IP in case it's a dynamic IP #ifndef NO_DHT
it->ResolveIp(); it->second.UpdateIP(g_Configure.GetString(g_Keys.ip.ipv6address).empty());
// send connect packet to re-initiate peer link if (it->second.GetIp().IsSet())
EncodeConnectPacket(&buffer, it->GetModules()); {
Send(buffer, it->GetIp(), m_Port); bool ok = true;
std::cout << "Sending connect packet to URF peer " << cs << " @ " << it->GetIp() << " for modules " << it->GetModules() << std::endl; // 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 // done
g_Reflector.ReleasePeers(); 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); callsign->CodeIn(Buffer.data()+4);
valid = callsign->IsValid(); valid = callsign->IsValid();
*version = CVersion(Buffer.at(37), Buffer.at(38), Buffer.at(39)); *version = CVersion(Buffer.at(37), Buffer.at(38), Buffer.at(39));
memcpy(modules, Buffer.data()+10, 27); if (valid)
for ( unsigned i = 0; i < strlen(modules); i++ )
{ {
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; return valid;

@ -34,6 +34,13 @@ public:
// destructor // destructor
~CUser() {} ~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 // operation
void HeardNow(void) { m_LastHeardTime = time(nullptr); } void HeardNow(void) { m_LastHeardTime = time(nullptr); }

@ -40,9 +40,11 @@ public:
int GetSize(void) const { return (int)m_Users.size(); } int GetSize(void) const { return (int)m_Users.size(); }
void AddUser(const CUser &); void AddUser(const CUser &);
// pass-thru // pass-through
std::list<CUser>::iterator begin() { return m_Users.begin(); } std::list<CUser>::iterator begin() { return m_Users.begin(); }
std::list<CUser>::iterator end() { return m_Users.end(); } std::list<CUser>::iterator end() { return m_Users.end(); }
std::list<CUser>::const_iterator cbegin() { return m_Users.cbegin(); }
std::list<CUser>::const_iterator cend() { return m_Users.cend(); }
// operation // operation
void Hearing(const CCallsign &, const CCallsign &, const CCallsign &); void Hearing(const CCallsign &, const CCallsign &, const CCallsign &);

@ -85,5 +85,8 @@ bool CVersion::operator <(const CVersion &v) const
std::ostream &operator <<(std::ostream &os, const CVersion &v) std::ostream &operator <<(std::ostream &os, const CVersion &v)
{ {
os << v.GetMajor() << '.' << v.GetMinor() << '.' << v.GetRevision(); os << v.GetMajor() << '.' << v.GetMinor() << '.' << v.GetRevision();
#ifndef NO_DHT
os << "-dht";
#endif
return os; return os;
}; };

@ -38,7 +38,7 @@ public:
// set // set
void Set(uint8_t, uint8_t, uint8_t); 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; bool operator !=(const CVersion &v) const;
bool operator >=(const CVersion &v) const; bool operator >=(const CVersion &v) const;

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#pragma once
#include <opendht.h>
/* HELPERS */
#ifndef TO_U_TYPE_DEF
#define TO_U_TYPE_DEF
template<typename E> constexpr auto toUType(E enumerator) noexcept
{
return static_cast<std::underlying_type_t<E>>(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<std::string, std::string, std::time_t>;
enum class EUrfdPeerFields { Callsign, Modules, ConnectTime };
struct SUrfdPeers1
{
std::time_t timestamp;
unsigned int sequence;
std::list<UrfdPeerTuple> list;
MSGPACK_DEFINE(timestamp, sequence, list)
};
/* CLIENTS */
using UrfdClientTuple = std::tuple<std::string, std::string, char, std::time_t, std::time_t>;
enum class EUrfdClientFields { Callsign, Ip, Module, ConnectTime, LastHeardTime };
struct SUrfdClients1
{
std::time_t timestamp;
unsigned int sequence;
std::list<UrfdClientTuple> list;
MSGPACK_DEFINE(timestamp, sequence, list)
};
/* USERS */
using UrfdUserTuple = std::tuple<std::string, std::string, char, std::string, std::time_t>;
enum class EUrfdUserFields { Callsign, ViaNode, OnModule, ViaPeer, LastHeardTime };
struct SUrfdUsers1
{
std::time_t timestamp;
unsigned int sequence;
std::list<UrfdUserTuple> 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<uint16_t, toUType(EUrfdPorts::SIZE)> port;
std::array<char, toUType(EUrfdAlMod::SIZE)> almod;
std::array<unsigned long, toUType(EUrfdTxRx::SIZE)> ysffreq;
std::array<unsigned, toUType(EUrfdRefId::SIZE)> refid;
std::unordered_map<char, std::string> description;
bool g3enabled;
MSGPACK_DEFINE(timestamp, cs, ipv4, ipv6, mods, tcmods, url, email, sponsor, country, version, almod, ysffreq, refid, g3enabled, port, description)
};
Loading…
Cancel
Save

Powered by TurnKey Linux.