commit
4291a56db0
@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2018,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 "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_ID_LOOKUP,
|
||||
SECTION_LOG,
|
||||
SECTION_NETWORK,
|
||||
SECTION_ICOM_NETWORK,
|
||||
SECTION_KENWOOD_NETWORK
|
||||
};
|
||||
|
||||
CConf::CConf(const std::string& file) :
|
||||
m_file(file),
|
||||
m_tg(9999U),
|
||||
m_daemon(false),
|
||||
m_lookupName(),
|
||||
m_lookupTime(0U),
|
||||
m_logDisplayLevel(0U),
|
||||
m_logFileLevel(0U),
|
||||
m_logFilePath(),
|
||||
m_logFileRoot(),
|
||||
m_logFileRotate(true),
|
||||
m_networkPort(0U),
|
||||
m_networkDebug(false),
|
||||
m_icomEnabled(false),
|
||||
m_icomAddress(),
|
||||
m_icomTGEnable(0U),
|
||||
m_icomTGDisable(0U),
|
||||
m_icomDebug(false),
|
||||
m_kenwoodEnabled(false),
|
||||
m_kenwoodAddress(),
|
||||
m_kenwoodTGEnable(0U),
|
||||
m_kenwoodTGDisable(0U),
|
||||
m_kenwoodDebug(false)
|
||||
{
|
||||
}
|
||||
|
||||
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, "[Id Lookup]", 11U) == 0)
|
||||
section = SECTION_ID_LOOKUP;
|
||||
else if (::strncmp(buffer, "[Log]", 5U) == 0)
|
||||
section = SECTION_LOG;
|
||||
else if (::strncmp(buffer, "[Network]", 9U) == 0)
|
||||
section = SECTION_NETWORK;
|
||||
else if (::strncmp(buffer, "[Icom Network]", 14U) == 0)
|
||||
section = SECTION_ICOM_NETWORK;
|
||||
else if (::strncmp(buffer, "[Kenwood Network]", 17U) == 0)
|
||||
section = SECTION_KENWOOD_NETWORK;
|
||||
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 (::strcmp(key, "TG") == 0)
|
||||
m_tg = (unsigned short)::atoi(value);
|
||||
} else if (section == SECTION_ID_LOOKUP) {
|
||||
if (::strcmp(key, "Name") == 0)
|
||||
m_lookupName = value;
|
||||
else if (::strcmp(key, "Time") == 0)
|
||||
m_lookupTime = (unsigned int)::atoi(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_ICOM_NETWORK) {
|
||||
if (::strcmp(key, "Enabled") == 0)
|
||||
m_icomEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Address") == 0)
|
||||
m_icomAddress = value;
|
||||
else if (::strcmp(key, "TGEnable") == 0)
|
||||
m_icomTGEnable = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "TGDisable") == 0)
|
||||
m_icomTGDisable = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "Debug") == 0)
|
||||
m_icomDebug = ::atoi(value) == 1;
|
||||
} else if (section == SECTION_KENWOOD_NETWORK) {
|
||||
if (::strcmp(key, "Enabled") == 0)
|
||||
m_kenwoodEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Address") == 0)
|
||||
m_kenwoodAddress = value;
|
||||
else if (::strcmp(key, "TGEnable") == 0)
|
||||
m_kenwoodTGEnable = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "TGDisable") == 0)
|
||||
m_kenwoodTGDisable = (unsigned short)::atoi(value);
|
||||
else if (::strcmp(key, "Debug") == 0)
|
||||
m_kenwoodDebug = ::atoi(value) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
::fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConf::getDaemon() const
|
||||
{
|
||||
return m_daemon;
|
||||
}
|
||||
|
||||
unsigned short CConf::getTG() const
|
||||
{
|
||||
return m_tg;
|
||||
}
|
||||
|
||||
std::string CConf::getLookupName() const
|
||||
{
|
||||
return m_lookupName;
|
||||
}
|
||||
|
||||
unsigned int CConf::getLookupTime() const
|
||||
{
|
||||
return m_lookupTime;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool CConf::getIcomEnabled() const
|
||||
{
|
||||
return m_icomEnabled;
|
||||
}
|
||||
|
||||
std::string CConf::getIcomAddress() const
|
||||
{
|
||||
return m_icomAddress;
|
||||
}
|
||||
|
||||
unsigned short CConf::getIcomTGEnable() const
|
||||
{
|
||||
return m_icomTGEnable;
|
||||
}
|
||||
|
||||
unsigned short CConf::getIcomTGDisable() const
|
||||
{
|
||||
return m_icomTGDisable;
|
||||
}
|
||||
|
||||
bool CConf::getIcomDebug() const
|
||||
{
|
||||
return m_icomDebug;
|
||||
}
|
||||
|
||||
bool CConf::getKenwoodEnabled() const
|
||||
{
|
||||
return m_kenwoodEnabled;
|
||||
}
|
||||
|
||||
std::string CConf::getKenwoodAddress() const
|
||||
{
|
||||
return m_kenwoodAddress;
|
||||
}
|
||||
|
||||
unsigned short CConf::getKenwoodTGEnable() const
|
||||
{
|
||||
return m_kenwoodTGEnable;
|
||||
}
|
||||
|
||||
unsigned short CConf::getKenwoodTGDisable() const
|
||||
{
|
||||
return m_kenwoodTGDisable;
|
||||
}
|
||||
|
||||
bool CConf::getKenwoodDebug() const
|
||||
{
|
||||
return m_kenwoodDebug;
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016,2018,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(CONF_H)
|
||||
#define CONF_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class CConf
|
||||
{
|
||||
public:
|
||||
CConf(const std::string& file);
|
||||
~CConf();
|
||||
|
||||
bool read();
|
||||
|
||||
// The General section
|
||||
unsigned short getTG() const;
|
||||
bool getDaemon() const;
|
||||
|
||||
// The Id Lookup section
|
||||
std::string getLookupName() const;
|
||||
unsigned int getLookupTime() 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 Icom Network section
|
||||
bool getIcomEnabled() const;
|
||||
std::string getIcomAddress() const;
|
||||
unsigned short getIcomTGEnable() const;
|
||||
unsigned short getIcomTGDisable() const;
|
||||
bool getIcomDebug() const;
|
||||
|
||||
// The Kenwood Network section
|
||||
bool getKenwoodEnabled() const;
|
||||
std::string getKenwoodAddress() const;
|
||||
unsigned short getKenwoodTGEnable() const;
|
||||
unsigned short getKenwoodTGDisable() const;
|
||||
bool getKenwoodDebug() const;
|
||||
|
||||
private:
|
||||
std::string m_file;
|
||||
unsigned short m_tg;
|
||||
bool m_daemon;
|
||||
|
||||
std::string m_lookupName;
|
||||
unsigned int m_lookupTime;
|
||||
|
||||
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;
|
||||
|
||||
bool m_icomEnabled;
|
||||
std::string m_icomAddress;
|
||||
unsigned short m_icomTGEnable;
|
||||
unsigned short m_icomTGDisable;
|
||||
bool m_icomDebug;
|
||||
|
||||
bool m_kenwoodEnabled;
|
||||
std::string m_kenwoodAddress;
|
||||
unsigned short m_kenwoodTGEnable;
|
||||
unsigned short m_kenwoodTGDisable;
|
||||
bool m_kenwoodDebug;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2014,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 "IcomNetwork.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
|
||||
const unsigned int ICOM_PORT = 41300U;
|
||||
|
||||
CIcomNetwork::CIcomNetwork(const std::string& address, bool debug) :
|
||||
m_socket(ICOM_PORT),
|
||||
m_addr(),
|
||||
m_addrLen(0U),
|
||||
m_debug(debug)
|
||||
{
|
||||
assert(!address.empty());
|
||||
|
||||
if (CUDPSocket::lookup(address, ICOM_PORT, m_addr, m_addrLen) != 0)
|
||||
m_addrLen = 0U;
|
||||
}
|
||||
|
||||
CIcomNetwork::~CIcomNetwork()
|
||||
{
|
||||
}
|
||||
|
||||
bool CIcomNetwork::open()
|
||||
{
|
||||
if (m_addrLen == 0U) {
|
||||
LogError("Unable to resolve the address of the Icom network");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = m_socket.open(m_addr);
|
||||
if (!ret) {
|
||||
LogError("Unable to open the Icom network connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
LogMessage("Opened the Icom network connection");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CIcomNetwork::write(const unsigned char* data, unsigned int len)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
unsigned char buffer[110U];
|
||||
::memset(buffer, 0x00U, 110U);
|
||||
|
||||
buffer[0U] = 'I';
|
||||
buffer[1U] = 'C';
|
||||
buffer[2U] = 'O';
|
||||
buffer[3U] = 'M';
|
||||
buffer[4U] = 0x01U;
|
||||
buffer[5U] = 0x01U;
|
||||
buffer[6U] = 0x08U;
|
||||
buffer[7U] = 0xE0U;
|
||||
|
||||
if ((data[9U] & 0x02U) == 0x02U) {
|
||||
buffer[37U] = 0x23U;
|
||||
buffer[38U] = 0x02U;
|
||||
buffer[39U] = 0x18U;
|
||||
} else {
|
||||
buffer[37U] = 0x23U;
|
||||
buffer[38U] = (data[9U] & 0x0CU) != 0x00U ? 0x1CU : 0x10U;
|
||||
buffer[39U] = 0x21U;
|
||||
}
|
||||
|
||||
::memcpy(buffer + 40U, data + 10U, 33U);
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Icom Network Data Sent", buffer, 102U);
|
||||
|
||||
return m_socket.write(buffer, 102U, m_addr, m_addrLen);
|
||||
}
|
||||
|
||||
unsigned int CIcomNetwork::read(unsigned char* data)
|
||||
{
|
||||
unsigned char buffer[BUFFER_LENGTH];
|
||||
|
||||
sockaddr_storage addr;
|
||||
unsigned int addrLen;
|
||||
int length = m_socket.read(buffer, BUFFER_LENGTH, addr, addrLen);
|
||||
if (length <= 0)
|
||||
return 0U;
|
||||
|
||||
// Check if the data is for us
|
||||
if (!CUDPSocket::match(m_addr, addr)) {
|
||||
LogMessage("Icom packet received from an invalid source");
|
||||
return 0U;
|
||||
}
|
||||
|
||||
// Invalid packet type?
|
||||
if (::memcmp(buffer, "ICOM", 4U) != 0)
|
||||
return 0U;
|
||||
|
||||
if (length != 102)
|
||||
return 0U;
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Icom Network Data Received", buffer, length);
|
||||
|
||||
::memcpy(data, buffer + 40U, 33U);
|
||||
|
||||
return 33U;
|
||||
}
|
||||
|
||||
void CIcomNetwork::clock(unsigned int ms)
|
||||
{
|
||||
}
|
||||
|
||||
void CIcomNetwork::close()
|
||||
{
|
||||
m_socket.close();
|
||||
|
||||
LogMessage("Closing Icom network connection");
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2014,2016,2018,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 IcomNetwork_H
|
||||
#define IcomNetwork_H
|
||||
|
||||
#include "UDPSocket.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class CIcomNetwork {
|
||||
public:
|
||||
CIcomNetwork(const std::string& address, bool debug);
|
||||
~CIcomNetwork();
|
||||
|
||||
bool open();
|
||||
|
||||
bool write(const unsigned char* data, unsigned int len);
|
||||
|
||||
unsigned int read(unsigned char* data);
|
||||
|
||||
void close();
|
||||
|
||||
void clock(unsigned int ms);
|
||||
|
||||
private:
|
||||
CUDPSocket m_socket;
|
||||
sockaddr_storage m_addr;
|
||||
unsigned int m_addrLen;
|
||||
bool m_debug;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,941 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2014,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 "KenwoodNetwork.h"
|
||||
#include "NXDNCRC.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
|
||||
|
||||
#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||
#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
|
||||
const unsigned int RTP_PORT = 64000U;
|
||||
const unsigned int RTCP_PORT = 64001U;
|
||||
|
||||
CKenwoodNetwork::CKenwoodNetwork(const std::string& address, bool debug) :
|
||||
m_rtpSocket(RTP_PORT),
|
||||
m_rtcpSocket(RTCP_PORT),
|
||||
m_rtpAddr(),
|
||||
m_rtpAddrLen(0U),
|
||||
m_rtcpAddr(),
|
||||
m_rtcpAddrLen(0U),
|
||||
m_headerSeen(false),
|
||||
m_seen1(false),
|
||||
m_seen2(false),
|
||||
m_seen3(false),
|
||||
m_seen4(false),
|
||||
m_sacch(NULL),
|
||||
m_sessionId(1U),
|
||||
m_seqNo(0U),
|
||||
m_ssrc(0U),
|
||||
m_debug(debug),
|
||||
m_startSecs(0U),
|
||||
m_startUSecs(0U),
|
||||
m_rtcpTimer(1000U, 0U, 200U),
|
||||
m_hangTimer(1000U, 5U),
|
||||
m_hangType(0U),
|
||||
m_hangSrc(0U),
|
||||
m_hangDst(0U),
|
||||
m_random()
|
||||
{
|
||||
assert(!address.empty());
|
||||
|
||||
m_sacch = new unsigned char[10U];
|
||||
|
||||
if (CUDPSocket::lookup(address, RTP_PORT, m_rtpAddr, m_rtpAddrLen) != 0)
|
||||
m_rtpAddrLen = 0U;
|
||||
|
||||
if (CUDPSocket::lookup(address, RTCP_PORT, m_rtcpAddr, m_rtcpAddrLen) != 0)
|
||||
m_rtcpAddrLen = 0U;
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 mt(rd());
|
||||
m_random = mt;
|
||||
}
|
||||
|
||||
CKenwoodNetwork::~CKenwoodNetwork()
|
||||
{
|
||||
delete[] m_sacch;
|
||||
}
|
||||
|
||||
bool CKenwoodNetwork::open()
|
||||
{
|
||||
if (m_rtpAddrLen == 0U || m_rtcpAddrLen == 0U) {
|
||||
LogError("Unable to resolve the address of the Kenwood network");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_rtcpSocket.open(m_rtcpAddr)) {
|
||||
LogError("Unable to open the Kenwood network connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_rtpSocket.open(m_rtpAddr)) {
|
||||
LogError("Unable to open the Kenwood network connection");
|
||||
m_rtcpSocket.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
LogMessage("Opened the Kenwood network connection");
|
||||
|
||||
std::uniform_int_distribution<unsigned int> dist(0x00000001, 0xfffffffe);
|
||||
m_ssrc = dist(m_random);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKenwoodNetwork::write(const unsigned char* data, unsigned int length)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
switch (data[0U]) {
|
||||
case 0x81U: // Voice header or trailer
|
||||
case 0x83U:
|
||||
return processIcomVoiceHeader(data);
|
||||
case 0xACU: // Voice data
|
||||
case 0xAEU:
|
||||
return processIcomVoiceData(data);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CKenwoodNetwork::processIcomVoiceHeader(const unsigned char* inData)
|
||||
{
|
||||
assert(inData != NULL);
|
||||
|
||||
unsigned char outData[30U];
|
||||
::memset(outData, 0x00U, 30U);
|
||||
|
||||
// SACCH
|
||||
outData[0U] = inData[2U];
|
||||
outData[1U] = inData[1U];
|
||||
outData[2U] = inData[4U] & 0xC0U;
|
||||
outData[3U] = inData[3U];
|
||||
|
||||
// FACCH 1+2
|
||||
outData[4U] = outData[14U] = inData[6U];
|
||||
outData[5U] = outData[15U] = inData[5U];
|
||||
outData[6U] = outData[16U] = inData[8U];
|
||||
outData[7U] = outData[17U] = inData[7U];
|
||||
outData[8U] = outData[18U] = inData[10U];
|
||||
outData[9U] = outData[19U] = inData[9U];
|
||||
outData[10U] = outData[20U] = inData[12U];
|
||||
outData[11U] = outData[21U] = inData[11U];
|
||||
|
||||
unsigned short src = (inData[8U] << 8) + (inData[9U] << 0);
|
||||
unsigned short dst = (inData[10U] << 8) + (inData[11U] << 0);
|
||||
unsigned char type = (inData[7U] >> 5) & 0x07U;
|
||||
|
||||
switch (inData[5U] & 0x3FU) {
|
||||
case 0x01U:
|
||||
m_hangTimer.stop();
|
||||
m_rtcpTimer.start();
|
||||
writeRTCPStart();
|
||||
return writeRTPVoiceHeader(outData);
|
||||
case 0x08U: {
|
||||
m_hangTimer.start();
|
||||
bool ret = writeRTPVoiceTrailer(outData);
|
||||
writeRTCPHang(type, src, dst);
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CKenwoodNetwork::processIcomVoiceData(const unsigned char* inData)
|
||||
{
|
||||
assert(inData != NULL);
|
||||
|
||||
unsigned char outData[40U], temp[10U];
|
||||
::memset(outData, 0x00U, 40U);
|
||||
|
||||
// SACCH
|
||||
outData[0U] = inData[2U];
|
||||
outData[1U] = inData[1U];
|
||||
outData[2U] = inData[4U] & 0xC0U;
|
||||
outData[3U] = inData[3U];
|
||||
|
||||
// Audio 1
|
||||
::memset(temp, 0x00U, 10U);
|
||||
for (unsigned int i = 0U; i < 49U; i++) {
|
||||
unsigned int offset = (5U * 8U) + i;
|
||||
bool b = READ_BIT(inData, offset);
|
||||
WRITE_BIT(temp, i, b);
|
||||
}
|
||||
outData[4U] = temp[1U];
|
||||
outData[5U] = temp[0U];
|
||||
outData[6U] = temp[3U];
|
||||
outData[7U] = temp[2U];
|
||||
outData[8U] = temp[5U];
|
||||
outData[9U] = temp[4U];
|
||||
outData[10U] = temp[7U];
|
||||
outData[11U] = temp[6U];
|
||||
|
||||
// Audio 2
|
||||
::memset(temp, 0x00U, 10U);
|
||||
for (unsigned int i = 0U; i < 49U; i++) {
|
||||
unsigned int offset = (5U * 8U) + 49U + i;
|
||||
bool b = READ_BIT(inData, offset);
|
||||
WRITE_BIT(temp, i, b);
|
||||
}
|
||||
outData[12U] = temp[1U];
|
||||
outData[13U] = temp[0U];
|
||||
outData[14U] = temp[3U];
|
||||
outData[15U] = temp[2U];
|
||||
outData[16U] = temp[5U];
|
||||
outData[17U] = temp[4U];
|
||||
outData[18U] = temp[7U];
|
||||
outData[19U] = temp[6U];
|
||||
|
||||
// Audio 3
|
||||
::memset(temp, 0x00U, 10U);
|
||||
for (unsigned int i = 0U; i < 49U; i++) {
|
||||
unsigned int offset = (19U * 8U) + i;
|
||||
bool b = READ_BIT(inData, offset);
|
||||
WRITE_BIT(temp, i, b);
|
||||
}
|
||||
outData[20U] = temp[1U];
|
||||
outData[21U] = temp[0U];
|
||||
outData[22U] = temp[3U];
|
||||
outData[23U] = temp[2U];
|
||||
outData[24U] = temp[5U];
|
||||
outData[25U] = temp[4U];
|
||||
outData[26U] = temp[7U];
|
||||
outData[27U] = temp[6U];
|
||||
|
||||
// Audio 4
|
||||
::memset(temp, 0x00U, 10U);
|
||||
for (unsigned int i = 0U; i < 49U; i++) {
|
||||
unsigned int offset = (19U * 8U) + 49U + i;
|
||||
bool b = READ_BIT(inData, offset);
|
||||
WRITE_BIT(temp, i, b);
|
||||
}
|
||||
outData[28U] = temp[1U];
|
||||
outData[29U] = temp[0U];
|
||||
outData[30U] = temp[3U];
|
||||
outData[31U] = temp[2U];
|
||||
outData[32U] = temp[5U];
|
||||
outData[33U] = temp[4U];
|
||||
outData[34U] = temp[7U];
|
||||
outData[35U] = temp[6U];
|
||||
|
||||
return writeRTPVoiceData(outData);
|
||||
}
|
||||
|
||||
bool CKenwoodNetwork::writeRTPVoiceHeader(const unsigned char* data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
unsigned char buffer[50U];
|
||||
::memset(buffer, 0x00U, 50U);
|
||||
|
||||
buffer[0U] = 0x80U;
|
||||
buffer[1U] = 0x66U;
|
||||
|
||||
m_seqNo++;
|
||||
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
|
||||
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
|
||||
|
||||
unsigned long timeStamp = getTimeStamp();
|
||||
buffer[4U] = (timeStamp >> 24) & 0xFFU;
|
||||
buffer[5U] = (timeStamp >> 16) & 0xFFU;
|
||||
buffer[6U] = (timeStamp >> 8) & 0xFFU;
|
||||
buffer[7U] = (timeStamp >> 0) & 0xFFU;
|
||||
|
||||
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
|
||||
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
|
||||
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
|
||||
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
|
||||
|
||||
m_sessionId++;
|
||||
buffer[12U] = m_sessionId;
|
||||
|
||||
buffer[13U] = 0x00U;
|
||||
buffer[14U] = 0x00U;
|
||||
buffer[15U] = 0x00U;
|
||||
buffer[16U] = 0x03U;
|
||||
buffer[17U] = 0x03U;
|
||||
buffer[18U] = 0x04U;
|
||||
buffer[19U] = 0x04U;
|
||||
buffer[20U] = 0x0AU;
|
||||
buffer[21U] = 0x05U;
|
||||
buffer[22U] = 0x0AU;
|
||||
|
||||
::memcpy(buffer + 23U, data, 24U);
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 47U);
|
||||
|
||||
return m_rtpSocket.write(buffer, 47U, m_rtpAddr, m_rtpAddrLen);
|
||||
}
|
||||
|
||||
bool CKenwoodNetwork::writeRTPVoiceTrailer(const unsigned char* data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
unsigned char buffer[50U];
|
||||
::memset(buffer, 0x00U, 50U);
|
||||
|
||||
buffer[0U] = 0x80U;
|
||||
buffer[1U] = 0x66U;
|
||||
|
||||
m_seqNo++;
|
||||
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
|
||||
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
|
||||
|
||||
unsigned long timeStamp = getTimeStamp();
|
||||
buffer[4U] = (timeStamp >> 24) & 0xFFU;
|
||||
buffer[5U] = (timeStamp >> 16) & 0xFFU;
|
||||
buffer[6U] = (timeStamp >> 8) & 0xFFU;
|
||||
buffer[7U] = (timeStamp >> 0) & 0xFFU;
|
||||
|
||||
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
|
||||
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
|
||||
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
|
||||
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
|
||||
|
||||
buffer[12U] = m_sessionId;
|
||||
|
||||
buffer[13U] = 0x00U;
|
||||
buffer[14U] = 0x00U;
|
||||
buffer[15U] = 0x00U;
|
||||
buffer[16U] = 0x03U;
|
||||
buffer[17U] = 0x03U;
|
||||
buffer[18U] = 0x04U;
|
||||
buffer[19U] = 0x04U;
|
||||
buffer[20U] = 0x0AU;
|
||||
buffer[21U] = 0x05U;
|
||||
buffer[22U] = 0x0AU;
|
||||
|
||||
::memcpy(buffer + 23U, data, 24U);
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 47U);
|
||||
|
||||
return m_rtpSocket.write(buffer, 47U, m_rtpAddr, m_rtpAddrLen);
|
||||
}
|
||||
|
||||
bool CKenwoodNetwork::writeRTPVoiceData(const unsigned char* data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
unsigned char buffer[60U];
|
||||
::memset(buffer, 0x00U, 60U);
|
||||
|
||||
buffer[0U] = 0x80U;
|
||||
buffer[1U] = 0x66U;
|
||||
|
||||
m_seqNo++;
|
||||
buffer[2U] = (m_seqNo >> 8) & 0xFFU;
|
||||
buffer[3U] = (m_seqNo >> 0) & 0xFFU;
|
||||
|
||||
unsigned long timeStamp = getTimeStamp();
|
||||
buffer[4U] = (timeStamp >> 24) & 0xFFU;
|
||||
buffer[5U] = (timeStamp >> 16) & 0xFFU;
|
||||
buffer[6U] = (timeStamp >> 8) & 0xFFU;
|
||||
buffer[7U] = (timeStamp >> 0) & 0xFFU;
|
||||
|
||||
buffer[8U] = (m_ssrc >> 24) & 0xFFU;
|
||||
buffer[9U] = (m_ssrc >> 16) & 0xFFU;
|
||||
buffer[10U] = (m_ssrc >> 8) & 0xFFU;
|
||||
buffer[11U] = (m_ssrc >> 0) & 0xFFU;
|
||||
|
||||
buffer[12U] = m_sessionId;
|
||||
|
||||
buffer[13U] = 0x00U;
|
||||
buffer[14U] = 0x00U;
|
||||
buffer[15U] = 0x00U;
|
||||
buffer[16U] = 0x03U;
|
||||
buffer[17U] = 0x02U;
|
||||
buffer[18U] = 0x04U;
|
||||
buffer[19U] = 0x07U;
|
||||
buffer[20U] = 0x10U;
|
||||
buffer[21U] = 0x08U;
|
||||
buffer[22U] = 0x10U;
|
||||
|
||||
::memcpy(buffer + 23U, data, 36U);
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Kenwood Network RTP Data Sent", buffer, 59U);
|
||||
|
||||
return m_rtpSocket.write(buffer, 59U, m_rtpAddr, m_rtpAddrLen);
|
||||
}
|
||||
|
||||
bool CKenwoodNetwork::writeRTCPStart()
|
||||
{
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
time_t now;
|
||||
::time(&now);
|
||||
|
||||
m_startSecs = uint32_t(now);
|
||||
|
||||
SYSTEMTIME st;
|
||||
::GetSystemTime(&st);
|
||||
|
||||
m_startUSecs = st.wMilliseconds * 1000U;
|
||||
#else
|
||||
struct timeval tod;
|
||||
::gettimeofday(&tod, NULL);
|
||||
|
||||
m_startSecs = tod.tv_sec;
|
||||
m_startUSecs = tod.tv_usec;
|
||||
#endif
|
||||
|
||||
unsigned char buffer[30U];
|
||||
::memset(buffer, 0x00U, 30U);
|
||||
|
||||
buffer[0U] = 0x8AU;
|
||||
buffer[1U] = 0xCCU;
|
||||
buffer[2U] = 0x00U;
|
||||
buffer[3U] = 0x06U;
|
||||
|
||||
buffer[4U] = (m_ssrc >> 24) & 0xFFU;
|
||||
buffer[5U] = (m_ssrc >> 16) & 0xFFU;
|
||||
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
|
||||
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
|
||||
|
||||
buffer[8U] = 'K';
|
||||
buffer[9U] = 'W';
|
||||
buffer[10U] = 'N';
|
||||
buffer[11U] = 'E';
|
||||
|
||||
buffer[12U] = (m_startSecs >> 24) & 0xFFU;
|
||||
buffer[13U] = (m_startSecs >> 16) & 0xFFU;
|
||||
buffer[14U] = (m_startSecs >> 8) & 0xFFU;
|
||||
buffer[15U] = (m_startSecs >> 0) & 0xFFU;
|
||||
|
||||
buffer[16U] = (m_startUSecs >> 24) & 0xFFU;
|
||||
buffer[17U] = (m_startUSecs >> 16) & 0xFFU;
|
||||
buffer[18U] = (m_startUSecs >> 8) & 0xFFU;
|
||||
buffer[19U] = (m_startUSecs >> 0) & 0xFFU;
|
||||
|
||||
buffer[22U] = 0x02U;
|
||||
|
||||
buffer[24U] = 0x01U;
|
||||
|
||||
buffer[27U] = 0x0AU;
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U);
|
||||
|
||||
return m_rtcpSocket.write(buffer, 28U, m_rtcpAddr, m_rtcpAddrLen);
|
||||
}
|
||||
|
||||
bool CKenwoodNetwork::writeRTCPPing()
|
||||
{
|
||||
unsigned char buffer[30U];
|
||||
::memset(buffer, 0x00U, 30U);
|
||||
|
||||
buffer[0U] = 0x8AU;
|
||||
buffer[1U] = 0xCCU;
|
||||
buffer[2U] = 0x00U;
|
||||
buffer[3U] = 0x06U;
|
||||
|
||||
buffer[4U] = (m_ssrc >> 24) & 0xFFU;
|
||||
buffer[5U] = (m_ssrc >> 16) & 0xFFU;
|
||||
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
|
||||
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
|
||||
|
||||
buffer[8U] = 'K';
|
||||
buffer[9U] = 'W';
|
||||
buffer[10U] = 'N';
|
||||
buffer[11U] = 'E';
|
||||
|
||||
buffer[12U] = (m_startSecs >> 24) & 0xFFU;
|
||||
buffer[13U] = (m_startSecs >> 16) & 0xFFU;
|
||||
buffer[14U] = (m_startSecs >> 8) & 0xFFU;
|
||||
buffer[15U] = (m_startSecs >> 0) & 0xFFU;
|
||||
|
||||
buffer[16U] = (m_startUSecs >> 24) & 0xFFU;
|
||||
buffer[17U] = (m_startUSecs >> 16) & 0xFFU;
|
||||
buffer[18U] = (m_startUSecs >> 8) & 0xFFU;
|
||||
buffer[19U] = (m_startUSecs >> 0) & 0xFFU;
|
||||
|
||||
buffer[22U] = 0x02U;
|
||||
|
||||
buffer[24U] = 0x01U;
|
||||
|
||||
buffer[27U] = 0x7BU;
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 28U);
|
||||
|
||||
return m_rtcpSocket.write(buffer, 28U, m_rtcpAddr, m_rtcpAddrLen);
|
||||
}
|
||||
|
||||
bool CKenwoodNetwork::writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst)
|
||||
{
|
||||
m_hangType = type;
|
||||
m_hangSrc = src;
|
||||
m_hangDst = dst;
|
||||
|
||||
return writeRTCPHang();
|
||||
}
|
||||
|
||||
bool CKenwoodNetwork::writeRTCPHang()
|
||||
{
|
||||
unsigned char buffer[30U];
|
||||
::memset(buffer, 0x00U, 30U);
|
||||
|
||||
buffer[0U] = 0x8BU;
|
||||
buffer[1U] = 0xCCU;
|
||||
buffer[2U] = 0x00U;
|
||||
buffer[3U] = 0x04U;
|
||||
|
||||
buffer[4U] = (m_ssrc >> 24) & 0xFFU;
|
||||
buffer[5U] = (m_ssrc >> 16) & 0xFFU;
|
||||
buffer[6U] = (m_ssrc >> 8) & 0xFFU;
|
||||
buffer[7U] = (m_ssrc >> 0) & 0xFFU;
|
||||
|
||||
buffer[8U] = 'K';
|
||||
buffer[9U] = 'W';
|
||||
buffer[10U] = 'N';
|
||||
buffer[11U] = 'E';
|
||||
|
||||
buffer[12U] = (m_hangSrc >> 8) & 0xFFU;
|
||||
buffer[13U] = (m_hangSrc >> 0) & 0xFFU;
|
||||
|
||||
buffer[14U] = (m_hangDst >> 8) & 0xFFU;
|
||||
buffer[15U] = (m_hangDst >> 0) & 0xFFU;
|
||||
|
||||
buffer[16U] = m_hangType;
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Kenwood Network RTCP Data Sent", buffer, 20U);
|
||||
|
||||
return m_rtcpSocket.write(buffer, 20U, m_rtcpAddr, m_rtcpAddrLen);
|
||||
}
|
||||
|
||||
unsigned int CKenwoodNetwork::read(unsigned char* data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
unsigned char dummy[BUFFER_LENGTH];
|
||||
readRTCP(dummy);
|
||||
|
||||
unsigned int len = readRTP(data);
|
||||
switch (len) {
|
||||
case 0U: // Nothing received
|
||||
return 0U;
|
||||
case 35U: // Voice header or trailer
|
||||
return processKenwoodVoiceHeader(data);
|
||||
case 47U: // Voice data
|
||||
if (m_headerSeen)
|
||||
return processKenwoodVoiceData(data);
|
||||
else
|
||||
return processKenwoodVoiceLateEntry(data);
|
||||
case 31U: // Data
|
||||
return processKenwoodData(data);
|
||||
default:
|
||||
CUtils::dump(5U, "Unknown data received from the Kenwood network", data, len);
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CKenwoodNetwork::readRTP(unsigned char* data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
unsigned char buffer[BUFFER_LENGTH];
|
||||
|
||||
sockaddr_storage addr;
|
||||
unsigned int addrLen;
|
||||
int length = m_rtpSocket.read(buffer, BUFFER_LENGTH, addr, addrLen);
|
||||
if (length <= 0)
|
||||
return 0U;
|
||||
|
||||
// Check if the data is for us
|
||||
if (!CUDPSocket::match(m_rtpAddr, addr, IMT_ADDRESS_ONLY)) {
|
||||
LogMessage("Kenwood RTP packet received from an invalid source");
|
||||
return 0U;
|
||||
}
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Kenwood Network RTP Data Received", buffer, length);
|
||||
|
||||
::memcpy(data, buffer + 12U, length - 12U);
|
||||
|
||||
return length - 12U;
|
||||
}
|
||||
|
||||
unsigned int CKenwoodNetwork::readRTCP(unsigned char* data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
unsigned char buffer[BUFFER_LENGTH];
|
||||
|
||||
sockaddr_storage addr;
|
||||
unsigned int addrLen;
|
||||
int length = m_rtcpSocket.read(buffer, BUFFER_LENGTH, addr, addrLen);
|
||||
if (length <= 0)
|
||||
return 0U;
|
||||
|
||||
// Check if the data is for us
|
||||
if (!CUDPSocket::match(m_rtcpAddr, addr, IMT_ADDRESS_ONLY)) {
|
||||
LogMessage("Kenwood RTCP packet received from an invalid source");
|
||||
return 0U;
|
||||
}
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "Kenwood Network RTCP Data Received", buffer, length);
|
||||
|
||||
if (::memcmp(buffer + 8U, "KWNE", 4U) != 0) {
|
||||
LogError("Missing RTCP KWNE signature");
|
||||
return 0U;
|
||||
}
|
||||
|
||||
::memcpy(data, buffer + 12U, length - 12U);
|
||||
|
||||
return length - 12U;
|
||||
}
|
||||
|
||||
void CKenwoodNetwork::close()
|
||||
{
|
||||
m_rtcpSocket.close();
|
||||
m_rtpSocket.close();
|
||||
|
||||
LogMessage("Closing Kenwood connection");
|
||||
}
|
||||
|
||||
void CKenwoodNetwork::clock(unsigned int ms)
|
||||
{
|
||||
m_rtcpTimer.clock(ms);
|
||||
if (m_rtcpTimer.isRunning() && m_rtcpTimer.hasExpired()) {
|
||||
if (m_hangTimer.isRunning())
|
||||
writeRTCPHang();
|
||||
else
|
||||
writeRTCPPing();
|
||||
m_rtcpTimer.start();
|
||||
}
|
||||
|
||||
m_hangTimer.clock(ms);
|
||||
if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) {
|
||||
m_rtcpTimer.stop();
|
||||
m_hangTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CKenwoodNetwork::processKenwoodVoiceHeader(unsigned char* inData)
|
||||
{
|
||||
assert(inData != NULL);
|
||||
|
||||
unsigned char outData[50U], temp[20U];
|
||||
::memset(outData, 0x00U, 50U);
|
||||
|
||||
// LICH
|
||||
outData[0U] = 0x83U;
|
||||
|
||||
// SACCH
|
||||
::memset(temp, 0x00U, 20U);
|
||||
temp[0U] = inData[12U];
|
||||
temp[1U] = inData[11U];
|
||||
temp[2U] = inData[14U];
|
||||
temp[3U] = inData[13U];
|
||||
CNXDNCRC::encodeCRC6(temp, 26U);
|
||||
::memcpy(outData + 1U, temp, 4U);
|
||||
|
||||
// FACCH 1+2
|
||||
::memset(temp, 0x00U, 20U);
|
||||
temp[0U] = inData[16U];
|
||||
temp[1U] = inData[15U];
|
||||
temp[2U] = inData[18U];
|
||||
temp[3U] = inData[17U];
|
||||
temp[4U] = inData[20U];
|
||||
temp[5U] = inData[19U];
|
||||
temp[6U] = inData[22U];
|
||||
temp[7U] = inData[21U];
|
||||
temp[8U] = inData[24U];
|
||||
temp[9U] = inData[23U];
|
||||
CNXDNCRC::encodeCRC12(temp, 80U);
|
||||
::memcpy(outData + 5U, temp, 12U);
|
||||
::memcpy(outData + 19U, temp, 12U);
|
||||
|
||||
switch (outData[5U] & 0x3FU) {
|
||||
case 0x01U:
|
||||
::memcpy(inData, outData, 33U);
|
||||
m_headerSeen = true;
|
||||
m_seen1 = false;
|
||||
m_seen2 = false;
|
||||
m_seen3 = false;
|
||||
m_seen4 = false;
|
||||
return 33U;
|
||||
case 0x08U:
|
||||
::memcpy(inData, outData, 33U);
|
||||
m_headerSeen = false;
|
||||
m_seen1 = false;
|
||||
m_seen2 = false;
|
||||
m_seen3 = false;
|
||||
m_seen4 = false;
|
||||
return 33U;
|
||||
default:
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CKenwoodNetwork::processKenwoodVoiceData(unsigned char* inData)
|
||||
{
|
||||
assert(inData != NULL);
|
||||
|
||||
unsigned char outData[50U], temp[20U];
|
||||
::memset(outData, 0x00U, 50U);
|
||||
|
||||
// LICH
|
||||
outData[0U] = 0xAEU;
|
||||
|
||||
// SACCH
|
||||
::memset(temp, 0x00U, 20U);
|
||||
temp[0U] = inData[12U];
|
||||
temp[1U] = inData[11U];
|
||||
temp[2U] = inData[14U];
|
||||
temp[3U] = inData[13U];
|
||||
CNXDNCRC::encodeCRC6(temp, 26U);
|
||||
::memcpy(outData + 1U, temp, 4U);
|
||||
|
||||
// AMBE 1+2
|
||||
unsigned int n = 5U * 8U;
|
||||
|
||||
temp[0U] = inData[16U];
|
||||
temp[1U] = inData[15U];
|
||||
temp[2U] = inData[18U];
|
||||
temp[3U] = inData[17U];
|
||||
temp[4U] = inData[20U];
|
||||
temp[5U] = inData[19U];
|
||||
temp[6U] = inData[22U];
|
||||
temp[7U] = inData[21U];
|
||||
|
||||
for (unsigned int i = 0U; i < 49U; i++, n++) {
|
||||
bool b = READ_BIT(temp, i);
|
||||
WRITE_BIT(outData, n, b);
|
||||
}
|
||||
|
||||
temp[0U] = inData[24U];
|
||||
temp[1U] = inData[23U];
|
||||
temp[2U] = inData[26U];
|
||||
temp[3U] = inData[25U];
|
||||
temp[4U] = inData[28U];
|
||||
temp[5U] = inData[27U];
|
||||
temp[6U] = inData[30U];
|
||||
temp[7U] = inData[29U];
|
||||
|
||||
for (unsigned int i = 0U; i < 49U; i++, n++) {
|
||||
bool b = READ_BIT(temp, i);
|
||||
WRITE_BIT(outData, n, b);
|
||||
}
|
||||
|
||||
// AMBE 3+4
|
||||
n = 19U * 8U;
|
||||
|
||||
temp[0U] = inData[32U];
|
||||
temp[1U] = inData[31U];
|
||||
temp[2U] = inData[34U];
|
||||
temp[3U] = inData[33U];
|
||||
temp[4U] = inData[36U];
|
||||
temp[5U] = inData[35U];
|
||||
temp[6U] = inData[38U];
|
||||
temp[7U] = inData[37U];
|
||||
|
||||
for (unsigned int i = 0U; i < 49U; i++, n++) {
|
||||
bool b = READ_BIT(temp, i);
|
||||
WRITE_BIT(outData, n, b);
|
||||
}
|
||||
|
||||
temp[0U] = inData[40U];
|
||||
temp[1U] = inData[39U];
|
||||
temp[2U] = inData[42U];
|
||||
temp[3U] = inData[41U];
|
||||
temp[4U] = inData[44U];
|
||||
temp[5U] = inData[43U];
|
||||
temp[6U] = inData[46U];
|
||||
temp[7U] = inData[45U];
|
||||
|
||||
for (unsigned int i = 0U; i < 49U; i++, n++) {
|
||||
bool b = READ_BIT(temp, i);
|
||||
WRITE_BIT(outData, n, b);
|
||||
}
|
||||
|
||||
::memcpy(inData, outData, 33U);
|
||||
|
||||
return 33U;
|
||||
}
|
||||
|
||||
unsigned int CKenwoodNetwork::processKenwoodData(unsigned char* inData)
|
||||
{
|
||||
if (inData[7U] != 0x09U && inData[7U] != 0x0BU && inData[7U] != 0x08U)
|
||||
return 0U;
|
||||
|
||||
unsigned char outData[50U];
|
||||
|
||||
if (inData[7U] == 0x09U || inData[7U] == 0x08U) {
|
||||
outData[0U] = 0x90U;
|
||||
outData[1U] = inData[8U];
|
||||
outData[2U] = inData[7U];
|
||||
outData[3U] = inData[10U];
|
||||
outData[4U] = inData[9U];
|
||||
outData[5U] = inData[12U];
|
||||
outData[6U] = inData[11U];
|
||||
::memcpy(inData, outData, 7U);
|
||||
return 7U;
|
||||
} else {
|
||||
outData[0U] = 0x90U;
|
||||
outData[1U] = inData[8U];
|
||||
outData[2U] = inData[7U];
|
||||
outData[3U] = inData[10U];
|
||||
outData[4U] = inData[9U];
|
||||
outData[5U] = inData[12U];
|
||||
outData[6U] = inData[11U];
|
||||
outData[7U] = inData[14U];
|
||||
outData[8U] = inData[13U];
|
||||
outData[9U] = inData[16U];
|
||||
outData[10U] = inData[15U];
|
||||
outData[11U] = inData[18U];
|
||||
outData[12U] = inData[17U];
|
||||
outData[13U] = inData[20U];
|
||||
outData[14U] = inData[19U];
|
||||
outData[15U] = inData[22U];
|
||||
outData[16U] = inData[21U];
|
||||
outData[17U] = inData[24U];
|
||||
outData[18U] = inData[23U];
|
||||
outData[19U] = inData[26U];
|
||||
outData[20U] = inData[25U];
|
||||
outData[21U] = inData[28U];
|
||||
outData[22U] = inData[27U];
|
||||
outData[23U] = inData[29U];
|
||||
::memcpy(inData, outData, 24U);
|
||||
return 24U;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long CKenwoodNetwork::getTimeStamp() const
|
||||
{
|
||||
unsigned long timeStamp = 0UL;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
SYSTEMTIME st;
|
||||
::GetSystemTime(&st);
|
||||
|
||||
unsigned int hh = st.wHour;
|
||||
unsigned int mm = st.wMinute;
|
||||
unsigned int ss = st.wSecond;
|
||||
unsigned int ms = st.wMilliseconds;
|
||||
|
||||
timeStamp += hh * 3600U * 1000U * 80U;
|
||||
timeStamp += mm * 60U * 1000U * 80U;
|
||||
timeStamp += ss * 1000U * 80U;
|
||||
timeStamp += ms * 80U;
|
||||
#else
|
||||
struct timeval tod;
|
||||
::gettimeofday(&tod, NULL);
|
||||
|
||||
unsigned int ss = tod.tv_sec;
|
||||
unsigned int ms = tod.tv_usec / 1000U;
|
||||
|
||||
timeStamp += ss * 1000U * 80U;
|
||||
timeStamp += ms * 80U;
|
||||
#endif
|
||||
|
||||
return timeStamp;
|
||||
}
|
||||
|
||||
unsigned int CKenwoodNetwork::processKenwoodVoiceLateEntry(unsigned char* inData)
|
||||
{
|
||||
assert(inData != NULL);
|
||||
|
||||
unsigned char sacch[4U];
|
||||
sacch[0U] = inData[12U];
|
||||
sacch[1U] = inData[11U];
|
||||
sacch[2U] = inData[14U];
|
||||
sacch[3U] = inData[13U];
|
||||
|
||||
switch (sacch[0U] & 0xC0U) {
|
||||
case 0xC0U:
|
||||
if (!m_seen1) {
|
||||
unsigned int offset = 0U;
|
||||
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
||||
bool b = READ_BIT(sacch, i) != 0U;
|
||||
WRITE_BIT(m_sacch, offset, b);
|
||||
}
|
||||
m_seen1 = true;
|
||||
}
|
||||
break;
|
||||
case 0x80U:
|
||||
if (!m_seen2) {
|
||||
unsigned int offset = 18U;
|
||||
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
||||
bool b = READ_BIT(sacch, i) != 0U;
|
||||
WRITE_BIT(m_sacch, offset, b);
|
||||
}
|
||||
m_seen2 = true;
|
||||
}
|
||||
break;
|
||||
case 0x40U:
|
||||
if (!m_seen3) {
|
||||
unsigned int offset = 36U;
|
||||
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
||||
bool b = READ_BIT(sacch, i) != 0U;
|
||||
WRITE_BIT(m_sacch, offset, b);
|
||||
}
|
||||
m_seen3 = true;
|
||||
}
|
||||
break;
|
||||
case 0x00U:
|
||||
if (!m_seen4) {
|
||||
unsigned int offset = 54U;
|
||||
for (unsigned int i = 8U; i < 26U; i++, offset++) {
|
||||
bool b = READ_BIT(sacch, i) != 0U;
|
||||
WRITE_BIT(m_sacch, offset, b);
|
||||
}
|
||||
m_seen4 = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_seen1 || !m_seen2 || !m_seen3 || !m_seen4)
|
||||
return 0U;
|
||||
|
||||
// Create a dummy header
|
||||
// Header SACCH
|
||||
inData[11U] = 0x10U;
|
||||
inData[12U] = 0x01U;
|
||||
inData[13U] = 0x00U;
|
||||
inData[14U] = 0x00U;
|
||||
|
||||
// Header FACCH
|
||||
inData[15U] = m_sacch[1U];
|
||||
inData[16U] = m_sacch[0U];
|
||||
inData[17U] = m_sacch[3U];
|
||||
inData[18U] = m_sacch[2U];
|
||||
inData[19U] = m_sacch[5U];
|
||||
inData[20U] = m_sacch[4U];
|
||||
inData[21U] = m_sacch[7U];
|
||||
inData[22U] = m_sacch[6U];
|
||||
inData[23U] = 0x00U;
|
||||
inData[24U] = m_sacch[8U];
|
||||
|
||||
return processKenwoodVoiceHeader(inData);
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2014,2016,2018,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 KenwoodNetwork_H
|
||||
#define KenwoodNetwork_H
|
||||
|
||||
#include "UDPSocket.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <random>
|
||||
|
||||
class CKenwoodNetwork {
|
||||
public:
|
||||
CKenwoodNetwork(const std::string& address, bool debug);
|
||||
~CKenwoodNetwork();
|
||||
|
||||
bool open();
|
||||
|
||||
bool write(const unsigned char* data, unsigned int length);
|
||||
|
||||
unsigned int read(unsigned char* data);
|
||||
|
||||
void close();
|
||||
|
||||
void clock(unsigned int ms);
|
||||
|
||||
private:
|
||||
CUDPSocket m_rtpSocket;
|
||||
CUDPSocket m_rtcpSocket;
|
||||
sockaddr_storage m_rtpAddr;
|
||||
unsigned int m_rtpAddrLen;
|
||||
sockaddr_storage m_rtcpAddr;
|
||||
unsigned int m_rtcpAddrLen;
|
||||
bool m_headerSeen;
|
||||
bool m_seen1;
|
||||
bool m_seen2;
|
||||
bool m_seen3;
|
||||
bool m_seen4;
|
||||
unsigned char* m_sacch;
|
||||
uint8_t m_sessionId;
|
||||
uint16_t m_seqNo;
|
||||
unsigned int m_ssrc;
|
||||
bool m_debug;
|
||||
uint32_t m_startSecs;
|
||||
uint32_t m_startUSecs;
|
||||
CTimer m_rtcpTimer;
|
||||
CTimer m_hangTimer;
|
||||
unsigned char m_hangType;
|
||||
unsigned short m_hangSrc;
|
||||
unsigned short m_hangDst;
|
||||
std::mt19937 m_random;
|
||||
|
||||
bool processIcomVoiceHeader(const unsigned char* data);
|
||||
bool processIcomVoiceData(const unsigned char* data);
|
||||
unsigned int processKenwoodVoiceHeader(unsigned char* data);
|
||||
unsigned int processKenwoodVoiceData(unsigned char* data);
|
||||
unsigned int processKenwoodVoiceLateEntry(unsigned char* data);
|
||||
unsigned int processKenwoodData(unsigned char* data);
|
||||
bool writeRTPVoiceHeader(const unsigned char* data);
|
||||
bool writeRTPVoiceData(const unsigned char* data);
|
||||
bool writeRTPVoiceTrailer(const unsigned char* data);
|
||||
bool writeRTCPStart();
|
||||
bool writeRTCPPing();
|
||||
bool writeRTCPHang(unsigned char type, unsigned short src, unsigned short dst);
|
||||
bool writeRTCPHang();
|
||||
unsigned int readRTP(unsigned char* data);
|
||||
unsigned int readRTCP(unsigned char* data);
|
||||
unsigned long getTimeStamp() const;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,22 @@
|
||||
CC = cc
|
||||
CXX = c++
|
||||
CFLAGS = -g -O3 -Wall -DHAVE_LOG_H -DUDP_SOCKET_MAX=2 -std=c++0x -pthread
|
||||
LIBS = -lpthread
|
||||
LDFLAGS = -g
|
||||
|
||||
OBJECTS = Conf.o IcomNetwork.o KenwoodNetwork.o Log.o Mutex.o NXDNCRC.o NXDNLookup.o NXDNNetwork.o NXDNReflector.o StopWatch.o Thread.o Timer.o UDPSocket.o Utils.o
|
||||
|
||||
all: NXDNReflector
|
||||
|
||||
NXDNReflector: $(OBJECTS)
|
||||
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o NXDNReflector
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
install:
|
||||
install -m 755 NXDNReflector /usr/local/bin/
|
||||
|
||||
clean:
|
||||
$(RM) NXDNReflector *.o *.d *.bak *~
|
||||
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "Mutex.h"
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
CMutex::CMutex() :
|
||||
m_handle()
|
||||
{
|
||||
m_handle = ::CreateMutex(NULL, FALSE, NULL);
|
||||
}
|
||||
|
||||
CMutex::~CMutex()
|
||||
{
|
||||
::CloseHandle(m_handle);
|
||||
}
|
||||
|
||||
void CMutex::lock()
|
||||
{
|
||||
::WaitForSingleObject(m_handle, INFINITE);
|
||||
}
|
||||
|
||||
void CMutex::unlock()
|
||||
{
|
||||
::ReleaseMutex(m_handle);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
CMutex::CMutex() :
|
||||
m_mutex(PTHREAD_MUTEX_INITIALIZER)
|
||||
{
|
||||
}
|
||||
|
||||
CMutex::~CMutex()
|
||||
{
|
||||
}
|
||||
|
||||
void CMutex::lock()
|
||||
{
|
||||
::pthread_mutex_lock(&m_mutex);
|
||||
}
|
||||
|
||||
void CMutex::unlock()
|
||||
{
|
||||
::pthread_mutex_unlock(&m_mutex);
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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(MUTEX_H)
|
||||
#define MUTEX_H
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
class CMutex
|
||||
{
|
||||
public:
|
||||
CMutex();
|
||||
~CMutex();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
HANDLE m_handle;
|
||||
#else
|
||||
pthread_mutex_t m_mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) 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 "NXDNCRC.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
const uint8_t BIT_MASK_TABLE1[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
|
||||
|
||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE1[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE1[(i)&7])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE1[(i)&7])
|
||||
|
||||
bool CNXDNCRC::checkCRC6(const unsigned char* in, unsigned int length)
|
||||
{
|
||||
assert(in != NULL);
|
||||
|
||||
uint8_t crc = createCRC6(in, length);
|
||||
|
||||
uint8_t temp[1U];
|
||||
temp[0U] = 0x00U;
|
||||
unsigned int j = length;
|
||||
for (unsigned int i = 2U; i < 8U; i++, j++) {
|
||||
bool b = READ_BIT1(in, j);
|
||||
WRITE_BIT1(temp, i, b);
|
||||
}
|
||||
|
||||
return crc == temp[0U];
|
||||
}
|
||||
|
||||
void CNXDNCRC::encodeCRC6(unsigned char* in, unsigned int length)
|
||||
{
|
||||
assert(in != NULL);
|
||||
|
||||
uint8_t crc[1U];
|
||||
crc[0U] = createCRC6(in, length);
|
||||
|
||||
unsigned int n = length;
|
||||
for (unsigned int i = 2U; i < 8U; i++, n++) {
|
||||
bool b = READ_BIT1(crc, i);
|
||||
WRITE_BIT1(in, n, b);
|
||||
}
|
||||
}
|
||||
|
||||
bool CNXDNCRC::checkCRC12(const unsigned char* in, unsigned int length)
|
||||
{
|
||||
assert(in != NULL);
|
||||
|
||||
uint16_t crc = createCRC12(in, length);
|
||||
uint8_t temp1[2U];
|
||||
temp1[0U] = (crc >> 8) & 0xFFU;
|
||||
temp1[1U] = (crc >> 0) & 0xFFU;
|
||||
|
||||
uint8_t temp2[2U];
|
||||
temp2[0U] = 0x00U;
|
||||
temp2[1U] = 0x00U;
|
||||
unsigned int j = length;
|
||||
for (unsigned int i = 4U; i < 16U; i++, j++) {
|
||||
bool b = READ_BIT1(in, j);
|
||||
WRITE_BIT1(temp2, i, b);
|
||||
}
|
||||
|
||||
return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U];
|
||||
}
|
||||
|
||||
void CNXDNCRC::encodeCRC12(unsigned char* in, unsigned int length)
|
||||
{
|
||||
assert(in != NULL);
|
||||
|
||||
uint16_t crc = createCRC12(in, length);
|
||||
|
||||
uint8_t temp[2U];
|
||||
temp[0U] = (crc >> 8) & 0xFFU;
|
||||
temp[1U] = (crc >> 0) & 0xFFU;
|
||||
|
||||
unsigned int n = length;
|
||||
for (unsigned int i = 4U; i < 16U; i++, n++) {
|
||||
bool b = READ_BIT1(temp, i);
|
||||
WRITE_BIT1(in, n, b);
|
||||
}
|
||||
}
|
||||
|
||||
bool CNXDNCRC::checkCRC15(const unsigned char* in, unsigned int length)
|
||||
{
|
||||
assert(in != NULL);
|
||||
|
||||
uint16_t crc = createCRC15(in, length);
|
||||
uint8_t temp1[2U];
|
||||
temp1[0U] = (crc >> 8) & 0xFFU;
|
||||
temp1[1U] = (crc >> 0) & 0xFFU;
|
||||
|
||||
uint8_t temp2[2U];
|
||||
temp2[0U] = 0x00U;
|
||||
temp2[1U] = 0x00U;
|
||||
unsigned int j = length;
|
||||
for (unsigned int i = 1U; i < 16U; i++, j++) {
|
||||
bool b = READ_BIT1(in, j);
|
||||
WRITE_BIT1(temp2, i, b);
|
||||
}
|
||||
|
||||
return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U];
|
||||
}
|
||||
|
||||
void CNXDNCRC::encodeCRC15(unsigned char* in, unsigned int length)
|
||||
{
|
||||
assert(in != NULL);
|
||||
|
||||
uint16_t crc = createCRC15(in, length);
|
||||
|
||||
uint8_t temp[2U];
|
||||
temp[0U] = (crc >> 8) & 0xFFU;
|
||||
temp[1U] = (crc >> 0) & 0xFFU;
|
||||
|
||||
unsigned int n = length;
|
||||
for (unsigned int i = 1U; i < 16U; i++, n++) {
|
||||
bool b = READ_BIT1(temp, i);
|
||||
WRITE_BIT1(in, n, b);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t CNXDNCRC::createCRC6(const unsigned char* in, unsigned int length)
|
||||
{
|
||||
uint8_t crc = 0x3FU;
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++) {
|
||||
bool bit1 = READ_BIT1(in, i) != 0x00U;
|
||||
bool bit2 = (crc & 0x20U) == 0x20U;
|
||||
|
||||
crc <<= 1;
|
||||
|
||||
if (bit1 ^ bit2)
|
||||
crc ^= 0x27U;
|
||||
}
|
||||
|
||||
return crc & 0x3FU;
|
||||
}
|
||||
|
||||
uint16_t CNXDNCRC::createCRC12(const unsigned char* in, unsigned int length)
|
||||
{
|
||||
uint16_t crc = 0x0FFFU;
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++) {
|
||||
bool bit1 = READ_BIT1(in, i) != 0x00U;
|
||||
bool bit2 = (crc & 0x0800U) == 0x0800U;
|
||||
|
||||
crc <<= 1;
|
||||
|
||||
if (bit1 ^ bit2)
|
||||
crc ^= 0x080FU;
|
||||
}
|
||||
|
||||
return crc & 0x0FFFU;
|
||||
}
|
||||
|
||||
uint16_t CNXDNCRC::createCRC15(const unsigned char* in, unsigned int length)
|
||||
{
|
||||
uint16_t crc = 0x7FFFU;
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++) {
|
||||
bool bit1 = READ_BIT1(in, i) != 0x00U;
|
||||
bool bit2 = (crc & 0x4000U) == 0x4000U;
|
||||
|
||||
crc <<= 1;
|
||||
|
||||
if (bit1 ^ bit2)
|
||||
crc ^= 0x4CC5U;
|
||||
}
|
||||
|
||||
return crc & 0x7FFFU;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 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(NXDNCRC_H)
|
||||
#define NXDNCRC_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class CNXDNCRC
|
||||
{
|
||||
public:
|
||||
static bool checkCRC6(const unsigned char* in, unsigned int length);
|
||||
static void encodeCRC6(unsigned char* in, unsigned int length);
|
||||
|
||||
static bool checkCRC12(const unsigned char* in, unsigned int length);
|
||||
static void encodeCRC12(unsigned char* in, unsigned int length);
|
||||
|
||||
static bool checkCRC15(const unsigned char* in, unsigned int length);
|
||||
static void encodeCRC15(unsigned char* in, unsigned int length);
|
||||
|
||||
private:
|
||||
static uint8_t createCRC6(const unsigned char* in, unsigned int length);
|
||||
static uint16_t createCRC12(const unsigned char* in, unsigned int length);
|
||||
static uint16_t createCRC15(const unsigned char* in, unsigned int length);
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) 2016,2017,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 "NXDNLookup.h"
|
||||
#include "Timer.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
|
||||
CNXDNLookup::CNXDNLookup(const std::string& filename, unsigned int reloadTime) :
|
||||
CThread(),
|
||||
m_filename(filename),
|
||||
m_reloadTime(reloadTime),
|
||||
m_table(),
|
||||
m_mutex(),
|
||||
m_stop(false)
|
||||
{
|
||||
}
|
||||
|
||||
CNXDNLookup::~CNXDNLookup()
|
||||
{
|
||||
}
|
||||
|
||||
bool CNXDNLookup::read()
|
||||
{
|
||||
bool ret = load();
|
||||
|
||||
if (m_reloadTime > 0U)
|
||||
run();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CNXDNLookup::entry()
|
||||
{
|
||||
LogInfo("Started the NXDN Id lookup reload thread");
|
||||
|
||||
CTimer timer(1U, 3600U * m_reloadTime);
|
||||
timer.start();
|
||||
|
||||
while (!m_stop) {
|
||||
sleep(1000U);
|
||||
|
||||
timer.clock();
|
||||
if (timer.hasExpired()) {
|
||||
load();
|
||||
timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
LogInfo("Stopped the NXDN Id lookup reload thread");
|
||||
}
|
||||
|
||||
void CNXDNLookup::stop()
|
||||
{
|
||||
if (m_reloadTime == 0U) {
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
m_stop = true;
|
||||
|
||||
wait();
|
||||
}
|
||||
|
||||
std::string CNXDNLookup::find(unsigned int id)
|
||||
{
|
||||
std::string callsign;
|
||||
|
||||
if (id == 0xFFFFU)
|
||||
return std::string("ALL");
|
||||
|
||||
m_mutex.lock();
|
||||
|
||||
try {
|
||||
callsign = m_table.at(id);
|
||||
} catch (...) {
|
||||
char text[10U];
|
||||
::sprintf(text, "%u", id);
|
||||
callsign = std::string(text);
|
||||
}
|
||||
|
||||
m_mutex.unlock();
|
||||
|
||||
return callsign;
|
||||
}
|
||||
|
||||
bool CNXDNLookup::exists(unsigned int id)
|
||||
{
|
||||
m_mutex.lock();
|
||||
|
||||
bool found = m_table.count(id) == 1U;
|
||||
|
||||
m_mutex.unlock();
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool CNXDNLookup::load()
|
||||
{
|
||||
FILE* fp = ::fopen(m_filename.c_str(), "rt");
|
||||
if (fp == NULL) {
|
||||
LogWarning("Cannot open the NXDN Id lookup file - %s", m_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_mutex.lock();
|
||||
|
||||
// Remove the old entries
|
||||
m_table.clear();
|
||||
|
||||
char buffer[100U];
|
||||
while (::fgets(buffer, 100U, fp) != NULL) {
|
||||
if (buffer[0U] == '#')
|
||||
continue;
|
||||
|
||||
char* p1 = ::strtok(buffer, ",\t\r\n");
|
||||
char* p2 = ::strtok(NULL, ",\t\r\n");
|
||||
|
||||
if (p1 != NULL && p2 != NULL) {
|
||||
unsigned int id = (unsigned int)::atoi(p1);
|
||||
if (id > 0U) {
|
||||
for (char* p = p2; *p != 0x00U; p++)
|
||||
*p = ::toupper(*p);
|
||||
|
||||
m_table[id] = std::string(p2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_mutex.unlock();
|
||||
|
||||
::fclose(fp);
|
||||
|
||||
size_t size = m_table.size();
|
||||
if (size == 0U)
|
||||
return false;
|
||||
|
||||
LogInfo("Loaded %u Ids to the NXDN callsign lookup table", size);
|
||||
|
||||
return true;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2016,2017,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.
|
||||
*/
|
||||
|
||||
#ifndef NXDNLookup_H
|
||||
#define NXDNLookup_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Mutex.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class CNXDNLookup : public CThread {
|
||||
public:
|
||||
CNXDNLookup(const std::string& filename, unsigned int reloadTime);
|
||||
virtual ~CNXDNLookup();
|
||||
|
||||
bool read();
|
||||
|
||||
virtual void entry();
|
||||
|
||||
std::string find(unsigned int id);
|
||||
|
||||
bool exists(unsigned int id);
|
||||
|
||||
void stop();
|
||||
|
||||
private:
|
||||
std::string m_filename;
|
||||
unsigned int m_reloadTime;
|
||||
std::unordered_map<unsigned int, std::string> m_table;
|
||||
CMutex m_mutex;
|
||||
bool m_stop;
|
||||
|
||||
bool load();
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2014,2016,2018,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 "NXDNNetwork.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
CNXDNNetwork::CNXDNNetwork(unsigned short port, bool debug) :
|
||||
m_socket(port),
|
||||
m_debug(debug)
|
||||
{
|
||||
}
|
||||
|
||||
CNXDNNetwork::~CNXDNNetwork()
|
||||
{
|
||||
}
|
||||
|
||||
bool CNXDNNetwork::open()
|
||||
{
|
||||
LogInfo("Opening NXDN network connection");
|
||||
|
||||
return m_socket.open();
|
||||
}
|
||||
|
||||
bool CNXDNNetwork::write(const unsigned char* data, unsigned int length, const sockaddr_storage& addr, unsigned int addrLen)
|
||||
{
|
||||
assert(data != NULL);
|
||||
assert(length > 0U);
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "NXDN Network Data Sent", data, length);
|
||||
|
||||
return m_socket.write(data, length, addr, addrLen);
|
||||
}
|
||||
|
||||
bool CNXDNNetwork::write(const unsigned char* data, unsigned int length, unsigned short srcId, unsigned short dstId, bool grp, const sockaddr_storage& addr, unsigned int addrLen)
|
||||
{
|
||||
assert(data != NULL);
|
||||
assert(length > 0U);
|
||||
|
||||
unsigned char buffer[50U];
|
||||
|
||||
buffer[0U] = 'N';
|
||||
buffer[1U] = 'X';
|
||||
buffer[2U] = 'D';
|
||||
buffer[3U] = 'N';
|
||||
buffer[4U] = 'D';
|
||||
|
||||
buffer[5U] = (srcId >> 8) & 0xFFU;
|
||||
buffer[6U] = (srcId >> 0) & 0xFFU;
|
||||
|
||||
buffer[7U] = (dstId >> 8) & 0xFFU;
|
||||
buffer[8U] = (dstId >> 0) & 0xFFU;
|
||||
|
||||
buffer[9U] = 0x00U;
|
||||
buffer[9U] |= grp ? 0x01U : 0x00U;
|
||||
|
||||
if (data[0U] == 0x81U || data[0U] == 0x83U) {
|
||||
// This is a voice header or trailer.
|
||||
buffer[9U] |= data[5U] == 0x01U ? 0x04U : 0x00U;
|
||||
buffer[9U] |= data[5U] == 0x08U ? 0x08U : 0x00U;
|
||||
} else if ((data[0U] & 0xF0U) == 0x90U) {
|
||||
// This if data.
|
||||
buffer[9U] |= 0x02U;
|
||||
if (data[0U] == 0x90U || data[0U] == 0x92U || data[0U] == 0x9CU || data[0U] == 0x9EU) {
|
||||
// This is data header or trailer.
|
||||
buffer[9U] |= data[2U] == 0x09U ? 0x04U : 0x00U;
|
||||
buffer[9U] |= data[2U] == 0x08U ? 0x08U : 0x00U;
|
||||
}
|
||||
}
|
||||
|
||||
::memcpy(buffer + 10U, data, 33U);
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "NXDN Network Data Sent", buffer, 43U);
|
||||
|
||||
return m_socket.write(buffer, 43U, addr, addrLen);
|
||||
}
|
||||
|
||||
unsigned int CNXDNNetwork::read(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;
|
||||
|
||||
// Invalid packet type?
|
||||
if (::memcmp(data, "NXDN", 4U) != 0)
|
||||
return 0U;
|
||||
|
||||
if (len != 17 && len != 43)
|
||||
return 0U;
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "NXDN Network Data Received", data, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void CNXDNNetwork::close()
|
||||
{
|
||||
m_socket.close();
|
||||
|
||||
LogInfo("Closing NXDN network connection");
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2014,2016,2018,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 NXDNNetwork_H
|
||||
#define NXDNNetwork_H
|
||||
|
||||
#include "UDPSocket.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class CNXDNNetwork {
|
||||
public:
|
||||
CNXDNNetwork(unsigned short port, bool debug);
|
||||
~CNXDNNetwork();
|
||||
|
||||
bool open();
|
||||
|
||||
bool write(const unsigned char* data, unsigned int length, const sockaddr_storage& address, unsigned int addrLen);
|
||||
bool write(const unsigned char* data, unsigned int length, unsigned short srcId, unsigned short dstId, bool grp, const sockaddr_storage& addr, unsigned int addrLen);
|
||||
|
||||
unsigned int read(unsigned char* data, unsigned int length, sockaddr_storage& addr, unsigned int& addrLen);
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
CUDPSocket m_socket;
|
||||
bool m_debug;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,629 @@
|
||||
/*
|
||||
* 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 "NXDNReflector.h"
|
||||
#include "NXDNNetwork.h"
|
||||
#include "NXDNLookup.h"
|
||||
#include "StopWatch.h"
|
||||
#include "Version.h"
|
||||
#include "Thread.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.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 = "NXDNReflector.ini";
|
||||
#else
|
||||
const char* DEFAULT_INI_FILE = "/etc/NXDNReflector.ini";
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstdarg>
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
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, "NXDNReflector version %s\n", VERSION);
|
||||
return 0;
|
||||
} else if (arg.substr(0, 1) == "-") {
|
||||
::fprintf(stderr, "Usage: NXDNReflector [-v|--version] [filename]\n");
|
||||
return 1;
|
||||
} else {
|
||||
iniFile = argv[currentArg];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CNXDNReflector* reflector = new CNXDNReflector(std::string(iniFile));
|
||||
reflector->run();
|
||||
delete reflector;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CNXDNReflector::CNXDNReflector(const std::string& file) :
|
||||
m_conf(file),
|
||||
m_icomNetwork(NULL),
|
||||
m_kenwoodNetwork(NULL),
|
||||
m_repeaters()
|
||||
{
|
||||
CUDPSocket::startup();
|
||||
}
|
||||
|
||||
CNXDNReflector::~CNXDNReflector()
|
||||
{
|
||||
CUDPSocket::shutdown();
|
||||
}
|
||||
|
||||
void CNXDNReflector::run()
|
||||
{
|
||||
bool ret = m_conf.read();
|
||||
if (!ret) {
|
||||
::fprintf(stderr, "NXDNReflector: 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, "NXDNReflector: 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
|
||||
|
||||
unsigned short tg = m_conf.getTG();
|
||||
|
||||
CNXDNNetwork nxdnNetwork(m_conf.getNetworkPort(), m_conf.getNetworkDebug());
|
||||
ret = nxdnNetwork.open();
|
||||
if (!ret) {
|
||||
::LogFinalise();
|
||||
return;
|
||||
}
|
||||
|
||||
bool icomEnabled = m_conf.getIcomEnabled();
|
||||
|
||||
unsigned short icomTGEnable = 0U;
|
||||
unsigned short icomTGDisable = 0U;
|
||||
|
||||
if (icomEnabled) {
|
||||
ret = openIcomNetwork();
|
||||
if (!ret) {
|
||||
nxdnNetwork.close();
|
||||
::LogFinalise();
|
||||
return;
|
||||
}
|
||||
|
||||
icomTGEnable = m_conf.getIcomTGEnable();
|
||||
icomTGDisable = m_conf.getIcomTGDisable();
|
||||
}
|
||||
|
||||
bool kenwoodEnabled = m_conf.getKenwoodEnabled();
|
||||
|
||||
unsigned short kenwoodTGEnable = 0U;
|
||||
unsigned short kenwoodTGDisable = 0U;
|
||||
|
||||
if (kenwoodEnabled) {
|
||||
ret = openKenwoodNetwork();
|
||||
if (!ret) {
|
||||
nxdnNetwork.close();
|
||||
::LogFinalise();
|
||||
return;
|
||||
}
|
||||
|
||||
kenwoodTGEnable = m_conf.getKenwoodTGEnable();
|
||||
kenwoodTGDisable = m_conf.getKenwoodTGDisable();
|
||||
}
|
||||
|
||||
CNXDNLookup* lookup = new CNXDNLookup(m_conf.getLookupName(), m_conf.getLookupTime());
|
||||
lookup->read();
|
||||
|
||||
CStopWatch stopWatch;
|
||||
stopWatch.start();
|
||||
|
||||
CTimer dumpTimer(1000U, 120U);
|
||||
dumpTimer.start();
|
||||
|
||||
LogMessage("Starting NXDNReflector-%s", VERSION);
|
||||
|
||||
enum {
|
||||
ACTIVE_NONE,
|
||||
ACTIVE_NXDN,
|
||||
ACTIVE_ICOM,
|
||||
ACTIVE_KENWOOD
|
||||
} active = ACTIVE_NONE;
|
||||
|
||||
CNXDNRepeater* current = NULL;
|
||||
|
||||
unsigned short srcId = 0U;
|
||||
unsigned short dstId = 0U;
|
||||
bool grp = false;
|
||||
|
||||
CTimer watchdogTimer(1000U, 0U, 1500U);
|
||||
|
||||
for (;;) {
|
||||
unsigned char buffer[200U];
|
||||
sockaddr_storage address;
|
||||
unsigned int addressLen;
|
||||
|
||||
unsigned int len = nxdnNetwork.read(buffer, 200U, address, addressLen);
|
||||
if (len > 0U) {
|
||||
CNXDNRepeater* rpt = findRepeater(address);
|
||||
|
||||
if (::memcmp(buffer, "NXDNP", 5U) == 0 && len == 17U) {
|
||||
unsigned short id = (buffer[15U] << 8) | buffer[16U];
|
||||
if (id == tg) {
|
||||
if (rpt == NULL) {
|
||||
rpt = new CNXDNRepeater;
|
||||
rpt->m_timer.start();
|
||||
::memcpy(&rpt->m_addr, &address, sizeof(struct sockaddr_storage));
|
||||
rpt->m_addrLen = addressLen;
|
||||
rpt->m_callsign = std::string((char*)(buffer + 5U), 10U);
|
||||
m_repeaters.push_back(rpt);
|
||||
|
||||
char buff[80U];
|
||||
LogMessage("Adding %s (%s)", rpt->m_callsign.c_str(), CUDPSocket::display(address, buff, 80U));
|
||||
} else {
|
||||
rpt->m_timer.start();
|
||||
}
|
||||
|
||||
// Return the poll
|
||||
nxdnNetwork.write(buffer, len, address, addressLen);
|
||||
}
|
||||
} else if (::memcmp(buffer, "NXDNU", 5U) == 0 && len == 17U) {
|
||||
unsigned short id = (buffer[15U] << 8) | buffer[16U];
|
||||
if (id == tg) {
|
||||
if (rpt != NULL) {
|
||||
std::string callsign = std::string((char*)(buffer + 5U), 10U);
|
||||
|
||||
char buff[80U];
|
||||
LogMessage("Removing %s (%s) unlinked", callsign.c_str(), CUDPSocket::display(address, buff, 80U));
|
||||
|
||||
for (std::vector<CNXDNRepeater*>::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
|
||||
if (*it == rpt) {
|
||||
m_repeaters.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete rpt;
|
||||
}
|
||||
}
|
||||
} else if (::memcmp(buffer, "NXDND", 5U) == 0 && len == 43U) {
|
||||
if (rpt != NULL) {
|
||||
unsigned short srcId = (buffer[5U] << 8) | buffer[6U];
|
||||
unsigned short dstId = (buffer[7U] << 8) | buffer[8U];
|
||||
bool grp = (buffer[9U] & 0x01U) == 0x01U;
|
||||
|
||||
if (icomEnabled && icomTGEnable != 0U && grp && dstId == icomTGEnable) {
|
||||
if (m_icomNetwork == NULL) {
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Icom Network link enabled by %s at %s", callsign.c_str(), current->m_callsign.c_str());
|
||||
bool ok = openIcomNetwork();
|
||||
if (!ok)
|
||||
LogWarning("Unable to open the Icom Network link");
|
||||
}
|
||||
}
|
||||
|
||||
if (kenwoodEnabled && kenwoodTGEnable != 0U && grp && dstId == kenwoodTGEnable) {
|
||||
if (m_kenwoodNetwork == NULL) {
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Kenwood Network link enabled by %s at %s", callsign.c_str(), current->m_callsign.c_str());
|
||||
bool ok = openKenwoodNetwork();
|
||||
if (!ok)
|
||||
LogWarning("Unable to open the Kenwood Network link");
|
||||
}
|
||||
}
|
||||
|
||||
if (icomEnabled && icomTGDisable != 0U && grp && dstId == icomTGDisable) {
|
||||
if (m_icomNetwork != NULL) {
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Icom Network link disabled by %s at %s", callsign.c_str(), current->m_callsign.c_str());
|
||||
closeIcomNetwork();
|
||||
}
|
||||
}
|
||||
|
||||
if (kenwoodEnabled && kenwoodTGDisable != 0U && grp && dstId == kenwoodTGDisable) {
|
||||
if (m_kenwoodNetwork != NULL) {
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Kenwood Network link disabled by %s at %s", callsign.c_str(), current->m_callsign.c_str());
|
||||
closeKenwoodNetwork();
|
||||
}
|
||||
}
|
||||
|
||||
if (grp && dstId == tg) {
|
||||
rpt->m_timer.start();
|
||||
|
||||
if (current == NULL && active == ACTIVE_NONE) {
|
||||
current = rpt;
|
||||
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Transmission from %s at %s to %s%u", callsign.c_str(), current->m_callsign.c_str(), grp ? "TG " : "", dstId);
|
||||
|
||||
active = ACTIVE_NXDN;
|
||||
}
|
||||
|
||||
if (active == ACTIVE_NXDN) {
|
||||
watchdogTimer.start();
|
||||
|
||||
for (std::vector<CNXDNRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
|
||||
if (!CUDPSocket::match((*it)->m_addr, address))
|
||||
nxdnNetwork.write(buffer, len, (*it)->m_addr, (*it)->m_addrLen);
|
||||
}
|
||||
|
||||
if (m_icomNetwork != NULL)
|
||||
m_icomNetwork->write(buffer, len);
|
||||
|
||||
if (m_kenwoodNetwork != NULL)
|
||||
m_kenwoodNetwork->write(buffer, len);
|
||||
|
||||
if ((buffer[9U] & 0x08U) == 0x08U) {
|
||||
LogMessage("Received end of transmission");
|
||||
current = NULL;
|
||||
active = ACTIVE_NONE;
|
||||
watchdogTimer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LogMessage("Data received from an unknown source");
|
||||
CUtils::dump(2U, "Data", buffer, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_icomNetwork != NULL) {
|
||||
len = m_icomNetwork->read(buffer);
|
||||
if (len > 0U) {
|
||||
if (current == NULL) {
|
||||
if (active == ACTIVE_NONE) {
|
||||
if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && buffer[5U] == 0x01U) {
|
||||
bool tempGrp = (buffer[7U] & 0x20U) == 0x20U;
|
||||
unsigned short tempSrcId = (buffer[8U] << 8) | buffer[9U];
|
||||
unsigned short tempDstId = (buffer[10U] << 8) | buffer[11U];
|
||||
|
||||
if (tempGrp && tempDstId == tg) {
|
||||
// Save the grp, src and dest for use in the NXDN Protocol messages
|
||||
grp = tempGrp;
|
||||
srcId = tempSrcId;
|
||||
dstId = tempDstId;
|
||||
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Transmission from %s on Icom Network to %s%u", callsign.c_str(), grp ? "TG " : "", dstId);
|
||||
|
||||
active = ACTIVE_ICOM;
|
||||
}
|
||||
}
|
||||
if ((buffer[0U] & 0xF0U) == 0x90U && buffer[2U] == 0x09U) {
|
||||
bool tempGrp = (buffer[4U] & 0x20U) == 0x20U;
|
||||
unsigned short tempSrcId = (buffer[5U] << 8) | buffer[6U];
|
||||
unsigned short tempDstId = (buffer[7U] << 8) | buffer[8U];
|
||||
|
||||
if (tempGrp && tempDstId == tg) {
|
||||
// Save the grp, src and dest for use in the NXDN Protocol messages
|
||||
grp = tempGrp;
|
||||
srcId = tempSrcId;
|
||||
dstId = tempDstId;
|
||||
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Transmission from %s on Icom Network to %s%u", callsign.c_str(), grp ? "TG " : "", dstId);
|
||||
|
||||
active = ACTIVE_ICOM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active == ACTIVE_ICOM) {
|
||||
watchdogTimer.start();
|
||||
|
||||
for (std::vector<CNXDNRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it)
|
||||
nxdnNetwork.write(buffer, len, srcId, dstId, grp, (*it)->m_addr, (*it)->m_addrLen);
|
||||
|
||||
if (m_kenwoodNetwork != NULL)
|
||||
m_kenwoodNetwork->write(buffer, len);
|
||||
|
||||
if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && buffer[5U] == 0x08U) {
|
||||
LogMessage("Received end of transmission");
|
||||
active = ACTIVE_NONE;
|
||||
watchdogTimer.stop();
|
||||
}
|
||||
if ((buffer[0U] & 0xF0U) == 0x90U && buffer[2U] == 0x08U) {
|
||||
LogMessage("Received end of transmission");
|
||||
active = ACTIVE_NONE;
|
||||
watchdogTimer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_kenwoodNetwork != NULL) {
|
||||
len = m_kenwoodNetwork->read(buffer);
|
||||
if (len > 0U) {
|
||||
if (current == NULL) {
|
||||
if (active == ACTIVE_NONE) {
|
||||
if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && buffer[5U] == 0x01U) {
|
||||
bool tempGrp = (buffer[7U] & 0x20U) == 0x20U;
|
||||
unsigned short tempSrcId = (buffer[8U] << 8) | buffer[9U];
|
||||
unsigned short tempDstId = (buffer[10U] << 8) | buffer[11U];
|
||||
|
||||
if (tempGrp && tempDstId == tg) {
|
||||
// Save the grp, src and dest for use in the NXDN Protocol messages
|
||||
grp = tempGrp;
|
||||
srcId = tempSrcId;
|
||||
dstId = tempDstId;
|
||||
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Transmission from %s on Kenwood Network to %s%u", callsign.c_str(), grp ? "TG " : "", dstId);
|
||||
|
||||
active = ACTIVE_KENWOOD;
|
||||
}
|
||||
}
|
||||
if ((buffer[0U] & 0xF0U) == 0x90U && buffer[2U] == 0x09U) {
|
||||
bool tempGrp = (buffer[4U] & 0x20U) == 0x20U;
|
||||
unsigned short tempSrcId = (buffer[5U] << 8) | buffer[6U];
|
||||
unsigned short tempDstId = (buffer[7U] << 8) | buffer[8U];
|
||||
|
||||
if (tempGrp && tempDstId == tg) {
|
||||
// Save the grp, src and dest for use in the NXDN Protocol messages
|
||||
grp = tempGrp;
|
||||
srcId = tempSrcId;
|
||||
dstId = tempDstId;
|
||||
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Transmission from %s on Kenwood Network to %s%u", callsign.c_str(), grp ? "TG " : "", dstId);
|
||||
|
||||
active = ACTIVE_KENWOOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active == ACTIVE_KENWOOD) {
|
||||
watchdogTimer.start();
|
||||
|
||||
for (std::vector<CNXDNRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it)
|
||||
nxdnNetwork.write(buffer, len, srcId, dstId, grp, (*it)->m_addr, (*it)->m_addrLen);
|
||||
|
||||
if (m_icomNetwork != NULL)
|
||||
m_icomNetwork->write(buffer, len);
|
||||
|
||||
if ((buffer[0U] == 0x81U || buffer[0U] == 0x83U) && buffer[5U] == 0x08U) {
|
||||
LogMessage("Received end of transmission");
|
||||
active = ACTIVE_NONE;
|
||||
watchdogTimer.stop();
|
||||
}
|
||||
if ((buffer[0U] & 0xF0U) == 0x90U && buffer[2U] == 0x08U) {
|
||||
LogMessage("Received end of transmission");
|
||||
active = ACTIVE_NONE;
|
||||
watchdogTimer.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ms = stopWatch.elapsed();
|
||||
stopWatch.start();
|
||||
|
||||
// Remove any repeaters that haven't reported for a while
|
||||
for (std::vector<CNXDNRepeater*>::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it)
|
||||
(*it)->m_timer.clock(ms);
|
||||
|
||||
for (std::vector<CNXDNRepeater*>::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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
watchdogTimer.clock(ms);
|
||||
if (watchdogTimer.isRunning() && watchdogTimer.hasExpired()) {
|
||||
LogMessage("Network watchdog has expired");
|
||||
watchdogTimer.stop();
|
||||
current = NULL;
|
||||
active = ACTIVE_NONE;
|
||||
}
|
||||
|
||||
dumpTimer.clock(ms);
|
||||
if (dumpTimer.hasExpired()) {
|
||||
dumpRepeaters();
|
||||
dumpTimer.start();
|
||||
}
|
||||
|
||||
if (m_icomNetwork != NULL)
|
||||
m_icomNetwork->clock(ms);
|
||||
|
||||
if (m_kenwoodNetwork != NULL)
|
||||
m_kenwoodNetwork->clock(ms);
|
||||
|
||||
if (ms < 5U)
|
||||
CThread::sleep(5U);
|
||||
}
|
||||
|
||||
nxdnNetwork.close();
|
||||
|
||||
closeIcomNetwork();
|
||||
|
||||
closeKenwoodNetwork();
|
||||
|
||||
lookup->stop();
|
||||
|
||||
::LogFinalise();
|
||||
}
|
||||
|
||||
CNXDNRepeater* CNXDNReflector::findRepeater(const sockaddr_storage& addr) const
|
||||
{
|
||||
for (std::vector<CNXDNRepeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
|
||||
if (CUDPSocket::match(addr, (*it)->m_addr))
|
||||
return *it;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CNXDNReflector::dumpRepeaters() const
|
||||
{
|
||||
if (m_repeaters.size() == 0U) {
|
||||
LogMessage("No repeaters linked");
|
||||
return;
|
||||
}
|
||||
|
||||
LogMessage("Currently linked repeaters:");
|
||||
|
||||
for (std::vector<CNXDNRepeater*>::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());
|
||||
}
|
||||
}
|
||||
|
||||
bool CNXDNReflector::openIcomNetwork()
|
||||
{
|
||||
m_icomNetwork = new CIcomNetwork(m_conf.getIcomAddress(), m_conf.getIcomDebug());
|
||||
bool ret = m_icomNetwork->open();
|
||||
if (!ret) {
|
||||
delete m_icomNetwork;
|
||||
m_icomNetwork = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNXDNReflector::openKenwoodNetwork()
|
||||
{
|
||||
m_kenwoodNetwork = new CKenwoodNetwork(m_conf.getKenwoodAddress(), m_conf.getKenwoodDebug());
|
||||
bool ret = m_kenwoodNetwork->open();
|
||||
if (!ret) {
|
||||
delete m_kenwoodNetwork;
|
||||
m_kenwoodNetwork = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CNXDNReflector::closeIcomNetwork()
|
||||
{
|
||||
if (m_icomNetwork != NULL) {
|
||||
m_icomNetwork->close();
|
||||
delete m_icomNetwork;
|
||||
m_icomNetwork = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CNXDNReflector::closeKenwoodNetwork()
|
||||
{
|
||||
if (m_kenwoodNetwork != NULL) {
|
||||
m_kenwoodNetwork->close();
|
||||
delete m_kenwoodNetwork;
|
||||
m_kenwoodNetwork = NULL;
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2016,2018,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(NXDNReflector_H)
|
||||
#define NXDNReflector_H
|
||||
|
||||
#include "KenwoodNetwork.h"
|
||||
#include "IcomNetwork.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 <winsock.h>
|
||||
#endif
|
||||
|
||||
class CNXDNRepeater {
|
||||
public:
|
||||
CNXDNRepeater() :
|
||||
m_addr(),
|
||||
m_addrLen(0U),
|
||||
m_callsign(),
|
||||
m_timer(1000U, 120U)
|
||||
{
|
||||
}
|
||||
|
||||
sockaddr_storage m_addr;
|
||||
unsigned int m_addrLen;
|
||||
std::string m_callsign;
|
||||
CTimer m_timer;
|
||||
};
|
||||
|
||||
class CNXDNReflector
|
||||
{
|
||||
public:
|
||||
CNXDNReflector(const std::string& file);
|
||||
~CNXDNReflector();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
CConf m_conf;
|
||||
CIcomNetwork* m_icomNetwork;
|
||||
CKenwoodNetwork* m_kenwoodNetwork;
|
||||
std::vector<CNXDNRepeater*> m_repeaters;
|
||||
|
||||
CNXDNRepeater* findRepeater(const sockaddr_storage& addr) const;
|
||||
void dumpRepeaters() const;
|
||||
|
||||
bool openIcomNetwork();
|
||||
bool openKenwoodNetwork();
|
||||
void closeIcomNetwork();
|
||||
void closeKenwoodNetwork();
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,35 @@
|
||||
[General]
|
||||
TG=9999
|
||||
Daemon=1
|
||||
|
||||
[Id Lookup]
|
||||
Name=NXDN.csv
|
||||
Time=24
|
||||
|
||||
[Log]
|
||||
# Logging levels, 0=No logging
|
||||
DisplayLevel=1
|
||||
FileLevel=1
|
||||
FilePath=.
|
||||
FileRoot=NXDNReflector
|
||||
FileRotate=1
|
||||
|
||||
[Network]
|
||||
Port=41400
|
||||
Debug=0
|
||||
|
||||
# Please visit www.nxdninfo.com if you are planning to link to the Icom NXCore server in Florida.
|
||||
[Icom Network]
|
||||
Enabled=0
|
||||
Address=flicom.nxcore.org
|
||||
# TGEnable=1234
|
||||
# TGDisable=3456
|
||||
Debug=0
|
||||
|
||||
# Note that the Kenwood NXCore server in Florida is offline.
|
||||
[Kenwood Network]
|
||||
Enabled=0
|
||||
Address=flkenwood.nxcore.org
|
||||
# TGEnable=1234
|
||||
# TGDisable=3456
|
||||
Debug=0
|
@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
### BEGIN INIT INFO
|
||||
#
|
||||
# Provides: NXDNReflector
|
||||
# Required-Start: $all
|
||||
# Required-Stop:
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Example startscript NXDNReflector
|
||||
|
||||
#
|
||||
### END INIT INFO
|
||||
## Fill in name of program here.
|
||||
PROG="NXDNReflector"
|
||||
PROG_PATH="/usr/local/bin/"
|
||||
PROG_ARGS="/etc/NXDNReflector.ini"
|
||||
PIDFILE="/var/run/NXDNReflector.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
|
@ -0,0 +1,189 @@
|
||||
<?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>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Conf.h" />
|
||||
<ClInclude Include="IcomNetwork.h" />
|
||||
<ClInclude Include="KenwoodNetwork.h" />
|
||||
<ClInclude Include="NXDNCRC.h" />
|
||||
<ClInclude Include="NXDNLookup.h" />
|
||||
<ClInclude Include="Log.h" />
|
||||
<ClInclude Include="Mutex.h" />
|
||||
<ClInclude Include="NXDNNetwork.h" />
|
||||
<ClInclude Include="NXDNReflector.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" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Conf.cpp" />
|
||||
<ClCompile Include="IcomNetwork.cpp" />
|
||||
<ClCompile Include="KenwoodNetwork.cpp" />
|
||||
<ClCompile Include="NXDNCRC.cpp" />
|
||||
<ClCompile Include="NXDNLookup.cpp" />
|
||||
<ClCompile Include="Log.cpp" />
|
||||
<ClCompile Include="Mutex.cpp" />
|
||||
<ClCompile Include="NXDNNetwork.cpp" />
|
||||
<ClCompile Include="NXDNReflector.cpp" />
|
||||
<ClCompile Include="StopWatch.cpp" />
|
||||
<ClCompile Include="Thread.cpp" />
|
||||
<ClCompile Include="Timer.cpp" />
|
||||
<ClCompile Include="UDPSocket.cpp" />
|
||||
<ClCompile Include="Utils.cpp" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>NXDNReflector</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>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</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>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
@ -0,0 +1,104 @@
|
||||
<?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="Conf.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NXDNLookup.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Log.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Mutex.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NXDNReflector.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StopWatch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Thread.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="Version.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NXDNNetwork.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IcomNetwork.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="KenwoodNetwork.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NXDNCRC.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Conf.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="NXDNLookup.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Log.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Mutex.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="NXDNReflector.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StopWatch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Thread.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="NXDNNetwork.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IcomNetwork.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KenwoodNetwork.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="NXDNCRC.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,391 @@
|
||||
/*
|
||||
* 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 LogMessage(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__)
|
||||
#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 short 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 short 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 short 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 short 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 short 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 %hu", 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 %hu", m_port[index]);
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 short port = 0U);
|
||||
CUDPSocket(unsigned short 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 short 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 short port, sockaddr_storage& address, unsigned int& address_length);
|
||||
static int lookup(const std::string& hostName, unsigned short 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
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2015,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.
|
||||
*/
|
||||
|
||||
#if !defined(VERSION_H)
|
||||
#define VERSION_H
|
||||
|
||||
const char* VERSION = "20210912";
|
||||
|
||||
#endif
|
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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 "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_ID_LOOKUP,
|
||||
SECTION_LOG,
|
||||
SECTION_NETWORK
|
||||
};
|
||||
|
||||
CConf::CConf(const std::string& file) :
|
||||
m_file(file),
|
||||
m_daemon(false),
|
||||
m_lookupName(),
|
||||
m_lookupTime(0U),
|
||||
m_logDisplayLevel(0U),
|
||||
m_logFileLevel(0U),
|
||||
m_logFilePath(),
|
||||
m_logFileRoot(),
|
||||
m_logFileRotate(true),
|
||||
m_networkPort(0U),
|
||||
m_networkDebug(false)
|
||||
{
|
||||
}
|
||||
|
||||
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, "[Id Lookup]", 11U) == 0)
|
||||
section = SECTION_ID_LOOKUP;
|
||||
else if (::strncmp(buffer, "[Log]", 5U) == 0)
|
||||
section = SECTION_LOG;
|
||||
else if (::strncmp(buffer, "[Network]", 9U) == 0)
|
||||
section = SECTION_NETWORK;
|
||||
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_ID_LOOKUP) {
|
||||
if (::strcmp(key, "Name") == 0)
|
||||
m_lookupName = value;
|
||||
else if (::strcmp(key, "Time") == 0)
|
||||
m_lookupTime = (unsigned int)::atoi(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;
|
||||
}
|
||||
}
|
||||
|
||||
::fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConf::getDaemon() const
|
||||
{
|
||||
return m_daemon;
|
||||
}
|
||||
|
||||
std::string CConf::getLookupName() const
|
||||
{
|
||||
return m_lookupName;
|
||||
}
|
||||
|
||||
unsigned int CConf::getLookupTime() const
|
||||
{
|
||||
return m_lookupTime;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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(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 Id Lookup section
|
||||
std::string getLookupName() const;
|
||||
unsigned int getLookupTime() 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;
|
||||
|
||||
private:
|
||||
std::string m_file;
|
||||
bool m_daemon;
|
||||
|
||||
std::string m_lookupName;
|
||||
unsigned int m_lookupTime;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#include "DMRLookup.h"
|
||||
#include "Timer.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
|
||||
CDMRLookup::CDMRLookup(const std::string& filename, unsigned int reloadTime) :
|
||||
CThread(),
|
||||
m_filename(filename),
|
||||
m_reloadTime(reloadTime),
|
||||
m_table(),
|
||||
m_mutex(),
|
||||
m_stop(false)
|
||||
{
|
||||
}
|
||||
|
||||
CDMRLookup::~CDMRLookup()
|
||||
{
|
||||
}
|
||||
|
||||
bool CDMRLookup::read()
|
||||
{
|
||||
bool ret = load();
|
||||
|
||||
if (m_reloadTime > 0U)
|
||||
run();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CDMRLookup::entry()
|
||||
{
|
||||
LogInfo("Started the DMR Id lookup reload thread");
|
||||
|
||||
CTimer timer(1U, 3600U * m_reloadTime);
|
||||
timer.start();
|
||||
|
||||
while (!m_stop) {
|
||||
sleep(1000U);
|
||||
|
||||
timer.clock();
|
||||
if (timer.hasExpired()) {
|
||||
load();
|
||||
timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
LogInfo("Stopped the DMR Id lookup reload thread");
|
||||
}
|
||||
|
||||
void CDMRLookup::stop()
|
||||
{
|
||||
if (m_reloadTime == 0U) {
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
m_stop = true;
|
||||
|
||||
wait();
|
||||
}
|
||||
|
||||
std::string CDMRLookup::find(unsigned int id)
|
||||
{
|
||||
std::string callsign;
|
||||
|
||||
if (id == 0xFFFFFFU)
|
||||
return std::string("ALL");
|
||||
|
||||
m_mutex.lock();
|
||||
|
||||
try {
|
||||
callsign = m_table.at(id);
|
||||
} catch (...) {
|
||||
char text[10U];
|
||||
::sprintf(text, "%u", id);
|
||||
callsign = std::string(text);
|
||||
}
|
||||
|
||||
m_mutex.unlock();
|
||||
|
||||
return callsign;
|
||||
}
|
||||
|
||||
bool CDMRLookup::load()
|
||||
{
|
||||
FILE* fp = ::fopen(m_filename.c_str(), "rt");
|
||||
if (fp == NULL) {
|
||||
LogWarning("Cannot open the Id lookup file - %s", m_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_mutex.lock();
|
||||
|
||||
// Remove the old entries
|
||||
m_table.clear();
|
||||
|
||||
char buffer[100U];
|
||||
while (::fgets(buffer, 100U, fp) != NULL) {
|
||||
if (buffer[0U] == '#')
|
||||
continue;
|
||||
|
||||
char* p1 = ::strtok(buffer, " \t\r\n");
|
||||
char* p2 = ::strtok(NULL, " \t\r\n");
|
||||
|
||||
if (p1 != NULL && p2 != NULL) {
|
||||
unsigned int id = (unsigned int)::atoi(p1);
|
||||
for (char* p = p2; *p != 0x00U; p++)
|
||||
*p = ::toupper(*p);
|
||||
|
||||
m_table[id] = std::string(p2);
|
||||
}
|
||||
}
|
||||
|
||||
m_mutex.unlock();
|
||||
|
||||
::fclose(fp);
|
||||
|
||||
size_t size = m_table.size();
|
||||
if (size == 0U)
|
||||
return false;
|
||||
|
||||
LogInfo("Loaded %u Ids to the callsign lookup table", size);
|
||||
|
||||
return true;
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
#ifndef DMRLookup_H
|
||||
#define DMRLookup_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Mutex.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class CDMRLookup : public CThread {
|
||||
public:
|
||||
CDMRLookup(const std::string& filename, unsigned int reloadTime);
|
||||
virtual ~CDMRLookup();
|
||||
|
||||
bool read();
|
||||
|
||||
virtual void entry();
|
||||
|
||||
std::string find(unsigned int id);
|
||||
|
||||
void stop();
|
||||
|
||||
private:
|
||||
std::string m_filename;
|
||||
unsigned int m_reloadTime;
|
||||
std::unordered_map<unsigned int, std::string> m_table;
|
||||
CMutex m_mutex;
|
||||
bool m_stop;
|
||||
|
||||
bool load();
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,22 @@
|
||||
CC = cc
|
||||
CXX = c++
|
||||
CFLAGS = -g -O3 -Wall -DHAVE_LOG_H -DUDP_SOCKET_MAX=2 -std=c++0x -pthread
|
||||
LIBS = -lpthread
|
||||
LDFLAGS = -g
|
||||
|
||||
OBJECTS = Conf.o DMRLookup.o Log.o Mutex.o Network.o P25Reflector.o StopWatch.o Thread.o Timer.o UDPSocket.o Utils.o
|
||||
|
||||
all: P25Reflector
|
||||
|
||||
P25Reflector: $(OBJECTS)
|
||||
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o P25Reflector
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
install:
|
||||
install -m 755 P25Reflector /usr/local/bin/
|
||||
|
||||
clean:
|
||||
$(RM) P25Reflector *.o *.d *.bak *~
|
||||
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "Mutex.h"
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
CMutex::CMutex() :
|
||||
m_handle()
|
||||
{
|
||||
m_handle = ::CreateMutex(NULL, FALSE, NULL);
|
||||
}
|
||||
|
||||
CMutex::~CMutex()
|
||||
{
|
||||
::CloseHandle(m_handle);
|
||||
}
|
||||
|
||||
void CMutex::lock()
|
||||
{
|
||||
::WaitForSingleObject(m_handle, INFINITE);
|
||||
}
|
||||
|
||||
void CMutex::unlock()
|
||||
{
|
||||
::ReleaseMutex(m_handle);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
CMutex::CMutex() :
|
||||
m_mutex(PTHREAD_MUTEX_INITIALIZER)
|
||||
{
|
||||
}
|
||||
|
||||
CMutex::~CMutex()
|
||||
{
|
||||
}
|
||||
|
||||
void CMutex::lock()
|
||||
{
|
||||
::pthread_mutex_lock(&m_mutex);
|
||||
}
|
||||
|
||||
void CMutex::unlock()
|
||||
{
|
||||
::pthread_mutex_unlock(&m_mutex);
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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(MUTEX_H)
|
||||
#define MUTEX_H
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <WS2tcpip.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
class CMutex
|
||||
{
|
||||
public:
|
||||
CMutex();
|
||||
~CMutex();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
HANDLE m_handle;
|
||||
#else
|
||||
pthread_mutex_t m_mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 "Network.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
CNetwork::CNetwork(unsigned short port, bool debug) :
|
||||
m_socket(port),
|
||||
m_debug(debug)
|
||||
{
|
||||
}
|
||||
|
||||
CNetwork::~CNetwork()
|
||||
{
|
||||
}
|
||||
|
||||
bool CNetwork::open()
|
||||
{
|
||||
LogInfo("Opening P25 network connection");
|
||||
|
||||
return m_socket.open();
|
||||
}
|
||||
|
||||
bool CNetwork::writeData(const unsigned char* data, unsigned int length, const sockaddr_storage& addr, unsigned int addrLen)
|
||||
{
|
||||
assert(data != NULL);
|
||||
assert(length > 0U);
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "P25 Network Data Sent", data, length);
|
||||
|
||||
return m_socket.write(data, length, 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, "P25 Network Data Received", data, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void CNetwork::close()
|
||||
{
|
||||
m_socket.close();
|
||||
|
||||
LogInfo("Closing P25 network connection");
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 "UDPSocket.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class CNetwork {
|
||||
public:
|
||||
CNetwork(unsigned short port, bool debug);
|
||||
~CNetwork();
|
||||
|
||||
bool open();
|
||||
|
||||
bool writeData(const unsigned char* data, unsigned int length, const sockaddr_storage& addr, unsigned int addrLen);
|
||||
|
||||
unsigned int readData(unsigned char* data, unsigned int length, sockaddr_storage& addr, unsigned int& addrLen);
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
CUDPSocket m_socket;
|
||||
bool m_debug;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,363 @@
|
||||
/*
|
||||
* 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 "P25Reflector.h"
|
||||
#include "StopWatch.h"
|
||||
#include "DMRLookup.h"
|
||||
#include "Network.h"
|
||||
#include "Version.h"
|
||||
#include "Thread.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <WS2tcpip.h>
|
||||
#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 = "P25Reflector.ini";
|
||||
#else
|
||||
const char* DEFAULT_INI_FILE = "/etc/P25Reflector.ini";
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstdarg>
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
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, "P25Reflector version %s\n", VERSION);
|
||||
return 0;
|
||||
} else if (arg.substr(0, 1) == "-") {
|
||||
::fprintf(stderr, "Usage: P25Reflector [-v|--version] [filename]\n");
|
||||
return 1;
|
||||
} else {
|
||||
iniFile = argv[currentArg];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CP25Reflector* reflector = new CP25Reflector(std::string(iniFile));
|
||||
reflector->run();
|
||||
delete reflector;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CP25Reflector::CP25Reflector(const std::string& file) :
|
||||
m_conf(file),
|
||||
m_repeaters()
|
||||
{
|
||||
CUDPSocket::startup();
|
||||
}
|
||||
|
||||
CP25Reflector::~CP25Reflector()
|
||||
{
|
||||
CUDPSocket::shutdown();
|
||||
}
|
||||
|
||||
void CP25Reflector::run()
|
||||
{
|
||||
bool ret = m_conf.read();
|
||||
if (!ret) {
|
||||
::fprintf(stderr, "P25Reflector: 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, "P25Gateway: 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.getNetworkDebug());
|
||||
|
||||
ret = network.open();
|
||||
if (!ret) {
|
||||
::LogFinalise();
|
||||
return;
|
||||
}
|
||||
|
||||
CDMRLookup* lookup = new CDMRLookup(m_conf.getLookupName(), m_conf.getLookupTime());
|
||||
lookup->read();
|
||||
|
||||
CStopWatch stopWatch;
|
||||
stopWatch.start();
|
||||
|
||||
CTimer dumpTimer(1000U, 120U);
|
||||
dumpTimer.start();
|
||||
|
||||
LogMessage("Starting P25Reflector-%s", VERSION);
|
||||
|
||||
CP25Repeater* current = NULL;
|
||||
bool displayed = false;
|
||||
bool seen64 = false;
|
||||
bool seen65 = false;
|
||||
|
||||
CTimer watchdogTimer(1000U, 0U, 1500U);
|
||||
|
||||
unsigned char lcf = 0U;
|
||||
unsigned int srcId = 0U;
|
||||
unsigned int dstId = 0U;
|
||||
|
||||
for (;;) {
|
||||
unsigned char buffer[200U];
|
||||
sockaddr_storage addr;
|
||||
unsigned int addrLen;
|
||||
|
||||
unsigned int len = network.readData(buffer, 200U, addr, addrLen);
|
||||
if (len > 0U) {
|
||||
CP25Repeater* rpt = findRepeater(addr);
|
||||
|
||||
if (buffer[0U] == 0xF0U) {
|
||||
if (rpt == NULL) {
|
||||
rpt = new CP25Repeater;
|
||||
rpt->m_timer.start();
|
||||
::memcpy(&rpt->m_addr, &addr, sizeof(struct sockaddr_storage));
|
||||
rpt->m_addrLen = addrLen;
|
||||
rpt->m_callsign = std::string((char*)(buffer + 1U), 10U);
|
||||
m_repeaters.push_back(rpt);
|
||||
|
||||
char buff[80U];
|
||||
LogMessage("Adding %s (%s)", rpt->m_callsign.c_str(), CUDPSocket::display(addr, buff, 80U));
|
||||
} else {
|
||||
rpt->m_timer.start();
|
||||
}
|
||||
|
||||
// Return the poll
|
||||
network.writeData(buffer, len, addr, addrLen);
|
||||
} else if (buffer[0U] == 0xF1U && rpt != NULL) {
|
||||
char buff[80U];
|
||||
LogMessage("Removing %s (%s) unlinked", rpt->m_callsign.c_str(), CUDPSocket::display(addr, buff, 80U));
|
||||
|
||||
for (std::vector<CP25Repeater*>::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;
|
||||
}
|
||||
}
|
||||
} else if (rpt != NULL) {
|
||||
rpt->m_timer.start();
|
||||
|
||||
if (current == NULL) {
|
||||
current = rpt;
|
||||
displayed = false;
|
||||
seen64 = false;
|
||||
seen65 = false;
|
||||
LogMessage("Transmission started from %s", current->m_callsign.c_str());
|
||||
}
|
||||
|
||||
if (current == rpt) {
|
||||
watchdogTimer.start();
|
||||
|
||||
if (buffer[0U] == 0x64U && !seen64) {
|
||||
lcf = buffer[1U];
|
||||
seen64 = true;
|
||||
}
|
||||
|
||||
if (buffer[0U] == 0x65U && !seen65) {
|
||||
dstId = (buffer[1U] << 16) & 0xFF0000U;
|
||||
dstId |= (buffer[2U] << 8) & 0x00FF00U;
|
||||
dstId |= (buffer[3U] << 0) & 0x0000FFU;
|
||||
seen65 = true;
|
||||
}
|
||||
|
||||
if (buffer[0U] == 0x66U && seen64 && seen65 && !displayed) {
|
||||
srcId = (buffer[1U] << 16) & 0xFF0000U;
|
||||
srcId |= (buffer[2U] << 8) & 0x00FF00U;
|
||||
srcId |= (buffer[3U] << 0) & 0x0000FFU;
|
||||
displayed = true;
|
||||
|
||||
std::string callsign = lookup->find(srcId);
|
||||
LogMessage("Transmission from %s at %s to %s%u", callsign.c_str(), current->m_callsign.c_str(), lcf == 0x00U ? "TG " : "", dstId);
|
||||
}
|
||||
|
||||
for (std::vector<CP25Repeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
|
||||
if (!CUDPSocket::match(addr, (*it)->m_addr))
|
||||
network.writeData(buffer, len, (*it)->m_addr, (*it)->m_addrLen);
|
||||
}
|
||||
|
||||
if (buffer[0U] == 0x80U) {
|
||||
LogMessage("Received end of transmission");
|
||||
watchdogTimer.stop();
|
||||
current = NULL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LogMessage("Data received from an unknown source");
|
||||
CUtils::dump(2U, "Data", buffer, len);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ms = stopWatch.elapsed();
|
||||
stopWatch.start();
|
||||
|
||||
// Remove any repeaters that haven't reported for a while
|
||||
for (std::vector<CP25Repeater*>::iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it)
|
||||
(*it)->m_timer.clock(ms);
|
||||
|
||||
for (std::vector<CP25Repeater*>::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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
watchdogTimer.clock(ms);
|
||||
if (watchdogTimer.isRunning() && watchdogTimer.hasExpired()) {
|
||||
LogMessage("Network watchdog has expired");
|
||||
watchdogTimer.stop();
|
||||
current = NULL;
|
||||
}
|
||||
|
||||
dumpTimer.clock(ms);
|
||||
if (dumpTimer.hasExpired()) {
|
||||
dumpRepeaters();
|
||||
dumpTimer.start();
|
||||
}
|
||||
|
||||
if (ms < 5U)
|
||||
CThread::sleep(5U);
|
||||
}
|
||||
|
||||
network.close();
|
||||
|
||||
lookup->stop();
|
||||
|
||||
::LogFinalise();
|
||||
}
|
||||
|
||||
CP25Repeater* CP25Reflector::findRepeater(const sockaddr_storage& addr) const
|
||||
{
|
||||
for (std::vector<CP25Repeater*>::const_iterator it = m_repeaters.begin(); it != m_repeaters.end(); ++it) {
|
||||
if (CUDPSocket::match(addr, (*it)->m_addr))
|
||||
return *it;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CP25Reflector::dumpRepeaters() const
|
||||
{
|
||||
if (m_repeaters.size() == 0U) {
|
||||
LogMessage("No repeaters linked");
|
||||
return;
|
||||
}
|
||||
|
||||
LogMessage("Currently linked repeaters:");
|
||||
|
||||
for (std::vector<CP25Repeater*>::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());
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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(P25Reflector_H)
|
||||
#define P25Reflector_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 CP25Repeater {
|
||||
public:
|
||||
CP25Repeater() :
|
||||
m_addr(),
|
||||
m_addrLen(0U),
|
||||
m_callsign(),
|
||||
m_timer(1000U, 120U)
|
||||
{
|
||||
}
|
||||
|
||||
sockaddr_storage m_addr;
|
||||
unsigned int m_addrLen;
|
||||
std::string m_callsign;
|
||||
CTimer m_timer;
|
||||
};
|
||||
|
||||
class CP25Reflector
|
||||
{
|
||||
public:
|
||||
CP25Reflector(const std::string& file);
|
||||
~CP25Reflector();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
CConf m_conf;
|
||||
std::vector<CP25Repeater*> m_repeaters;
|
||||
|
||||
CP25Repeater* findRepeater(const sockaddr_storage& addr) const;
|
||||
void dumpRepeaters() const;
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,18 @@
|
||||
[General]
|
||||
Daemon=1
|
||||
|
||||
[Id Lookup]
|
||||
Name=DMRIds.dat
|
||||
Time=24
|
||||
|
||||
[Log]
|
||||
# Logging levels, 0=No logging
|
||||
DisplayLevel=1
|
||||
FileLevel=1
|
||||
FilePath=.
|
||||
FileRoot=P25Reflector
|
||||
FileRotate=1
|
||||
|
||||
[Network]
|
||||
Port=41000
|
||||
Debug=0
|
@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
### BEGIN INIT INFO
|
||||
#
|
||||
# Provides: P25Reflector
|
||||
# Required-Start: $all
|
||||
# Required-Stop:
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Example startscript P25Reflector
|
||||
|
||||
#
|
||||
### END INIT INFO
|
||||
## Fill in name of program here.
|
||||
PROG="P25Reflector"
|
||||
PROG_PATH="/usr/local/bin/"
|
||||
PROG_ARGS="/etc/P25Reflector.ini"
|
||||
PIDFILE="/var/run/P25Reflector.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
|
@ -0,0 +1,183 @@
|
||||
<?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>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Conf.h" />
|
||||
<ClInclude Include="DMRLookup.h" />
|
||||
<ClInclude Include="Log.h" />
|
||||
<ClInclude Include="Mutex.h" />
|
||||
<ClInclude Include="Network.h" />
|
||||
<ClInclude Include="P25Reflector.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" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Conf.cpp" />
|
||||
<ClCompile Include="DMRLookup.cpp" />
|
||||
<ClCompile Include="Log.cpp" />
|
||||
<ClCompile Include="Mutex.cpp" />
|
||||
<ClCompile Include="Network.cpp" />
|
||||
<ClCompile Include="P25Reflector.cpp" />
|
||||
<ClCompile Include="StopWatch.cpp" />
|
||||
<ClCompile Include="Thread.cpp" />
|
||||
<ClCompile Include="Timer.cpp" />
|
||||
<ClCompile Include="UDPSocket.cpp" />
|
||||
<ClCompile Include="Utils.cpp" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{C68ABEB3-5CDD-4B26-8D66-77FE81EC6BB5}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>P25Reflector</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>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</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>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>HAVE_LOG_H;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
@ -0,0 +1,86 @@
|
||||
<?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="Conf.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DMRLookup.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Log.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Mutex.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Network.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="P25Reflector.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StopWatch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Thread.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="Version.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Conf.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DMRLookup.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Log.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Mutex.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Network.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="P25Reflector.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StopWatch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Thread.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>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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
|
||||
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 <WS2tcpip.h>
|
||||
#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
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,391 @@
|
||||
/*
|
||||
* Copyright (C) 2006-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 "UDPSocket.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LOG_H)
|
||||
#include "Log.h"
|
||||
#else
|
||||
#define LogMessage(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__)
|
||||
#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 short 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 short 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 short 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 short 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 short 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 %hu", 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 %hu", m_port[index]);
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 short port = 0U);
|
||||
CUDPSocket(unsigned short 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 short 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 short port, sockaddr_storage& address, unsigned int& address_length);
|
||||
static int lookup(const std::string& hostName, unsigned short 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
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 = "20210912";
|
||||
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
#DVREFlectors
|
||||
This repo contains the reflectors originally from G4KLX's NXDNClients, P25Clients, and YSFClients repositories.
|
||||
|
||||
The NXDN and P25 reflecotrs are used as a single talkgroup. The NXDN reflector also includes the option to link it to NXCore to allow for interchange of audio between the two. At the NXCore end, it should be set up to receive the traffic from only one talk group.
|
||||
|
||||
They build on 32-bit and 64-bit Linux as well as on Windows using Visual Studio 2017 on x86 and x64.
|
||||
|
||||
This software is licenced under the GPL v2 and is primarily intended for amateur and educational use.
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,35 @@
|
||||
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
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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");
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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
|
||||
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,390 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,375 @@
|
||||
/*
|
||||
* 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());
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,25 @@
|
||||
[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
|
@ -0,0 +1,76 @@
|
||||
#!/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
|
@ -0,0 +1,83 @@
|
||||
<?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>
|
@ -0,0 +1,29 @@
|
||||
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
|
@ -0,0 +1,25 @@
|
||||
# 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
|
||||
```
|
@ -0,0 +1,18 @@
|
||||
#!/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…
Reference in new issue