1
0
Fork 0

Remove the YSF Reflector.

master
Jonathan Naylor 2 years ago
parent 3731800162
commit 9eb6d3967f

@ -1,4 +1,4 @@
SUBDIRS = DGIdGateway YSFGateway YSFParrot YSFReflector
SUBDIRS = DGIdGateway YSFGateway YSFParrot
CLEANDIRS = $(SUBDIRS:%=clean-%)
INSTALLDIRS = $(SUBDIRS:%=install-%)

@ -6,9 +6,7 @@ The YSF Gateway allows for use of Yaesu Wires-X commands from the radio to contr
The DG-ID Gateway allows the use of the DG-ID setting on the radio to control access to the different System Fusion network systems. It optionally sends System Fusion GPS information to aprs.fi.
The Reflector retransmits any received System Fusion data to other MMDVM Hosts or Gateways logged into the reflector at the time. It also provides status information to potential clients.
The Gateways and the Reflector have ini files that contain the parameters for running the software. The filename of the ini file is passed as a parameter on the command line. The Parrot takes the UDP port number to listen on as an argument.
The Gateways have ini files that contain the parameters for running the software. The filename of the ini file is passed as a parameter on the command line. The Parrot takes the UDP port number to listen on as an argument.
The MMDVM .ini file should have the IP address and port number of the client in the [System Fusion Network] settings.

@ -5,8 +5,6 @@ VisualStudioVersion = 16.0.30406.217
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YSFParrot", "YSFParrot\YSFParrot.vcxproj", "{D3BBE5EC-91F7-457B-B782-B616B918708F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YSFReflector", "YSFReflector\YSFReflector.vcxproj", "{317D87F1-3485-4739-9F94-A07738B8E19D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YSFGateway", "YSFGateway\YSFGateway.vcxproj", "{4F82857B-D2CC-48DC-91A8-6275BDD3081B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DGIdGateway", "DGIdGateway\DGIdGateway.vcxproj", "{1A4724B0-257E-45E9-943D-EABC56A1027E}"
@ -27,14 +25,6 @@ Global
{D3BBE5EC-91F7-457B-B782-B616B918708F}.Release|x64.Build.0 = Release|x64
{D3BBE5EC-91F7-457B-B782-B616B918708F}.Release|x86.ActiveCfg = Release|Win32
{D3BBE5EC-91F7-457B-B782-B616B918708F}.Release|x86.Build.0 = Release|Win32
{317D87F1-3485-4739-9F94-A07738B8E19D}.Debug|x64.ActiveCfg = Debug|x64
{317D87F1-3485-4739-9F94-A07738B8E19D}.Debug|x64.Build.0 = Debug|x64
{317D87F1-3485-4739-9F94-A07738B8E19D}.Debug|x86.ActiveCfg = Debug|Win32
{317D87F1-3485-4739-9F94-A07738B8E19D}.Debug|x86.Build.0 = Debug|Win32
{317D87F1-3485-4739-9F94-A07738B8E19D}.Release|x64.ActiveCfg = Release|x64
{317D87F1-3485-4739-9F94-A07738B8E19D}.Release|x64.Build.0 = Release|x64
{317D87F1-3485-4739-9F94-A07738B8E19D}.Release|x86.ActiveCfg = Release|Win32
{317D87F1-3485-4739-9F94-A07738B8E19D}.Release|x86.Build.0 = Release|Win32
{4F82857B-D2CC-48DC-91A8-6275BDD3081B}.Debug|x64.ActiveCfg = Debug|x64
{4F82857B-D2CC-48DC-91A8-6275BDD3081B}.Debug|x64.Build.0 = Debug|x64
{4F82857B-D2CC-48DC-91A8-6275BDD3081B}.Debug|x86.ActiveCfg = Debug|Win32

@ -1,144 +0,0 @@
/*
* Copyright (C) 2021 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "BlockList.h"
#include "YSFDefines.h"
#include "Log.h"
#include <algorithm>
#include <cstdio>
#include <cassert>
#include <cstring>
const int BUFFER_SIZE = 500;
CBlockList::CBlockList(const std::string& file, unsigned int time) :
m_file(file),
m_time(time),
m_callsigns(),
m_timer(1000U, time * 60U),
m_checksum(0U)
{
}
CBlockList::~CBlockList()
{
}
bool CBlockList::start()
{
loadFile();
m_timer.start();
return true;
}
bool CBlockList::check(const unsigned char* callsign) const
{
assert(callsign != NULL);
if (m_callsigns.empty())
return false;
std::string call = std::string((char*)callsign, YSF_CALLSIGN_LENGTH);
std::for_each(call.begin(), call.end(), [](char& c) {
c = std::toupper(c);
});
for (std::vector<std::string>::const_iterator it = m_callsigns.cbegin(); it != m_callsigns.cend(); ++it) {
const std::string blocked = *it;
if (call.find(blocked) != std::string::npos)
return true;
}
return false;
}
void CBlockList::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_timer.isRunning() && m_timer.hasExpired()) {
loadFile();
m_timer.start();
}
}
bool CBlockList::loadFile()
{
FILE* fp = ::fopen(m_file.c_str(), "rt");
if (fp == NULL) {
if (!m_callsigns.empty()) {
m_callsigns.clear();
LogInfo("Loaded %u callsigns from the block list", m_callsigns.size());
}
return false;
}
char buffer[BUFFER_SIZE];
// First perform the checksum (Fletcher 16)
uint16_t sum1 = 0U;
uint16_t sum2 = 0U;
while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) {
for (char* p = buffer; *p != '\0'; p++) {
sum1 = (sum1 + uint8_t(*p)) % 255U;
sum2 = (sum2 + sum1) % 255U;
}
}
uint16_t checksum = (sum2 << 8) | sum1;
if (checksum == m_checksum) {
::fclose(fp);
return false;
}
m_checksum = checksum;
// Rewind the file
::fseek(fp, 0L, SEEK_SET);
// Read the callsigns into the array
m_callsigns.clear();
while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) {
char* p;
if ((p = ::strchr(buffer, '\n')) != NULL)
*p = '\0';
if ((p = ::strchr(buffer, '\r')) != NULL)
*p = '\0';
if (::strlen(buffer) > 0U) {
std::string callsign = std::string(buffer);
std::for_each(callsign.begin(), callsign.end(), [](char& c) {
c = std::toupper(c);
});
m_callsigns.push_back(callsign);
}
}
::fclose(fp);
LogInfo("Loaded %u callsigns from the block list", m_callsigns.size());
return true;
}

@ -1,50 +0,0 @@
/*
* Copyright (C) 2021 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(BLOCKLIST_H)
#define BLOCKLIST_H
#include "Timer.h"
#include <cstdint>
#include <string>
#include <vector>
class CBlockList
{
public:
CBlockList(const std::string& file, unsigned int time);
~CBlockList();
bool start();
bool check(const unsigned char* callsign) const;
void clock(unsigned int ms);
private:
std::string m_file;
unsigned int m_time;
std::vector<std::string> m_callsigns;
CTimer m_timer;
uint16_t m_checksum;
bool loadFile();
};
#endif

@ -1,219 +0,0 @@
/*
* Copyright (C) 2015,2016,2020,2021 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Conf.h"
#include "Log.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
const int BUFFER_SIZE = 500;
enum SECTION {
SECTION_NONE,
SECTION_GENERAL,
SECTION_INFO,
SECTION_LOG,
SECTION_NETWORK,
SECTION_BLOCKLIST
};
CConf::CConf(const std::string& file) :
m_file(file),
m_daemon(false),
m_id(0U),
m_name(),
m_description(),
m_logDisplayLevel(0U),
m_logFileLevel(0U),
m_logFilePath(),
m_logFileRoot(),
m_logFileRotate(true),
m_networkPort(0U),
m_networkDebug(false),
m_blockListFile(),
m_blockListTime(5U)
{
}
CConf::~CConf()
{
}
bool CConf::read()
{
FILE* fp = ::fopen(m_file.c_str(), "rt");
if (fp == NULL) {
::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str());
return false;
}
SECTION section = SECTION_NONE;
char buffer[BUFFER_SIZE];
while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) {
if (buffer[0U] == '#')
continue;
if (buffer[0U] == '[') {
if (::strncmp(buffer, "[General]", 9U) == 0)
section = SECTION_GENERAL;
else if (::strncmp(buffer, "[Info]", 6U) == 0)
section = SECTION_INFO;
else if (::strncmp(buffer, "[Log]", 5U) == 0)
section = SECTION_LOG;
else if (::strncmp(buffer, "[Network]", 9U) == 0)
section = SECTION_NETWORK;
else if (::strncmp(buffer, "[Block List]", 12U) == 0)
section = SECTION_BLOCKLIST;
else
section = SECTION_NONE;
continue;
}
char* key = ::strtok(buffer, " \t=\r\n");
if (key == NULL)
continue;
char* value = ::strtok(NULL, "\r\n");
if (value == NULL)
continue;
// Remove quotes from the value
size_t len = ::strlen(value);
if (len > 1U && *value == '"' && value[len - 1U] == '"') {
value[len - 1U] = '\0';
value++;
} else {
char *p;
// if value is not quoted, remove after # (to make comment)
if ((p = strchr(value, '#')) != NULL)
*p = '\0';
// remove trailing tab/space
for (p = value + strlen(value) - 1U; p >= value && (*p == '\t' || *p == ' '); p--)
*p = '\0';
}
if (section == SECTION_GENERAL) {
if (::strcmp(key, "Daemon") == 0)
m_daemon = ::atoi(value) == 1;
} else if (section == SECTION_INFO) {
if (::strcmp(key, "Id") == 0)
m_id = (unsigned int)::atoi(value);
else if (::strcmp(key, "Name") == 0)
m_name = value;
else if (::strcmp(key, "Description") == 0)
m_description = value;
} else if (section == SECTION_LOG) {
if (::strcmp(key, "FilePath") == 0)
m_logFilePath = value;
else if (::strcmp(key, "FileRoot") == 0)
m_logFileRoot = value;
else if (::strcmp(key, "FileLevel") == 0)
m_logFileLevel = (unsigned int)::atoi(value);
else if (::strcmp(key, "DisplayLevel") == 0)
m_logDisplayLevel = (unsigned int)::atoi(value);
else if (::strcmp(key, "FileRotate") == 0)
m_logFileRotate = ::atoi(value) == 1;
} else if (section == SECTION_NETWORK) {
if (::strcmp(key, "Port") == 0)
m_networkPort = (unsigned short)::atoi(value);
else if (::strcmp(key, "Debug") == 0)
m_networkDebug = ::atoi(value) == 1;
} else if (section == SECTION_BLOCKLIST) {
if (::strcmp(key, "File") == 0)
m_blockListFile = value;
else if (::strcmp(key, "Time") == 0)
m_blockListTime = (unsigned int)::atoi(value);
}
}
::fclose(fp);
return true;
}
bool CConf::getDaemon() const
{
return m_daemon;
}
unsigned int CConf::getId() const
{
return m_id;
}
std::string CConf::getName() const
{
return m_name;
}
std::string CConf::getDescription() const
{
return m_description;
}
unsigned int CConf::getLogDisplayLevel() const
{
return m_logDisplayLevel;
}
unsigned int CConf::getLogFileLevel() const
{
return m_logFileLevel;
}
std::string CConf::getLogFilePath() const
{
return m_logFilePath;
}
std::string CConf::getLogFileRoot() const
{
return m_logFileRoot;
}
bool CConf::getLogFileRotate() const
{
return m_logFileRotate;
}
unsigned short CConf::getNetworkPort() const
{
return m_networkPort;
}
bool CConf::getNetworkDebug() const
{
return m_networkDebug;
}
std::string CConf::getBlockListFile() const
{
return m_blockListFile;
}
unsigned int CConf::getBlockListTime() const
{
return m_blockListTime;
}

@ -1,77 +0,0 @@
/*
* Copyright (C) 2015,2016,2020,2021 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(CONF_H)
#define CONF_H
#include <string>
#include <vector>
class CConf
{
public:
CConf(const std::string& file);
~CConf();
bool read();
// The General section
bool getDaemon() const;
// The Info section
unsigned int getId() const;
std::string getName() const;
std::string getDescription() const;
// The Log section
unsigned int getLogDisplayLevel() const;
unsigned int getLogFileLevel() const;
std::string getLogFilePath() const;
std::string getLogFileRoot() const;
bool getLogFileRotate() const;
// The Network section
unsigned short getNetworkPort() const;
bool getNetworkDebug() const;
// The Block List section
std::string getBlockListFile() const;
unsigned int getBlockListTime() const;
private:
std::string m_file;
bool m_daemon;
unsigned int m_id;
std::string m_name;
std::string m_description;
unsigned int m_logDisplayLevel;
unsigned int m_logFileLevel;
std::string m_logFilePath;
std::string m_logFileRoot;
bool m_logFileRotate;
unsigned short m_networkPort;
bool m_networkDebug;
std::string m_blockListFile;
unsigned int m_blockListTime;
};
#endif

@ -1,192 +0,0 @@
/*
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Log.h"
#if defined(_WIN32) || defined(_WIN64)
#include <Windows.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <ctime>
#include <cassert>
#include <cstring>
static unsigned int m_fileLevel = 2U;
static std::string m_filePath;
static std::string m_fileRoot;
static bool m_fileRotate = true;
static FILE* m_fpLog = NULL;
static bool m_daemon = false;
static unsigned int m_displayLevel = 2U;
static struct tm m_tm;
static char LEVELS[] = " DMIWEF";
static bool logOpenRotate()
{
bool status = false;
if (m_fileLevel == 0U)
return true;
time_t now;
::time(&now);
struct tm* tm = ::gmtime(&now);
if (tm->tm_mday == m_tm.tm_mday && tm->tm_mon == m_tm.tm_mon && tm->tm_year == m_tm.tm_year) {
if (m_fpLog != NULL)
return true;
} else {
if (m_fpLog != NULL)
::fclose(m_fpLog);
}
char filename[200U];
#if defined(_WIN32) || defined(_WIN64)
::sprintf(filename, "%s\\%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
#else
::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
#endif
if ((m_fpLog = ::fopen(filename, "a+t")) != NULL) {
status = true;
#if !defined(_WIN32) && !defined(_WIN64)
if (m_daemon)
dup2(fileno(m_fpLog), fileno(stderr));
#endif
}
m_tm = *tm;
return status;
}
static bool logOpenNoRotate()
{
bool status = false;
if (m_fileLevel == 0U)
return true;
if (m_fpLog != NULL)
return true;
char filename[200U];
#if defined(_WIN32) || defined(_WIN64)
::sprintf(filename, "%s\\%s.log", m_filePath.c_str(), m_fileRoot.c_str());
#else
::sprintf(filename, "%s/%s.log", m_filePath.c_str(), m_fileRoot.c_str());
#endif
if ((m_fpLog = ::fopen(filename, "a+t")) != NULL) {
status = true;
#if !defined(_WIN32) && !defined(_WIN64)
if (m_daemon)
dup2(fileno(m_fpLog), fileno(stderr));
#endif
}
return status;
}
bool LogOpen()
{
if (m_fileRotate)
return logOpenRotate();
else
return logOpenNoRotate();
}
bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel, bool rotate)
{
m_filePath = filePath;
m_fileRoot = fileRoot;
m_fileLevel = fileLevel;
m_displayLevel = displayLevel;
m_daemon = daemon;
m_fileRotate = rotate;
if (m_daemon)
m_displayLevel = 0U;
return ::LogOpen();
}
void LogFinalise()
{
if (m_fpLog != NULL)
::fclose(m_fpLog);
}
void Log(unsigned int level, const char* fmt, ...)
{
assert(fmt != NULL);
char buffer[501U];
#if defined(_WIN32) || defined(_WIN64)
SYSTEMTIME st;
::GetSystemTime(&st);
::sprintf(buffer, "%c: %04u-%02u-%02u %02u:%02u:%02u.%03u ", LEVELS[level], st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
#else
struct timeval now;
::gettimeofday(&now, NULL);
struct tm* tm = ::gmtime(&now.tv_sec);
::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lld ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000LL);
#endif
va_list vl;
va_start(vl, fmt);
::vsnprintf(buffer + ::strlen(buffer), 500, fmt, vl);
va_end(vl);
if (level >= m_fileLevel && m_fileLevel != 0U) {
bool ret = ::LogOpen();
if (!ret)
return;
::fprintf(m_fpLog, "%s\n", buffer);
::fflush(m_fpLog);
}
if (level >= m_displayLevel && m_displayLevel != 0U) {
::fprintf(stdout, "%s\n", buffer);
::fflush(stdout);
}
if (level == 6U) { // Fatal
::fclose(m_fpLog);
exit(1);
}
}

@ -1,36 +0,0 @@
/*
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(LOG_H)
#define LOG_H
#include <string>
#define LogDebug(fmt, ...) Log(1U, fmt, ##__VA_ARGS__)
#define LogMessage(fmt, ...) Log(2U, fmt, ##__VA_ARGS__)
#define LogInfo(fmt, ...) Log(3U, fmt, ##__VA_ARGS__)
#define LogWarning(fmt, ...) Log(4U, fmt, ##__VA_ARGS__)
#define LogError(fmt, ...) Log(5U, fmt, ##__VA_ARGS__)
#define LogFatal(fmt, ...) Log(6U, fmt, ##__VA_ARGS__)
extern void Log(unsigned int level, const char* fmt, ...);
extern bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel, bool rotate);
extern void LogFinalise();
#endif

@ -1,35 +0,0 @@
CC = cc
CXX = c++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DUDP_SOCKET_MAX=2
LIBS = -lpthread
LDFLAGS = -g
OBJECTS = BlockList.o Conf.o Log.o Network.o StopWatch.o Thread.o Timer.o UDPSocket.o Utils.o YSFReflector.o
all: YSFReflector
YSFReflector: $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o YSFReflector
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
YSFReflector.o: GitVersion.h FORCE
.PHONY: GitVersion.h
FORCE:
install:
install -m 755 YSFReflector /usr/local/bin/
clean:
$(RM) YSFReflector *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard ../.git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

@ -1,155 +0,0 @@
/*
* Copyright (C) 2009-2014,2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "YSFDefines.h"
#include "Network.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
CNetwork::CNetwork(unsigned short port, unsigned int id, const std::string& name, const std::string& description, bool debug) :
m_socket(),
m_port(port),
m_id(id),
m_name(name),
m_description(description),
m_callsign(),
m_debug(debug),
m_status(NULL)
{
m_name.resize(16U, ' ');
m_description.resize(14U, ' ');
m_status = new unsigned char[50U];
}
CNetwork::~CNetwork()
{
delete[] m_status;
}
bool CNetwork::open()
{
LogInfo("Opening YSF network connection");
bool status = false;
unsigned int af[] = {AF_INET, AF_INET6};
for (unsigned int i = 0U; i < UDP_SOCKET_MAX && i < (sizeof(af) / sizeof(unsigned int)); i++)
status |= m_socket.open(i, af[i], "", m_port);
return status;
}
bool CNetwork::writeData(const unsigned char* data, const sockaddr_storage& addr, unsigned int addrLen)
{
assert(data != NULL);
if (m_debug)
CUtils::dump(1U, "YSF Network Data Sent", data, 155U);
return m_socket.write(data, 155U, addr, addrLen);
}
bool CNetwork::writePoll(const sockaddr_storage& addr, unsigned int addrLen)
{
unsigned char buffer[20U];
buffer[0] = 'Y';
buffer[1] = 'S';
buffer[2] = 'F';
buffer[3] = 'P';
buffer[4U] = 'R';
buffer[5U] = 'E';
buffer[6U] = 'F';
buffer[7U] = 'L';
buffer[8U] = 'E';
buffer[9U] = 'C';
buffer[10U] = 'T';
buffer[11U] = 'O';
buffer[12U] = 'R';
buffer[13U] = ' ';
if (m_debug)
CUtils::dump(1U, "YSF Network Poll Sent", buffer, 14U);
return m_socket.write(buffer, 14U, addr, addrLen);
}
unsigned int CNetwork::readData(unsigned char* data, unsigned int length, sockaddr_storage& addr, unsigned int& addrLen)
{
assert(data != NULL);
assert(length > 0U);
int len = m_socket.read(data, length, addr, addrLen);
if (len <= 0)
return 0U;
if (m_debug)
CUtils::dump(1U, "YSF Network Data Received", data, len);
// Throw away any options messages
if (::memcmp(data, "YSFO", 4U) == 0)
return 0U;
// Throw away any info messages
if (::memcmp(data, "YSFI", 4U) == 0)
return 0U;
// Handle incoming status requests
if (::memcmp(data, "YSFS", 4U) == 0) {
m_socket.write(m_status, 42U, addr, addrLen);
return 0U;
}
return len;
}
void CNetwork::setCount(unsigned int count)
{
if (count > 999U)
count = 999U;
unsigned int hash = m_id;
if (hash == 0U) {
for (unsigned int i = 0U; i < m_name.size(); i++) {
hash += m_name.at(i);
hash += (hash << 10);
hash ^= (hash >> 6);
}
// Final avalanche
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
}
::sprintf((char*)m_status, "YSFS%05u%16.16s%14.14s%03u", hash % 100000U, m_name.c_str(), m_description.c_str(), count);
}
void CNetwork::close()
{
m_socket.close();
LogInfo("Closing YSF network connection");
}

@ -1,57 +0,0 @@
/*
* Copyright (C) 2009-2014,2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef Network_H
#define Network_H
#include "YSFDefines.h"
#include "UDPSocket.h"
#include "Timer.h"
#include <cstdint>
#include <string>
class CNetwork {
public:
CNetwork(unsigned short port, unsigned int id, const std::string& name, const std::string& description, bool debug);
~CNetwork();
bool open();
bool writeData(const unsigned char* data, const sockaddr_storage& addr, unsigned int addrLen);
bool writePoll(const sockaddr_storage& addr, unsigned int addrLen);
unsigned int readData(unsigned char* data, unsigned int length, sockaddr_storage& addr, unsigned int& addrLen);
void close();
void setCount(unsigned int count);
private:
CUDPSocket m_socket;
unsigned short m_port;
unsigned int m_id;
std::string m_name;
std::string m_description;
std::string m_callsign;
bool m_debug;
unsigned char* m_status;
};
#endif

@ -1,105 +0,0 @@
/*
* Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "StopWatch.h"
#if defined(_WIN32) || defined(_WIN64)
CStopWatch::CStopWatch() :
m_frequencyS(),
m_frequencyMS(),
m_start()
{
::QueryPerformanceFrequency(&m_frequencyS);
m_frequencyMS.QuadPart = m_frequencyS.QuadPart / 1000ULL;
}
CStopWatch::~CStopWatch()
{
}
unsigned long long CStopWatch::time() const
{
LARGE_INTEGER now;
::QueryPerformanceCounter(&now);
return (unsigned long long)(now.QuadPart / m_frequencyMS.QuadPart);
}
unsigned long long CStopWatch::start()
{
::QueryPerformanceCounter(&m_start);
return (unsigned long long)(m_start.QuadPart / m_frequencyS.QuadPart);
}
unsigned int CStopWatch::elapsed()
{
LARGE_INTEGER now;
::QueryPerformanceCounter(&now);
LARGE_INTEGER temp;
temp.QuadPart = (now.QuadPart - m_start.QuadPart) * 1000;
return (unsigned int)(temp.QuadPart / m_frequencyS.QuadPart);
}
#else
#include <cstdio>
#include <ctime>
CStopWatch::CStopWatch() :
m_startMS(0ULL)
{
}
CStopWatch::~CStopWatch()
{
}
unsigned long long CStopWatch::time() const
{
struct timeval now;
::gettimeofday(&now, NULL);
return now.tv_sec * 1000ULL + now.tv_usec / 1000ULL;
}
unsigned long long CStopWatch::start()
{
struct timespec now;
::clock_gettime(CLOCK_MONOTONIC, &now);
m_startMS = now.tv_sec * 1000ULL + now.tv_nsec / 1000000ULL;
return m_startMS;
}
unsigned int CStopWatch::elapsed()
{
struct timespec now;
::clock_gettime(CLOCK_MONOTONIC, &now);
unsigned long long nowMS = now.tv_sec * 1000ULL + now.tv_nsec / 1000000ULL;
return nowMS - m_startMS;
}
#endif

@ -1,50 +0,0 @@
/*
* Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(STOPWATCH_H)
#define STOPWATCH_H
#if defined(_WIN32) || defined(_WIN64)
#include <WS2tcpip.h>
#include <windows.h>
#else
#include <sys/time.h>
#endif
class CStopWatch
{
public:
CStopWatch();
~CStopWatch();
unsigned long long time() const;
unsigned long long start();
unsigned int elapsed();
private:
#if defined(_WIN32) || defined(_WIN64)
LARGE_INTEGER m_frequencyS;
LARGE_INTEGER m_frequencyMS;
LARGE_INTEGER m_start;
#else
unsigned long long m_startMS;
#endif
};
#endif

@ -1,107 +0,0 @@
/*
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Thread.h"
#if defined(_WIN32) || defined(_WIN64)
CThread::CThread() :
m_handle()
{
}
CThread::~CThread()
{
}
bool CThread::run()
{
m_handle = ::CreateThread(NULL, 0, &helper, this, 0, NULL);
return m_handle != NULL;
}
void CThread::wait()
{
::WaitForSingleObject(m_handle, INFINITE);
::CloseHandle(m_handle);
}
DWORD CThread::helper(LPVOID arg)
{
CThread* p = (CThread*)arg;
p->entry();
return 0UL;
}
void CThread::sleep(unsigned int ms)
{
::Sleep(ms);
}
#else
#include <unistd.h>
CThread::CThread() :
m_thread()
{
}
CThread::~CThread()
{
}
bool CThread::run()
{
return ::pthread_create(&m_thread, NULL, helper, this) == 0;
}
void CThread::wait()
{
::pthread_join(m_thread, NULL);
}
void* CThread::helper(void* arg)
{
CThread* p = (CThread*)arg;
p->entry();
return NULL;
}
void CThread::sleep(unsigned int ms)
{
struct timespec ts;
ts.tv_sec = ms / 1000U;
ts.tv_nsec = (ms % 1000U) * 1000000U;
::nanosleep(&ts, NULL);
}
#endif

@ -1,56 +0,0 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(THREAD_H)
#define THREAD_H
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#else
#include <pthread.h>
#endif
class CThread
{
public:
CThread();
virtual ~CThread();
virtual bool run();
virtual void entry() = 0;
virtual void wait();
static void sleep(unsigned int ms);
private:
#if defined(_WIN32) || defined(_WIN64)
HANDLE m_handle;
#else
pthread_t m_thread;
#endif
#if defined(_WIN32) || defined(_WIN64)
static DWORD __stdcall helper(LPVOID arg);
#else
static void* helper(void* arg);
#endif
};
#endif

@ -1,68 +0,0 @@
/*
* Copyright (C) 2009,2010,2015 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Timer.h"
#include <cstdio>
#include <cassert>
CTimer::CTimer(unsigned int ticksPerSec, unsigned int secs, unsigned int msecs) :
m_ticksPerSec(ticksPerSec),
m_timeout(0U),
m_timer(0U)
{
assert(ticksPerSec > 0U);
if (secs > 0U || msecs > 0U) {
// m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U;
unsigned long long temp = (secs * 1000ULL + msecs) * m_ticksPerSec;
m_timeout = (unsigned int)(temp / 1000ULL + 1ULL);
}
}
CTimer::~CTimer()
{
}
void CTimer::setTimeout(unsigned int secs, unsigned int msecs)
{
if (secs > 0U || msecs > 0U) {
// m_timeout = ((secs * 1000U + msecs) * m_ticksPerSec) / 1000U + 1U;
unsigned long long temp = (secs * 1000ULL + msecs) * m_ticksPerSec;
m_timeout = (unsigned int)(temp / 1000ULL + 1ULL);
} else {
m_timeout = 0U;
m_timer = 0U;
}
}
unsigned int CTimer::getTimeout() const
{
if (m_timeout == 0U)
return 0U;
return (m_timeout - 1U) / m_ticksPerSec;
}
unsigned int CTimer::getTimer() const
{
if (m_timer == 0U)
return 0U;
return (m_timer - 1U) / m_ticksPerSec;
}

@ -1,89 +0,0 @@
/*
* Copyright (C) 2009,2010,2011,2014 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef Timer_H
#define Timer_H
class CTimer {
public:
CTimer(unsigned int ticksPerSec, unsigned int secs = 0U, unsigned int msecs = 0U);
~CTimer();
void setTimeout(unsigned int secs, unsigned int msecs = 0U);
unsigned int getTimeout() const;
unsigned int getTimer() const;
unsigned int getRemaining()
{
if (m_timeout == 0U || m_timer == 0U)
return 0U;
if (m_timer >= m_timeout)
return 0U;
return (m_timeout - m_timer) / m_ticksPerSec;
}
bool isRunning()
{
return m_timer > 0U;
}
void start(unsigned int secs, unsigned int msecs = 0U)
{
setTimeout(secs, msecs);
start();
}
void start()
{
if (m_timeout > 0U)
m_timer = 1U;
}
void stop()
{
m_timer = 0U;
}
bool hasExpired()
{
if (m_timeout == 0U || m_timer == 0U)
return false;
if (m_timer >= m_timeout)
return true;
return false;
}
void clock(unsigned int ticks = 1U)
{
if (m_timer > 0U && m_timeout > 0U)
m_timer += ticks;
}
private:
unsigned int m_ticksPerSec;
unsigned int m_timeout;
unsigned int m_timer;
};
#endif

@ -1,390 +0,0 @@
/*
* Copyright (C) 2006-2016,2020,2021 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "UDPSocket.h"
#include <cassert>
#if !defined(_WIN32) && !defined(_WIN64)
#include <cerrno>
#include <cstring>
#endif
#if defined(HAVE_LOG_H)
#include "Log.h"
#else
#define LogError(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__)
#define LogInfo(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__)
#endif
CUDPSocket::CUDPSocket(const std::string& address, unsigned int port) :
m_address_save(address),
m_port_save(port),
m_counter(0U)
{
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
m_address[i] = "";
m_port[i] = 0U;
m_af[i] = 0U;
m_fd[i] = -1;
}
}
CUDPSocket::CUDPSocket(unsigned int port) :
m_address_save(),
m_port_save(port),
m_counter(0U)
{
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
m_address[i] = "";
m_port[i] = 0U;
m_af[i] = 0U;
m_fd[i] = -1;
}
}
CUDPSocket::~CUDPSocket()
{
}
void CUDPSocket::startup()
{
#if defined(_WIN32) || defined(_WIN64)
WSAData data;
int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data);
if (wsaRet != 0)
LogError("Error from WSAStartup");
#endif
}
void CUDPSocket::shutdown()
{
#if defined(_WIN32) || defined(_WIN64)
::WSACleanup();
#endif
}
int CUDPSocket::lookup(const std::string& hostname, unsigned int port, sockaddr_storage& addr, unsigned int& address_length)
{
struct addrinfo hints;
::memset(&hints, 0, sizeof(hints));
return lookup(hostname, port, addr, address_length, hints);
}
int CUDPSocket::lookup(const std::string& hostname, unsigned int port, sockaddr_storage& addr, unsigned int& address_length, struct addrinfo& hints)
{
std::string portstr = std::to_string(port);
struct addrinfo *res;
/* port is always digits, no needs to lookup service */
hints.ai_flags |= AI_NUMERICSERV;
int err = getaddrinfo(hostname.empty() ? NULL : hostname.c_str(), portstr.c_str(), &hints, &res);
if (err != 0) {
sockaddr_in* paddr = (sockaddr_in*)&addr;
::memset(paddr, 0x00U, address_length = sizeof(sockaddr_in));
paddr->sin_family = AF_INET;
paddr->sin_port = htons(port);
paddr->sin_addr.s_addr = htonl(INADDR_NONE);
LogError("Cannot find address for host %s", hostname.c_str());
return err;
}
::memcpy(&addr, res->ai_addr, address_length = res->ai_addrlen);
freeaddrinfo(res);
return 0;
}
bool CUDPSocket::match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type)
{
if (addr1.ss_family != addr2.ss_family)
return false;
if (type == IMT_ADDRESS_AND_PORT) {
switch (addr1.ss_family) {
case AF_INET:
struct sockaddr_in *in_1, *in_2;
in_1 = (struct sockaddr_in*)&addr1;
in_2 = (struct sockaddr_in*)&addr2;
return (in_1->sin_addr.s_addr == in_2->sin_addr.s_addr) && (in_1->sin_port == in_2->sin_port);
case AF_INET6:
struct sockaddr_in6 *in6_1, *in6_2;
in6_1 = (struct sockaddr_in6*)&addr1;
in6_2 = (struct sockaddr_in6*)&addr2;
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr) && (in6_1->sin6_port == in6_2->sin6_port);
default:
return false;
}
} else if (type == IMT_ADDRESS_ONLY) {
switch (addr1.ss_family) {
case AF_INET:
struct sockaddr_in *in_1, *in_2;
in_1 = (struct sockaddr_in*)&addr1;
in_2 = (struct sockaddr_in*)&addr2;
return in_1->sin_addr.s_addr == in_2->sin_addr.s_addr;
case AF_INET6:
struct sockaddr_in6 *in6_1, *in6_2;
in6_1 = (struct sockaddr_in6*)&addr1;
in6_2 = (struct sockaddr_in6*)&addr2;
return IN6_ARE_ADDR_EQUAL(&in6_1->sin6_addr, &in6_2->sin6_addr);
default:
return false;
}
} else {
return false;
}
}
bool CUDPSocket::isNone(const sockaddr_storage& addr)
{
struct sockaddr_in *in = (struct sockaddr_in *)&addr;
return ((addr.ss_family == AF_INET) && (in->sin_addr.s_addr == htonl(INADDR_NONE)));
}
char* CUDPSocket::display(const sockaddr_storage& addr, char* buffer, unsigned int length)
{
assert(buffer != NULL);
assert(length > INET6_ADDRSTRLEN);
switch (addr.ss_family) {
case AF_INET: {
struct sockaddr_in* in4 = (struct sockaddr_in*)&addr;
::inet_ntop(AF_INET, &in4->sin_addr, buffer, length);
::sprintf(buffer + ::strlen(buffer), ":%u", in4->sin_port);
}
break;
case AF_INET6: {
struct sockaddr_in6* in6 = (struct sockaddr_in6*)&addr;
::inet_ntop(AF_INET6, &in6->sin6_addr, buffer, length);
::sprintf(buffer + ::strlen(buffer), ":%u", in6->sin6_port);
}
break;
default:
::strcpy(buffer, "Unknown");
break;
}
return buffer;
}
bool CUDPSocket::open(const sockaddr_storage& address)
{
return open(address.ss_family);
}
bool CUDPSocket::open(unsigned int af)
{
return open(0, af, m_address_save, m_port_save);
}
bool CUDPSocket::open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned int port)
{
sockaddr_storage addr;
unsigned int addrlen;
struct addrinfo hints;
::memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = af;
/* to determine protocol family, call lookup() first. */
int err = lookup(address, port, addr, addrlen, hints);
if (err != 0) {
LogError("The local address is invalid - %s", address.c_str());
return false;
}
close(index);
int fd = ::socket(addr.ss_family, SOCK_DGRAM, 0);
if (fd < 0) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Cannot create the UDP socket, err: %lu", ::GetLastError());
#else
LogError("Cannot create the UDP socket, err: %d", errno);
#endif
return false;
}
m_address[index] = address;
m_port[index] = port;
m_af[index] = addr.ss_family;
m_fd[index] = fd;
if (port > 0U) {
int reuse = 1;
if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError());
#else
LogError("Cannot set the UDP socket option, err: %d", errno);
#endif
return false;
}
if (::bind(fd, (sockaddr*)&addr, addrlen) == -1) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Cannot bind the UDP address, err: %lu", ::GetLastError());
#else
LogError("Cannot bind the UDP address, err: %d", errno);
#endif
return false;
}
LogInfo("Opening UDP port on %u", port);
}
return true;
}
int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &address_length)
{
assert(buffer != NULL);
assert(length > 0U);
// Check that the readfrom() won't block
int i, n;
struct pollfd pfd[UDP_SOCKET_MAX];
for (i = n = 0; i < UDP_SOCKET_MAX; i++) {
if (m_fd[i] >= 0) {
pfd[n].fd = m_fd[i];
pfd[n].events = POLLIN;
n++;
}
}
// no socket descriptor to receive
if (n == 0)
return 0;
// Return immediately
#if defined(_WIN32) || defined(_WIN64)
int ret = WSAPoll(pfd, n, 0);
#else
int ret = ::poll(pfd, n, 0);
#endif
if (ret < 0) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Error returned from UDP poll, err: %lu", ::GetLastError());
#else
LogError("Error returned from UDP poll, err: %d", errno);
#endif
return -1;
}
int index;
for (i = 0; i < n; i++) {
// round robin
index = (i + m_counter) % n;
if (pfd[index].revents & POLLIN)
break;
}
if (i == n)
return 0;
#if defined(_WIN32) || defined(_WIN64)
int size = sizeof(sockaddr_storage);
#else
socklen_t size = sizeof(sockaddr_storage);
#endif
#if defined(_WIN32) || defined(_WIN64)
int len = ::recvfrom(pfd[index].fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
#else
ssize_t len = ::recvfrom(pfd[index].fd, (char*)buffer, length, 0, (sockaddr *)&address, &size);
#endif
if (len <= 0) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Error returned from recvfrom, err: %lu", ::GetLastError());
#else
LogError("Error returned from recvfrom, err: %d", errno);
if (len == -1 && errno == ENOTSOCK) {
LogMessage("Re-opening UDP port on %u", m_port);
close();
open();
}
#endif
return -1;
}
m_counter++;
address_length = size;
return len;
}
bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int address_length)
{
assert(buffer != NULL);
assert(length > 0U);
bool result = false;
for (int i = 0; i < UDP_SOCKET_MAX; i++) {
if (m_fd[i] < 0 || m_af[i] != address.ss_family)
continue;
#if defined(_WIN32) || defined(_WIN64)
int ret = ::sendto(m_fd[i], (char *)buffer, length, 0, (sockaddr *)&address, address_length);
#else
ssize_t ret = ::sendto(m_fd[i], (char *)buffer, length, 0, (sockaddr *)&address, address_length);
#endif
if (ret < 0) {
#if defined(_WIN32) || defined(_WIN64)
LogError("Error returned from sendto, err: %lu", ::GetLastError());
#else
LogError("Error returned from sendto, err: %d", errno);
#endif
} else {
#if defined(_WIN32) || defined(_WIN64)
if (ret == int(length))
result = true;
#else
if (ret == ssize_t(length))
result = true;
#endif
}
}
return result;
}
void CUDPSocket::close()
{
for (unsigned int i = 0; i < UDP_SOCKET_MAX; i++)
close(i);
}
void CUDPSocket::close(const unsigned int index)
{
if ((index < UDP_SOCKET_MAX) && (m_fd[index] >= 0)) {
#if defined(_WIN32) || defined(_WIN64)
::closesocket(m_fd[index]);
#else
::close(m_fd[index]);
#endif
m_fd[index] = -1;
}
}

@ -1,85 +0,0 @@
/*
* Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef UDPSocket_H
#define UDPSocket_H
#include <string>
#if !defined(_WIN32) && !defined(_WIN64)
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#else
#include <ws2tcpip.h>
#endif
#if !defined(UDP_SOCKET_MAX)
#define UDP_SOCKET_MAX 1
#endif
enum IPMATCHTYPE {
IMT_ADDRESS_AND_PORT,
IMT_ADDRESS_ONLY
};
class CUDPSocket {
public:
CUDPSocket(const std::string& address, unsigned int port = 0U);
CUDPSocket(unsigned int port = 0U);
~CUDPSocket();
bool open(unsigned int af = AF_UNSPEC);
bool open(const sockaddr_storage& address);
bool open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned int port);
int read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &address_length);
bool write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int address_length);
void close();
void close(const unsigned int index);
static void startup();
static void shutdown();
static int lookup(const std::string& hostName, unsigned int port, sockaddr_storage& address, unsigned int& address_length);
static int lookup(const std::string& hostName, unsigned int port, sockaddr_storage& address, unsigned int& address_length, struct addrinfo& hints);
static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT);
static bool isNone(const sockaddr_storage& addr);
static char* display(const sockaddr_storage& address, char* buffer, unsigned int length);
private:
std::string m_address_save;
unsigned short m_port_save;
std::string m_address[UDP_SOCKET_MAX];
unsigned short m_port[UDP_SOCKET_MAX];
unsigned int m_af[UDP_SOCKET_MAX];
int m_fd[UDP_SOCKET_MAX];
unsigned int m_counter;
};
#endif

@ -1,146 +0,0 @@
/*
* Copyright (C) 2009,2014,2015,2016 Jonathan Naylor, G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* 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.
*/
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
void CUtils::dump(const std::string& title, const unsigned char* data, unsigned int length)
{
assert(data != NULL);
dump(2U, title, data, length);
}
void CUtils::dump(int level, const std::string& title, const unsigned char* data, unsigned int length)
{
assert(data != NULL);
::Log(level, "%s", title.c_str());
unsigned int offset = 0U;
while (length > 0U) {
std::string output;
unsigned int bytes = (length > 16U) ? 16U : length;
for (unsigned i = 0U; i < bytes; i++) {
char temp[10U];
::sprintf(temp, "%02X ", data[offset + i]);
output += temp;
}
for (unsigned int i = bytes; i < 16U; i++)
output += " ";
output += " *";
for (unsigned i = 0U; i < bytes; i++) {
unsigned char c = data[offset + i];
if (::isprint(c))
output += c;
else
output += '.';
}
output += '*';
::Log(level, "%04X: %s", offset, output.c_str());
offset += 16U;
if (length >= 16U)
length -= 16U;
else
length = 0U;
}
}
void CUtils::dump(const std::string& title, const bool* bits, unsigned int length)
{
assert(bits != NULL);
dump(2U, title, bits, length);
}
void CUtils::dump(int level, const std::string& title, const bool* bits, unsigned int length)
{
assert(bits != NULL);
unsigned char bytes[100U];
unsigned int nBytes = 0U;
for (unsigned int n = 0U; n < length; n += 8U, nBytes++)
bitsToByteBE(bits + n, bytes[nBytes]);
dump(level, title, bytes, nBytes);
}
void CUtils::byteToBitsBE(unsigned char byte, bool* bits)
{
assert(bits != NULL);
bits[0U] = (byte & 0x80U) == 0x80U;
bits[1U] = (byte & 0x40U) == 0x40U;
bits[2U] = (byte & 0x20U) == 0x20U;
bits[3U] = (byte & 0x10U) == 0x10U;
bits[4U] = (byte & 0x08U) == 0x08U;
bits[5U] = (byte & 0x04U) == 0x04U;
bits[6U] = (byte & 0x02U) == 0x02U;
bits[7U] = (byte & 0x01U) == 0x01U;
}
void CUtils::byteToBitsLE(unsigned char byte, bool* bits)
{
assert(bits != NULL);
bits[0U] = (byte & 0x01U) == 0x01U;
bits[1U] = (byte & 0x02U) == 0x02U;
bits[2U] = (byte & 0x04U) == 0x04U;
bits[3U] = (byte & 0x08U) == 0x08U;
bits[4U] = (byte & 0x10U) == 0x10U;
bits[5U] = (byte & 0x20U) == 0x20U;
bits[6U] = (byte & 0x40U) == 0x40U;
bits[7U] = (byte & 0x80U) == 0x80U;
}
void CUtils::bitsToByteBE(const bool* bits, unsigned char& byte)
{
assert(bits != NULL);
byte = bits[0U] ? 0x80U : 0x00U;
byte |= bits[1U] ? 0x40U : 0x00U;
byte |= bits[2U] ? 0x20U : 0x00U;
byte |= bits[3U] ? 0x10U : 0x00U;
byte |= bits[4U] ? 0x08U : 0x00U;
byte |= bits[5U] ? 0x04U : 0x00U;
byte |= bits[6U] ? 0x02U : 0x00U;
byte |= bits[7U] ? 0x01U : 0x00U;
}
void CUtils::bitsToByteLE(const bool* bits, unsigned char& byte)
{
assert(bits != NULL);
byte = bits[0U] ? 0x01U : 0x00U;
byte |= bits[1U] ? 0x02U : 0x00U;
byte |= bits[2U] ? 0x04U : 0x00U;
byte |= bits[3U] ? 0x08U : 0x00U;
byte |= bits[4U] ? 0x10U : 0x00U;
byte |= bits[5U] ? 0x20U : 0x00U;
byte |= bits[6U] ? 0x40U : 0x00U;
byte |= bits[7U] ? 0x80U : 0x00U;
}

@ -1,36 +0,0 @@
/*
* Copyright (C) 2009,2014,2015 by Jonathan Naylor, G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* 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.
*/
#ifndef Utils_H
#define Utils_H
#include <string>
class CUtils {
public:
static void dump(const std::string& title, const unsigned char* data, unsigned int length);
static void dump(int level, const std::string& title, const unsigned char* data, unsigned int length);
static void dump(const std::string& title, const bool* bits, unsigned int length);
static void dump(int level, const std::string& title, const bool* bits, unsigned int length);
static void byteToBitsBE(unsigned char byte, bool* bits);
static void byteToBitsLE(unsigned char byte, bool* bits);
static void bitsToByteBE(const bool* bits, unsigned char& byte);
static void bitsToByteLE(const bool* bits, unsigned char& byte);
private:
};
#endif

@ -1,24 +0,0 @@
/*
* Copyright (C) 2015,2016,2020,2021 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(VERSION_H)
#define VERSION_H
const char* VERSION = "20210824";
#endif

@ -1,49 +0,0 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(YSFDefines_H)
#define YSFDefines_H
const unsigned int YSF_FRAME_LENGTH_BYTES = 120U;
const unsigned char YSF_SYNC_BYTES[] = {0xD4U, 0x71U, 0xC9U, 0x63U, 0x4DU};
const unsigned int YSF_SYNC_LENGTH_BYTES = 5U;
const unsigned int YSF_FICH_LENGTH_BYTES = 25U;
const unsigned char YSF_SYNC_OK = 0x01U;
const unsigned int YSF_CALLSIGN_LENGTH = 10U;
const unsigned char YSF_FI_HEADER = 0x00U;
const unsigned char YSF_FI_COMMUNICATIONS = 0x01U;
const unsigned char YSF_FI_TERMINATOR = 0x02U;
const unsigned char YSF_FI_TEST = 0x03U;
const unsigned char YSF_DT_VD_MODE1 = 0x00U;
const unsigned char YSF_DT_DATA_FR_MODE = 0x01U;
const unsigned char YSF_DT_VD_MODE2 = 0x02U;
const unsigned char YSF_DT_VOICE_FR_MODE = 0x03U;
const unsigned char YSF_CM_GROUP = 0x00U;
const unsigned char YSF_CM_INDIVIDUAL = 0x03U;
const unsigned char YSF_MR_NOT_BUSY = 0x01U;
const unsigned char YSF_MR_BUSY = 0x02U;
#endif

@ -1,375 +0,0 @@
/*
* Copyright (C) 2016,2018,2020,2021 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "YSFReflector.h"
#include "BlockList.h"
#include "StopWatch.h"
#include "Network.h"
#include "Version.h"
#include "Thread.h"
#include "Log.h"
#include "GitVersion.h"
#if defined(_WIN32) || defined(_WIN64)
#include <Windows.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <pwd.h>
#endif
#if defined(_WIN32) || defined(_WIN64)
const char* DEFAULT_INI_FILE = "YSFReflector.ini";
#else
const char* DEFAULT_INI_FILE = "/etc/YSFReflector.ini";
#endif
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <ctime>
#include <cstring>
int main(int argc, char** argv)
{
const char* iniFile = DEFAULT_INI_FILE;
if (argc > 1) {
for (int currentArg = 1; currentArg < argc; ++currentArg) {
std::string arg = argv[currentArg];
if ((arg == "-v") || (arg == "--version")) {
::fprintf(stdout, "YSFReflector version %s git #%.7s\n", VERSION, gitversion);
return 0;
} else if (arg.substr(0, 1) == "-") {
::fprintf(stderr, "Usage: YSFReflector [-v|--version] [filename]\n");
return 1;
} else {
iniFile = argv[currentArg];
}
}
}
CYSFReflector* reflector = new CYSFReflector(std::string(iniFile));
reflector->run();
delete reflector;
return 0;
}
CYSFReflector::CYSFReflector(const std::string& file) :
m_conf(file),
m_repeaters()
{
CUDPSocket::startup();
}
CYSFReflector::~CYSFReflector()
{
CUDPSocket::shutdown();
}
void CYSFReflector::run()
{
bool ret = m_conf.read();
if (!ret) {
::fprintf(stderr, "YSFReflector: cannot read the .ini file\n");
return;
}
#if !defined(_WIN32) && !defined(_WIN64)
bool m_daemon = m_conf.getDaemon();
if (m_daemon) {
// Create new process
pid_t pid = ::fork();
if (pid == -1) {
::fprintf(stderr, "Couldn't fork() , exiting\n");
return;
} else if (pid != 0) {
exit(EXIT_SUCCESS);
}
// Create new session and process group
if (::setsid() == -1) {
::fprintf(stderr, "Couldn't setsid(), exiting\n");
return;
}
// Set the working directory to the root directory
if (::chdir("/") == -1) {
::fprintf(stderr, "Couldn't cd /, exiting\n");
return;
}
// If we are currently root...
if (getuid() == 0) {
struct passwd* user = ::getpwnam("mmdvm");
if (user == NULL) {
::fprintf(stderr, "Could not get the mmdvm user, exiting\n");
return;
}
uid_t mmdvm_uid = user->pw_uid;
gid_t mmdvm_gid = user->pw_gid;
// Set user and group ID's to mmdvm:mmdvm
if (setgid(mmdvm_gid) != 0) {
::fprintf(stderr, "Could not set mmdvm GID, exiting\n");
return;
}
if (setuid(mmdvm_uid) != 0) {
::fprintf(stderr, "Could not set mmdvm UID, exiting\n");
return;
}
// Double check it worked (AKA Paranoia)
if (setuid(0) != -1) {
::fprintf(stderr, "It's possible to regain root - something is wrong!, exiting\n");
return;
}
}
}
#endif
#if !defined(_WIN32) && !defined(_WIN64)
ret = ::LogInitialise(m_daemon, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel(), m_conf.getLogFileRotate());
#else
ret = ::LogInitialise(false, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel(), m_conf.getLogFileRotate());
#endif
if (!ret) {
::fprintf(stderr, "YSFReflector: unable to open the log file\n");
return;
}
#if !defined(_WIN32) && !defined(_WIN64)
if (m_daemon) {
::close(STDIN_FILENO);
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
}
#endif
CNetwork network(m_conf.getNetworkPort(), m_conf.getId(), m_conf.getName(), m_conf.getDescription(), m_conf.getNetworkDebug());
ret = network.open();
if (!ret) {
::LogFinalise();
return;
}
CBlockList blockList(m_conf.getBlockListFile(), m_conf.getBlockListTime());
blockList.start();
network.setCount(0);
CStopWatch stopWatch;
stopWatch.start();
CTimer dumpTimer(1000U, 120U);
dumpTimer.start();
CTimer pollTimer(1000U, 5U);
pollTimer.start();
LogMessage("YSFReflector-%s is starting", VERSION);
LogMessage("Built %s %s (GitID #%.7s)", __TIME__, __DATE__, gitversion);
CTimer watchdogTimer(1000U, 0U, 1500U);
unsigned char tag[YSF_CALLSIGN_LENGTH];
unsigned char src[YSF_CALLSIGN_LENGTH];
unsigned char dst[YSF_CALLSIGN_LENGTH];
bool blocked = false;
for (;;) {
unsigned char buffer[200U];
sockaddr_storage addr;
unsigned int addrLen;
unsigned int len = network.readData(buffer, 200U, addr, addrLen);
if (len > 0U) {
CYSFRepeater* rpt = findRepeater(addr);
if (::memcmp(buffer, "YSFP", 4U) == 0) {
if (rpt == NULL) {
rpt = new CYSFRepeater;
rpt->m_callsign = std::string((char*)(buffer + 4U), 10U);
::memcpy(&rpt->m_addr, &addr, sizeof(struct sockaddr_storage));
rpt->m_addrLen = addrLen;
m_repeaters.push_back(rpt);
network.setCount(m_repeaters.size());
char buff[80U];
LogMessage("Adding %s (%s)", rpt->m_callsign.c_str(), CUDPSocket::display(addr, buff, 80U));
}
rpt->m_timer.start();
network.writePoll(addr, addrLen);
} else if (::memcmp(buffer + 0U, "YSFU", 4U) == 0 && rpt != NULL) {
char buff[80U];
LogMessage("Removing %s (%s) unlinked", rpt->m_callsign.c_str(), CUDPSocket::display(addr, buff, 80U));
for (std::vector<CYSFRepeater*>::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
if (CUDPSocket::match((*it)->m_addr, rpt->m_addr)) {
delete *it;
m_repeaters.erase(it);
break;
}
}
network.setCount(m_repeaters.size());
} else if (::memcmp(buffer + 0U, "YSFD", 4U) == 0 && rpt != NULL) {
if (!watchdogTimer.isRunning()) {
::memcpy(tag, buffer + 4U, YSF_CALLSIGN_LENGTH);
if (::memcmp(buffer + 14U, " ", YSF_CALLSIGN_LENGTH) != 0)
::memcpy(src, buffer + 14U, YSF_CALLSIGN_LENGTH);
else
::memcpy(src, "??????????", YSF_CALLSIGN_LENGTH);
if (::memcmp(buffer + 24U, " ", YSF_CALLSIGN_LENGTH) != 0)
::memcpy(dst, buffer + 24U, YSF_CALLSIGN_LENGTH);
else
::memcpy(dst, "??????????", YSF_CALLSIGN_LENGTH);
blocked = blockList.check(src);
if (blocked)
LogMessage("Data from %10.10s at %10.10s blocked", src, tag);
else
LogMessage("Received data from %10.10s to %10.10s at %10.10s", src, dst, tag);
} else {
if (::memcmp(tag, buffer + 4U, YSF_CALLSIGN_LENGTH) == 0) {
bool changed = false;
if (::memcmp(buffer + 14U, " ", YSF_CALLSIGN_LENGTH) != 0 && ::memcmp(src, "??????????", YSF_CALLSIGN_LENGTH) == 0) {
::memcpy(src, buffer + 14U, YSF_CALLSIGN_LENGTH);
changed = true;
}
if (::memcmp(buffer + 24U, " ", YSF_CALLSIGN_LENGTH) != 0 && ::memcmp(dst, "??????????", YSF_CALLSIGN_LENGTH) == 0) {
::memcpy(dst, buffer + 24U, YSF_CALLSIGN_LENGTH);
changed = true;
}
if (changed) {
blocked = blockList.check(src);
if (blocked)
LogMessage("Data from %10.10s at %10.10s blocked", src, tag);
else
LogMessage("Received data from %10.10s to %10.10s at %10.10s", src, dst, tag);
}
}
}
if (!blocked) {
watchdogTimer.start();
for (std::vector<CYSFRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
if (!CUDPSocket::match((*it)->m_addr, addr))
network.writeData(buffer, (*it)->m_addr, (*it)->m_addrLen);
}
if ((buffer[34U] & 0x01U) == 0x01U) {
LogMessage("Received end of transmission");
watchdogTimer.stop();
}
}
}
}
unsigned int ms = stopWatch.elapsed();
stopWatch.start();
pollTimer.clock(ms);
if (pollTimer.hasExpired()) {
for (std::vector<CYSFRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it)
network.writePoll((*it)->m_addr, (*it)->m_addrLen);
pollTimer.start();
}
// Remove any repeaters that haven't reported for a while
for (std::vector<CYSFRepeater*>::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it)
(*it)->m_timer.clock(ms);
for (std::vector<CYSFRepeater*>::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
if ((*it)->m_timer.hasExpired()) {
char buff[80U];
LogMessage("Removing %s (%s) disappeared", (*it)->m_callsign.c_str(),
CUDPSocket::display((*it)->m_addr, buff, 80U));
delete *it;
m_repeaters.erase(it);
network.setCount(m_repeaters.size());
break;
}
}
watchdogTimer.clock(ms);
if (watchdogTimer.isRunning() && watchdogTimer.hasExpired()) {
LogMessage("Network watchdog has expired");
watchdogTimer.stop();
}
dumpTimer.clock(ms);
if (dumpTimer.hasExpired()) {
dumpRepeaters();
dumpTimer.start();
}
blockList.clock(ms);
if (ms < 5U)
CThread::sleep(5U);
}
network.close();
::LogFinalise();
}
CYSFRepeater* CYSFReflector::findRepeater(const sockaddr_storage& addr) const
{
for (std::vector<CYSFRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
if (CUDPSocket::match(addr, (*it)->m_addr))
return *it;
}
return NULL;
}
void CYSFReflector::dumpRepeaters() const
{
if (m_repeaters.size() == 0U) {
LogMessage("No repeaters/gateways linked");
return;
}
LogMessage("Currently linked repeaters/gateways:");
for (std::vector<CYSFRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
char buffer[80U];
LogMessage(" %s: %s %u/%u", (*it)->m_callsign.c_str(),
CUDPSocket::display((*it)->m_addr, buffer, 80U),
(*it)->m_timer.getTimer(),
(*it)->m_timer.getTimeout());
}
}

@ -1,73 +0,0 @@
/*
* Copyright (C) 2016,2020 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(YSFReflector_H)
#define YSFReflector_H
#include "Timer.h"
#include "Conf.h"
#include <cstdio>
#include <string>
#include <vector>
#if !defined(_WIN32) && !defined(_WIN64)
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#else
#include <WS2tcpip.h>
#endif
class CYSFRepeater {
public:
CYSFRepeater() :
m_callsign(),
m_addr(),
m_addrLen(0U),
m_timer(1000U, 60U)
{
}
std::string m_callsign;
sockaddr_storage m_addr;
unsigned int m_addrLen;
CTimer m_timer;
};
class CYSFReflector
{
public:
CYSFReflector(const std::string& file);
~CYSFReflector();
void run();
private:
CConf m_conf;
std::vector<CYSFRepeater*> m_repeaters;
CYSFRepeater* findRepeater(const sockaddr_storage& addr) const;
void dumpRepeaters() const;
};
#endif

@ -1,25 +0,0 @@
[General]
Daemon=1
[Info]
# Remember to register your YSFReflector at:
# https://register.ysfreflector.de
# Id=5 digits only
Name=16 characters max
Description=14 characters max
[Log]
# Logging levels, 0=No logging
DisplayLevel=1
FileLevel=1
FilePath=.
FileRoot=YSFReflector
FileRotate=1
[Network]
Port=42000
Debug=0
[Block List]
File=BlockList.txt
Time=5

@ -1,76 +0,0 @@
#!/bin/bash
### BEGIN INIT INFO
#
# Provides: YSFReflector
# Required-Start: $all
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Example startscript YSFReflector
#
### END INIT INFO
## Fill in name of program here.
PROG="YSFReflector"
PROG_PATH="/usr/local/bin/"
PROG_ARGS="/etc/YSFReflector.ini"
PIDFILE="/var/run/YSFReflector.pid"
USER="root"
start() {
if [ -e $PIDFILE ]; then
## Program is running, exit with error.
echo "Error! $PROG is currently running!" 1>&2
exit 1
else
cd $PROG_PATH
./$PROG $PROG_ARGS
echo "$PROG started"
touch $PIDFILE
fi
}
stop() {
if [ -e $PIDFILE ]; then
## Program is running, so stop it
echo "$PROG is running"
rm -f $PIDFILE
killall $PROG
echo "$PROG stopped"
else
## Program is not running, exit with error.
echo "Error! $PROG not started!" 1>&2
exit 1
fi
}
## Check to see if we are running as root first.
## Found at
## http://www.cyberciti.biz/tips/shell-root-user-check-script.html
if [ "$(id -u)" != "0" ]; then
echo "This script must be run as root" 1>&2
exit 1
fi
case "$1" in
start)
start
exit 0
;;
stop)
stop
exit 0
;;
reload|restart|force-reload)
stop
sleep 5
start
exit 0
;;
**)
echo "Usage: $0 {start|stop|reload}" 1>&2
exit 1
;;
esac
exit 0
### END

@ -1,177 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{317D87F1-3485-4739-9F94-A07738B8E19D}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>YSFReflector</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;HAVE_LOG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;HAVE_LOG_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;HAVE_LOG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;HAVE_LOG_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="BlockList.h" />
<ClInclude Include="Conf.h" />
<ClInclude Include="Log.h" />
<ClInclude Include="Network.h" />
<ClInclude Include="StopWatch.h" />
<ClInclude Include="Thread.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="UDPSocket.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="Version.h" />
<ClInclude Include="YSFDefines.h" />
<ClInclude Include="YSFReflector.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="BlockList.cpp" />
<ClCompile Include="Conf.cpp" />
<ClCompile Include="Log.cpp" />
<ClCompile Include="Network.cpp" />
<ClCompile Include="StopWatch.cpp" />
<ClCompile Include="Thread.cpp" />
<ClCompile Include="Timer.cpp" />
<ClCompile Include="UDPSocket.cpp" />
<ClCompile Include="Utils.cpp" />
<ClCompile Include="YSFReflector.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -1,83 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Network.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Timer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UDPSocket.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="YSFDefines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="YSFReflector.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="StopWatch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Version.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Log.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Conf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Thread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="BlockList.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Network.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Timer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UDPSocket.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="YSFReflector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="StopWatch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Log.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Conf.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Thread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="BlockList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

@ -1,29 +0,0 @@
FROM debian:buster-slim AS builder
RUN apt-get update && \
apt-get install -y build-essential git
RUN mkdir /code && \
git clone https://github.com/g4klx/YSFClients.git /code && \
cd /code/YSFReflector/ && \
make clean all
FROM debian:buster-slim
ENV REFLECTOR_NAME set_me
ENV REFLECTOR_DESCRIPTION set_me
RUN apt-get update && \
apt-get install -y procps && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
mkdir /app
COPY --from=builder /code/YSFReflector/YSFReflector.ini /app/YSFReflector.ini
COPY --from=builder /code/YSFReflector/YSFReflector /app/YSFReflector
COPY entrypoint.sh /entrypoint.sh
EXPOSE 42000/udp
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/app/YSFReflector", "/app/YSFReflector.ini"]
HEALTHCHECK CMD ps aux | grep [Y]SFReflector || exit 1

@ -1,25 +0,0 @@
# YSFReflector Docker Image
The `Dockerfile` here is intended to produce an image which will be stored on [Docker Hub](https://hub.docker.com/).
# Requirements
* [Docker](https://docs.docker.com/install/)
* Firewall setup to accept 42000 (UDP)
# Usage
`docker run -e REFLECTOR_NAME=YOUR_NAME_HERE -eREFLECTOR_DESCRIPTION=YOUR_DESCRIPTION_HERE -p 42000:42000/udp neilbartley/ysfreflector:latest`
# How to build
Building isn't required unless you need a newer image or have made changes.
```
cd YSFReflector/docker
YSF_TAG=$(date +'%Y%m%d')-$(git rev-parse --short HEAD)
docker build --rm -t neilbartley/ysfreflector:$YSF_TAG .
docker tag neilbartley/ysfreflector:$YSF_TAG neilbartley/ysfreflector:latest
docker push neilbartley/ysfreflector:$YSF_TAG
docker push neilbartley/ysfreflector:latest
```

@ -1,18 +0,0 @@
#!/bin/bash
# Disable daemon mode
sed -i -e "s/Daemon=1/Daemon=0/g" /app/YSFReflector.ini
# Reflector name and description validation
if [ "${REFLECTOR_NAME}" == "set_me" ] ; then echo "Please set REFLECTOR_NAME environment variable with -e (max 16 characters)"; exit 1 ; fi
if [ ${#REFLECTOR_NAME} -gt 16 ] ; then echo "REFLECTOR_NAME environment variable can be at most 16 characters"; exit 1 ; fi
if [ "${REFLECTOR_DESCRIPTION}" == "set_me" ] ; then echo "Please set REFLECTOR_DESCRIPTION environment variable with -e (min 14 characters)"; exit 1 ; fi
if [ ${#REFLECTOR_DESCRIPTION} -gt 14 ] ; then echo "REFLECTOR_DESCRIPTION environment variable can be at most 14 characters"; exit 1 ; fi
# Reflector name and description replacement in config file
sed -i -e "s/Name=.*/Name=${REFLECTOR_NAME}/g" /app/YSFReflector.ini
sed -i -e "s/Description=.*/Description=${REFLECTOR_DESCRIPTION}/g" /app/YSFReflector.ini
echo "Remember to register your YSFReflector at: https://register.ysfreflector.de"
exec "$@"
Loading…
Cancel
Save