mirror of
https://github.com/LX3JL/xlxd.git
synced 2026-04-21 06:13:43 +00:00
change declaration size_t to unsigned int
This commit is contained in:
parent
15fa8e4a97
commit
9a710d823c
26 changed files with 8923 additions and 0 deletions
30
Makefile
Normal file
30
Makefile
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
CXX=g++
|
||||
CXXFLAGS=-Os -Wall -std=c++11 -pthread
|
||||
LDFLAGS=-Os -Wall -std=c++11 -pthread
|
||||
SOURCES=$(wildcard *.cpp)
|
||||
OBJECTS=$(SOURCES:.cpp=.o)
|
||||
EXECUTABLE=xlxd
|
||||
|
||||
all: $(SOURCES) $(EXECUTABLE)
|
||||
|
||||
$(EXECUTABLE): $(OBJECTS)
|
||||
$(CXX) $(LDFLAGS) $(OBJECTS) -o $@
|
||||
|
||||
.cpp.o:
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
$(RM) $(EXECUTABLE) *.o *.d *.bak
|
||||
|
||||
install:
|
||||
mkdir -p /xlxd
|
||||
cp -f $(EXECUTABLE) /xlxd/
|
||||
[ -f /xlxd/xlxd.blacklist ] && \
|
||||
cp ../config/xlxd.blacklist /xlxd/xlxd.blacklist.sample || \
|
||||
cp ../config/xlxd.blacklist /xlxd/xlxd.blacklist
|
||||
[ -f /xlxd/xlxd.whitelist ] && \
|
||||
cp ../config/xlxd.whitelist /xlxd/xlxd.whitelist.sample || \
|
||||
cp ../config/xlxd.whitelist /xlxd/xlxd.whitelist
|
||||
[ -f /xlxd/xlxd.interlink ] && \
|
||||
cp ../config/xlxd.interlink /xlxd/xlxd.interlink.sample || \
|
||||
cp ../config/xlxd.interlink /xlxd/xlxd.interlink
|
||||
91
cbmpeer.cpp
Normal file
91
cbmpeer.cpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// cbmpeer.cpp
|
||||
// xlxd
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include "creflector.h"
|
||||
#include "cbmpeer.h"
|
||||
#include "cbmclient.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
|
||||
CBmPeer::CBmPeer()
|
||||
{
|
||||
}
|
||||
|
||||
CBmPeer::CBmPeer(const CCallsign &callsign, const CIp &ip, char *modules, const CVersion &version)
|
||||
: CPeer(callsign, ip, modules, version)
|
||||
{
|
||||
std::cout << "Adding BM peer" << std::endl;
|
||||
|
||||
// and construct all xlx clients
|
||||
for ( unsigned int i = 0; i < ::strlen(modules); i++ )
|
||||
{
|
||||
// create
|
||||
CBmClient *client = new CBmClient(callsign, ip, modules[i]);
|
||||
// and append to vector
|
||||
m_Clients.push_back(client);
|
||||
}
|
||||
}
|
||||
|
||||
CBmPeer::CBmPeer(const CBmPeer &peer)
|
||||
: CPeer(peer)
|
||||
{
|
||||
for ( unsigned int i = 0; i < peer.m_Clients.size(); i++ )
|
||||
{
|
||||
CBmClient *client = new CBmClient((const CBmClient &)*(peer.m_Clients[i]));
|
||||
// grow vector capacity if needed
|
||||
if ( m_Clients.capacity() == m_Clients.size() )
|
||||
{
|
||||
m_Clients.reserve(m_Clients.capacity()+10);
|
||||
}
|
||||
// and append
|
||||
m_Clients.push_back(client);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// destructors
|
||||
|
||||
CBmPeer::~CBmPeer()
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// status
|
||||
|
||||
bool CBmPeer::IsAlive(void) const
|
||||
{
|
||||
return (m_LastKeepaliveTime.DurationSinceNow() < XLX_KEEPALIVE_TIMEOUT);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// revision helper
|
||||
|
||||
int CBmPeer::GetProtocolRevision(const CVersion &version)
|
||||
{
|
||||
return XLX_PROTOCOL_REVISION_2;
|
||||
}
|
||||
|
||||
201
cbuffer.cpp
Normal file
201
cbuffer.cpp
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
//
|
||||
// cbuffer.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 02/11/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include "cprotocol.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
CBuffer::CBuffer(uint8 *buffer, int len)
|
||||
{
|
||||
resize(len);
|
||||
::memcpy(data(), buffer, len);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// set
|
||||
|
||||
void CBuffer::Set(uint8 *buffer, int len)
|
||||
{
|
||||
resize(len);
|
||||
::memcpy(data(), buffer, len);
|
||||
}
|
||||
|
||||
void CBuffer::Set(const char *sz)
|
||||
{
|
||||
resize(::strlen(sz)+1);
|
||||
::strcpy((char *)data(), sz);
|
||||
}
|
||||
|
||||
void CBuffer::Append(uint8 *buffer, int len)
|
||||
{
|
||||
int n = (int)size();
|
||||
resize(n+len);
|
||||
::memcpy(&(data()[n]), buffer, len);
|
||||
}
|
||||
|
||||
void CBuffer::Append(uint8 ui, int len)
|
||||
{
|
||||
int n = (int)size();
|
||||
resize(n+len);
|
||||
::memset(&(data()[n]), ui, len);
|
||||
}
|
||||
|
||||
void CBuffer::Append(uint8 ui)
|
||||
{
|
||||
int n = (int)size();
|
||||
resize(n+sizeof(uint8));
|
||||
::memcpy(&(data()[n]), &ui, sizeof(uint8));
|
||||
}
|
||||
|
||||
void CBuffer::Append(uint16 ui)
|
||||
{
|
||||
int n = (int)size();
|
||||
resize(n+sizeof(uint16));
|
||||
::memcpy(&(data()[n]), &ui, sizeof(uint16));
|
||||
}
|
||||
|
||||
void CBuffer::Append(uint32 ui)
|
||||
{
|
||||
int n = (int)size();
|
||||
resize(n+sizeof(uint32));
|
||||
::memcpy(&(data()[n]), &ui, sizeof(uint32));
|
||||
}
|
||||
|
||||
void CBuffer::Append(const char *sz)
|
||||
{
|
||||
Append((uint8 *)sz, (int)strlen(sz));
|
||||
Append((uint8)0x00);
|
||||
}
|
||||
|
||||
void CBuffer::ReplaceAt(int i, uint8 ui)
|
||||
{
|
||||
if ( size() < (i+sizeof(uint8)) )
|
||||
{
|
||||
resize(i+sizeof(uint8));
|
||||
}
|
||||
*(uint8 *)(&(data()[i])) = ui;
|
||||
}
|
||||
|
||||
void CBuffer::ReplaceAt(int i, uint16 ui)
|
||||
{
|
||||
if ( size() < (i+sizeof(uint16)) )
|
||||
{
|
||||
resize(i+sizeof(uint16));
|
||||
}
|
||||
*(uint16 *)(&(data()[i])) = ui;
|
||||
}
|
||||
|
||||
void CBuffer::ReplaceAt(int i, uint32 ui)
|
||||
{
|
||||
if ( size() < (i+sizeof(uint32)) )
|
||||
{
|
||||
resize(i+sizeof(uint32));
|
||||
}
|
||||
*(uint32 *)(&(data()[i])) = ui;
|
||||
}
|
||||
|
||||
void CBuffer::ReplaceAt(int i, const uint8 *ptr, int len)
|
||||
{
|
||||
if ( size() < (unsigned int)(i+len) )
|
||||
{
|
||||
resize(i+len);
|
||||
}
|
||||
::memcpy(&(data()[i]), ptr, len);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operation
|
||||
|
||||
int CBuffer::Compare(uint8 *buffer, int len) const
|
||||
{
|
||||
int result = -1;
|
||||
if ( size() >= (unsigned int)len )
|
||||
{
|
||||
result = ::memcmp(data(), buffer, len);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int CBuffer::Compare(uint8 *buffer, int off, int len) const
|
||||
{
|
||||
int result = -1;
|
||||
if ( size() >= (unsigned int)(off+len) )
|
||||
{
|
||||
result = ::memcmp(&(data()[off]), buffer, len);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operator
|
||||
|
||||
bool CBuffer::operator ==(const CBuffer &Buffer) const
|
||||
{
|
||||
if ( size() == Buffer.size() )
|
||||
{
|
||||
return (::memcmp((const char *)data(), (const char *)Buffer.data(), size()) == 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBuffer::operator ==(const char *sz) const
|
||||
{
|
||||
if ( size() == ::strlen(sz) )
|
||||
{
|
||||
return (::memcmp((const char *)data(), sz, size()) == 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CBuffer::operator const char *() const
|
||||
{
|
||||
return (const char *)data();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// debug
|
||||
|
||||
void CBuffer::DebugDump(std::ofstream &debugout) const
|
||||
{
|
||||
for ( unsigned int i = 0; i < size(); i++ )
|
||||
{
|
||||
char sz[16];
|
||||
//sprintf(sz, "%02X", data()[i]);
|
||||
sprintf(sz, "0x%02X", data()[i]);
|
||||
debugout << sz;
|
||||
if ( i == size()-1 )
|
||||
{
|
||||
debugout << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
debugout << ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
367
ccallsign.cpp
Normal file
367
ccallsign.cpp
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
//
|
||||
// ccallsign.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include <cctype>
|
||||
#include "cdmriddirfile.h"
|
||||
#include "cdmriddirhttp.h"
|
||||
#include "ccallsign.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructors
|
||||
|
||||
CCallsign::CCallsign()
|
||||
{
|
||||
// blank all
|
||||
::memset(m_Callsign, ' ', sizeof(m_Callsign));
|
||||
::memset(m_Suffix, ' ', sizeof(m_Suffix));
|
||||
m_Module = ' ';
|
||||
m_uiDmrid = 0;
|
||||
}
|
||||
|
||||
CCallsign::CCallsign(const char *sz, uint32 dmrid)
|
||||
{
|
||||
// blank all
|
||||
::memset(m_Callsign, ' ', sizeof(m_Callsign));
|
||||
::memset(m_Suffix, ' ', sizeof(m_Suffix));
|
||||
m_Module = ' ';
|
||||
m_uiDmrid = dmrid;
|
||||
|
||||
// and populate
|
||||
if ( ::strlen(sz) > 0 )
|
||||
{
|
||||
// callsign valid
|
||||
::memcpy(m_Callsign, sz, MIN(strlen(sz), sizeof(m_Callsign)-1));
|
||||
if ( strlen(sz) >= sizeof(m_Callsign) )
|
||||
{
|
||||
m_Module = sz[sizeof(m_Callsign)-1];
|
||||
}
|
||||
// dmrid ok ?
|
||||
if ( m_uiDmrid == 0 )
|
||||
{
|
||||
g_DmridDir.Lock();
|
||||
{
|
||||
m_uiDmrid = g_DmridDir.FindDmrid(*this);
|
||||
}
|
||||
g_DmridDir.Unlock();
|
||||
}
|
||||
}
|
||||
else if ( m_uiDmrid != 0 )
|
||||
{
|
||||
g_DmridDir.Lock();
|
||||
{
|
||||
const CCallsign *callsign = g_DmridDir.FindCallsign(m_uiDmrid);
|
||||
if ( callsign != NULL )
|
||||
{
|
||||
::memcpy(m_Callsign, callsign->m_Callsign, sizeof(m_Callsign));
|
||||
}
|
||||
}
|
||||
g_DmridDir.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
CCallsign::CCallsign(const CCallsign &callsign)
|
||||
{
|
||||
::memcpy(m_Callsign, callsign.m_Callsign, sizeof(m_Callsign));
|
||||
::memcpy(m_Suffix, callsign.m_Suffix, sizeof(m_Suffix));
|
||||
m_Module = callsign.m_Module;
|
||||
m_uiDmrid = callsign.m_uiDmrid;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// status
|
||||
|
||||
bool CCallsign::IsValid(void) const
|
||||
{
|
||||
bool valid = true;
|
||||
int i;
|
||||
|
||||
// callsign
|
||||
// first 3 chars are letter or number but cannot be all number
|
||||
int iNum = 0;
|
||||
for ( i = 0; i < 3; i++ )
|
||||
{
|
||||
valid &= IsLetter(m_Callsign[i]) || IsNumber(m_Callsign[i]);
|
||||
if ( IsNumber(m_Callsign[i]) )
|
||||
{
|
||||
iNum++;
|
||||
}
|
||||
}
|
||||
valid &= (iNum < 3);
|
||||
// all remaining char are letter, number or space
|
||||
for ( ; i < CALLSIGN_LEN; i++)
|
||||
{
|
||||
valid &= IsLetter(m_Callsign[i]) || IsNumber(m_Callsign[i]) || IsSpace(m_Callsign[i]);
|
||||
}
|
||||
|
||||
// prefix
|
||||
// all chars are number, uppercase or space
|
||||
for ( i = 0; i < CALLSUFFIX_LEN; i++ )
|
||||
{
|
||||
valid &= IsLetter(m_Suffix[i]) || IsNumber(m_Suffix[i]) || IsSpace(m_Suffix[i]);
|
||||
}
|
||||
|
||||
// module
|
||||
// is an letter or space
|
||||
valid &= IsLetter(m_Module) || IsSpace(m_Module);
|
||||
|
||||
// dmrid is not tested, as it can be NULL
|
||||
// if station does is not dmr registered
|
||||
|
||||
// done
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CCallsign::HasSuffix(void) const
|
||||
{
|
||||
bool has = false;
|
||||
for ( unsigned int i = 0; i < CALLSUFFIX_LEN; i++ )
|
||||
{
|
||||
has |= (m_Suffix[i] != ' ');
|
||||
}
|
||||
return has;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// set
|
||||
|
||||
void CCallsign::SetCallsign(const char *sz, bool UpdateDmrid)
|
||||
{
|
||||
// set callsign
|
||||
::memset(m_Callsign, ' ', sizeof(m_Callsign));
|
||||
m_Module = ' ';
|
||||
::memcpy(m_Callsign, sz, MIN(strlen(sz), sizeof(m_Callsign)-1));
|
||||
if ( strlen(sz) >= sizeof(m_Callsign) )
|
||||
{
|
||||
m_Module = sz[sizeof(m_Callsign)-1];
|
||||
}
|
||||
// and update dmrid
|
||||
if ( UpdateDmrid )
|
||||
{
|
||||
g_DmridDir.Lock();
|
||||
{
|
||||
m_uiDmrid = g_DmridDir.FindDmrid(*this);
|
||||
}
|
||||
g_DmridDir.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CCallsign::SetCallsign(const uint8 *buffer, int len, bool UpdateDmrid)
|
||||
{
|
||||
// set callsign
|
||||
::memset(m_Callsign, ' ', sizeof(m_Callsign));
|
||||
m_Module = ' ';
|
||||
::memcpy(m_Callsign, buffer, MIN(len, sizeof(m_Callsign)-1));
|
||||
for ( unsigned int i = 0; i < sizeof(m_Callsign); i++ )
|
||||
{
|
||||
if ( m_Callsign[i] == 0 )
|
||||
{
|
||||
m_Callsign[i] = ' ';
|
||||
}
|
||||
}
|
||||
if ( ((unsigned int)len >= sizeof(m_Callsign)) && ((char)buffer[sizeof(m_Callsign)-1] != 0) )
|
||||
{
|
||||
m_Module = (char)buffer[sizeof(m_Callsign)-1];
|
||||
}
|
||||
if ( UpdateDmrid )
|
||||
{
|
||||
g_DmridDir.Lock();
|
||||
{
|
||||
m_uiDmrid = g_DmridDir.FindDmrid(*this);
|
||||
}
|
||||
g_DmridDir.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CCallsign::SetDmrid(uint32 dmrid, bool UpdateCallsign)
|
||||
{
|
||||
m_uiDmrid = dmrid;
|
||||
if ( UpdateCallsign )
|
||||
{
|
||||
g_DmridDir.Lock();
|
||||
{
|
||||
const CCallsign *callsign = g_DmridDir.FindCallsign(dmrid);
|
||||
if ( callsign != NULL )
|
||||
{
|
||||
::memcpy(m_Callsign, callsign->m_Callsign, sizeof(m_Callsign));
|
||||
}
|
||||
}
|
||||
g_DmridDir.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CCallsign::SetDmrid(const uint8 *buffer, bool UpdateCallsign)
|
||||
{
|
||||
char sz[9];
|
||||
::memcpy(sz, buffer, 8);
|
||||
sz[8] = 0;
|
||||
SetDmrid((uint32)::strtol(sz, NULL, 16), UpdateCallsign);
|
||||
}
|
||||
|
||||
void CCallsign::SetModule(char c)
|
||||
{
|
||||
m_Module = c;
|
||||
}
|
||||
|
||||
|
||||
void CCallsign::SetSuffix(const char *sz)
|
||||
{
|
||||
::memset(m_Suffix, ' ', sizeof(m_Suffix));
|
||||
::memcpy(m_Suffix, sz, MIN(strlen(sz), sizeof(m_Suffix)));
|
||||
}
|
||||
|
||||
void CCallsign::SetSuffix(const uint8 *buffer, int len)
|
||||
{
|
||||
len = MIN(len, sizeof(m_Suffix));
|
||||
::memset(m_Suffix, ' ', sizeof(m_Suffix));
|
||||
::memcpy(m_Suffix, buffer, len);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// modify
|
||||
|
||||
void CCallsign::PatchCallsign(int off, const uint8 *patch, int len)
|
||||
{
|
||||
if ( (unsigned int)off < sizeof(m_Callsign) )
|
||||
{
|
||||
::memcpy(m_Callsign, patch, MIN(len, sizeof(m_Callsign) - off));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// get
|
||||
|
||||
void CCallsign::GetCallsign(uint8 *buffer) const
|
||||
{
|
||||
::memcpy(buffer, m_Callsign, sizeof(m_Callsign));
|
||||
if ( HasModule() )
|
||||
{
|
||||
buffer[sizeof(m_Callsign)-1] = m_Module;
|
||||
}
|
||||
}
|
||||
|
||||
void CCallsign::GetCallsignString(char *sz) const
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; ((unsigned int)i < sizeof(m_Callsign)) && (m_Callsign[i] != ' '); i++ )
|
||||
{
|
||||
sz[i] = m_Callsign[i];
|
||||
}
|
||||
sz[i] = 0;
|
||||
}
|
||||
|
||||
void CCallsign::GetSuffix(uint8 *buffer) const
|
||||
{
|
||||
::memcpy(buffer, m_Suffix, sizeof(m_Suffix));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// compare
|
||||
|
||||
bool CCallsign::HasSameCallsign(const CCallsign &Callsign) const
|
||||
{
|
||||
return (::memcmp(m_Callsign, Callsign.m_Callsign, sizeof(m_Callsign)) == 0);
|
||||
}
|
||||
|
||||
bool CCallsign::HasSameCallsignWithWildcard(const CCallsign &callsign) const
|
||||
{
|
||||
bool same = true;
|
||||
bool done = false;
|
||||
|
||||
for ( unsigned int i = 0; (i < sizeof(m_Callsign)) && same && !done; i++ )
|
||||
{
|
||||
if ( !(done = ((m_Callsign[i] == '*') || (callsign[i] == '*'))) )
|
||||
{
|
||||
same &= (m_Callsign[i] == callsign[i]);
|
||||
}
|
||||
}
|
||||
return same;
|
||||
}
|
||||
|
||||
bool CCallsign::HasLowerCallsign(const CCallsign &Callsign) const
|
||||
{
|
||||
return (::memcmp(m_Callsign, Callsign.m_Callsign, sizeof(m_Callsign)) < 0);
|
||||
}
|
||||
|
||||
bool CCallsign::HasSameModule(const CCallsign &Callsign) const
|
||||
{
|
||||
return (m_Module == Callsign.m_Module);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operators
|
||||
|
||||
bool CCallsign::operator ==(const CCallsign &callsign) const
|
||||
{
|
||||
return ((::memcmp(callsign.m_Callsign, m_Callsign, sizeof(m_Callsign)) == 0) &&
|
||||
(m_Module == callsign.m_Module) &&
|
||||
(::memcmp(callsign.m_Suffix, m_Suffix, sizeof(m_Suffix)) == 0) &&
|
||||
(m_uiDmrid == callsign.m_uiDmrid) );
|
||||
}
|
||||
|
||||
CCallsign::operator const char *() const
|
||||
{
|
||||
char *sz = (char *)(const char *)m_sz;
|
||||
|
||||
// empty
|
||||
::memset(sz, 0, sizeof(m_sz));
|
||||
// callsign
|
||||
sz[CALLSIGN_LEN] = 0;
|
||||
::memcpy(sz, m_Callsign, sizeof(m_Callsign));
|
||||
// module
|
||||
if ( HasModule() )
|
||||
{
|
||||
sz[sizeof(m_Callsign)] = m_Module;
|
||||
}
|
||||
// suffix
|
||||
if ( HasSuffix() )
|
||||
{
|
||||
::strcat(sz, " / ");
|
||||
::strncat(sz, m_Suffix, sizeof(m_Suffix));
|
||||
}
|
||||
|
||||
// done
|
||||
return m_sz;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// helper
|
||||
|
||||
bool CCallsign::IsNumber(char c) const
|
||||
{
|
||||
return ((c >= '0') && (c <= '9'));
|
||||
}
|
||||
|
||||
bool CCallsign::IsLetter(char c) const
|
||||
{
|
||||
return ((c >= 'A') && (c <= 'Z'));
|
||||
}
|
||||
|
||||
bool CCallsign::IsSpace(char c) const
|
||||
{
|
||||
return (c == ' ');
|
||||
}
|
||||
244
ccallsignlist.cpp
Normal file
244
ccallsignlist.cpp
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
//
|
||||
// ccallsignlist.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 30/12/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include "main.h"
|
||||
#include "ccallsignlist.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
CCallsignList::CCallsignList()
|
||||
{
|
||||
m_Filename = NULL;
|
||||
::memset(&m_LastModTime, 0, sizeof(time_t));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// file io
|
||||
|
||||
bool CCallsignList::LoadFromFile(const char *filename)
|
||||
{
|
||||
bool ok = false;
|
||||
char sz[256];
|
||||
char szStar[2] = "*";
|
||||
|
||||
// and load
|
||||
std::ifstream file (filename);
|
||||
if ( file.is_open() )
|
||||
{
|
||||
Lock();
|
||||
|
||||
// empty list
|
||||
clear();
|
||||
// fill with file content
|
||||
while ( file.getline(sz, sizeof(sz)).good() )
|
||||
{
|
||||
// remove leading & trailing spaces
|
||||
char *szt = TrimWhiteSpaces(sz);
|
||||
|
||||
// crack it
|
||||
if ( (::strlen(szt) > 0) && (szt[0] != '#') )
|
||||
{
|
||||
// 1st token is callsign
|
||||
if ( (szt = ::strtok(szt, " ,\t")) != NULL )
|
||||
{
|
||||
CCallsign callsign(szt);
|
||||
// 2nd token is modules list
|
||||
szt = ::strtok(NULL, " ,\t");
|
||||
// if token absent, use wildcard
|
||||
if ( szt == NULL )
|
||||
{
|
||||
szt = szStar;
|
||||
}
|
||||
// and add to list
|
||||
push_back(CCallsignListItem(callsign, CIp(), szt));
|
||||
}
|
||||
}
|
||||
}
|
||||
// close file
|
||||
file.close();
|
||||
|
||||
// keep file path
|
||||
m_Filename = filename;
|
||||
|
||||
// update time
|
||||
GetLastModTime(&m_LastModTime);
|
||||
|
||||
// and done
|
||||
Unlock();
|
||||
ok = true;
|
||||
std::cout << "Gatekeeper loaded " << size() << " lines from " << filename << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Gatekeeper cannot find " << filename << std::endl;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool CCallsignList::ReloadFromFile(void)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
if ( m_Filename != NULL )
|
||||
{
|
||||
ok = LoadFromFile(m_Filename);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool CCallsignList::NeedReload(void)
|
||||
{
|
||||
bool needReload = false;
|
||||
|
||||
time_t time;
|
||||
if ( GetLastModTime(&time) )
|
||||
{
|
||||
needReload = time != m_LastModTime;
|
||||
}
|
||||
return needReload;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// compare
|
||||
|
||||
bool CCallsignList::IsCallsignListedWithWildcard(const CCallsign &callsign) const
|
||||
{
|
||||
bool listed = false;
|
||||
|
||||
for ( unsigned int i = 0; (i < size()) && !listed; i++ )
|
||||
{
|
||||
listed = (data()[i]).HasSameCallsignWithWildcard(callsign);
|
||||
}
|
||||
|
||||
return listed;
|
||||
}
|
||||
|
||||
bool CCallsignList::IsCallsignListedWithWildcard(const CCallsign &callsign, char module) const
|
||||
{
|
||||
bool listed = false;
|
||||
|
||||
for ( unsigned int i = 0; (i < size()) && !listed; i++ )
|
||||
{
|
||||
const CCallsignListItem *item = &(data()[i]);
|
||||
listed = (item->HasSameCallsignWithWildcard(callsign) &&
|
||||
((module == ' ') || item->HasModuleListed(module)) );
|
||||
|
||||
}
|
||||
|
||||
return listed;
|
||||
}
|
||||
|
||||
bool CCallsignList::IsCallsignListed(const CCallsign &callsign, char module) const
|
||||
{
|
||||
bool listed = false;
|
||||
|
||||
for ( unsigned int i = 0; (i < size()) && !listed; i++ )
|
||||
{
|
||||
const CCallsignListItem *item = &(data()[i]);
|
||||
listed = (item->HasSameCallsign(callsign) && item->HasModuleListed(module));
|
||||
|
||||
}
|
||||
|
||||
return listed;
|
||||
}
|
||||
|
||||
bool CCallsignList::IsCallsignListed(const CCallsign &callsign, char *modules) const
|
||||
{
|
||||
bool listed = false;
|
||||
|
||||
for ( unsigned int i = 0; (i < size()) && !listed; i++ )
|
||||
{
|
||||
const CCallsignListItem *item = &(data()[i]);
|
||||
listed = (item->HasSameCallsign(callsign) && item->CheckListedModules(modules));
|
||||
|
||||
}
|
||||
|
||||
return listed;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// find
|
||||
|
||||
CCallsignListItem *CCallsignList::FindListItem(const CCallsign &Callsign)
|
||||
{
|
||||
CCallsignListItem *item = NULL;
|
||||
|
||||
// find client
|
||||
for ( unsigned int i = 0; (i < size()) && (item == NULL); i++ )
|
||||
{
|
||||
if ( (data()[i]).GetCallsign().HasSameCallsign(Callsign) )
|
||||
{
|
||||
item = &(data()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return item;
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// helpers
|
||||
|
||||
char *CCallsignList::TrimWhiteSpaces(char *str)
|
||||
{
|
||||
char *end;
|
||||
|
||||
// Trim leading space & tabs
|
||||
while((*str == ' ') || (*str == '\t')) str++;
|
||||
|
||||
// All spaces?
|
||||
if(*str == 0)
|
||||
return str;
|
||||
|
||||
// Trim trailing space, tab or lf
|
||||
end = str + ::strlen(str) - 1;
|
||||
while((end > str) && ((*end == ' ') || (*end == '\t') || (*end == '\r'))) end--;
|
||||
|
||||
// Write new null terminator
|
||||
*(end+1) = 0;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
bool CCallsignList::GetLastModTime(time_t *time)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
if ( m_Filename != NULL )
|
||||
{
|
||||
struct stat fileStat;
|
||||
if( ::stat(m_Filename, &fileStat) != -1 )
|
||||
{
|
||||
*time = fileStat.st_mtime;
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
147
ccallsignlistitem.cpp
Normal file
147
ccallsignlistitem.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
//
|
||||
// ccallsignlistitem.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 31/01/2016.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include <string.h>
|
||||
#include "main.h"
|
||||
#include "ccallsignlistitem.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
CCallsignListItem::CCallsignListItem()
|
||||
{
|
||||
::memset(m_Modules, 0, sizeof(m_Modules));
|
||||
::memset(m_szUrl, 0, sizeof(m_szUrl));
|
||||
}
|
||||
|
||||
CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const CIp &ip, const char *modules)
|
||||
{
|
||||
m_Callsign = callsign;
|
||||
::memset(m_szUrl, 0, sizeof(m_szUrl));
|
||||
m_Ip = ip;
|
||||
if ( modules != NULL )
|
||||
{
|
||||
::memset(m_Modules, 0, sizeof(m_Modules));
|
||||
if ( modules[0] == '*' )
|
||||
{
|
||||
for ( unsigned int i = 0; i < NB_OF_MODULES; i++ )
|
||||
{
|
||||
m_Modules[i] = 'A' + i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int n = MIN((int)::strlen(modules), sizeof(m_Modules)-1);
|
||||
int j = 0;
|
||||
for ( int i = 0; i < n; i++ )
|
||||
{
|
||||
if ( (modules[i] - 'A') < NB_OF_MODULES )
|
||||
{
|
||||
m_Modules[j++] = modules[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const char *url, const char *modules)
|
||||
{
|
||||
m_Callsign = callsign;
|
||||
::strncpy(m_szUrl, url, URL_MAXLEN);
|
||||
m_Ip = CIp(m_szUrl);
|
||||
if ( modules != NULL )
|
||||
{
|
||||
::memset(m_Modules, 0, sizeof(m_Modules));
|
||||
if ( modules[0] == '*' )
|
||||
{
|
||||
for ( unsigned int i = 0; i < NB_OF_MODULES; i++ )
|
||||
{
|
||||
m_Modules[i] = 'A' + i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int n = MIN((int)::strlen(modules), sizeof(m_Modules)-1);
|
||||
int j = 0;
|
||||
for ( int i = 0; i < n; i++ )
|
||||
{
|
||||
if ( (modules[i] - 'A') < NB_OF_MODULES )
|
||||
{
|
||||
m_Modules[j++] = modules[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CCallsignListItem::CCallsignListItem(const CCallsignListItem &item)
|
||||
{
|
||||
m_Callsign = item.m_Callsign;
|
||||
::memcpy(m_szUrl, item.m_szUrl, sizeof(m_szUrl));
|
||||
m_Ip = item.m_Ip;
|
||||
::memcpy(m_Modules, item.m_Modules, sizeof(m_Modules));
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// compare
|
||||
|
||||
bool CCallsignListItem::HasSameCallsign(const CCallsign &callsign) const
|
||||
{
|
||||
return m_Callsign.HasSameCallsign(callsign);
|
||||
}
|
||||
|
||||
bool CCallsignListItem::HasSameCallsignWithWildcard(const CCallsign &callsign) const
|
||||
{
|
||||
return m_Callsign.HasSameCallsignWithWildcard(callsign);
|
||||
}
|
||||
|
||||
bool CCallsignListItem::HasModuleListed(char module) const
|
||||
{
|
||||
return (::strchr(m_Modules, (int)module) != NULL);
|
||||
}
|
||||
|
||||
bool CCallsignListItem::CheckListedModules(char *Modules) const
|
||||
{
|
||||
bool listed = false;
|
||||
|
||||
if ( Modules != NULL )
|
||||
{
|
||||
// build a list of common modules
|
||||
char list[NB_MODULES_MAX+1];
|
||||
list[0] = 0;
|
||||
//
|
||||
for ( unsigned int i = 0; i < ::strlen(Modules); i++ )
|
||||
{
|
||||
if ( HasModuleListed(Modules[i]) )
|
||||
{
|
||||
::strncat(list, &(Modules[i]), 1);
|
||||
listed = true;
|
||||
}
|
||||
}
|
||||
::strcpy(Modules, list);
|
||||
}
|
||||
return listed;
|
||||
}
|
||||
|
||||
323
cclients.cpp
Normal file
323
cclients.cpp
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
//
|
||||
// cclients.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include "creflector.h"
|
||||
#include "cclients.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
|
||||
CClients::CClients()
|
||||
{
|
||||
m_Clients.reserve(100);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// destructors
|
||||
|
||||
CClients::~CClients()
|
||||
{
|
||||
m_Mutex.lock();
|
||||
{
|
||||
for ( unsigned int i = 0; i < m_Clients.size(); i++ )
|
||||
{
|
||||
delete m_Clients[i];
|
||||
}
|
||||
m_Clients.clear();
|
||||
|
||||
}
|
||||
m_Mutex.unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// manage Clients
|
||||
|
||||
void CClients::AddClient(CClient *client)
|
||||
{
|
||||
// first check if client already exists
|
||||
bool found = false;
|
||||
for ( unsigned int i = 0; (i < m_Clients.size()) && !found; i++ )
|
||||
{
|
||||
found = (*client == *m_Clients[i]);
|
||||
// if found, just do nothing
|
||||
// so *client keep pointing on a valid object
|
||||
// on function return
|
||||
if ( found )
|
||||
{
|
||||
// delete new one
|
||||
delete client;
|
||||
//std::cout << "Adding existing client " << client->GetCallsign() << " at " << client->GetIp() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// if not, append to the vector
|
||||
if ( !found )
|
||||
{
|
||||
// grow vector capacity if needed
|
||||
if ( m_Clients.capacity() == m_Clients.size() )
|
||||
{
|
||||
m_Clients.reserve(m_Clients.capacity()+10);
|
||||
}
|
||||
// and append
|
||||
m_Clients.push_back(client);
|
||||
std::cout << "New client " << client->GetCallsign() << " at " << client->GetIp()
|
||||
<< " added with protocol " << client->GetProtocolName();
|
||||
if ( client->GetReflectorModule() != ' ' )
|
||||
{
|
||||
std::cout << " on module " << client->GetReflectorModule();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
// notify
|
||||
g_Reflector.OnClientsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void CClients::RemoveClient(CClient *client)
|
||||
{
|
||||
// look for the client
|
||||
bool found = false;
|
||||
for ( unsigned int i = 0; (i < m_Clients.size()) && !found; i++ )
|
||||
{
|
||||
// compare objetc pointers
|
||||
if ( (m_Clients[i]) == client )
|
||||
{
|
||||
// found it !
|
||||
if ( !m_Clients[i]->IsAMaster() )
|
||||
{
|
||||
// remove it
|
||||
std::cout << "Client " << m_Clients[i]->GetCallsign() << " at " << m_Clients[i]->GetIp()
|
||||
<< " removed with protocol " << client->GetProtocolName();
|
||||
if ( client->GetReflectorModule() != ' ' )
|
||||
{
|
||||
std::cout << " on module " << client->GetReflectorModule();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
delete m_Clients[i];
|
||||
m_Clients.erase(m_Clients.begin()+i);
|
||||
found = true;
|
||||
// notify
|
||||
g_Reflector.OnClientsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CClient *CClients::GetClient(int i)
|
||||
{
|
||||
if ( (i >= 0) && ((unsigned int)i < m_Clients.size()) )
|
||||
{
|
||||
return m_Clients[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool CClients::IsClient(CClient *client) const
|
||||
{
|
||||
bool found = false;
|
||||
for ( unsigned int i = 0; (i < m_Clients.size()) && !found; i++ )
|
||||
{
|
||||
found = (m_Clients[i] == client);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// find Clients
|
||||
|
||||
CClient *CClients::FindClient(const CIp &Ip)
|
||||
{
|
||||
CClient *client = NULL;
|
||||
|
||||
// find client
|
||||
for ( unsigned int i = 0; (i < m_Clients.size()) && (client == NULL); i++ )
|
||||
{
|
||||
if ( m_Clients[i]->GetIp() == Ip )
|
||||
{
|
||||
client = m_Clients[i];
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return client;
|
||||
}
|
||||
|
||||
CClient *CClients::FindClient(const CIp &Ip, int Protocol)
|
||||
{
|
||||
CClient *client = NULL;
|
||||
|
||||
// find client
|
||||
for ( unsigned int i = 0; (i < m_Clients.size()) && (client == NULL); i++ )
|
||||
{
|
||||
if ( (m_Clients[i]->GetIp() == Ip) && (m_Clients[i]->GetProtocol() == Protocol))
|
||||
{
|
||||
client = m_Clients[i];
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return client;
|
||||
}
|
||||
|
||||
CClient *CClients::FindClient(const CIp &Ip, int Protocol, char ReflectorModule)
|
||||
{
|
||||
CClient *client = NULL;
|
||||
|
||||
// find client
|
||||
for ( unsigned int i = 0; (i < m_Clients.size()) && (client == NULL); i++ )
|
||||
{
|
||||
if ( (m_Clients[i]->GetIp() == Ip) &&
|
||||
(m_Clients[i]->GetReflectorModule() == ReflectorModule) &&
|
||||
(m_Clients[i]->GetProtocol() == Protocol) )
|
||||
{
|
||||
client = m_Clients[i];
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return client;
|
||||
}
|
||||
|
||||
CClient *CClients::FindClient(const CCallsign &Callsign, const CIp &Ip, int Protocol)
|
||||
{
|
||||
CClient *client = NULL;
|
||||
|
||||
// find client
|
||||
for ( unsigned int i = 0; (i < m_Clients.size()) && (client == NULL); i++ )
|
||||
{
|
||||
if ( m_Clients[i]->GetCallsign().HasSameCallsign(Callsign) &&
|
||||
(m_Clients[i]->GetIp() == Ip) &&
|
||||
(m_Clients[i]->GetProtocol() == Protocol) )
|
||||
{
|
||||
client = m_Clients[i];
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return client;
|
||||
}
|
||||
|
||||
CClient *CClients::FindClient(const CCallsign &Callsign, char module, const CIp &Ip, int Protocol)
|
||||
{
|
||||
CClient *client = NULL;
|
||||
|
||||
// find client
|
||||
for ( unsigned int i = 0; (i < m_Clients.size()) && (client == NULL); i++ )
|
||||
{
|
||||
if ( m_Clients[i]->GetCallsign().HasSameCallsign(Callsign) &&
|
||||
(m_Clients[i]->GetModule() == module) &&
|
||||
(m_Clients[i]->GetIp() == Ip) &&
|
||||
(m_Clients[i]->GetProtocol() == Protocol) )
|
||||
{
|
||||
client = m_Clients[i];
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return client;
|
||||
}
|
||||
|
||||
CClient *CClients::FindClient(const CCallsign &Callsign, int Protocol)
|
||||
{
|
||||
CClient *client = NULL;
|
||||
|
||||
// find client
|
||||
for ( unsigned int i = 0; (i < m_Clients.size()) && (client == NULL); i++ )
|
||||
{
|
||||
if ( (m_Clients[i]->GetProtocol() == Protocol) &&
|
||||
m_Clients[i]->GetCallsign().HasSameCallsign(Callsign) )
|
||||
{
|
||||
client = m_Clients[i];
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return client;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// iterate on clients
|
||||
|
||||
CClient *CClients::FindNextClient(int Protocol, int *index)
|
||||
{
|
||||
CClient *client = NULL;
|
||||
|
||||
// find next client
|
||||
bool found = false;
|
||||
for ( unsigned int i = *index+1; (i < m_Clients.size()) && !found; i++ )
|
||||
{
|
||||
if ( m_Clients[i]->GetProtocol() == Protocol )
|
||||
{
|
||||
found = true;
|
||||
client = m_Clients[i];
|
||||
*index = i;
|
||||
}
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
CClient *CClients::FindNextClient(const CIp &Ip, int Protocol, int *index)
|
||||
{
|
||||
CClient *client = NULL;
|
||||
|
||||
// find next client
|
||||
bool found = false;
|
||||
for ( unsigned int i = *index+1; (i < m_Clients.size()) && !found; i++ )
|
||||
{
|
||||
if ( (m_Clients[i]->GetProtocol() == Protocol) &&
|
||||
(m_Clients[i]->GetIp() == Ip) )
|
||||
{
|
||||
found = true;
|
||||
client = m_Clients[i];
|
||||
*index = i;
|
||||
}
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
CClient *CClients::FindNextClient(const CCallsign &Callsign, const CIp &Ip, int Protocol, int *index)
|
||||
{
|
||||
CClient *client = NULL;
|
||||
|
||||
// find next client
|
||||
bool found = false;
|
||||
for ( unsigned int i = *index+1; (i < m_Clients.size()) && !found; i++ )
|
||||
{
|
||||
if ( (m_Clients[i]->GetProtocol() == Protocol) &&
|
||||
(m_Clients[i]->GetIp() == Ip) &&
|
||||
m_Clients[i]->GetCallsign().HasSameCallsign(Callsign) )
|
||||
{
|
||||
found = true;
|
||||
client = m_Clients[i];
|
||||
*index = i;
|
||||
}
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
264
ccodecstream.cpp
Normal file
264
ccodecstream.cpp
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
//
|
||||
// ccodecstream.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include "ccodecstream.h"
|
||||
#include "cdvframepacket.h"
|
||||
#include "creflector.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// define
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
CCodecStream::CCodecStream(CPacketStream *PacketStream, uint16 uiId, uint8 uiCodecIn, uint8 uiCodecOut)
|
||||
{
|
||||
m_bStopThread = false;
|
||||
m_pThread = NULL;
|
||||
m_uiStreamId = uiId;
|
||||
m_uiPid = 0;
|
||||
m_uiCodecIn = uiCodecIn;
|
||||
m_uiCodecOut = uiCodecOut;
|
||||
m_bConnected = false;
|
||||
m_fPingMin = -1;
|
||||
m_fPingMax = -1;
|
||||
m_fPingSum = 0;
|
||||
m_fPingCount = 0;
|
||||
m_uiTotalPackets = 0;
|
||||
m_uiTimeoutPackets = 0;
|
||||
m_PacketStream = PacketStream;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// destructor
|
||||
|
||||
CCodecStream::~CCodecStream()
|
||||
{
|
||||
// close socket
|
||||
m_Socket.Close();
|
||||
|
||||
// kill threads
|
||||
m_bStopThread = true;
|
||||
if ( m_pThread != NULL )
|
||||
{
|
||||
m_pThread->join();
|
||||
delete m_pThread;
|
||||
}
|
||||
|
||||
// empty local queue
|
||||
while ( !m_LocalQueue.empty() )
|
||||
{
|
||||
delete m_LocalQueue.front();
|
||||
m_LocalQueue.pop();
|
||||
}
|
||||
// empty ourselves
|
||||
while ( !empty() )
|
||||
{
|
||||
delete front();
|
||||
pop();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// initialization
|
||||
|
||||
bool CCodecStream::Init(uint16 uiPort)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
// reset stop flag
|
||||
m_bStopThread = false;
|
||||
|
||||
// create server's IP
|
||||
m_Ip = g_Reflector.GetTranscoderIp();
|
||||
m_uiPort = uiPort;
|
||||
|
||||
// create our socket
|
||||
ok = m_Socket.Open(uiPort);
|
||||
if ( ok )
|
||||
{
|
||||
// start thread;
|
||||
m_pThread = new std::thread(CCodecStream::Thread, this);
|
||||
m_bConnected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error opening socket on port UDP" << uiPort << " on ip " << g_Reflector.GetListenIp() << std::endl;
|
||||
m_bConnected = false;
|
||||
}
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
void CCodecStream::Close(void)
|
||||
{
|
||||
// close socket
|
||||
m_bConnected = false;
|
||||
m_Socket.Close();
|
||||
|
||||
// kill threads
|
||||
m_bStopThread = true;
|
||||
if ( m_pThread != NULL )
|
||||
{
|
||||
m_pThread->join();
|
||||
delete m_pThread;
|
||||
m_pThread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// get
|
||||
|
||||
bool CCodecStream::IsEmpty(void) const
|
||||
{
|
||||
return (m_LocalQueue.empty() && m_PacketStream->empty());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// thread
|
||||
|
||||
void CCodecStream::Thread(CCodecStream *This)
|
||||
{
|
||||
while ( !This->m_bStopThread )
|
||||
{
|
||||
This->Task();
|
||||
}
|
||||
}
|
||||
|
||||
void CCodecStream::Task(void)
|
||||
{
|
||||
CBuffer Buffer;
|
||||
CIp Ip;
|
||||
uint8 Ambe[AMBE_SIZE];
|
||||
uint8 DStarSync[] = { 0x55,0x2D,0x16 };
|
||||
|
||||
// any packet from transcoder
|
||||
if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
|
||||
{
|
||||
// crack
|
||||
if ( IsValidAmbePacket(Buffer, Ambe) )
|
||||
{
|
||||
// tickle
|
||||
m_TimeoutTimer.Now();
|
||||
|
||||
// update statistics
|
||||
double ping = m_StatsTimer.DurationSinceNow();
|
||||
if ( m_fPingMin == -1 )
|
||||
{
|
||||
m_fPingMin = ping;
|
||||
m_fPingMax = ping;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fPingMin = MIN(m_fPingMin, ping);
|
||||
m_fPingMax = MAX(m_fPingMax, ping);
|
||||
|
||||
}
|
||||
m_fPingSum += ping;
|
||||
m_fPingCount += 1;
|
||||
|
||||
// pop the original packet
|
||||
if ( !m_LocalQueue.empty() )
|
||||
{
|
||||
CDvFramePacket *Packet = (CDvFramePacket *)m_LocalQueue.front();
|
||||
m_LocalQueue.pop();
|
||||
// todo: check the PID
|
||||
// update content with transcoded ambe
|
||||
Packet->SetAmbe(m_uiCodecOut, Ambe);
|
||||
// tag syncs in DvData
|
||||
if ( (m_uiCodecOut == CODEC_AMBEPLUS) && (Packet->GetPacketId() % 21) == 0 )
|
||||
{
|
||||
Packet->SetDvData(DStarSync);
|
||||
}
|
||||
// and push it back to client
|
||||
m_PacketStream->Lock();
|
||||
m_PacketStream->push(Packet);
|
||||
m_PacketStream->Unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Unexpected transcoded packet received from ambed" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// anything in our queue
|
||||
while ( !empty() )
|
||||
{
|
||||
// yes, pop it from queue
|
||||
CPacket *Packet = front();
|
||||
pop();
|
||||
|
||||
// yes, send to ambed
|
||||
// this assume that thread pushing the Packet
|
||||
// have verified that the CodecStream is connected
|
||||
// and that the packet needs transcoding
|
||||
m_StatsTimer.Now();
|
||||
m_uiTotalPackets++;
|
||||
EncodeAmbePacket(&Buffer, ((CDvFramePacket *)Packet)->GetAmbe(m_uiCodecIn));
|
||||
m_Socket.Send(Buffer, m_Ip, m_uiPort);
|
||||
|
||||
// and push to our local queue
|
||||
m_LocalQueue.push(Packet);
|
||||
}
|
||||
|
||||
// handle timeout
|
||||
if ( !m_LocalQueue.empty() && (m_TimeoutTimer.DurationSinceNow() >= (TRANSCODER_AMBEPACKET_TIMEOUT/1000.0f)) )
|
||||
{
|
||||
//std::cout << "ambed packet timeout" << std::endl;
|
||||
m_uiTimeoutPackets++;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// packet decoding helpers
|
||||
|
||||
bool CCodecStream::IsValidAmbePacket(const CBuffer &Buffer, uint8 *Ambe)
|
||||
{
|
||||
bool valid = false;
|
||||
|
||||
if ( (Buffer.size() == 11) && (Buffer.data()[0] == m_uiCodecOut) )
|
||||
{
|
||||
::memcpy(Ambe, &(Buffer.data()[2]), 9);
|
||||
valid = true;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// packet encoding helpers
|
||||
|
||||
void CCodecStream::EncodeAmbePacket(CBuffer *Buffer, const uint8 *Ambe)
|
||||
{
|
||||
Buffer->clear();
|
||||
Buffer->Append(m_uiCodecIn);
|
||||
Buffer->Append(m_uiPid);
|
||||
Buffer->Append((uint8 *)Ambe, 9);
|
||||
}
|
||||
559
cdcsprotocol.cpp
Normal file
559
cdcsprotocol.cpp
Normal file
|
|
@ -0,0 +1,559 @@
|
|||
//
|
||||
// cdcsprotocol.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 07/11/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include "cdcsclient.h"
|
||||
#include "cdcsprotocol.h"
|
||||
#include "creflector.h"
|
||||
#include "cgatekeeper.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operation
|
||||
|
||||
bool CDcsProtocol::Init(void)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
// base class
|
||||
ok = CProtocol::Init();
|
||||
|
||||
// update the reflector callsign
|
||||
m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"DCS", 3);
|
||||
|
||||
// create our socket
|
||||
ok &= m_Socket.Open(DCS_PORT);
|
||||
if ( !ok )
|
||||
{
|
||||
std::cout << "Error opening socket on port UDP" << DCS_PORT << " on ip " << g_Reflector.GetListenIp() << std::endl;
|
||||
}
|
||||
|
||||
// update time
|
||||
m_LastKeepaliveTime.Now();
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// task
|
||||
|
||||
void CDcsProtocol::Task(void)
|
||||
{
|
||||
CBuffer Buffer;
|
||||
CIp Ip;
|
||||
CCallsign Callsign;
|
||||
char ToLinkModule;
|
||||
CDvHeaderPacket *Header;
|
||||
CDvFramePacket *Frame;
|
||||
|
||||
// handle incoming packets
|
||||
if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
|
||||
{
|
||||
// crack the packet
|
||||
if ( IsValidDvPacket(Buffer, &Header, &Frame) )
|
||||
{
|
||||
//std::cout << "DCS DV packet" << std::endl;
|
||||
|
||||
// callsign muted?
|
||||
if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DCS, Header->GetRpt2Module()) )
|
||||
{
|
||||
// handle it
|
||||
OnDvHeaderPacketIn(Header, Ip);
|
||||
|
||||
if ( !Frame->IsLastPacket() )
|
||||
{
|
||||
//std::cout << "DCS DV frame" << std::endl;
|
||||
OnDvFramePacketIn(Frame, &Ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
//std::cout << "DCS DV last frame" << std::endl;
|
||||
OnDvLastFramePacketIn((CDvLastFramePacket *)Frame, &Ip);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete Header;
|
||||
delete Frame;
|
||||
}
|
||||
}
|
||||
else if ( IsValidConnectPacket(Buffer, &Callsign, &ToLinkModule) )
|
||||
{
|
||||
std::cout << "DCS connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// callsign authorized?
|
||||
if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DCS) && g_Reflector.IsValidModule(ToLinkModule) )
|
||||
{
|
||||
// valid module ?
|
||||
if ( g_Reflector.IsValidModule(ToLinkModule) )
|
||||
{
|
||||
// acknowledge the request
|
||||
EncodeConnectAckPacket(Callsign, ToLinkModule, &Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
|
||||
// create the client
|
||||
CDcsClient *client = new CDcsClient(Callsign, Ip, ToLinkModule);
|
||||
|
||||
// and append
|
||||
g_Reflector.GetClients()->AddClient(client);
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "DCS node " << Callsign << " connect attempt on non-existing module" << std::endl;
|
||||
|
||||
// deny the request
|
||||
EncodeConnectNackPacket(Callsign, ToLinkModule, &Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// deny the request
|
||||
EncodeConnectNackPacket(Callsign, ToLinkModule, &Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
|
||||
}
|
||||
else if ( IsValidDisconnectPacket(Buffer, &Callsign) )
|
||||
{
|
||||
std::cout << "DCS disconnect packet from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// find client
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
CClient *client = clients->FindClient(Ip, PROTOCOL_DCS);
|
||||
if ( client != NULL )
|
||||
{
|
||||
// remove it
|
||||
clients->RemoveClient(client);
|
||||
// and acknowledge the disconnect
|
||||
EncodeConnectNackPacket(Callsign, ' ', &Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else if ( IsValidKeepAlivePacket(Buffer, &Callsign) )
|
||||
{
|
||||
//std::cout << "DCS keepalive packet from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// find all clients with that callsign & ip and keep them alive
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(Callsign, Ip, PROTOCOL_DCS, &index)) != NULL )
|
||||
{
|
||||
client->Alive();
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else if ( IsIgnorePacket(Buffer) )
|
||||
{
|
||||
// valid but ignore packet
|
||||
//std::cout << "DCS ignored packet from " << Ip << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// invalid packet
|
||||
std::cout << "DCS packet (" << Buffer.size() << ") from " << Ip << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// handle end of streaming timeout
|
||||
CheckStreamsTimeout();
|
||||
|
||||
// handle queue from reflector
|
||||
HandleQueue();
|
||||
|
||||
// keep client alive
|
||||
if ( m_LastKeepaliveTime.DurationSinceNow() > DCS_KEEPALIVE_PERIOD )
|
||||
{
|
||||
//
|
||||
HandleKeepalives();
|
||||
|
||||
// update time
|
||||
m_LastKeepaliveTime.Now();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// streams helpers
|
||||
|
||||
bool CDcsProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
|
||||
{
|
||||
bool newstream = false;
|
||||
|
||||
// find the stream
|
||||
CPacketStream *stream = GetStream(Header->GetStreamId());
|
||||
if ( stream == NULL )
|
||||
{
|
||||
// no stream open yet, open a new one
|
||||
CCallsign via(Header->GetRpt1Callsign());
|
||||
|
||||
// find this client
|
||||
CClient *client = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DCS);
|
||||
if ( client != NULL )
|
||||
{
|
||||
// get client callsign
|
||||
via = client->GetCallsign();
|
||||
// and try to open the stream
|
||||
if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
|
||||
{
|
||||
// keep the handle
|
||||
m_Streams.push_back(stream);
|
||||
newstream = true;
|
||||
}
|
||||
}
|
||||
// release
|
||||
g_Reflector.ReleaseClients();
|
||||
|
||||
// update last heard
|
||||
g_Reflector.GetUsers()->Hearing(Header->GetMyCallsign(), via, Header->GetRpt2Callsign());
|
||||
g_Reflector.ReleaseUsers();
|
||||
|
||||
// delete header if needed
|
||||
if ( !newstream )
|
||||
{
|
||||
delete Header;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// stream already open
|
||||
// skip packet, but tickle the stream
|
||||
stream->Tickle();
|
||||
// and delete packet
|
||||
delete Header;
|
||||
}
|
||||
|
||||
// done
|
||||
return newstream;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// queue helper
|
||||
|
||||
void CDcsProtocol::HandleQueue(void)
|
||||
{
|
||||
m_Queue.Lock();
|
||||
while ( !m_Queue.empty() )
|
||||
{
|
||||
// get the packet
|
||||
CPacket *packet = m_Queue.front();
|
||||
m_Queue.pop();
|
||||
|
||||
// get our sender's id
|
||||
int iModId = g_Reflector.GetModuleIndex(packet->GetModuleId());
|
||||
|
||||
// check if it's header and update cache
|
||||
if ( packet->IsDvHeader() )
|
||||
{
|
||||
// this relies on queue feeder setting valid module id
|
||||
m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet);
|
||||
m_StreamsCache[iModId].m_iSeqCounter = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// encode it
|
||||
CBuffer buffer;
|
||||
if ( packet->IsLastPacket() )
|
||||
{
|
||||
EncodeDvLastPacket(
|
||||
m_StreamsCache[iModId].m_dvHeader,
|
||||
(const CDvFramePacket &)*packet,
|
||||
m_StreamsCache[iModId].m_iSeqCounter++,
|
||||
&buffer);
|
||||
}
|
||||
else if ( packet->IsDvFrame() )
|
||||
{
|
||||
EncodeDvPacket(
|
||||
m_StreamsCache[iModId].m_dvHeader,
|
||||
(const CDvFramePacket &)*packet,
|
||||
m_StreamsCache[iModId].m_iSeqCounter++,
|
||||
&buffer);
|
||||
}
|
||||
|
||||
// send it
|
||||
if ( buffer.size() > 0 )
|
||||
{
|
||||
// and push it to all our clients linked to the module and who are not streaming in
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(PROTOCOL_DCS, &index)) != NULL )
|
||||
{
|
||||
// is this client busy ?
|
||||
if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) )
|
||||
{
|
||||
// no, send the packet
|
||||
m_Socket.Send(buffer, client->GetIp());
|
||||
|
||||
}
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
delete packet;
|
||||
}
|
||||
m_Queue.Unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// keepalive helpers
|
||||
|
||||
void CDcsProtocol::HandleKeepalives(void)
|
||||
{
|
||||
// DCS protocol sends and monitors keepalives packets
|
||||
// event if the client is currently streaming
|
||||
// so, send keepalives to all
|
||||
CBuffer keepalive1;
|
||||
EncodeKeepAlivePacket(&keepalive1);
|
||||
|
||||
// iterate on clients
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(PROTOCOL_DCS, &index)) != NULL )
|
||||
{
|
||||
// encode client's specific keepalive packet
|
||||
CBuffer keepalive2;
|
||||
EncodeKeepAlivePacket(&keepalive2, client);
|
||||
|
||||
// send keepalive
|
||||
m_Socket.Send(keepalive1, client->GetIp());
|
||||
m_Socket.Send(keepalive2, client->GetIp());
|
||||
|
||||
// is this client busy ?
|
||||
if ( client->IsAMaster() )
|
||||
{
|
||||
// yes, just tickle it
|
||||
client->Alive();
|
||||
}
|
||||
// check it's still with us
|
||||
else if ( !client->IsAlive() )
|
||||
{
|
||||
// no, disconnect
|
||||
CBuffer disconnect;
|
||||
EncodeDisconnectPacket(&disconnect, client);
|
||||
m_Socket.Send(disconnect, client->GetIp());
|
||||
|
||||
// remove it
|
||||
std::cout << "DCS client " << client->GetCallsign() << " keepalive timeout" << std::endl;
|
||||
clients->RemoveClient(client);
|
||||
}
|
||||
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet decoding helpers
|
||||
|
||||
bool CDcsProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule)
|
||||
{
|
||||
bool valid = false;
|
||||
if ( Buffer.size() == 519 )
|
||||
{
|
||||
callsign->SetCallsign(Buffer.data(), 8);
|
||||
callsign->SetModule(Buffer.data()[8]);
|
||||
*reflectormodule = Buffer.data()[9];
|
||||
valid = (callsign->IsValid() && IsLetter(*reflectormodule));
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CDcsProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign)
|
||||
{
|
||||
bool valid = false;
|
||||
if ((Buffer.size() == 11) && (Buffer.data()[9] == ' '))
|
||||
{
|
||||
callsign->SetCallsign(Buffer.data(), 8);
|
||||
callsign->SetModule(Buffer.data()[8]);
|
||||
valid = callsign->IsValid();
|
||||
}
|
||||
else if ((Buffer.size() == 19) && (Buffer.data()[9] == ' ') && (Buffer.data()[10] == 0x00))
|
||||
{
|
||||
callsign->SetCallsign(Buffer.data(), 8);
|
||||
callsign->SetModule(Buffer.data()[8]);
|
||||
valid = callsign->IsValid();
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CDcsProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign *callsign)
|
||||
{
|
||||
bool valid = false;
|
||||
if ( (Buffer.size() == 17) || (Buffer.size() == 15) || (Buffer.size() == 22) )
|
||||
{
|
||||
callsign->SetCallsign(Buffer.data(), 8);
|
||||
valid = callsign->IsValid();
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CDcsProtocol::IsValidDvPacket(const CBuffer &Buffer, CDvHeaderPacket **header, CDvFramePacket **frame)
|
||||
{
|
||||
uint8 tag[] = { '0','0','0','1' };
|
||||
|
||||
bool valid = false;
|
||||
*header = NULL;
|
||||
*frame = NULL;
|
||||
|
||||
if ( (Buffer.size() >= 100) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
|
||||
{
|
||||
// get the header
|
||||
*header = new CDvHeaderPacket((struct dstar_header *)&(Buffer.data()[4]),
|
||||
*((uint16 *)&(Buffer.data()[43])), 0x80);
|
||||
|
||||
// get the frame
|
||||
if ( ((Buffer.data()[45]) & 0x40) != 0 )
|
||||
{
|
||||
// it's the last frame
|
||||
*frame = new CDvLastFramePacket((struct dstar_dvframe *)&(Buffer.data()[46]),
|
||||
*((uint16 *)&(Buffer.data()[43])), Buffer.data()[45]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// it's a regular DV frame
|
||||
*frame = new CDvFramePacket((struct dstar_dvframe *)&(Buffer.data()[46]),
|
||||
*((uint16 *)&(Buffer.data()[43])), Buffer.data()[45]);
|
||||
}
|
||||
|
||||
// check validity of packets
|
||||
if ( !((*header)->IsValid() && (*frame)->IsValid()) )
|
||||
{
|
||||
delete *header;
|
||||
delete *frame;
|
||||
*header = NULL;
|
||||
*frame = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
// done
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CDcsProtocol::IsIgnorePacket(const CBuffer &Buffer)
|
||||
{
|
||||
bool valid = false;
|
||||
uint8 tag[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, };
|
||||
|
||||
if ( Buffer.size() == 15 )
|
||||
{
|
||||
valid = (Buffer.Compare(tag, sizeof(tag)) == 0);
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet encoding helpers
|
||||
|
||||
void CDcsProtocol::EncodeKeepAlivePacket(CBuffer *Buffer)
|
||||
{
|
||||
Buffer->Set(GetReflectorCallsign());
|
||||
}
|
||||
|
||||
void CDcsProtocol::EncodeKeepAlivePacket(CBuffer *Buffer, CClient *Client)
|
||||
{
|
||||
uint8 tag[] = { 0x0A,0x00,0x20,0x20 };
|
||||
|
||||
Buffer->Set((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN-1);
|
||||
Buffer->Append((uint8)Client->GetReflectorModule());
|
||||
Buffer->Append((uint8)' ');
|
||||
Buffer->Append((uint8 *)(const char *)Client->GetCallsign(), CALLSIGN_LEN-1);
|
||||
Buffer->Append((uint8)Client->GetModule());
|
||||
Buffer->Append((uint8)Client->GetModule());
|
||||
Buffer->Append(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
void CDcsProtocol::EncodeConnectAckPacket(const CCallsign &Callsign, char ReflectorModule, CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 'A','C','K',0x00 };
|
||||
uint8 cs[CALLSIGN_LEN];
|
||||
|
||||
Callsign.GetCallsign(cs);
|
||||
Buffer->Set(cs, CALLSIGN_LEN-1);
|
||||
Buffer->Append((uint8)' ');
|
||||
Buffer->Append((uint8)Callsign.GetModule());
|
||||
Buffer->Append((uint8)ReflectorModule);
|
||||
Buffer->Append(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
void CDcsProtocol::EncodeConnectNackPacket(const CCallsign &Callsign, char ReflectorModule, CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 'N','A','K',0x00 };
|
||||
uint8 cs[CALLSIGN_LEN];
|
||||
|
||||
Callsign.GetCallsign(cs);
|
||||
Buffer->Set(cs, CALLSIGN_LEN-1);
|
||||
Buffer->Append((uint8)' ');
|
||||
Buffer->Append((uint8)Callsign.GetModule());
|
||||
Buffer->Append((uint8)ReflectorModule);
|
||||
Buffer->Append(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
void CDcsProtocol::EncodeDisconnectPacket(CBuffer *Buffer, CClient *Client)
|
||||
{
|
||||
Buffer->Set((uint8 *)(const char *)Client->GetCallsign(), CALLSIGN_LEN-1);
|
||||
Buffer->Append((uint8)' ');
|
||||
Buffer->Append((uint8)Client->GetModule());
|
||||
Buffer->Append((uint8)0x00);
|
||||
Buffer->Append((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN-1);
|
||||
Buffer->Append((uint8)' ');
|
||||
Buffer->Append((uint8)0x00);
|
||||
}
|
||||
|
||||
void CDcsProtocol::EncodeDvPacket(const CDvHeaderPacket &Header, const CDvFramePacket &DvFrame, uint32 iSeq, CBuffer *Buffer) const
|
||||
{
|
||||
uint8 tag[] = { '0','0','0','1' };
|
||||
struct dstar_header DstarHeader;
|
||||
|
||||
Header.ConvertToDstarStruct(&DstarHeader);
|
||||
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
Buffer->Append((uint8 *)&DstarHeader, sizeof(struct dstar_header) - sizeof(uint16));
|
||||
Buffer->Append(DvFrame.GetStreamId());
|
||||
Buffer->Append((uint8)(DvFrame.GetPacketId() % 21));
|
||||
Buffer->Append((uint8 *)DvFrame.GetAmbe(), AMBE_SIZE);
|
||||
Buffer->Append((uint8 *)DvFrame.GetDvData(), DVDATA_SIZE);
|
||||
Buffer->Append((uint8)((iSeq >> 0) & 0xFF));
|
||||
Buffer->Append((uint8)((iSeq >> 8) & 0xFF));
|
||||
Buffer->Append((uint8)((iSeq >> 16) & 0xFF));
|
||||
Buffer->Append((uint8)0x01);
|
||||
Buffer->Append((uint8)0x00, 38);
|
||||
}
|
||||
|
||||
void CDcsProtocol::EncodeDvLastPacket(const CDvHeaderPacket &Header, const CDvFramePacket &DvFrame, uint32 iSeq, CBuffer *Buffer) const
|
||||
{
|
||||
EncodeDvPacket(Header, DvFrame, iSeq, Buffer);
|
||||
(Buffer->data())[45] |= 0x40;
|
||||
}
|
||||
560
cdextraprotocol.cpp
Normal file
560
cdextraprotocol.cpp
Normal file
|
|
@ -0,0 +1,560 @@
|
|||
//
|
||||
// cdextraprotocol.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include "cdextraclient.h"
|
||||
#include "cdextraprotocol.h"
|
||||
#include "creflector.h"
|
||||
#include "cgatekeeper.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operation
|
||||
|
||||
bool CDextraProtocol::Init(void)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
// base class
|
||||
ok = CProtocol::Init();
|
||||
|
||||
// update the reflector callsign
|
||||
m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"XRF", 3);
|
||||
|
||||
// create our socket
|
||||
ok &= m_Socket.Open(DEXTRA_PORT);
|
||||
if ( !ok )
|
||||
{
|
||||
std::cout << "Error opening socket on port UDP" << DEXTRA_PORT << " on ip " << g_Reflector.GetListenIp() << std::endl;
|
||||
}
|
||||
|
||||
// update time
|
||||
m_LastKeepaliveTime.Now();
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// task
|
||||
|
||||
void CDextraProtocol::Task(void)
|
||||
{
|
||||
CBuffer Buffer;
|
||||
CIp Ip;
|
||||
CCallsign Callsign;
|
||||
char ToLinkModule;
|
||||
int ProtRev;
|
||||
CDvHeaderPacket *Header;
|
||||
CDvFramePacket *Frame;
|
||||
CDvLastFramePacket *LastFrame;
|
||||
|
||||
// any incoming packet ?
|
||||
if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
|
||||
{
|
||||
// crack the packet
|
||||
if ( (Frame = IsValidDvFramePacket(Buffer)) != NULL )
|
||||
{
|
||||
//std::cout << "DExtra DV frame" << std::endl;
|
||||
|
||||
// handle it
|
||||
OnDvFramePacketIn(Frame, &Ip);
|
||||
}
|
||||
else if ( (Header = IsValidDvHeaderPacket(Buffer)) != NULL )
|
||||
{
|
||||
//std::cout << "DExtra DV header:" << std::endl << *Header << std::endl;
|
||||
//std::cout << "DExtra DV header:" << std::endl;
|
||||
|
||||
// callsign muted?
|
||||
if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DEXTRA, Header->GetRpt2Module()) )
|
||||
{
|
||||
// handle it
|
||||
OnDvHeaderPacketIn(Header, Ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete Header;
|
||||
}
|
||||
}
|
||||
else if ( (LastFrame = IsValidDvLastFramePacket(Buffer)) != NULL )
|
||||
{
|
||||
//std::cout << "DExtra DV last frame" << std::endl;
|
||||
|
||||
// handle it
|
||||
OnDvLastFramePacketIn(LastFrame, &Ip);
|
||||
}
|
||||
else if ( IsValidConnectPacket(Buffer, &Callsign, &ToLinkModule, &ProtRev) )
|
||||
{
|
||||
std::cout << "DExtra connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << " rev " << ProtRev << std::endl;
|
||||
|
||||
// callsign authorized?
|
||||
if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DEXTRA) )
|
||||
{
|
||||
// valid module ?
|
||||
if ( g_Reflector.IsValidModule(ToLinkModule) )
|
||||
{
|
||||
// acknowledge the request
|
||||
EncodeConnectAckPacket(&Buffer, ProtRev);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
|
||||
// create the client
|
||||
CDextraClient *client = new CDextraClient(Callsign, Ip, ToLinkModule, ProtRev);
|
||||
|
||||
// and append
|
||||
g_Reflector.GetClients()->AddClient(client);
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "DExtra node " << Callsign << " connect attempt on non-existing module" << std::endl;
|
||||
|
||||
// deny the request
|
||||
EncodeConnectNackPacket(&Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// deny the request
|
||||
EncodeConnectNackPacket(&Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
}
|
||||
else if ( IsValidDisconnectPacket(Buffer, &Callsign) )
|
||||
{
|
||||
std::cout << "DExtra disconnect packet from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// find client & remove it
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
CClient *client = clients->FindClient(Ip, PROTOCOL_DEXTRA);
|
||||
if ( client != NULL )
|
||||
{
|
||||
// ack disconnect packet
|
||||
if ( client->GetProtocolRevision() == 1 )
|
||||
{
|
||||
EncodeDisconnectedPacket(&Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
else if ( client->GetProtocolRevision() == 2 )
|
||||
{
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
// and remove it
|
||||
clients->RemoveClient(client);
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else if ( IsValidKeepAlivePacket(Buffer, &Callsign) )
|
||||
{
|
||||
//std::cout << "DExtra keepalive packet from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// find all clients with that callsign & ip and keep them alive
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(Callsign, Ip, PROTOCOL_DEXTRA, &index)) != NULL )
|
||||
{
|
||||
client->Alive();
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "DExtra packet (" << Buffer.size() << ")" << std::endl;
|
||||
//std::cout << Buffer.data() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// handle end of streaming timeout
|
||||
CheckStreamsTimeout();
|
||||
|
||||
// handle queue from reflector
|
||||
HandleQueue();
|
||||
|
||||
// keep client alive
|
||||
if ( m_LastKeepaliveTime.DurationSinceNow() > DEXTRA_KEEPALIVE_PERIOD )
|
||||
{
|
||||
//
|
||||
HandleKeepalives();
|
||||
|
||||
// update time
|
||||
m_LastKeepaliveTime.Now();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// queue helper
|
||||
|
||||
void CDextraProtocol::HandleQueue(void)
|
||||
{
|
||||
m_Queue.Lock();
|
||||
while ( !m_Queue.empty() )
|
||||
{
|
||||
// get the packet
|
||||
CPacket *packet = m_Queue.front();
|
||||
m_Queue.pop();
|
||||
|
||||
// encode it
|
||||
CBuffer buffer;
|
||||
if ( EncodeDvPacket(*packet, &buffer) )
|
||||
{
|
||||
// and push it to all our clients linked to the module and who are not streaming in
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(PROTOCOL_DEXTRA, &index)) != NULL )
|
||||
{
|
||||
// is this client busy ?
|
||||
if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) )
|
||||
{
|
||||
// no, send the packet
|
||||
int n = packet->IsDvHeader() ? 5 : 1;
|
||||
for ( int i = 0; i < n; i++ )
|
||||
{
|
||||
m_Socket.Send(buffer, client->GetIp());
|
||||
}
|
||||
}
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
|
||||
|
||||
// done
|
||||
delete packet;
|
||||
}
|
||||
m_Queue.Unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// keepalive helpers
|
||||
|
||||
void CDextraProtocol::HandleKeepalives(void)
|
||||
{
|
||||
// DExtra protocol sends and monitors keepalives packets
|
||||
// event if the client is currently streaming
|
||||
// so, send keepalives to all
|
||||
CBuffer keepalive;
|
||||
EncodeKeepAlivePacket(&keepalive);
|
||||
|
||||
// iterate on clients
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(PROTOCOL_DEXTRA, &index)) != NULL )
|
||||
{
|
||||
// send keepalive
|
||||
m_Socket.Send(keepalive, client->GetIp());
|
||||
|
||||
// client busy ?
|
||||
if ( client->IsAMaster() )
|
||||
{
|
||||
// yes, just tickle it
|
||||
client->Alive();
|
||||
}
|
||||
// otherwise check if still with us
|
||||
else if ( !client->IsAlive() )
|
||||
{
|
||||
// no, disconnect
|
||||
CBuffer disconnect;
|
||||
EncodeDisconnectPacket(&disconnect);
|
||||
m_Socket.Send(disconnect, client->GetIp());
|
||||
|
||||
// remove it
|
||||
std::cout << "DExtra client " << client->GetCallsign() << " keepalive timeout" << std::endl;
|
||||
clients->RemoveClient(client);
|
||||
}
|
||||
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// streams helpers
|
||||
|
||||
bool CDextraProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
|
||||
{
|
||||
bool newstream = false;
|
||||
|
||||
// find the stream
|
||||
CPacketStream *stream = GetStream(Header->GetStreamId());
|
||||
if ( stream == NULL )
|
||||
{
|
||||
// no stream open yet, open a new one
|
||||
CCallsign via(Header->GetRpt1Callsign());
|
||||
|
||||
// find this client
|
||||
CClient *client = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DEXTRA);
|
||||
if ( client != NULL )
|
||||
{
|
||||
// get client callsign
|
||||
via = client->GetCallsign();
|
||||
// apply protocol revision details
|
||||
if ( client->GetProtocolRevision() == 2 )
|
||||
{
|
||||
// update Header RPT2 module letter with
|
||||
// the module the client is linked to
|
||||
Header->SetRpt2Module(client->GetReflectorModule());
|
||||
}
|
||||
// and try to open the stream
|
||||
if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
|
||||
{
|
||||
// keep the handle
|
||||
m_Streams.push_back(stream);
|
||||
newstream = true;
|
||||
}
|
||||
}
|
||||
// release
|
||||
g_Reflector.ReleaseClients();
|
||||
|
||||
// update last heard
|
||||
g_Reflector.GetUsers()->Hearing(Header->GetMyCallsign(), via, Header->GetRpt2Callsign());
|
||||
g_Reflector.ReleaseUsers();
|
||||
|
||||
// delete header if needed
|
||||
if ( !newstream )
|
||||
{
|
||||
delete Header;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// stream already open
|
||||
// skip packet, but tickle the stream
|
||||
stream->Tickle();
|
||||
// and delete packet
|
||||
delete Header;
|
||||
}
|
||||
|
||||
// done
|
||||
return newstream;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet decoding helpers
|
||||
|
||||
bool CDextraProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule, int *revision)
|
||||
{
|
||||
bool valid = false;
|
||||
if ((Buffer.size() == 11) && (Buffer.data()[9] != ' '))
|
||||
{
|
||||
callsign->SetCallsign(Buffer.data(), 8);
|
||||
callsign->SetModule(Buffer.data()[8]);
|
||||
*reflectormodule = Buffer.data()[9];
|
||||
*revision = (Buffer.data()[10] == 11) ? 1 : 0;
|
||||
valid = (callsign->IsValid() && IsLetter(*reflectormodule));
|
||||
// detect revision
|
||||
if ( (Buffer.data()[10] == 11) )
|
||||
{
|
||||
*revision = 1;
|
||||
}
|
||||
else if ( callsign->HasSameCallsignWithWildcard(CCallsign("XRF*")) )
|
||||
{
|
||||
*revision = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
*revision = 0;
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CDextraProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign)
|
||||
{
|
||||
bool valid = false;
|
||||
if ((Buffer.size() == 11) && (Buffer.data()[9] == ' '))
|
||||
{
|
||||
callsign->SetCallsign(Buffer.data(), 8);
|
||||
callsign->SetModule(Buffer.data()[8]);
|
||||
valid = callsign->IsValid();
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CDextraProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign *callsign)
|
||||
{
|
||||
bool valid = false;
|
||||
if (Buffer.size() == 9)
|
||||
{
|
||||
callsign->SetCallsign(Buffer.data(), 8);
|
||||
valid = callsign->IsValid();
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
CDvHeaderPacket *CDextraProtocol::IsValidDvHeaderPacket(const CBuffer &Buffer)
|
||||
{
|
||||
CDvHeaderPacket *header = NULL;
|
||||
|
||||
if ( (Buffer.size() == 56) && (Buffer.Compare((uint8 *)"DSVT", 4) == 0) &&
|
||||
(Buffer.data()[4] == 0x10) && (Buffer.data()[8] == 0x20) )
|
||||
{
|
||||
// create packet
|
||||
header = new CDvHeaderPacket((struct dstar_header *)&(Buffer.data()[15]),
|
||||
*((uint16 *)&(Buffer.data()[12])), 0x80);
|
||||
// check validity of packet
|
||||
if ( !header->IsValid() )
|
||||
{
|
||||
delete header;
|
||||
header = NULL;
|
||||
}
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
CDvFramePacket *CDextraProtocol::IsValidDvFramePacket(const CBuffer &Buffer)
|
||||
{
|
||||
CDvFramePacket *dvframe = NULL;
|
||||
|
||||
if ( (Buffer.size() == 27) && (Buffer.Compare((uint8 *)"DSVT", 4) == 0) &&
|
||||
(Buffer.data()[4] == 0x20) && (Buffer.data()[8] == 0x20) &&
|
||||
((Buffer.data()[14] & 0x40) == 0) )
|
||||
{
|
||||
// create packet
|
||||
dvframe = new CDvFramePacket((struct dstar_dvframe *)&(Buffer.data()[15]),
|
||||
*((uint16 *)&(Buffer.data()[12])), Buffer.data()[14]);
|
||||
// check validity of packet
|
||||
if ( !dvframe->IsValid() )
|
||||
{
|
||||
delete dvframe;
|
||||
dvframe = NULL;
|
||||
}
|
||||
}
|
||||
return dvframe;
|
||||
}
|
||||
|
||||
CDvLastFramePacket *CDextraProtocol::IsValidDvLastFramePacket(const CBuffer &Buffer)
|
||||
{
|
||||
CDvLastFramePacket *dvframe = NULL;
|
||||
|
||||
if ( (Buffer.size() == 27) && (Buffer.Compare((uint8 *)"DSVT", 4) == 0) &&
|
||||
(Buffer.data()[4] == 0x20) && (Buffer.data()[8] == 0x20) &&
|
||||
((Buffer.data()[14] & 0x40) != 0) )
|
||||
{
|
||||
// create packet
|
||||
dvframe = new CDvLastFramePacket((struct dstar_dvframe *)&(Buffer.data()[15]),
|
||||
*((uint16 *)&(Buffer.data()[12])), Buffer.data()[14]);
|
||||
// check validity of packet
|
||||
if ( !dvframe->IsValid() )
|
||||
{
|
||||
delete dvframe;
|
||||
dvframe = NULL;
|
||||
}
|
||||
}
|
||||
return dvframe;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet encoding helpers
|
||||
|
||||
void CDextraProtocol::EncodeKeepAlivePacket(CBuffer *Buffer)
|
||||
{
|
||||
Buffer->Set(GetReflectorCallsign());
|
||||
}
|
||||
|
||||
void CDextraProtocol::EncodeConnectAckPacket(CBuffer *Buffer, int ProtRev)
|
||||
{
|
||||
// is it for a XRF or repeater
|
||||
if ( ProtRev == 2 )
|
||||
{
|
||||
// XRFxxx
|
||||
uint8 rm = (Buffer->data())[8];
|
||||
uint8 lm = (Buffer->data())[9];
|
||||
Buffer->clear();
|
||||
Buffer->Set((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN);
|
||||
Buffer->Append(lm);
|
||||
Buffer->Append(rm);
|
||||
Buffer->Append((uint8)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// regular repeater
|
||||
uint8 tag[] = { 'A','C','K',0 };
|
||||
Buffer->resize(Buffer->size()-1);
|
||||
Buffer->Append(tag, sizeof(tag));
|
||||
}
|
||||
}
|
||||
|
||||
void CDextraProtocol::EncodeConnectNackPacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 'N','A','K',0 };
|
||||
Buffer->resize(Buffer->size()-1);
|
||||
Buffer->Append(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
void CDextraProtocol::EncodeDisconnectPacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',0 };
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
void CDextraProtocol::EncodeDisconnectedPacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 'D','I','S','C','O','N','N','E','C','T','E','D' };
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
bool CDextraProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, CBuffer *Buffer) const
|
||||
{
|
||||
uint8 tag[] = { 'D','S','V','T',0x10,0x00,0x00,0x00,0x20,0x00,0x01,0x02 };
|
||||
struct dstar_header DstarHeader;
|
||||
|
||||
Packet.ConvertToDstarStruct(&DstarHeader);
|
||||
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
Buffer->Append(Packet.GetStreamId());
|
||||
Buffer->Append((uint8)0x80);
|
||||
Buffer->Append((uint8 *)&DstarHeader, sizeof(struct dstar_header));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDextraProtocol::EncodeDvFramePacket(const CDvFramePacket &Packet, CBuffer *Buffer) const
|
||||
{
|
||||
uint8 tag[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 };
|
||||
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
Buffer->Append(Packet.GetStreamId());
|
||||
Buffer->Append((uint8)(Packet.GetPacketId() % 21));
|
||||
Buffer->Append((uint8 *)Packet.GetAmbe(), AMBE_SIZE);
|
||||
Buffer->Append((uint8 *)Packet.GetDvData(), DVDATA_SIZE);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool CDextraProtocol::EncodeDvLastFramePacket(const CDvLastFramePacket &Packet, CBuffer *Buffer) const
|
||||
{
|
||||
uint8 tag1[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 };
|
||||
uint8 tag2[] = { 0x55,0xC8,0x7A,0x00,0x00,0x00,0x00,0x00,0x00,0x25,0x1A,0xC6 };
|
||||
|
||||
Buffer->Set(tag1, sizeof(tag1));
|
||||
Buffer->Append(Packet.GetStreamId());
|
||||
Buffer->Append((uint8)((Packet.GetPacketId() % 21) | 0x40));
|
||||
Buffer->Append(tag2, sizeof(tag2));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
160
cdmriddir.cpp
Normal file
160
cdmriddir.cpp
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
//
|
||||
// cdmriddir.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 08/10/2016.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include <string.h>
|
||||
#include "main.h"
|
||||
#include "creflector.h"
|
||||
#include "cdmriddir.h"
|
||||
#include "cdmriddirfile.h"
|
||||
#include "cdmriddirhttp.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor & destructor
|
||||
|
||||
CDmridDir::CDmridDir()
|
||||
{
|
||||
m_bStopThread = false;
|
||||
m_pThread = NULL;
|
||||
}
|
||||
|
||||
CDmridDir::~CDmridDir()
|
||||
{
|
||||
// kill threads
|
||||
m_bStopThread = true;
|
||||
if ( m_pThread != NULL )
|
||||
{
|
||||
m_pThread->join();
|
||||
delete m_pThread;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// init & close
|
||||
|
||||
bool CDmridDir::Init(void)
|
||||
{
|
||||
// load content
|
||||
Reload();
|
||||
|
||||
// reset stop flag
|
||||
m_bStopThread = false;
|
||||
|
||||
// start thread;
|
||||
m_pThread = new std::thread(CDmridDir::Thread, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDmridDir::Close(void)
|
||||
{
|
||||
m_bStopThread = true;
|
||||
if ( m_pThread != NULL )
|
||||
{
|
||||
m_pThread->join();
|
||||
delete m_pThread;
|
||||
m_pThread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// thread
|
||||
|
||||
void CDmridDir::Thread(CDmridDir *This)
|
||||
{
|
||||
while ( !This->m_bStopThread )
|
||||
{
|
||||
// Wait 30 seconds
|
||||
CTimePoint::TaskSleepFor(DMRIDDB_REFRESH_RATE * 60000);
|
||||
|
||||
// have lists files changed ?
|
||||
if ( This->NeedReload() )
|
||||
{
|
||||
This->Reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Reload
|
||||
|
||||
bool CDmridDir::Reload(void)
|
||||
{
|
||||
CBuffer buffer;
|
||||
bool ok = false;
|
||||
|
||||
if ( LoadContent(&buffer) )
|
||||
{
|
||||
Lock();
|
||||
{
|
||||
ok = RefreshContent(buffer);
|
||||
}
|
||||
Unlock();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// find
|
||||
|
||||
const CCallsign *CDmridDir::FindCallsign(uint32 dmrid)
|
||||
{
|
||||
auto found = m_CallsignMap.find(dmrid);
|
||||
if ( found != m_CallsignMap.end() )
|
||||
{
|
||||
return &(found->second);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32 CDmridDir::FindDmrid(const CCallsign &callsign)
|
||||
{
|
||||
auto found = m_DmridMap.find(callsign);
|
||||
if ( found != m_DmridMap.end() )
|
||||
{
|
||||
return (found->second);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// syntax helpers
|
||||
|
||||
bool CDmridDir::IsValidDmrid(const char *sz)
|
||||
{
|
||||
bool ok = false;
|
||||
unsigned int n = ::strlen(sz);
|
||||
if ( (n > 0) && (n <= 8) )
|
||||
{
|
||||
ok = true;
|
||||
for ( unsigned int i = 0; (i < n) && ok; i++ )
|
||||
{
|
||||
ok &= ::isdigit(sz[i]);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
182
cdmriddirhttp.cpp
Normal file
182
cdmriddirhttp.cpp
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
//
|
||||
// cdmriddirhttp.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 29/12/2017.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include <string.h>
|
||||
#include "main.h"
|
||||
#include "creflector.h"
|
||||
#include "cdmriddirhttp.h"
|
||||
|
||||
#if (DMRIDDB_USE_RLX_SERVER == 1)
|
||||
CDmridDirHttp g_DmridDir;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// refresh
|
||||
|
||||
bool CDmridDirHttp::LoadContent(CBuffer *buffer)
|
||||
{
|
||||
// get file from xlxapi server
|
||||
return HttpGet("xlxapi.rlx.lu", "api/exportdmr.php", 80, buffer);
|
||||
}
|
||||
|
||||
bool CDmridDirHttp::RefreshContent(const CBuffer &buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
// clear directory
|
||||
m_CallsignMap.clear();
|
||||
m_DmridMap.clear();
|
||||
|
||||
// scan file
|
||||
if ( buffer.size() > 0 )
|
||||
{
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
// get next line
|
||||
while ( (ptr2 = ::strchr(ptr1, '\n')) != NULL )
|
||||
{
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *dmrid;
|
||||
char *callsign;
|
||||
if ( ((dmrid = ::strtok(ptr1, ";")) != NULL) && IsValidDmrid(dmrid) )
|
||||
{
|
||||
if ( ((callsign = ::strtok(NULL, ";")) != NULL) )
|
||||
{
|
||||
// new entry
|
||||
uint32 ui = atoi(dmrid);
|
||||
CCallsign cs(callsign, ui);
|
||||
if ( cs.IsValid() )
|
||||
{
|
||||
m_CallsignMap.insert(std::pair<uint32,CCallsign>(ui, cs));
|
||||
m_DmridMap.insert(std::pair<CCallsign,uint32>(cs,ui));
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
// done
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// report
|
||||
std::cout << "Read " << m_DmridMap.size() << " DMR id from xlxapi.rlx.lu database " << std::endl;
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// httpd helpers
|
||||
|
||||
#define DMRID_HTTPGET_SIZEMAX (256)
|
||||
#define DMRID_TEXTFILE_SIZEMAX (10*1024*1024)
|
||||
|
||||
bool CDmridDirHttp::HttpGet(const char *hostname, const char *filename, int port, CBuffer *buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
int sock_id;
|
||||
|
||||
// open socket
|
||||
if ( (sock_id = ::socket(AF_INET, SOCK_STREAM, 0)) >= 0 )
|
||||
{
|
||||
// get hostname address
|
||||
struct sockaddr_in servaddr;
|
||||
struct hostent *hp;
|
||||
::memset(&servaddr,0,sizeof(servaddr));
|
||||
if( (hp = gethostbyname(hostname)) != NULL )
|
||||
{
|
||||
// dns resolved
|
||||
::memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
|
||||
servaddr.sin_port = htons(port);
|
||||
servaddr.sin_family = AF_INET;
|
||||
|
||||
// connect
|
||||
if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0)
|
||||
{
|
||||
// send the GET request
|
||||
char request[DMRID_HTTPGET_SIZEMAX];
|
||||
::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: xlxd\r\n\r\n",
|
||||
filename, (const char *)g_Reflector.GetCallsign());
|
||||
::write(sock_id, request, strlen(request));
|
||||
|
||||
// config receive timeouts
|
||||
fd_set read_set;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 5;
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&read_set);
|
||||
FD_SET(sock_id, &read_set);
|
||||
|
||||
// get the reply back
|
||||
buffer->clear();
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
char buf[1440];
|
||||
ssize_t len = 0;
|
||||
select(sock_id+1, &read_set, NULL, NULL, &timeout);
|
||||
//if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) )
|
||||
//if ( ret >= 0 )
|
||||
//{
|
||||
usleep(5000);
|
||||
len = read(sock_id, buf, 1440);
|
||||
if ( len > 0 )
|
||||
{
|
||||
buffer->Append((uint8 *)buf, (int)len);
|
||||
ok = true;
|
||||
}
|
||||
//}
|
||||
done = (len <= 0);
|
||||
|
||||
} while (!done);
|
||||
buffer->Append((uint8)0);
|
||||
|
||||
// and disconnect
|
||||
close(sock_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Cannot establish connection with host " << hostname << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Host " << hostname << " not found" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Failed to open wget socket" << std::endl;
|
||||
}
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
1203
cdmrmmdvmprotocol.cpp
Normal file
1203
cdmrmmdvmprotocol.cpp
Normal file
File diff suppressed because it is too large
Load diff
846
cdmrplusprotocol.cpp
Normal file
846
cdmrplusprotocol.cpp
Normal file
|
|
@ -0,0 +1,846 @@
|
|||
//
|
||||
// cdmrplusprotocol.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 10/01/2016.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include "cdmrplusclient.h"
|
||||
#include "cdmrplusprotocol.h"
|
||||
#include "creflector.h"
|
||||
#include "cgatekeeper.h"
|
||||
#include "cdmriddir.h"
|
||||
#include "cbptc19696.h"
|
||||
#include "crs129.h"
|
||||
#include "cgolay2087.h"
|
||||
#include "cqr1676.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constants
|
||||
|
||||
static uint8 g_DmrSyncBSVoice[] = { 0x07,0x55,0xFD,0x7D,0xF7,0x5F,0x70 };
|
||||
static uint8 g_DmrSyncBSData[] = { 0x0D,0xFF,0x57,0xD7,0x5D,0xF5,0xD0 };
|
||||
//static uint8 g_DmrSyncMSVoice[] = { 0x07,0xF7,0xD5,0xDD,0x57,0xDF,0xD0 }; //not used
|
||||
//static uint8 g_DmrSyncMSData[] = { 0x0D,0x5D,0x7F,0x77,0xFD,0x75,0x70 };
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operation
|
||||
|
||||
bool CDmrplusProtocol::Init(void)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
// base class
|
||||
ok = CProtocol::Init();
|
||||
|
||||
// update the reflector callsign
|
||||
//m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"DMR", 3);
|
||||
|
||||
// create our socket
|
||||
ok &= m_Socket.Open(DMRPLUS_PORT);
|
||||
|
||||
// update time
|
||||
m_LastKeepaliveTime.Now();
|
||||
|
||||
// random number generator
|
||||
time_t t;
|
||||
::srand((unsigned) time(&t));
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// task
|
||||
|
||||
void CDmrplusProtocol::Task(void)
|
||||
{
|
||||
CBuffer Buffer;
|
||||
CIp Ip;
|
||||
CCallsign Callsign;
|
||||
char ToLinkModule;
|
||||
CDvHeaderPacket *Header;
|
||||
CDvFramePacket *Frames[3];
|
||||
|
||||
// handle incoming packets
|
||||
if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
|
||||
{
|
||||
// crack the packet
|
||||
if ( IsValidDvFramePacket(Ip, Buffer, Frames) )
|
||||
{
|
||||
//std::cout << "DMRplus DV frame" << std::endl;
|
||||
//Buffer.DebugDump(g_Reflector.m_DebugFile);
|
||||
|
||||
for ( int i = 0; i < 3; i++ )
|
||||
{
|
||||
OnDvFramePacketIn(Frames[i], &Ip);
|
||||
/*if ( !Frames[i]->IsLastPacket() )
|
||||
{
|
||||
//std::cout << "DMRplus DV frame" << std::endl;
|
||||
OnDvFramePacketIn(Frames[i], &Ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
//std::cout << "DMRplus DV last frame" << std::endl;
|
||||
OnDvLastFramePacketIn((CDvLastFramePacket *)Frames[i], &Ip);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
else if ( IsValidDvHeaderPacket(Ip, Buffer, &Header) )
|
||||
{
|
||||
//std::cout << "DMRplus DV header:" << std::endl;
|
||||
//std::cout << "DMRplus DV header:" << std::endl << *Header << std::endl;
|
||||
//Buffer.DebugDump(g_Reflector.m_DebugFile);
|
||||
|
||||
// callsign muted?
|
||||
if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DMRPLUS) )
|
||||
{
|
||||
// handle it
|
||||
OnDvHeaderPacketIn(Header, Ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete Header;
|
||||
}
|
||||
}
|
||||
else if ( IsValidConnectPacket(Buffer, &Callsign, &ToLinkModule, Ip) )
|
||||
{
|
||||
//std::cout << "DMRplus keepalive/connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// callsign authorized?
|
||||
if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DMRPLUS) )
|
||||
{
|
||||
// acknowledge the request
|
||||
EncodeConnectAckPacket(&Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
|
||||
// add client if needed
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
CClient *client = clients->FindClient(Callsign, Ip, PROTOCOL_DMRPLUS);
|
||||
// client already connected ?
|
||||
if ( client == NULL )
|
||||
{
|
||||
std::cout << "DMRplus connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// create the client
|
||||
CDmrplusClient *newclient = new CDmrplusClient(Callsign, Ip, ToLinkModule);
|
||||
|
||||
// and append
|
||||
clients->AddClient(newclient);
|
||||
}
|
||||
else
|
||||
{
|
||||
client->Alive();
|
||||
}
|
||||
// and done
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else
|
||||
{
|
||||
// deny the request
|
||||
EncodeConnectNackPacket(&Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
|
||||
}
|
||||
else if ( IsValidDisconnectPacket(Buffer, &Callsign, &ToLinkModule) )
|
||||
{
|
||||
std::cout << "DMRplus disconnect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// find client & remove it
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
CClient *client = clients->FindClient(Ip, PROTOCOL_DMRPLUS);
|
||||
if ( client != NULL )
|
||||
{
|
||||
clients->RemoveClient(client);
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else
|
||||
{
|
||||
//std::cout << "DMRPlus packet (" << Buffer.size() << ")" << " at " << Ip << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// handle end of streaming timeout
|
||||
CheckStreamsTimeout();
|
||||
|
||||
// handle queue from reflector
|
||||
HandleQueue();
|
||||
|
||||
|
||||
// keep client alive
|
||||
if ( m_LastKeepaliveTime.DurationSinceNow() > DMRPLUS_KEEPALIVE_PERIOD )
|
||||
{
|
||||
//
|
||||
HandleKeepalives();
|
||||
|
||||
// update time
|
||||
m_LastKeepaliveTime.Now();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// streams helpers
|
||||
|
||||
bool CDmrplusProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
|
||||
{
|
||||
bool newstream = false;
|
||||
|
||||
// find the stream
|
||||
CPacketStream *stream = GetStream(Header->GetStreamId());
|
||||
if ( stream == NULL )
|
||||
{
|
||||
// no stream open yet, open a new one
|
||||
// find this client
|
||||
CClient *client = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DMRPLUS);
|
||||
if ( client != NULL )
|
||||
{
|
||||
// and try to open the stream
|
||||
if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
|
||||
{
|
||||
// keep the handle
|
||||
m_Streams.push_back(stream);
|
||||
newstream = true;
|
||||
}
|
||||
}
|
||||
// release
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else
|
||||
{
|
||||
// stream already open
|
||||
// skip packet, but tickle the stream
|
||||
stream->Tickle();
|
||||
// and delete packet
|
||||
delete Header;
|
||||
}
|
||||
|
||||
// update last heard
|
||||
g_Reflector.GetUsers()->Hearing(Header->GetMyCallsign(), Header->GetRpt1Callsign(), Header->GetRpt2Callsign());
|
||||
g_Reflector.ReleaseUsers();
|
||||
|
||||
// delete header if needed
|
||||
if ( !newstream )
|
||||
{
|
||||
delete Header;
|
||||
}
|
||||
|
||||
|
||||
// done
|
||||
return newstream;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// queue helper
|
||||
|
||||
void CDmrplusProtocol::HandleQueue(void)
|
||||
{
|
||||
|
||||
m_Queue.Lock();
|
||||
while ( !m_Queue.empty() )
|
||||
{
|
||||
// get the packet
|
||||
CPacket *packet = m_Queue.front();
|
||||
m_Queue.pop();
|
||||
|
||||
// get our sender's id
|
||||
int iModId = g_Reflector.GetModuleIndex(packet->GetModuleId());
|
||||
|
||||
// encode
|
||||
CBuffer buffer;
|
||||
|
||||
// check if it's header
|
||||
if ( packet->IsDvHeader() )
|
||||
{
|
||||
// update local stream cache
|
||||
// this relies on queue feeder setting valid module id
|
||||
m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet);
|
||||
m_StreamsCache[iModId].m_uiSeqId = 4;
|
||||
|
||||
// encode it
|
||||
EncodeDvHeaderPacket((const CDvHeaderPacket &)*packet, &buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// update local stream cache or send triplet when needed
|
||||
switch ( packet->GetDmrPacketSubid() )
|
||||
{
|
||||
case 1:
|
||||
m_StreamsCache[iModId].m_dvFrame0 = CDvFramePacket((const CDvFramePacket &)*packet);
|
||||
break;
|
||||
case 2:
|
||||
m_StreamsCache[iModId].m_dvFrame1 = CDvFramePacket((const CDvFramePacket &)*packet);
|
||||
break;
|
||||
case 3:
|
||||
EncodeDvPacket(
|
||||
m_StreamsCache[iModId].m_dvHeader,
|
||||
m_StreamsCache[iModId].m_dvFrame0,
|
||||
m_StreamsCache[iModId].m_dvFrame1,
|
||||
(const CDvFramePacket &)*packet,
|
||||
m_StreamsCache[iModId].m_uiSeqId,
|
||||
&buffer);
|
||||
m_StreamsCache[iModId].m_uiSeqId = GetNextSeqId(m_StreamsCache[iModId].m_uiSeqId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// send it
|
||||
if ( buffer.size() > 0 )
|
||||
{
|
||||
// and push it to all our clients linked to the module and who are not streaming in
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(PROTOCOL_DMRPLUS, &index)) != NULL )
|
||||
{
|
||||
// is this client busy ?
|
||||
if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) )
|
||||
{
|
||||
// no, send the packet
|
||||
m_Socket.Send(buffer, client->GetIp());
|
||||
}
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
|
||||
// debug
|
||||
//buffer.DebugDump(g_Reflector.m_DebugFile);
|
||||
}
|
||||
|
||||
// done
|
||||
delete packet;
|
||||
}
|
||||
m_Queue.Unlock();
|
||||
}
|
||||
|
||||
void CDmrplusProtocol::SendBufferToClients(const CBuffer &buffer, uint8 module)
|
||||
{
|
||||
if ( buffer.size() > 0 )
|
||||
{
|
||||
// and push it to all our clients linked to the module and who are not streaming in
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(PROTOCOL_DMRPLUS, &index)) != NULL )
|
||||
{
|
||||
// is this client busy ?
|
||||
if ( !client->IsAMaster() && (client->GetReflectorModule() == module) )
|
||||
{
|
||||
// no, send the packet
|
||||
m_Socket.Send(buffer, client->GetIp());
|
||||
}
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
|
||||
// debug
|
||||
//buffer.DebugDump(g_Reflector.m_DebugFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// keepalive helpers
|
||||
|
||||
void CDmrplusProtocol::HandleKeepalives(void)
|
||||
{
|
||||
// DMRplus protocol keepalive request is client tasks
|
||||
// here, just check that all clients are still alive
|
||||
// and disconnect them if not
|
||||
|
||||
// iterate on clients
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(PROTOCOL_DMRPLUS, &index)) != NULL )
|
||||
{
|
||||
// is this client busy ?
|
||||
if ( client->IsAMaster() )
|
||||
{
|
||||
// yes, just tickle it
|
||||
client->Alive();
|
||||
}
|
||||
// check it's still with us
|
||||
else if ( !client->IsAlive() )
|
||||
{
|
||||
// no, disconnect
|
||||
//CBuffer disconnect;
|
||||
//EncodeDisconnectPacket(&disconnect, client);
|
||||
//m_Socket.Send(disconnect, client->GetIp());
|
||||
|
||||
// remove it
|
||||
std::cout << "DMRplus client " << client->GetCallsign() << " keepalive timeout" << std::endl;
|
||||
clients->RemoveClient(client);
|
||||
}
|
||||
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet decoding helpers
|
||||
|
||||
bool CDmrplusProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule, const CIp &Ip)
|
||||
{
|
||||
bool valid = false;
|
||||
if ( Buffer.size() == 31 )
|
||||
{
|
||||
char sz[9];
|
||||
::memcpy(sz, Buffer.data(), 8);
|
||||
sz[8] = 0;
|
||||
uint32 dmrid = atoi(sz);
|
||||
callsign->SetDmrid(dmrid, true);
|
||||
callsign->SetModule(DMRPLUS_MODULE_ID);
|
||||
::memcpy(sz, &Buffer.data()[8], 4);
|
||||
sz[4] = 0;
|
||||
*reflectormodule = DmrDstIdToModule(atoi(sz));
|
||||
valid = (callsign->IsValid() && (std::isupper(*reflectormodule) || (*reflectormodule == ' ')) );
|
||||
if ( !valid)
|
||||
{
|
||||
std::cout << "DMRplus connect packet from IP address " << Ip << " / unrecognized id " << (int)callsign->GetDmrid() << std::endl;
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CDmrplusProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule)
|
||||
{
|
||||
bool valid = false;
|
||||
if ( Buffer.size() == 32 )
|
||||
{
|
||||
char sz[9];
|
||||
::memcpy(sz, Buffer.data(), 8);
|
||||
sz[8] = 0;
|
||||
uint32 dmrid = atoi(sz);
|
||||
callsign->SetDmrid(dmrid, true);
|
||||
callsign->SetModule(DMRPLUS_MODULE_ID);
|
||||
*reflectormodule = Buffer.data()[11] - '0' + 'A';
|
||||
valid = (callsign->IsValid() && std::isupper(*reflectormodule));
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CDmrplusProtocol::IsValidDvHeaderPacket(const CIp &Ip, const CBuffer &Buffer, CDvHeaderPacket **Header)
|
||||
{
|
||||
bool valid = false;
|
||||
*Header = NULL;
|
||||
|
||||
uint8 uiPacketType = Buffer.data()[8];
|
||||
if ( (Buffer.size() == 72) && ( uiPacketType == 2 ) )
|
||||
{
|
||||
// frame details
|
||||
uint8 uiSlot = (Buffer.data()[16] == 0x22) ? DMR_SLOT2 : DMR_SLOT1;
|
||||
uint8 uiCallType = (Buffer.data()[62] == 1) ? DMR_GROUP_CALL : DMR_PRIVATE_CALL;
|
||||
uint8 uiColourCode = Buffer.data()[20] & 0x0F;
|
||||
if ( (uiSlot == DMRPLUS_REFLECTOR_SLOT) && (uiCallType == DMR_GROUP_CALL) && (uiColourCode == DMRPLUS_REFLECTOR_COLOUR) )
|
||||
{
|
||||
// more frames details
|
||||
//uint8 uiSeqId = Buffer.data()[4];
|
||||
//uint8 uiVoiceSeq = (Buffer.data()[18] & 0x0F) - 7; // aka slot type
|
||||
uint32 uiDstId = *(uint32 *)(&Buffer.data()[64]) & 0x00FFFFFF;
|
||||
uint32 uiSrcId = *(uint32 *)(&Buffer.data()[68]) & 0x00FFFFFF;
|
||||
|
||||
// build DVHeader
|
||||
CCallsign csMY = CCallsign("", uiSrcId);
|
||||
CCallsign rpt1 = CCallsign("", uiSrcId);
|
||||
rpt1.SetModule(DMRPLUS_MODULE_ID);
|
||||
CCallsign rpt2 = m_ReflectorCallsign;
|
||||
rpt2.SetModule(DmrDstIdToModule(uiDstId));
|
||||
uint32 uiStreamId = IpToStreamId(Ip);
|
||||
|
||||
// and packet
|
||||
*Header = new CDvHeaderPacket(uiSrcId, CCallsign("CQCQCQ"), rpt1, rpt2, uiStreamId, 0, 0);
|
||||
valid = (*Header)->IsValid();
|
||||
if ( !valid )
|
||||
{
|
||||
delete *Header;
|
||||
*Header = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
// done
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CDmrplusProtocol::IsValidDvFramePacket(const CIp &Ip, const CBuffer &Buffer, CDvFramePacket **frames)
|
||||
{
|
||||
bool valid = false;
|
||||
frames[0] = NULL;
|
||||
frames[1] = NULL;
|
||||
frames[2] = NULL;
|
||||
|
||||
uint8 uiPacketType = Buffer.data()[8];
|
||||
if ( (Buffer.size() == 72) && ((uiPacketType == 1) || (uiPacketType == 3)) )
|
||||
{
|
||||
// frame details
|
||||
uint8 uiSlot = (Buffer.data()[16] == 0x22) ? DMR_SLOT2 : DMR_SLOT1;
|
||||
uint8 uiCallType = (Buffer.data()[62] == 1) ? DMR_GROUP_CALL : DMR_PRIVATE_CALL;
|
||||
uint8 uiColourCode = Buffer.data()[20] & 0x0F;
|
||||
if ( (uiSlot == DMRPLUS_REFLECTOR_SLOT) && (uiCallType == DMR_GROUP_CALL) && (uiColourCode == DMRPLUS_REFLECTOR_COLOUR) )
|
||||
{
|
||||
// more frames details
|
||||
//uint8 uiSeqId = Buffer.data()[4];
|
||||
uint8 uiVoiceSeq = (Buffer.data()[18] & 0x0F) - 7; // aka slot type
|
||||
//uint32 uiDstId = *(uint32 *)(&Buffer.data()[64]) & 0x00FFFFFF;
|
||||
//uint32 uiSrcId = *(uint32 *)(&Buffer.data()[68]) & 0x00FFFFFF;
|
||||
|
||||
// crack payload
|
||||
uint8 dmrframe[33];
|
||||
uint8 dmr3ambe[27];
|
||||
uint8 dmrambe[9];
|
||||
uint8 dmrsync[7];
|
||||
// get the 33 bytes ambe
|
||||
memcpy(dmrframe, &(Buffer.data()[26]), 33);
|
||||
// handle endianess
|
||||
SwapEndianess(dmrframe, sizeof(dmrframe));
|
||||
// extract the 3 ambe frames
|
||||
memcpy(dmr3ambe, dmrframe, 14);
|
||||
dmr3ambe[13] &= 0xF0;
|
||||
dmr3ambe[13] |= (dmrframe[19] & 0x0F);
|
||||
memcpy(&dmr3ambe[14], &dmrframe[20], 14);
|
||||
// extract sync
|
||||
dmrsync[0] = dmrframe[13] & 0x0F;
|
||||
::memcpy(&dmrsync[1], &dmrframe[14], 5);
|
||||
dmrsync[6] = dmrframe[19] & 0xF0;
|
||||
|
||||
// and create 3 dv frames
|
||||
uint32 uiStreamId = IpToStreamId(Ip);
|
||||
// frame1
|
||||
memcpy(dmrambe, &dmr3ambe[0], 9);
|
||||
frames[0] = new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 1);
|
||||
|
||||
// frame2
|
||||
memcpy(dmrambe, &dmr3ambe[9], 9);
|
||||
frames[1] = new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 2);
|
||||
|
||||
// frame3
|
||||
memcpy(dmrambe, &dmr3ambe[18], 9);
|
||||
if ( uiPacketType == 3 )
|
||||
{
|
||||
frames[2] = new CDvLastFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
frames[2] = new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 3);
|
||||
}
|
||||
|
||||
// check
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet encoding helpers
|
||||
|
||||
void CDmrplusProtocol::EncodeConnectAckPacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 'A','C','K',' ','O','K',0x0A,0x00 };
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
void CDmrplusProtocol::EncodeConnectNackPacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 'N','A','K',' ','O','K',0x0A,0x00 };
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
bool CDmrplusProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, CBuffer *Buffer) const
|
||||
{
|
||||
uint8 tag[] = { 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,
|
||||
0x00,0x05,0x01,0x02,0x00,0x00,0x00 } ;
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
|
||||
// uiSeqId
|
||||
//Buffer->ReplaceAt(4, 2);
|
||||
// uiPktType
|
||||
//Buffer->ReplaceAt(8, 2);
|
||||
// uiSlot
|
||||
Buffer->Append((uint16)((DMRPLUS_REFLECTOR_SLOT == DMR_SLOT1) ? 0x1111 : 0x2222));
|
||||
// uiSlotType
|
||||
Buffer->Append((uint16)0xEEEE);
|
||||
// uiColourCode
|
||||
uint8 uiColourCode = DMRPLUS_REFLECTOR_COLOUR | (DMRPLUS_REFLECTOR_COLOUR << 4);
|
||||
Buffer->Append((uint8)uiColourCode);
|
||||
Buffer->Append((uint8)uiColourCode);
|
||||
// uiFrameType
|
||||
Buffer->Append((uint16)0x1111);
|
||||
// reserved
|
||||
Buffer->Append((uint16)0x0000);
|
||||
// payload
|
||||
uint32 uiSrcId = Packet.GetMyCallsign().GetDmrid() & 0x00FFFFFF;
|
||||
uint32 uiDstId = ModuleToDmrDestId(Packet.GetRpt2Module()) & 0x00FFFFFF;
|
||||
Buffer->Append((uint8)0x00, 34);
|
||||
Buffer->ReplaceAt(36, HIBYTE(HIWORD(uiSrcId)));
|
||||
Buffer->ReplaceAt(38, LOBYTE(HIWORD(uiSrcId)));
|
||||
Buffer->ReplaceAt(40, HIBYTE(LOWORD(uiSrcId)));
|
||||
Buffer->ReplaceAt(42, LOBYTE(LOWORD(uiSrcId)));
|
||||
|
||||
// reserved
|
||||
Buffer->Append((uint16)0x0000);
|
||||
// uiCallType
|
||||
Buffer->Append((uint8)0x01);
|
||||
// reserved
|
||||
Buffer->Append((uint8)0x00);
|
||||
// uiDstId
|
||||
Buffer->Append(uiDstId);
|
||||
// uiSrcId
|
||||
Buffer->Append(uiSrcId);
|
||||
|
||||
// done
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDmrplusProtocol::EncodeDvPacket
|
||||
(const CDvHeaderPacket &Header,
|
||||
const CDvFramePacket &DvFrame0, const CDvFramePacket &DvFrame1, const CDvFramePacket &DvFrame2,
|
||||
uint8 seqid, CBuffer *Buffer) const
|
||||
{
|
||||
|
||||
uint8 tag[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
|
||||
0x00,0x05,0x01,0x02,0x00,0x00,0x00 } ;
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
|
||||
// uiSeqId
|
||||
Buffer->ReplaceAt(4, seqid);
|
||||
// uiPktType
|
||||
//Buffer->ReplaceAt(8, 1);
|
||||
// uiSlot
|
||||
Buffer->Append((uint16)((DMRPLUS_REFLECTOR_SLOT == DMR_SLOT1) ? 0x1111 : 0x2222));
|
||||
// uiVoiceSeq
|
||||
uint8 uiVoiceSeq = (DvFrame0.GetDmrPacketId() + 7) | ((DvFrame0.GetDmrPacketId() + 7) << 4);
|
||||
Buffer->Append((uint8)uiVoiceSeq);
|
||||
Buffer->Append((uint8)uiVoiceSeq);
|
||||
// uiColourCode
|
||||
uint8 uiColourCode = DMRPLUS_REFLECTOR_COLOUR | (DMRPLUS_REFLECTOR_COLOUR << 4);
|
||||
Buffer->Append((uint8)uiColourCode);
|
||||
Buffer->Append((uint8)uiColourCode);
|
||||
// uiFrameType
|
||||
Buffer->Append((uint16)0x1111);
|
||||
// reserved
|
||||
Buffer->Append((uint16)0x0000);
|
||||
|
||||
// payload
|
||||
uint32 uiSrcId = Header.GetMyCallsign().GetDmrid() & 0x00FFFFFF;
|
||||
uint32 uiDstId = ModuleToDmrDestId(Header.GetRpt2Module()) & 0x00FFFFFF;
|
||||
// frame0
|
||||
Buffer->ReplaceAt(26, DvFrame0.GetAmbePlus(), 9);
|
||||
// 1/2 frame1
|
||||
Buffer->ReplaceAt(35, DvFrame1.GetAmbePlus(), 5);
|
||||
Buffer->ReplaceAt(39, (uint8)(Buffer->at(39) & 0xF0));
|
||||
// 1/2 frame1
|
||||
Buffer->ReplaceAt(45, DvFrame1.GetAmbePlus()+4, 5);
|
||||
Buffer->ReplaceAt(45, (uint8)(Buffer->at(45) & 0x0F));
|
||||
// frame2
|
||||
Buffer->ReplaceAt(50, DvFrame2.GetAmbePlus(), 9);
|
||||
|
||||
// sync or embedded signaling
|
||||
ReplaceEMBInBuffer(Buffer, DvFrame0.GetDmrPacketId());
|
||||
|
||||
// reserved
|
||||
Buffer->Append((uint16)0x0000);
|
||||
Buffer->Append((uint8)0x00);
|
||||
// uiCallType
|
||||
Buffer->Append((uint8)0x01);
|
||||
// reserved
|
||||
Buffer->Append((uint8)0x00);
|
||||
// uiDstId
|
||||
Buffer->Append(uiDstId);
|
||||
// uiSrcId
|
||||
Buffer->Append(uiSrcId);
|
||||
|
||||
// handle indianess
|
||||
SwapEndianess(&(Buffer->data()[26]), 34);
|
||||
}
|
||||
|
||||
|
||||
void CDmrplusProtocol::EncodeDvLastPacket
|
||||
(const CDvHeaderPacket &Header,
|
||||
const CDvFramePacket &DvFrame0, const CDvFramePacket &DvFrame1, const CDvFramePacket &DvFrame2,
|
||||
uint8 seqid, CBuffer *Buffer) const
|
||||
{
|
||||
EncodeDvPacket(Header, DvFrame0, DvFrame1, DvFrame2, seqid, Buffer);
|
||||
Buffer->ReplaceAt(8, (uint8)3);
|
||||
Buffer->ReplaceAt(18, (uint16)0x2222);
|
||||
}
|
||||
|
||||
void CDmrplusProtocol::SwapEndianess(uint8 *buffer, int len) const
|
||||
{
|
||||
for ( int i = 0; i < len; i += 2 )
|
||||
{
|
||||
uint8 t = buffer[i];
|
||||
buffer[i] = buffer[i+1];
|
||||
buffer[i+1] = t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SeqId helper
|
||||
|
||||
uint8 CDmrplusProtocol::GetNextSeqId(uint8 uiSeqId) const
|
||||
{
|
||||
return (uiSeqId + 1) & 0xFF;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// DestId to Module helper
|
||||
|
||||
char CDmrplusProtocol::DmrDstIdToModule(uint32 tg) const
|
||||
{
|
||||
// is it a 4xxx ?
|
||||
if ( (tg >= 4001) && (tg <= (4000 + NB_OF_MODULES)) )
|
||||
{
|
||||
return ((char)(tg - 4001) + 'A');
|
||||
}
|
||||
return ' ';
|
||||
}
|
||||
|
||||
uint32 CDmrplusProtocol::ModuleToDmrDestId(char m) const
|
||||
{
|
||||
return (uint32)(m - 'A')+4001;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Buffer & LC helpers
|
||||
|
||||
void CDmrplusProtocol::AppendVoiceLCToBuffer(CBuffer *buffer, uint32 uiSrcId) const
|
||||
{
|
||||
uint8 payload[34];
|
||||
|
||||
// fill payload
|
||||
CBPTC19696 bptc;
|
||||
::memset(payload, 0, sizeof(payload));
|
||||
// LC data
|
||||
uint8 lc[12];
|
||||
{
|
||||
::memset(lc, 0, sizeof(lc));
|
||||
// uiDstId = TG9
|
||||
lc[5] = 9;
|
||||
// uiSrcId
|
||||
lc[6] = (uint8)LOBYTE(HIWORD(uiSrcId));
|
||||
lc[7] = (uint8)HIBYTE(LOWORD(uiSrcId));
|
||||
lc[8] = (uint8)LOBYTE(LOWORD(uiSrcId));
|
||||
// parity
|
||||
uint8 parity[4];
|
||||
CRS129::encode(lc, 9, parity);
|
||||
lc[9] = parity[2] ^ DMR_VOICE_LC_HEADER_CRC_MASK;
|
||||
lc[10] = parity[1] ^ DMR_VOICE_LC_HEADER_CRC_MASK;
|
||||
lc[11] = parity[0] ^ DMR_VOICE_LC_HEADER_CRC_MASK;
|
||||
}
|
||||
// sync
|
||||
::memcpy(payload+13, g_DmrSyncBSData, sizeof(g_DmrSyncBSData));
|
||||
// slot type
|
||||
{
|
||||
// slot type
|
||||
uint8 slottype[3];
|
||||
::memset(slottype, 0, sizeof(slottype));
|
||||
slottype[0] = (DMRPLUS_REFLECTOR_COLOUR << 4) & 0xF0;
|
||||
slottype[0] |= (DMR_DT_VOICE_LC_HEADER << 0) & 0x0FU;
|
||||
CGolay2087::encode(slottype);
|
||||
payload[12U] = (payload[12U] & 0xC0U) | ((slottype[0U] >> 2) & 0x3FU);
|
||||
payload[13U] = (payload[13U] & 0x0FU) | ((slottype[0U] << 6) & 0xC0U) | ((slottype[1U] >> 2) & 0x30U);
|
||||
payload[19U] = (payload[19U] & 0xF0U) | ((slottype[1U] >> 2) & 0x0FU);
|
||||
payload[20U] = (payload[20U] & 0x03U) | ((slottype[1U] << 6) & 0xC0U) | ((slottype[2U] >> 2) & 0x3CU);
|
||||
|
||||
}
|
||||
// and encode
|
||||
bptc.encode(lc, payload);
|
||||
|
||||
// and append
|
||||
buffer->Append(payload, sizeof(payload));
|
||||
}
|
||||
|
||||
void CDmrplusProtocol::ReplaceEMBInBuffer(CBuffer *buffer, uint8 uiDmrPacketId) const
|
||||
{
|
||||
// voice packet A ?
|
||||
if ( uiDmrPacketId == 0 )
|
||||
{
|
||||
// sync
|
||||
buffer->ReplaceAt(39, (uint8)(buffer->at(39) | (g_DmrSyncBSVoice[0] & 0x0F)));
|
||||
buffer->ReplaceAt(40, g_DmrSyncBSVoice+1, 5);
|
||||
buffer->ReplaceAt(45, (uint8)(buffer->at(45) | (g_DmrSyncBSVoice[6] & 0xF0)));
|
||||
}
|
||||
// voice packet B,C,D,E ?
|
||||
else if ( (uiDmrPacketId >= 1) && (uiDmrPacketId <= 4 ) )
|
||||
{
|
||||
// EMB LC
|
||||
uint8 emb[2];
|
||||
emb[0] = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0;
|
||||
//emb[0] |= PI ? 0x08U : 0x00;
|
||||
//emb[0] |= (LCSS << 1) & 0x06;
|
||||
emb[1] = 0x00;
|
||||
// encode
|
||||
CQR1676::encode(emb);
|
||||
// and append
|
||||
buffer->ReplaceAt(39, (uint8)((buffer->at(39) & 0xF0) | ((emb[0U] >> 4) & 0x0F)));
|
||||
buffer->ReplaceAt(40, (uint8)((buffer->at(40) & 0x0F) | ((emb[0U] << 4) & 0xF0)));
|
||||
buffer->ReplaceAt(40, (uint8)(buffer->at(40) & 0xF0));
|
||||
buffer->ReplaceAt(41, (uint8)0);
|
||||
buffer->ReplaceAt(42, (uint8)0);
|
||||
buffer->ReplaceAt(43, (uint8)0);
|
||||
buffer->ReplaceAt(44, (uint8)(buffer->at(44) & 0x0F));
|
||||
buffer->ReplaceAt(44, (uint8)((buffer->at(44) & 0xF0) | ((emb[1U] >> 4) & 0x0F)));
|
||||
buffer->ReplaceAt(45, (uint8)((buffer->at(45) & 0x0F) | ((emb[1U] << 4) & 0xF0)));
|
||||
}
|
||||
// voice packet F
|
||||
else
|
||||
{
|
||||
// NULL
|
||||
uint8 emb[2];
|
||||
emb[0] = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0;
|
||||
//emb[0] |= PI ? 0x08U : 0x00;
|
||||
//emb[0] |= (LCSS << 1) & 0x06;
|
||||
emb[1] = 0x00;
|
||||
// encode
|
||||
CQR1676::encode(emb);
|
||||
// and append
|
||||
buffer->ReplaceAt(39, (uint8)((buffer->at(39) & 0xF0) | ((emb[0U] >> 4) & 0x0F)));
|
||||
buffer->ReplaceAt(40, (uint8)((buffer->at(40) & 0x0F) | ((emb[0U] << 4) & 0xF0)));
|
||||
buffer->ReplaceAt(40, (uint8)(buffer->at(40) & 0xF0));
|
||||
buffer->ReplaceAt(41, (uint8)0);
|
||||
buffer->ReplaceAt(42, (uint8)0);
|
||||
buffer->ReplaceAt(43, (uint8)0);
|
||||
buffer->ReplaceAt(44, (uint8)(buffer->at(44) & 0x0F));
|
||||
buffer->ReplaceAt(44, (uint8)((buffer->at(44) & 0xF0) | ((emb[1U] >> 4) & 0x0F)));
|
||||
buffer->ReplaceAt(45, (uint8)((buffer->at(45) & 0x0F) | ((emb[1U] << 4) & 0xF0)));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// uiStreamId helpers
|
||||
|
||||
|
||||
// uiStreamId helpers
|
||||
uint32 CDmrplusProtocol::IpToStreamId(const CIp &ip) const
|
||||
{
|
||||
return ip.GetAddr() ^ (uint32)(MAKEDWORD(ip.GetPort(), ip.GetPort()));
|
||||
}
|
||||
590
cdplusprotocol.cpp
Normal file
590
cdplusprotocol.cpp
Normal file
|
|
@ -0,0 +1,590 @@
|
|||
//
|
||||
// cdplusprotocol.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include "cdplusclient.h"
|
||||
#include "cdplusprotocol.h"
|
||||
#include "creflector.h"
|
||||
#include "cgatekeeper.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operation
|
||||
|
||||
bool CDplusProtocol::Init(void)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
// base class
|
||||
ok = CProtocol::Init();
|
||||
|
||||
// update the reflector callsign
|
||||
m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"REF", 3);
|
||||
|
||||
// create our socket
|
||||
ok &= m_Socket.Open(DPLUS_PORT);
|
||||
if ( !ok )
|
||||
{
|
||||
std::cout << "Error opening socket on port UDP" << DPLUS_PORT << " on ip " << g_Reflector.GetListenIp() << std::endl;
|
||||
}
|
||||
|
||||
// update time
|
||||
m_LastKeepaliveTime.Now();
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// task
|
||||
|
||||
void CDplusProtocol::Task(void)
|
||||
{
|
||||
CBuffer Buffer;
|
||||
CIp Ip;
|
||||
CCallsign Callsign;
|
||||
CDvHeaderPacket *Header;
|
||||
CDvFramePacket *Frame;
|
||||
CDvLastFramePacket *LastFrame;
|
||||
|
||||
// handle incoming packets
|
||||
if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
|
||||
{
|
||||
// crack the packet
|
||||
if ( (Frame = IsValidDvFramePacket(Buffer)) != NULL )
|
||||
{
|
||||
//std::cout << "DPlus DV frame" << std::endl;
|
||||
|
||||
// handle it
|
||||
OnDvFramePacketIn(Frame, &Ip);
|
||||
}
|
||||
else if ( (Header = IsValidDvHeaderPacket(Buffer)) != NULL )
|
||||
{
|
||||
//std::cout << "DPlus DV header:" << std::endl << *Header << std::endl;
|
||||
|
||||
// callsign muted?
|
||||
if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DPLUS, Header->GetRpt2Module()) )
|
||||
{
|
||||
// handle it
|
||||
OnDvHeaderPacketIn(Header, Ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete Header;
|
||||
}
|
||||
}
|
||||
else if ( (LastFrame = IsValidDvLastFramePacket(Buffer)) != NULL )
|
||||
{
|
||||
//std::cout << "DPlus DV last frame" << std::endl;
|
||||
|
||||
// handle it
|
||||
OnDvLastFramePacketIn(LastFrame, &Ip);
|
||||
}
|
||||
else if ( IsValidConnectPacket(Buffer) )
|
||||
{
|
||||
std::cout << "DPlus connect request packet from " << Ip << std::endl;
|
||||
|
||||
// acknowledge the request
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
else if ( IsValidLoginPacket(Buffer, &Callsign) )
|
||||
{
|
||||
std::cout << "DPlus login packet from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// callsign authorized?
|
||||
if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DPLUS) )
|
||||
{
|
||||
// acknowledge the request
|
||||
EncodeLoginAckPacket(&Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
|
||||
// create the client
|
||||
CDplusClient *client = new CDplusClient(Callsign, Ip);
|
||||
|
||||
// and append
|
||||
g_Reflector.GetClients()->AddClient(client);
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else
|
||||
{
|
||||
// deny the request
|
||||
EncodeLoginNackPacket(&Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
|
||||
}
|
||||
else if ( IsValidDisconnectPacket(Buffer) )
|
||||
{
|
||||
std::cout << "DPlus disconnect packet from " << Ip << std::endl;
|
||||
|
||||
// find client
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
CClient *client = clients->FindClient(Ip, PROTOCOL_DPLUS);
|
||||
if ( client != NULL )
|
||||
{
|
||||
// remove it
|
||||
clients->RemoveClient(client);
|
||||
// and acknowledge the disconnect
|
||||
EncodeDisconnectPacket(&Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else if ( IsValidKeepAlivePacket(Buffer) )
|
||||
{
|
||||
//std::cout << "DPlus keepalive packet from " << Ip << std::endl;
|
||||
|
||||
// find all clients with that callsign & ip and keep them alive
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(Ip, PROTOCOL_DPLUS, &index)) != NULL )
|
||||
{
|
||||
client->Alive();
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "DPlus packet (" << Buffer.size() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// handle end of streaming timeout
|
||||
CheckStreamsTimeout();
|
||||
|
||||
// handle queue from reflector
|
||||
HandleQueue();
|
||||
|
||||
// keep client alive
|
||||
if ( m_LastKeepaliveTime.DurationSinceNow() > DPLUS_KEEPALIVE_PERIOD )
|
||||
{
|
||||
//
|
||||
HandleKeepalives();
|
||||
|
||||
// update time
|
||||
m_LastKeepaliveTime.Now();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// streams helpers
|
||||
|
||||
bool CDplusProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
|
||||
{
|
||||
bool newstream = false;
|
||||
|
||||
// find the stream
|
||||
CPacketStream *stream = GetStream(Header->GetStreamId());
|
||||
if ( stream == NULL )
|
||||
{
|
||||
// no stream open yet, open a new one
|
||||
CCallsign via(Header->GetRpt1Callsign());
|
||||
|
||||
// first, check module is valid
|
||||
if ( g_Reflector.IsValidModule(Header->GetRpt1Module()) )
|
||||
{
|
||||
// find this client
|
||||
CClient *client = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DPLUS);
|
||||
if ( client != NULL )
|
||||
{
|
||||
// now we know if it's a dextra dongle or a genuine dplus node
|
||||
if ( Header->GetRpt2Callsign().HasSameCallsignWithWildcard(CCallsign("XRF*")) )
|
||||
{
|
||||
client->SetDextraDongle();
|
||||
}
|
||||
// now we know its module, let's update it
|
||||
if ( !client->HasModule() )
|
||||
{
|
||||
client->SetModule(Header->GetRpt1Module());
|
||||
}
|
||||
// get client callsign
|
||||
via = client->GetCallsign();
|
||||
// and try to open the stream
|
||||
if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
|
||||
{
|
||||
// keep the handle
|
||||
m_Streams.push_back(stream);
|
||||
newstream = true;
|
||||
}
|
||||
}
|
||||
// release
|
||||
g_Reflector.ReleaseClients();
|
||||
|
||||
// update last heard
|
||||
g_Reflector.GetUsers()->Hearing(Header->GetMyCallsign(), via, Header->GetRpt2Callsign());
|
||||
g_Reflector.ReleaseUsers();
|
||||
|
||||
// delete header if needed
|
||||
if ( !newstream )
|
||||
{
|
||||
delete Header;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "DPlus node " << via << " link attempt on non-existing module" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// stream already open
|
||||
// skip packet, but tickle the stream
|
||||
stream->Tickle();
|
||||
// and delete packet
|
||||
delete Header;
|
||||
}
|
||||
|
||||
// done
|
||||
return newstream;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// queue helper
|
||||
|
||||
void CDplusProtocol::HandleQueue(void)
|
||||
{
|
||||
m_Queue.Lock();
|
||||
while ( !m_Queue.empty() )
|
||||
{
|
||||
// get the packet
|
||||
CPacket *packet = m_Queue.front();
|
||||
m_Queue.pop();
|
||||
|
||||
// get our sender's id
|
||||
int iModId = g_Reflector.GetModuleIndex(packet->GetModuleId());
|
||||
|
||||
// check if it's header and update cache
|
||||
if ( packet->IsDvHeader() )
|
||||
{
|
||||
// this relies on queue feeder setting valid module id
|
||||
m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet);
|
||||
m_StreamsCache[iModId].m_iSeqCounter = 0;
|
||||
}
|
||||
|
||||
// encode it
|
||||
CBuffer buffer;
|
||||
if ( EncodeDvPacket(*packet, &buffer) )
|
||||
{
|
||||
// and push it to all our clients who are not streaming in
|
||||
// note that for dplus protocol, all stream of all modules are push to all clients
|
||||
// it's client who decide which stream he's interrrested in
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(PROTOCOL_DPLUS, &index)) != NULL )
|
||||
{
|
||||
// is this client busy ?
|
||||
if ( !client->IsAMaster() )
|
||||
{
|
||||
// check if client is a dextra dongle
|
||||
// then replace RPT2 with XRF instead of REF
|
||||
// if the client type is not yet known, send bothheaders
|
||||
if ( packet->IsDvHeader() )
|
||||
{
|
||||
// sending header in Dplus is client specific
|
||||
SendDvHeader((CDvHeaderPacket *)packet, (CDplusClient *)client);
|
||||
}
|
||||
else if ( packet->IsDvFrame() )
|
||||
{
|
||||
// and send the DV frame
|
||||
m_Socket.Send(buffer, client->GetIp());
|
||||
|
||||
// is it time to insert a DVheader copy ?
|
||||
if ( (m_StreamsCache[iModId].m_iSeqCounter++ % 21) == 20 )
|
||||
{
|
||||
// yes, clone it
|
||||
CDvHeaderPacket packet2(m_StreamsCache[iModId].m_dvHeader);
|
||||
// and send it
|
||||
SendDvHeader(&packet2, (CDplusClient *)client);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, send the original packet
|
||||
m_Socket.Send(buffer, client->GetIp());
|
||||
}
|
||||
}
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
|
||||
|
||||
// done
|
||||
delete packet;
|
||||
}
|
||||
m_Queue.Unlock();
|
||||
}
|
||||
|
||||
void CDplusProtocol::SendDvHeader(CDvHeaderPacket *packet, CDplusClient *client)
|
||||
{
|
||||
// encode it
|
||||
CBuffer buffer;
|
||||
if ( EncodeDvPacket(*packet, &buffer) )
|
||||
{
|
||||
if ( (client->IsDextraDongle() || !client->HasModule()) )
|
||||
{
|
||||
// clone the packet and patch it
|
||||
CDvHeaderPacket packet2(*((CDvHeaderPacket *)packet));
|
||||
CCallsign rpt2 = packet2.GetRpt2Callsign();
|
||||
rpt2.PatchCallsign(0, (const uint8 *)"XRF", 3);
|
||||
packet2.SetRpt2Callsign(rpt2);
|
||||
|
||||
// encode it
|
||||
CBuffer buffer2;
|
||||
if ( EncodeDvPacket(packet2, &buffer2) )
|
||||
{
|
||||
// and send it
|
||||
m_Socket.Send(buffer2, client->GetIp());
|
||||
}
|
||||
|
||||
// client type known ?
|
||||
if ( !client->HasModule() )
|
||||
{
|
||||
// no, send also the genuine packet
|
||||
m_Socket.Send(buffer, client->GetIp());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, send the original packet
|
||||
m_Socket.Send(buffer, client->GetIp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// keepalive helpers
|
||||
|
||||
void CDplusProtocol::HandleKeepalives(void)
|
||||
{
|
||||
// send keepalives
|
||||
CBuffer keepalive;
|
||||
EncodeKeepAlivePacket(&keepalive);
|
||||
|
||||
// iterate on clients
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(PROTOCOL_DPLUS, &index)) != NULL )
|
||||
{
|
||||
// send keepalive
|
||||
//std::cout << "Sending DPlus packet @ " << client->GetIp() << std::endl;
|
||||
m_Socket.Send(keepalive, client->GetIp());
|
||||
|
||||
// is this client busy ?
|
||||
if ( client->IsAMaster() )
|
||||
{
|
||||
// yes, just tickle it
|
||||
client->Alive();
|
||||
}
|
||||
// check it's still with us
|
||||
else if ( !client->IsAlive() )
|
||||
{
|
||||
// no, disconnect
|
||||
CBuffer disconnect;
|
||||
EncodeDisconnectPacket(&disconnect);
|
||||
m_Socket.Send(disconnect, client->GetIp());
|
||||
|
||||
// and remove it
|
||||
std::cout << "DPlus client " << client->GetCallsign() << " keepalive timeout" << std::endl;
|
||||
clients->RemoveClient(client);
|
||||
}
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet decoding helpers
|
||||
|
||||
bool CDplusProtocol::IsValidConnectPacket(const CBuffer &Buffer)
|
||||
{
|
||||
uint8 tag[] = { 0x05,0x00,0x18,0x00,0x01 };
|
||||
return (Buffer == CBuffer(tag, sizeof(tag)));
|
||||
}
|
||||
|
||||
bool CDplusProtocol::IsValidLoginPacket(const CBuffer &Buffer, CCallsign *Callsign)
|
||||
{
|
||||
uint8 Tag[] = { 0x1C,0xC0,0x04,0x00 };
|
||||
bool valid = false;
|
||||
|
||||
if ( (Buffer.size() == 28) &&(::memcmp(Buffer.data(), Tag, sizeof(Tag)) == 0) )
|
||||
{
|
||||
Callsign->SetCallsign(&(Buffer.data()[4]), 8);
|
||||
valid = Callsign->IsValid();
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CDplusProtocol::IsValidDisconnectPacket(const CBuffer &Buffer)
|
||||
{
|
||||
uint8 tag[] = { 0x05,0x00,0x18,0x00,0x00 };
|
||||
return (Buffer == CBuffer(tag, sizeof(tag)));
|
||||
}
|
||||
|
||||
bool CDplusProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer)
|
||||
{
|
||||
uint8 tag[] = { 0x03,0x60,0x00 };
|
||||
return (Buffer == CBuffer(tag, sizeof(tag)));
|
||||
}
|
||||
|
||||
CDvHeaderPacket *CDplusProtocol::IsValidDvHeaderPacket(const CBuffer &Buffer)
|
||||
{
|
||||
CDvHeaderPacket *header = NULL;
|
||||
|
||||
if ( (Buffer.size() == 58) &&
|
||||
(Buffer.data()[0] == 0x3A) && (Buffer.data()[1] == 0x80) &&
|
||||
(Buffer.Compare((uint8 *)"DSVT", 2, 4) == 0) &&
|
||||
(Buffer.data()[6] == 0x10) && (Buffer.data()[10] == 0x20) )
|
||||
{
|
||||
// create packet
|
||||
header = new CDvHeaderPacket((struct dstar_header *)&(Buffer.data()[17]),
|
||||
*((uint16 *)&(Buffer.data()[14])), 0x80);
|
||||
// check validity of packet
|
||||
if ( !header->IsValid() )
|
||||
{
|
||||
delete header;
|
||||
header = NULL;
|
||||
}
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
CDvFramePacket *CDplusProtocol::IsValidDvFramePacket(const CBuffer &Buffer)
|
||||
{
|
||||
CDvFramePacket *dvframe = NULL;
|
||||
|
||||
if ( (Buffer.size() == 29) &&
|
||||
(Buffer.data()[0] == 0x1D) && (Buffer.data()[1] == 0x80) &&
|
||||
(Buffer.Compare((uint8 *)"DSVT", 2, 4) == 0) &&
|
||||
(Buffer.data()[6] == 0x20) && (Buffer.data()[10] == 0x20) )
|
||||
{
|
||||
// create packet
|
||||
dvframe = new CDvFramePacket((struct dstar_dvframe *)&(Buffer.data()[17]),
|
||||
*((uint16 *)&(Buffer.data()[14])), Buffer.data()[16]);
|
||||
// check validity of packet
|
||||
if ( !dvframe->IsValid() )
|
||||
{
|
||||
delete dvframe;
|
||||
dvframe = NULL;
|
||||
}
|
||||
}
|
||||
return dvframe;
|
||||
}
|
||||
|
||||
CDvLastFramePacket *CDplusProtocol::IsValidDvLastFramePacket(const CBuffer &Buffer)
|
||||
{
|
||||
CDvLastFramePacket *dvframe = NULL;
|
||||
|
||||
if ( (Buffer.size() == 32) &&
|
||||
(Buffer.Compare((uint8 *)"DSVT", 2, 4) == 0) &&
|
||||
(Buffer.data()[0] == 0x20) && (Buffer.data()[1] == 0x80) &&
|
||||
(Buffer.data()[6] == 0x20) && (Buffer.data()[10] == 0x20) )
|
||||
{
|
||||
// create packet
|
||||
dvframe = new CDvLastFramePacket((struct dstar_dvframe *)&(Buffer.data()[17]),
|
||||
*((uint16 *)&(Buffer.data()[14])), Buffer.data()[16]);
|
||||
// check validity of packet
|
||||
if ( !dvframe->IsValid() )
|
||||
{
|
||||
delete dvframe;
|
||||
dvframe = NULL;
|
||||
}
|
||||
}
|
||||
return dvframe;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet encoding helpers
|
||||
|
||||
void CDplusProtocol::EncodeKeepAlivePacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 0x03,0x60,0x00 };
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
void CDplusProtocol::EncodeLoginAckPacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 0x08,0xC0,0x04,0x00,'O','K','R','W' };
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
void CDplusProtocol::EncodeLoginNackPacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 0x08,0xC0,0x04,0x00,'B','U','S','Y' };
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
void CDplusProtocol::EncodeDisconnectPacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 0x05,0x00,0x18,0x00,0x00 };
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
}
|
||||
|
||||
|
||||
bool CDplusProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, CBuffer *Buffer) const
|
||||
{
|
||||
uint8 tag[] = { 0x3A,0x80,0x44,0x53,0x56,0x54,0x10,0x00,0x00,0x00,0x20,0x00,0x01,0x02 };
|
||||
struct dstar_header DstarHeader;
|
||||
|
||||
Packet.ConvertToDstarStruct(&DstarHeader);
|
||||
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
Buffer->Append(Packet.GetStreamId());
|
||||
Buffer->Append((uint8)0x80);
|
||||
Buffer->Append((uint8 *)&DstarHeader, sizeof(struct dstar_header));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDplusProtocol::EncodeDvFramePacket(const CDvFramePacket &Packet, CBuffer *Buffer) const
|
||||
{
|
||||
uint8 tag[] = { 0x1D,0x80,0x44,0x53,0x56,0x54,0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 };
|
||||
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
Buffer->Append(Packet.GetStreamId());
|
||||
Buffer->Append((uint8)(Packet.GetPacketId() % 21));
|
||||
Buffer->Append((uint8 *)Packet.GetAmbe(), AMBE_SIZE);
|
||||
Buffer->Append((uint8 *)Packet.GetDvData(), DVDATA_SIZE);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool CDplusProtocol::EncodeDvLastFramePacket(const CDvLastFramePacket &Packet, CBuffer *Buffer) const
|
||||
{
|
||||
uint8 tag1[] = { 0x20,0x80,0x44,0x53,0x56,0x54,0x20,0x00,0x81,0x00,0x20,0x00,0x01,0x02 };
|
||||
uint8 tag2[] = { 0x55,0xC8,0x7A,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x25,0x1A,0xC6 };
|
||||
|
||||
Buffer->Set(tag1, sizeof(tag1));
|
||||
Buffer->Append(Packet.GetStreamId());
|
||||
Buffer->Append((uint8)((Packet.GetPacketId() % 21) | 0x40));
|
||||
Buffer->Append(tag2, sizeof(tag2));
|
||||
|
||||
return true;
|
||||
}
|
||||
175
cpeer.cpp
Normal file
175
cpeer.cpp
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
//
|
||||
// cpeer.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 10/12/2016.
|
||||
// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include "creflector.h"
|
||||
#include "cpeer.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
|
||||
CPeer::CPeer()
|
||||
{
|
||||
::memset(m_ReflectorModules, 0, sizeof(m_ReflectorModules));
|
||||
m_Clients.reserve(100);
|
||||
m_ConnectTime = std::time(NULL);
|
||||
m_LastHeardTime = std::time(NULL);
|
||||
}
|
||||
|
||||
CPeer::CPeer(const CCallsign &callsign, const CIp &ip, char *modules, const CVersion &version)
|
||||
{
|
||||
m_Callsign = callsign;
|
||||
m_Ip = ip;
|
||||
::memset(m_ReflectorModules, 0, sizeof(m_ReflectorModules));
|
||||
::strncpy(m_ReflectorModules, modules, sizeof(m_ReflectorModules)-1);
|
||||
m_Version = version;
|
||||
m_LastKeepaliveTime.Now();
|
||||
m_ConnectTime = std::time(NULL);
|
||||
m_LastHeardTime = std::time(NULL);
|
||||
}
|
||||
|
||||
CPeer::CPeer(const CPeer &peer)
|
||||
{
|
||||
m_Callsign = peer.m_Callsign;
|
||||
m_Ip = peer.m_Ip;
|
||||
::memcpy(m_ReflectorModules, peer.m_ReflectorModules, sizeof(m_ReflectorModules));
|
||||
m_Version = peer.m_Version;
|
||||
m_LastKeepaliveTime = peer.m_LastKeepaliveTime;
|
||||
m_ConnectTime = peer.m_ConnectTime;
|
||||
m_LastHeardTime = peer.m_LastHeardTime;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// destructors
|
||||
|
||||
CPeer::~CPeer()
|
||||
{
|
||||
for ( unsigned int i = 0; i < m_Clients.size(); i++ )
|
||||
{
|
||||
delete m_Clients[i];
|
||||
}
|
||||
m_Clients.clear();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operators
|
||||
|
||||
bool CPeer::operator ==(const CPeer &peer) const
|
||||
{
|
||||
bool same = true;
|
||||
|
||||
same &= (peer.m_Callsign == m_Callsign);
|
||||
same &= (peer.m_Ip == m_Ip);
|
||||
same &= (peer.m_Version == m_Version);
|
||||
for ( unsigned int i = 0; (i < m_Clients.size()) && same ; i++ )
|
||||
{
|
||||
same &= (peer.m_Clients[i] == m_Clients[i]);
|
||||
}
|
||||
return same;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// status
|
||||
|
||||
bool CPeer::IsAMaster(void) const
|
||||
{
|
||||
bool master = false;
|
||||
for ( unsigned int i = 0; (i < m_Clients.size()) && !master ; i++ )
|
||||
{
|
||||
master |= m_Clients[i]->IsAMaster();
|
||||
}
|
||||
return master;
|
||||
}
|
||||
|
||||
void CPeer::Alive(void)
|
||||
{
|
||||
m_LastKeepaliveTime.Now();;
|
||||
for ( unsigned int i = 0; i < m_Clients.size(); i++ )
|
||||
{
|
||||
m_Clients[i]->Alive();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// manage clients
|
||||
|
||||
CClient *CPeer::GetClient(int i)
|
||||
{
|
||||
if ( (i >= 0) && ((unsigned int)i < m_Clients.size()) )
|
||||
{
|
||||
return m_Clients[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// reporting
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// reporting
|
||||
|
||||
void CPeer::WriteXml(std::ofstream &xmlFile)
|
||||
{
|
||||
xmlFile << "<PEER>" << std::endl;
|
||||
xmlFile << "\t<Callsign>" << m_Callsign << "</Callsign>" << std::endl;
|
||||
xmlFile << "\t<IP>" << m_Ip << "</IP>" << std::endl;
|
||||
xmlFile << "\t<LinkedModule>" << m_ReflectorModules << "</LinkedModule>" << std::endl;
|
||||
xmlFile << "\t<Protocol>" << GetProtocolName() << "</Protocol>" << std::endl;
|
||||
char mbstr[100];
|
||||
if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_ConnectTime)))
|
||||
{
|
||||
xmlFile << "\t<ConnectTime>" << mbstr << "</ConnectTime>" << std::endl;
|
||||
}
|
||||
if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_LastHeardTime)))
|
||||
{
|
||||
xmlFile << "\t<LastHeardTime>" << mbstr << "</LastHeardTime>" << std::endl;
|
||||
}
|
||||
xmlFile << "</PEER>" << std::endl;
|
||||
}
|
||||
|
||||
void CPeer::GetJsonObject(char *Buffer)
|
||||
{
|
||||
char sz[512];
|
||||
char mbstr[100];
|
||||
char cs[16];
|
||||
|
||||
if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_LastHeardTime)))
|
||||
{
|
||||
m_Callsign.GetCallsignString(cs);
|
||||
|
||||
::sprintf(sz, "{\"callsign\":\"%s\",\"linkedto\":\"%s\",\"time\":\"%s\"}",
|
||||
cs,
|
||||
m_ReflectorModules,
|
||||
mbstr);
|
||||
::strcat(Buffer, sz);
|
||||
}
|
||||
}
|
||||
230
cpeers.cpp
Normal file
230
cpeers.cpp
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
//
|
||||
// cpeers.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 10/12/2016.
|
||||
// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include "creflector.h"
|
||||
#include "cpeers.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
|
||||
CPeers::CPeers()
|
||||
{
|
||||
m_Peers.reserve(100);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// destructors
|
||||
|
||||
CPeers::~CPeers()
|
||||
{
|
||||
m_Mutex.lock();
|
||||
{
|
||||
for ( unsigned int i = 0; i < m_Peers.size(); i++ )
|
||||
{
|
||||
delete m_Peers[i];
|
||||
}
|
||||
m_Peers.clear();
|
||||
|
||||
}
|
||||
m_Mutex.unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// manage peers
|
||||
|
||||
void CPeers::AddPeer(CPeer *peer)
|
||||
{
|
||||
// first check if peer already exists
|
||||
bool found = false;
|
||||
for ( unsigned int i = 0; (i < m_Peers.size()) && !found; i++ )
|
||||
{
|
||||
found = (*peer == *m_Peers[i]);
|
||||
// if found, just do nothing
|
||||
// so *peer keep pointing on a valid object
|
||||
// on function return
|
||||
if ( found )
|
||||
{
|
||||
// delete new one
|
||||
delete peer;
|
||||
//std::cout << "Adding existing peer " << peer->GetCallsign() << " at " << peer->GetIp() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// if not, append to the vector
|
||||
if ( !found )
|
||||
{
|
||||
// grow vector capacity if needed
|
||||
if ( m_Peers.capacity() == m_Peers.size() )
|
||||
{
|
||||
m_Peers.reserve(m_Peers.capacity()+10);
|
||||
}
|
||||
// append peer to reflector peer list
|
||||
m_Peers.push_back(peer);
|
||||
std::cout << "New peer " << peer->GetCallsign() << " at " << peer->GetIp()
|
||||
<< " added with protocol " << peer->GetProtocolName() << std::endl;
|
||||
// and append all peer's client to reflector client list
|
||||
// it is double lock safe to lock Clients list after Peers list
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
for ( int i = 0; i < peer->GetNbClients(); i++ )
|
||||
{
|
||||
clients->AddClient(peer->GetClient(i));
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
|
||||
// notify
|
||||
g_Reflector.OnPeersChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void CPeers::RemovePeer(CPeer *peer)
|
||||
{
|
||||
// look for the client
|
||||
bool found = false;
|
||||
for ( unsigned int i = 0; (i < m_Peers.size()) && !found; i++ )
|
||||
{
|
||||
// compare objetc pointers
|
||||
if ( (m_Peers[i]) == peer )
|
||||
{
|
||||
// found it !
|
||||
if ( !m_Peers[i]->IsAMaster() )
|
||||
{
|
||||
// remove all clients from reflector client list
|
||||
// it is double lock safe to lock Clients list after Peers list
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
for ( int i = 0; i < peer->GetNbClients(); i++ )
|
||||
{
|
||||
// this also delete the client object
|
||||
clients->RemoveClient(peer->GetClient(i));
|
||||
}
|
||||
// so clear it then
|
||||
m_Peers[i]->ClearClients();
|
||||
g_Reflector.ReleaseClients();
|
||||
|
||||
// remove it
|
||||
std::cout << "Peer " << m_Peers[i]->GetCallsign() << " at " << m_Peers[i]->GetIp()
|
||||
<< " removed" << std::endl;
|
||||
delete m_Peers[i];
|
||||
m_Peers.erase(m_Peers.begin()+i);
|
||||
found = true;
|
||||
// notify
|
||||
g_Reflector.OnPeersChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CPeer *CPeers::GetPeer(int i)
|
||||
{
|
||||
if ( (i >= 0) && ((unsigned int)i < m_Peers.size()) )
|
||||
{
|
||||
return m_Peers[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// find peers
|
||||
|
||||
CPeer *CPeers::FindPeer(const CIp &Ip, int Protocol)
|
||||
{
|
||||
CPeer *peer = NULL;
|
||||
|
||||
// find peer
|
||||
for ( unsigned int i = 0; (i < m_Peers.size()) && (peer == NULL); i++ )
|
||||
{
|
||||
if ( (m_Peers[i]->GetIp() == Ip) && (m_Peers[i]->GetProtocol() == Protocol))
|
||||
{
|
||||
peer = m_Peers[i];
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return peer;
|
||||
}
|
||||
|
||||
CPeer *CPeers::FindPeer(const CCallsign &Callsign, const CIp &Ip, int Protocol)
|
||||
{
|
||||
CPeer *peer = NULL;
|
||||
|
||||
// find peer
|
||||
for ( unsigned int i = 0; (i < m_Peers.size()) && (peer == NULL); i++ )
|
||||
{
|
||||
if ( m_Peers[i]->GetCallsign().HasSameCallsign(Callsign) &&
|
||||
(m_Peers[i]->GetIp() == Ip) &&
|
||||
(m_Peers[i]->GetProtocol() == Protocol) )
|
||||
{
|
||||
peer = m_Peers[i];
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return peer;
|
||||
}
|
||||
|
||||
CPeer *CPeers::FindPeer(const CCallsign &Callsign, int Protocol)
|
||||
{
|
||||
CPeer *peer = NULL;
|
||||
|
||||
// find peer
|
||||
for ( unsigned int i = 0; (i < m_Peers.size()) && (peer == NULL); i++ )
|
||||
{
|
||||
if ( (m_Peers[i]->GetProtocol() == Protocol) &&
|
||||
m_Peers[i]->GetCallsign().HasSameCallsign(Callsign) )
|
||||
{
|
||||
peer = m_Peers[i];
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return peer;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// iterate on peers
|
||||
|
||||
CPeer *CPeers::FindNextPeer(int Protocol, int *index)
|
||||
{
|
||||
CPeer *peer = NULL;
|
||||
|
||||
// find next peer
|
||||
bool found = false;
|
||||
for ( unsigned int i = *index+1; (i < m_Peers.size()) && !found; i++ )
|
||||
{
|
||||
if ( m_Peers[i]->GetProtocol() == Protocol )
|
||||
{
|
||||
found = true;
|
||||
peer = m_Peers[i];
|
||||
*index = i;
|
||||
}
|
||||
}
|
||||
return peer;
|
||||
}
|
||||
|
||||
257
cprotocol.cpp
Normal file
257
cprotocol.cpp
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
//
|
||||
// cprotocol.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include "cprotocol.h"
|
||||
#include "cclients.h"
|
||||
#include "creflector.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
|
||||
CProtocol::CProtocol()
|
||||
{
|
||||
m_bStopThread = false;
|
||||
m_pThread = NULL;
|
||||
m_Streams.reserve(NB_OF_MODULES);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// destructor
|
||||
|
||||
CProtocol::~CProtocol()
|
||||
{
|
||||
// kill threads
|
||||
m_bStopThread = true;
|
||||
if ( m_pThread != NULL )
|
||||
{
|
||||
m_pThread->join();
|
||||
delete m_pThread;
|
||||
}
|
||||
|
||||
// empty queue
|
||||
m_Queue.Lock();
|
||||
while ( !m_Queue.empty() )
|
||||
{
|
||||
m_Queue.pop();
|
||||
}
|
||||
m_Queue.Unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// initialization
|
||||
|
||||
bool CProtocol::Init(void)
|
||||
{
|
||||
// init reflector apparent callsign
|
||||
m_ReflectorCallsign = g_Reflector.GetCallsign();
|
||||
|
||||
// reset stop flag
|
||||
m_bStopThread = false;
|
||||
|
||||
// start thread;
|
||||
m_pThread = new std::thread(CProtocol::Thread, this);
|
||||
|
||||
// done
|
||||
return true;
|
||||
}
|
||||
|
||||
void CProtocol::Close(void)
|
||||
{
|
||||
m_bStopThread = true;
|
||||
if ( m_pThread != NULL )
|
||||
{
|
||||
m_pThread->join();
|
||||
delete m_pThread;
|
||||
m_pThread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// thread
|
||||
|
||||
void CProtocol::Thread(CProtocol *This)
|
||||
{
|
||||
while ( !This->m_bStopThread )
|
||||
{
|
||||
This->Task();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet encoding helpers
|
||||
|
||||
bool CProtocol::EncodeDvPacket(const CPacket &packet, CBuffer *buffer) const
|
||||
{
|
||||
bool ok = false;
|
||||
if ( packet.IsDvFrame() )
|
||||
{
|
||||
if ( packet.IsLastPacket() )
|
||||
{
|
||||
ok = EncodeDvLastFramePacket((const CDvLastFramePacket &)packet, buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = EncodeDvFramePacket((const CDvFramePacket &)packet, buffer);
|
||||
}
|
||||
}
|
||||
else if ( packet.IsDvHeader() )
|
||||
{
|
||||
ok = EncodeDvHeaderPacket((const CDvHeaderPacket &)packet, buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->clear();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// streams helpers
|
||||
|
||||
void CProtocol::OnDvFramePacketIn(CDvFramePacket *Frame, const CIp *Ip)
|
||||
{
|
||||
// find the stream
|
||||
CPacketStream *stream = GetStream(Frame->GetStreamId(), Ip);
|
||||
if ( stream != NULL )
|
||||
{
|
||||
//std::cout << "DV frame" << "from " << *Ip << std::endl;
|
||||
// and push
|
||||
stream->Lock();
|
||||
stream->Push(Frame);
|
||||
stream->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CProtocol::OnDvLastFramePacketIn(CDvLastFramePacket *Frame, const CIp *Ip)
|
||||
{
|
||||
// find the stream
|
||||
CPacketStream *stream = GetStream(Frame->GetStreamId(), Ip);
|
||||
if ( stream != NULL )
|
||||
{
|
||||
// push
|
||||
stream->Lock();
|
||||
stream->Push(Frame);
|
||||
stream->Unlock();
|
||||
|
||||
// and close the stream
|
||||
g_Reflector.CloseStream(stream);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// stream handle helpers
|
||||
|
||||
CPacketStream *CProtocol::GetStream(uint16 uiStreamId, const CIp *Ip)
|
||||
{
|
||||
CPacketStream *stream = NULL;
|
||||
|
||||
// find if we have a stream with same streamid in our cache
|
||||
for ( unsigned int i = 0; (i < m_Streams.size()) && (stream == NULL); i++ )
|
||||
{
|
||||
if ( m_Streams[i]->GetStreamId() == uiStreamId )
|
||||
{
|
||||
// if Ip not NULL, also check if IP match
|
||||
if ( (Ip != NULL) && (*Ip == *(m_Streams[i]->GetOwnerIp())) )
|
||||
{
|
||||
stream = m_Streams[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// done
|
||||
return stream;
|
||||
}
|
||||
|
||||
void CProtocol::CheckStreamsTimeout(void)
|
||||
{
|
||||
for ( unsigned int i = 0; i < m_Streams.size(); i++ )
|
||||
{
|
||||
// time out ?
|
||||
m_Streams[i]->Lock();
|
||||
if ( m_Streams[i]->IsExpired() )
|
||||
{
|
||||
// yes, close it
|
||||
m_Streams[i]->Unlock();
|
||||
g_Reflector.CloseStream(m_Streams[i]);
|
||||
// and remove it
|
||||
m_Streams.erase(m_Streams.begin()+i);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Streams[i]->Unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// queue helper
|
||||
|
||||
void CProtocol::HandleQueue(void)
|
||||
{
|
||||
// the default protocol just keep queue empty
|
||||
m_Queue.Lock();
|
||||
while ( !m_Queue.empty() )
|
||||
{
|
||||
delete m_Queue.front();
|
||||
m_Queue.pop();
|
||||
}
|
||||
m_Queue.Unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// syntax helper
|
||||
|
||||
bool CProtocol::IsNumber(char c) const
|
||||
{
|
||||
return ((c >= '0') && (c <= '9'));
|
||||
}
|
||||
|
||||
bool CProtocol::IsLetter(char c) const
|
||||
{
|
||||
return ((c >= 'A') && (c <= 'Z'));
|
||||
}
|
||||
|
||||
bool CProtocol::IsSpace(char c) const
|
||||
{
|
||||
return (c == ' ');
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// DestId to Module helper
|
||||
|
||||
char CProtocol::DmrDstIdToModule(uint32 tg) const
|
||||
{
|
||||
return ((char)((tg % 26)-1) + 'A');
|
||||
}
|
||||
|
||||
uint32 CProtocol::ModuleToDmrDestId(char m) const
|
||||
{
|
||||
return (uint32)(m - 'A')+1;
|
||||
}
|
||||
|
||||
|
||||
116
cprotocols.cpp
Normal file
116
cprotocols.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
//
|
||||
// cprotocols.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include "cdextraprotocol.h"
|
||||
#include "cdplusprotocol.h"
|
||||
#include "cdcsprotocol.h"
|
||||
#include "cxlxprotocol.h"
|
||||
#include "cdmrplusprotocol.h"
|
||||
#include "cdmrmmdvmprotocol.h"
|
||||
#include "cprotocols.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
CProtocols::CProtocols()
|
||||
{
|
||||
for ( unsigned int i = 0; i < m_Protocols.size(); i++ )
|
||||
{
|
||||
m_Protocols[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// destructor
|
||||
|
||||
CProtocols::~CProtocols()
|
||||
{
|
||||
m_Mutex.lock();
|
||||
{
|
||||
for ( unsigned int i = 0; i < m_Protocols.size(); i++ )
|
||||
{
|
||||
delete m_Protocols[i];
|
||||
}
|
||||
}
|
||||
m_Mutex.unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// initialization
|
||||
|
||||
bool CProtocols::Init(void)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
m_Mutex.lock();
|
||||
{
|
||||
// create and initialize DEXTRA
|
||||
delete m_Protocols[0];
|
||||
m_Protocols[0] = new CDextraProtocol;
|
||||
ok &= m_Protocols[0]->Init();
|
||||
|
||||
// create and initialize DPLUS
|
||||
delete m_Protocols[1];
|
||||
m_Protocols[1] = new CDplusProtocol;
|
||||
ok &= m_Protocols[1]->Init();
|
||||
|
||||
// create and initialize DCS
|
||||
delete m_Protocols[2];
|
||||
m_Protocols[2] = new CDcsProtocol;
|
||||
ok &= m_Protocols[2]->Init();
|
||||
|
||||
// create and initialize XLX - interlink
|
||||
delete m_Protocols[3];
|
||||
m_Protocols[3] = new CXlxProtocol;
|
||||
ok &= m_Protocols[3]->Init();
|
||||
|
||||
// create and initialize DMRPLUS
|
||||
delete m_Protocols[4];
|
||||
m_Protocols[4] = new CDmrplusProtocol;
|
||||
ok &= m_Protocols[4]->Init();
|
||||
|
||||
// create and initialize DMRMMDVM
|
||||
delete m_Protocols[5];
|
||||
m_Protocols[5] = new CDmrmmdvmProtocol;
|
||||
ok &= m_Protocols[5]->Init();
|
||||
}
|
||||
m_Mutex.unlock();
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
void CProtocols::Close(void)
|
||||
{
|
||||
m_Mutex.lock();
|
||||
{
|
||||
for ( unsigned int i = 0; i < m_Protocols.size(); i++ )
|
||||
{
|
||||
m_Protocols[i]->Close();
|
||||
}
|
||||
}
|
||||
m_Mutex.unlock();
|
||||
}
|
||||
760
creflector.cpp
Normal file
760
creflector.cpp
Normal file
|
|
@ -0,0 +1,760 @@
|
|||
//
|
||||
// creflector.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include "creflector.h"
|
||||
#include "cgatekeeper.h"
|
||||
#include "cdmriddirfile.h"
|
||||
#include "cdmriddirhttp.h"
|
||||
#include "ctranscoder.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
CReflector::CReflector()
|
||||
{
|
||||
m_bStopThreads = false;
|
||||
m_XmlReportThread = NULL;
|
||||
m_JsonReportThread = NULL;
|
||||
for ( int i = 0; i < NB_OF_MODULES; i++ )
|
||||
{
|
||||
m_RouterThreads[i] = NULL;
|
||||
}
|
||||
#ifdef DEBUG_DUMPFILE
|
||||
m_DebugFile.open("/Users/jeanluc/Desktop/dmrdebug.txt");
|
||||
#endif
|
||||
}
|
||||
|
||||
CReflector::CReflector(const CCallsign &callsign)
|
||||
{
|
||||
#ifdef DEBUG_DUMPFILE
|
||||
m_DebugFile.close();
|
||||
#endif
|
||||
m_bStopThreads = false;
|
||||
m_XmlReportThread = NULL;
|
||||
m_JsonReportThread = NULL;
|
||||
for ( int i = 0; i < NB_OF_MODULES; i++ )
|
||||
{
|
||||
m_RouterThreads[i] = NULL;
|
||||
}
|
||||
m_Callsign = callsign;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// destructor
|
||||
|
||||
CReflector::~CReflector()
|
||||
{
|
||||
m_bStopThreads = true;
|
||||
if ( m_XmlReportThread != NULL )
|
||||
{
|
||||
m_XmlReportThread->join();
|
||||
delete m_XmlReportThread;
|
||||
}
|
||||
if ( m_JsonReportThread != NULL )
|
||||
{
|
||||
m_JsonReportThread->join();
|
||||
delete m_JsonReportThread;
|
||||
}
|
||||
for ( int i = 0; i < NB_OF_MODULES; i++ )
|
||||
{
|
||||
if ( m_RouterThreads[i] != NULL )
|
||||
{
|
||||
m_RouterThreads[i]->join();
|
||||
delete m_RouterThreads[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operation
|
||||
|
||||
bool CReflector::Start(void)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
// reset stop flag
|
||||
m_bStopThreads = false;
|
||||
|
||||
// init gate keeper
|
||||
ok &= g_GateKeeper.Init();
|
||||
|
||||
// init dmrid directory
|
||||
g_DmridDir.Init();
|
||||
|
||||
// init the transcoder
|
||||
g_Transcoder.Init();
|
||||
|
||||
// create protocols
|
||||
ok &= m_Protocols.Init();
|
||||
|
||||
// if ok, start threads
|
||||
if ( ok )
|
||||
{
|
||||
// start one thread per reflector module
|
||||
for ( int i = 0; i < NB_OF_MODULES; i++ )
|
||||
{
|
||||
m_RouterThreads[i] = new std::thread(CReflector::RouterThread, this, &(m_Streams[i]));
|
||||
}
|
||||
|
||||
// start the reporting threads
|
||||
m_XmlReportThread = new std::thread(CReflector::XmlReportThread, this);
|
||||
#ifdef JSON_MONITOR
|
||||
m_JsonReportThread = new std::thread(CReflector::JsonReportThread, this);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Protocols.Close();
|
||||
}
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
void CReflector::Stop(void)
|
||||
{
|
||||
// stop & delete all threads
|
||||
m_bStopThreads = true;
|
||||
|
||||
// stop & delete report threads
|
||||
if ( m_XmlReportThread != NULL )
|
||||
{
|
||||
m_XmlReportThread->join();
|
||||
delete m_XmlReportThread;
|
||||
m_XmlReportThread = NULL;
|
||||
}
|
||||
if ( m_JsonReportThread != NULL )
|
||||
{
|
||||
m_JsonReportThread->join();
|
||||
delete m_JsonReportThread;
|
||||
m_JsonReportThread = NULL;
|
||||
}
|
||||
|
||||
// stop & delete all router thread
|
||||
for ( int i = 0; i < NB_OF_MODULES; i++ )
|
||||
{
|
||||
if ( m_RouterThreads[i] != NULL )
|
||||
{
|
||||
m_RouterThreads[i]->join();
|
||||
delete m_RouterThreads[i];
|
||||
m_RouterThreads[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// close protocols
|
||||
m_Protocols.Close();
|
||||
|
||||
// close transcoder
|
||||
g_Transcoder.Close();
|
||||
|
||||
// close gatekeeper
|
||||
g_GateKeeper.Close();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// stream opening & closing
|
||||
|
||||
bool CReflector::IsStreaming(char module)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CPacketStream *CReflector::OpenStream(CDvHeaderPacket *DvHeader, CClient *client)
|
||||
{
|
||||
CPacketStream *retStream = NULL;
|
||||
|
||||
// clients MUST have bee locked by the caller
|
||||
// so we can freely access it within the fuction
|
||||
|
||||
// check sid is not NULL
|
||||
if ( DvHeader->GetStreamId() != 0 )
|
||||
{
|
||||
// check if client is valid candidate
|
||||
if ( m_Clients.IsClient(client) && !client->IsAMaster() )
|
||||
{
|
||||
// check if no stream with same streamid already open
|
||||
// to prevent loops
|
||||
if ( !IsStreamOpen(DvHeader) )
|
||||
{
|
||||
// get the module's queue
|
||||
char module = DvHeader->GetRpt2Module();
|
||||
CPacketStream *stream = GetStream(module);
|
||||
if ( stream != NULL )
|
||||
{
|
||||
// lock it
|
||||
stream->Lock();
|
||||
// is it available ?
|
||||
if ( stream->Open(*DvHeader, client) )
|
||||
{
|
||||
// stream open, mark client as master
|
||||
// so that it can't be deleted
|
||||
client->SetMasterOfModule(module);
|
||||
|
||||
// update last heard time
|
||||
client->Heard();
|
||||
retStream = stream;
|
||||
|
||||
// and push header packet
|
||||
stream->Push(DvHeader);
|
||||
|
||||
// report
|
||||
std::cout << "Opening stream on module " << module << " for client " << client->GetCallsign()
|
||||
<< " with sid " << DvHeader->GetStreamId() << std::endl;
|
||||
|
||||
// notify
|
||||
g_Reflector.OnStreamOpen(stream->GetUserCallsign());
|
||||
|
||||
}
|
||||
// unlock now
|
||||
stream->Unlock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// report
|
||||
std::cout << "Detected stream loop on module " << DvHeader->GetRpt2Module() << " for client " << client->GetCallsign()
|
||||
<< " with sid " << DvHeader->GetStreamId() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return retStream;
|
||||
}
|
||||
|
||||
void CReflector::CloseStream(CPacketStream *stream)
|
||||
{
|
||||
//
|
||||
if ( stream != NULL )
|
||||
{
|
||||
// wait queue is empty
|
||||
// this waits forever
|
||||
bool bEmpty = false;
|
||||
do
|
||||
{
|
||||
stream->Lock();
|
||||
// do not use stream->IsEmpty() has this "may" never succeed
|
||||
// and anyway, the DvLastFramPacket short-circuit the transcoder
|
||||
// loop queues
|
||||
bEmpty = stream->empty();
|
||||
stream->Unlock();
|
||||
if ( !bEmpty )
|
||||
{
|
||||
// wait a bit
|
||||
CTimePoint::TaskSleepFor(10);
|
||||
}
|
||||
} while (!bEmpty);
|
||||
|
||||
// lock clients
|
||||
GetClients();
|
||||
|
||||
// lock stream
|
||||
stream->Lock();
|
||||
|
||||
// get and check the master
|
||||
CClient *client = stream->GetOwnerClient();
|
||||
if ( client != NULL )
|
||||
{
|
||||
// client no longer a master
|
||||
client->NotAMaster();
|
||||
|
||||
// notify
|
||||
g_Reflector.OnStreamClose(stream->GetUserCallsign());
|
||||
|
||||
std::cout << "Closing stream of module " << GetStreamModule(stream) << std::endl;
|
||||
}
|
||||
|
||||
// release clients
|
||||
ReleaseClients();
|
||||
|
||||
// unlock before closing
|
||||
// to avoid double lock in assiociated
|
||||
// codecstream close/thread-join
|
||||
stream->Unlock();
|
||||
|
||||
// and stop the queue
|
||||
stream->Close();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// router threads
|
||||
|
||||
void CReflector::RouterThread(CReflector *This, CPacketStream *streamIn)
|
||||
{
|
||||
// get our module
|
||||
uint8 uiModuleId = This->GetStreamModule(streamIn);
|
||||
|
||||
// get on input queue
|
||||
CPacket *packet;
|
||||
|
||||
while ( !This->m_bStopThreads )
|
||||
{
|
||||
// any packet in our input queue ?
|
||||
streamIn->Lock();
|
||||
if ( !streamIn->empty() )
|
||||
{
|
||||
// get the packet
|
||||
packet = streamIn->front();
|
||||
streamIn->pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
packet = NULL;
|
||||
}
|
||||
streamIn->Unlock();
|
||||
|
||||
// route it
|
||||
if ( packet != NULL )
|
||||
{
|
||||
// set origin
|
||||
packet->SetModuleId(uiModuleId);
|
||||
|
||||
// iterate on all protocols
|
||||
for ( int i = 0; i < This->m_Protocols.Size(); i++ )
|
||||
{
|
||||
// duplicate packet
|
||||
CPacket *packetClone = packet->Duplicate();
|
||||
|
||||
// get protocol
|
||||
CProtocol *protocol = This->m_Protocols.GetProtocol(i);
|
||||
|
||||
// if packet is header, update RPT2 according to protocol
|
||||
if ( packetClone->IsDvHeader() )
|
||||
{
|
||||
// get our callsign
|
||||
CCallsign csRPT = protocol->GetReflectorCallsign();
|
||||
csRPT.SetModule(This->GetStreamModule(streamIn));
|
||||
((CDvHeaderPacket *)packetClone)->SetRpt2Callsign(csRPT);
|
||||
}
|
||||
|
||||
// and push it
|
||||
CPacketQueue *queue = protocol->GetQueue();
|
||||
queue->push(packetClone);
|
||||
protocol->ReleaseQueue();
|
||||
}
|
||||
// done
|
||||
delete packet;
|
||||
packet = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// wait a bit
|
||||
CTimePoint::TaskSleepFor(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// report threads
|
||||
|
||||
void CReflector::XmlReportThread(CReflector *This)
|
||||
{
|
||||
while ( !This->m_bStopThreads )
|
||||
{
|
||||
// report to xml file
|
||||
std::ofstream xmlFile;
|
||||
xmlFile.open(XML_PATH, std::ios::out | std::ios::trunc);
|
||||
if ( xmlFile.is_open() )
|
||||
{
|
||||
// write xml file
|
||||
This->WriteXmlFile(xmlFile);
|
||||
|
||||
// and close file
|
||||
xmlFile.close();
|
||||
}
|
||||
#ifndef NO_ERROR_ON_XML_OPEN_FAIL
|
||||
else
|
||||
{
|
||||
std::cout << "Failed to open " << XML_PATH << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
// and wait a bit
|
||||
CTimePoint::TaskSleepFor(XML_UPDATE_PERIOD * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
void CReflector::JsonReportThread(CReflector *This)
|
||||
{
|
||||
CUdpSocket Socket;
|
||||
CBuffer Buffer;
|
||||
CIp Ip;
|
||||
bool bOn;
|
||||
|
||||
// init variable
|
||||
bOn = false;
|
||||
|
||||
// create listening socket
|
||||
if ( Socket.Open(JSON_PORT) )
|
||||
{
|
||||
// and loop
|
||||
while ( !This->m_bStopThreads )
|
||||
{
|
||||
// any command ?
|
||||
if ( Socket.Receive(&Buffer, &Ip, 50) != -1 )
|
||||
{
|
||||
// check verb
|
||||
if ( Buffer.Compare((uint8 *)"hello", 5) == 0 )
|
||||
{
|
||||
std::cout << "Monitor socket connected with " << Ip << std::endl;
|
||||
|
||||
// connected
|
||||
bOn = true;
|
||||
|
||||
// announce ourselves
|
||||
This->SendJsonReflectorObject(Socket, Ip);
|
||||
|
||||
// dump tables
|
||||
This->SendJsonNodesObject(Socket, Ip);
|
||||
This->SendJsonStationsObject(Socket, Ip);
|
||||
}
|
||||
else if ( Buffer.Compare((uint8 *)"bye", 3) == 0 )
|
||||
{
|
||||
std::cout << "Monitor socket disconnected" << std::endl;
|
||||
|
||||
// diconnected
|
||||
bOn = false;
|
||||
}
|
||||
}
|
||||
|
||||
// any notifications ?
|
||||
CNotification notification;
|
||||
This->m_Notifications.Lock();
|
||||
if ( !This->m_Notifications.empty() )
|
||||
{
|
||||
// get the packet
|
||||
notification = This->m_Notifications.front();
|
||||
This->m_Notifications.pop();
|
||||
}
|
||||
This->m_Notifications.Unlock();
|
||||
|
||||
// handle it
|
||||
if ( bOn )
|
||||
{
|
||||
switch ( notification.GetId() )
|
||||
{
|
||||
case NOTIFICATION_CLIENTS:
|
||||
case NOTIFICATION_PEERS:
|
||||
//std::cout << "Monitor updating nodes table" << std::endl;
|
||||
This->SendJsonNodesObject(Socket, Ip);
|
||||
break;
|
||||
case NOTIFICATION_USERS:
|
||||
//std::cout << "Monitor updating stations table" << std::endl;
|
||||
This->SendJsonStationsObject(Socket, Ip);
|
||||
break;
|
||||
case NOTIFICATION_STREAM_OPEN:
|
||||
//std::cout << "Monitor notify station " << notification.GetCallsign() << "going ON air" << std::endl;
|
||||
This->SendJsonStationsObject(Socket, Ip);
|
||||
This->SendJsonOnairObject(Socket, Ip, notification.GetCallsign());
|
||||
break;
|
||||
case NOTIFICATION_STREAM_CLOSE:
|
||||
//std::cout << "Monitor notify station " << notification.GetCallsign() << "going OFF air" << std::endl;
|
||||
This->SendJsonOffairObject(Socket, Ip, notification.GetCallsign());
|
||||
break;
|
||||
case NOTIFICATION_NONE:
|
||||
default:
|
||||
// nothing to do, just sleep a bit
|
||||
CTimePoint::TaskSleepFor(250);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error creating monitor socket" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// notifications
|
||||
|
||||
void CReflector::OnPeersChanged(void)
|
||||
{
|
||||
CNotification notification(NOTIFICATION_PEERS);
|
||||
|
||||
m_Notifications.Lock();
|
||||
m_Notifications.push(notification);
|
||||
m_Notifications.Unlock();
|
||||
}
|
||||
|
||||
void CReflector::OnClientsChanged(void)
|
||||
{
|
||||
CNotification notification(NOTIFICATION_CLIENTS);
|
||||
|
||||
m_Notifications.Lock();
|
||||
m_Notifications.push(notification);
|
||||
m_Notifications.Unlock();
|
||||
}
|
||||
|
||||
void CReflector::OnUsersChanged(void)
|
||||
{
|
||||
CNotification notification(NOTIFICATION_USERS);
|
||||
|
||||
m_Notifications.Lock();
|
||||
m_Notifications.push(notification);
|
||||
m_Notifications.Unlock();
|
||||
}
|
||||
|
||||
void CReflector::OnStreamOpen(const CCallsign &callsign)
|
||||
{
|
||||
CNotification notification(NOTIFICATION_STREAM_OPEN, callsign);
|
||||
|
||||
m_Notifications.Lock();
|
||||
m_Notifications.push(notification);
|
||||
m_Notifications.Unlock();
|
||||
}
|
||||
|
||||
void CReflector::OnStreamClose(const CCallsign &callsign)
|
||||
{
|
||||
CNotification notification(NOTIFICATION_STREAM_CLOSE, callsign);
|
||||
|
||||
m_Notifications.Lock();
|
||||
m_Notifications.push(notification);
|
||||
m_Notifications.Unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// modules & queues
|
||||
|
||||
int CReflector::GetModuleIndex(char module) const
|
||||
{
|
||||
int i = (int)module - (int)'A';
|
||||
if ( (i < 0) || (i >= NB_OF_MODULES) )
|
||||
{
|
||||
i = -1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
CPacketStream *CReflector::GetStream(char module)
|
||||
{
|
||||
CPacketStream *stream = NULL;
|
||||
int i = GetModuleIndex(module);
|
||||
if ( i >= 0 )
|
||||
{
|
||||
stream = &(m_Streams[i]);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
bool CReflector::IsStreamOpen(const CDvHeaderPacket *DvHeader)
|
||||
{
|
||||
bool open = false;
|
||||
for ( unsigned int i = 0; (i < m_Streams.size()) && !open; i++ )
|
||||
{
|
||||
open = ( (m_Streams[i].GetStreamId() == DvHeader->GetStreamId()) &&
|
||||
(m_Streams[i].IsOpen()));
|
||||
}
|
||||
return open;
|
||||
}
|
||||
|
||||
char CReflector::GetStreamModule(CPacketStream *stream)
|
||||
{
|
||||
char module = ' ';
|
||||
for ( unsigned int i = 0; (i < m_Streams.size()) && (module == ' '); i++ )
|
||||
{
|
||||
if ( &(m_Streams[i]) == stream )
|
||||
{
|
||||
module = GetModuleLetter(i);
|
||||
}
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// xml helpers
|
||||
|
||||
void CReflector::WriteXmlFile(std::ofstream &xmlFile)
|
||||
{
|
||||
// write header
|
||||
xmlFile << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
|
||||
|
||||
// software version
|
||||
char sz[64];
|
||||
::sprintf(sz, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
|
||||
xmlFile << "<Version>" << sz << "</Version>" << std::endl;
|
||||
|
||||
// linked peers
|
||||
xmlFile << "<" << m_Callsign << "linked peers>" << std::endl;
|
||||
// lock
|
||||
CPeers *peers = GetPeers();
|
||||
// iterate on peers
|
||||
for ( int i = 0; i < peers->GetSize(); i++ )
|
||||
{
|
||||
peers->GetPeer(i)->WriteXml(xmlFile);
|
||||
}
|
||||
// unlock
|
||||
ReleasePeers();
|
||||
xmlFile << "</" << m_Callsign << "linked peers>" << std::endl;
|
||||
|
||||
// linked nodes
|
||||
xmlFile << "<" << m_Callsign << "linked nodes>" << std::endl;
|
||||
// lock
|
||||
CClients *clients = GetClients();
|
||||
// iterate on clients
|
||||
for ( int i = 0; i < clients->GetSize(); i++ )
|
||||
{
|
||||
if ( clients->GetClient(i)->IsNode() )
|
||||
{
|
||||
clients->GetClient(i)->WriteXml(xmlFile);
|
||||
}
|
||||
}
|
||||
// unlock
|
||||
ReleaseClients();
|
||||
xmlFile << "</" << m_Callsign << "linked nodes>" << std::endl;
|
||||
|
||||
// last heard users
|
||||
xmlFile << "<" << m_Callsign << "heard users>" << std::endl;
|
||||
// lock
|
||||
CUsers *users = GetUsers();
|
||||
// iterate on users
|
||||
for ( int i = 0; i < users->GetSize(); i++ )
|
||||
{
|
||||
users->GetUser(i)->WriteXml(xmlFile);
|
||||
}
|
||||
// unlock
|
||||
ReleaseUsers();
|
||||
xmlFile << "</" << m_Callsign << "heard users>" << std::endl;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// json helpers
|
||||
|
||||
void CReflector::SendJsonReflectorObject(CUdpSocket &Socket, CIp &Ip)
|
||||
{
|
||||
char Buffer[1024];
|
||||
char cs[CALLSIGN_LEN+1];
|
||||
char mod[8] = "\"A\"";
|
||||
|
||||
// get reflector callsign
|
||||
m_Callsign.GetCallsign((uint8 *)cs);
|
||||
cs[CALLSIGN_LEN] = 0;
|
||||
|
||||
// build string
|
||||
::sprintf(Buffer, "{\"reflector\":\"%s\",\"modules\":[", cs);
|
||||
for ( int i = 0; i < NB_OF_MODULES; i++ )
|
||||
{
|
||||
::strcat(Buffer, mod);
|
||||
mod[1]++;
|
||||
if ( i < NB_OF_MODULES-1 )
|
||||
{
|
||||
::strcat(Buffer, ",");
|
||||
}
|
||||
}
|
||||
::strcat(Buffer, "]}");
|
||||
|
||||
// and send
|
||||
Socket.Send(Buffer, Ip);
|
||||
}
|
||||
|
||||
#define JSON_NBMAX_NODES 250
|
||||
|
||||
void CReflector::SendJsonNodesObject(CUdpSocket &Socket, CIp &Ip)
|
||||
{
|
||||
char Buffer[12+(JSON_NBMAX_NODES*94)];
|
||||
|
||||
// nodes object table
|
||||
::sprintf(Buffer, "{\"nodes\":[");
|
||||
// lock
|
||||
CClients *clients = GetClients();
|
||||
// iterate on clients
|
||||
for ( int i = 0; (i < clients->GetSize()) && (i < JSON_NBMAX_NODES); i++ )
|
||||
{
|
||||
clients->GetClient(i)->GetJsonObject(Buffer);
|
||||
if ( i < clients->GetSize()-1 )
|
||||
{
|
||||
::strcat(Buffer, ",");
|
||||
}
|
||||
}
|
||||
// unlock
|
||||
ReleaseClients();
|
||||
::strcat(Buffer, "]}");
|
||||
|
||||
// and send
|
||||
//std::cout << Buffer << std::endl;
|
||||
Socket.Send(Buffer, Ip);
|
||||
}
|
||||
|
||||
void CReflector::SendJsonStationsObject(CUdpSocket &Socket, CIp &Ip)
|
||||
{
|
||||
char Buffer[15+(LASTHEARD_USERS_MAX_SIZE*94)];
|
||||
|
||||
// stations object table
|
||||
::sprintf(Buffer, "{\"stations\":[");
|
||||
|
||||
// lock
|
||||
CUsers *users = GetUsers();
|
||||
// iterate on users
|
||||
for ( int i = 0; i < users->GetSize(); i++ )
|
||||
{
|
||||
users->GetUser(i)->GetJsonObject(Buffer);
|
||||
if ( i < users->GetSize()-1 )
|
||||
{
|
||||
::strcat(Buffer, ",");
|
||||
}
|
||||
}
|
||||
// unlock
|
||||
ReleaseUsers();
|
||||
|
||||
::strcat(Buffer, "]}");
|
||||
|
||||
// and send
|
||||
//std::cout << Buffer << std::endl;
|
||||
Socket.Send(Buffer, Ip);
|
||||
}
|
||||
|
||||
void CReflector::SendJsonOnairObject(CUdpSocket &Socket, CIp &Ip, const CCallsign &Callsign)
|
||||
{
|
||||
char Buffer[128];
|
||||
char sz[CALLSIGN_LEN+1];
|
||||
|
||||
// onair object
|
||||
Callsign.GetCallsignString(sz);
|
||||
::sprintf(Buffer, "{\"onair\":\"%s\"}", sz);
|
||||
|
||||
// and send
|
||||
//std::cout << Buffer << std::endl;
|
||||
Socket.Send(Buffer, Ip);
|
||||
}
|
||||
|
||||
void CReflector::SendJsonOffairObject(CUdpSocket &Socket, CIp &Ip, const CCallsign &Callsign)
|
||||
{
|
||||
char Buffer[128];
|
||||
char sz[CALLSIGN_LEN+1];
|
||||
|
||||
// offair object
|
||||
Callsign.GetCallsignString(sz);
|
||||
::sprintf(Buffer, "{\"offair\":\"%s\"}", sz);
|
||||
|
||||
// and send
|
||||
//std::cout << Buffer << std::endl;
|
||||
Socket.Send(Buffer, Ip);
|
||||
}
|
||||
56
csemaphore.h
Normal file
56
csemaphore.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// csemaphore.h
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 16/04/2017.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifndef csemaphore_h
|
||||
#define csemaphore_h
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// class
|
||||
|
||||
class CSemaphore
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
CSemaphore();
|
||||
|
||||
// destructor
|
||||
virtual ~CSemaphore() {};
|
||||
|
||||
// operation
|
||||
void Reset(void);
|
||||
void Notify(void);
|
||||
void Wait(void);
|
||||
bool WaitFor(uint);
|
||||
|
||||
protected:
|
||||
// data
|
||||
std::mutex m_Mutex;
|
||||
std::condition_variable m_Condition;
|
||||
int m_Count;
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#endif /* csemaphore_h */
|
||||
401
ctranscoder.cpp
Normal file
401
ctranscoder.cpp
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
//
|
||||
// ctranscoder.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include "creflector.h"
|
||||
#include "ctranscoder.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// define
|
||||
|
||||
// status
|
||||
#define STATUS_IDLE 0
|
||||
#define STATUS_LOGGED 1
|
||||
|
||||
// timeout
|
||||
#define AMBED_OPENSTREAM_TIMEOUT 400 // in ms
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CTranscoder g_Transcoder;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
CTranscoder::CTranscoder()
|
||||
{
|
||||
m_bStopThread = false;
|
||||
m_pThread = NULL;
|
||||
m_Streams.reserve(12);
|
||||
m_bConnected = false;
|
||||
m_LastKeepaliveTime.Now();
|
||||
m_LastActivityTime.Now();
|
||||
m_bStreamOpened = false;
|
||||
m_StreamidOpenStream = 0;
|
||||
m_PortOpenStream = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// destructor
|
||||
|
||||
CTranscoder::~CTranscoder()
|
||||
{
|
||||
// close all streams
|
||||
m_Mutex.lock();
|
||||
{
|
||||
for ( unsigned int i = 0; i < m_Streams.size(); i++ )
|
||||
{
|
||||
delete m_Streams[i];
|
||||
}
|
||||
m_Streams.clear();
|
||||
|
||||
}
|
||||
m_Mutex.unlock();
|
||||
|
||||
// kill threads
|
||||
m_bStopThread = true;
|
||||
if ( m_pThread != NULL )
|
||||
{
|
||||
m_pThread->join();
|
||||
delete m_pThread;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// initialization
|
||||
|
||||
bool CTranscoder::Init(void)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
// reset stop flag
|
||||
m_bStopThread = false;
|
||||
|
||||
// create server's IP
|
||||
m_Ip = g_Reflector.GetTranscoderIp();
|
||||
|
||||
// create our socket
|
||||
ok = m_Socket.Open(TRANSCODER_PORT);
|
||||
if ( ok )
|
||||
{
|
||||
// start thread;
|
||||
m_pThread = new std::thread(CTranscoder::Thread, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error opening socket on port UDP" << TRANSCODER_PORT << " on ip " << g_Reflector.GetListenIp() << std::endl;
|
||||
}
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
void CTranscoder::Close(void)
|
||||
{
|
||||
// close socket
|
||||
m_Socket.Close();
|
||||
|
||||
// close all streams
|
||||
m_Mutex.lock();
|
||||
{
|
||||
for ( unsigned int i = 0; i < m_Streams.size(); i++ )
|
||||
{
|
||||
delete m_Streams[i];
|
||||
}
|
||||
m_Streams.clear();
|
||||
|
||||
}
|
||||
m_Mutex.unlock();
|
||||
|
||||
// kill threads
|
||||
m_bStopThread = true;
|
||||
if ( m_pThread != NULL )
|
||||
{
|
||||
m_pThread->join();
|
||||
delete m_pThread;
|
||||
m_pThread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// thread
|
||||
|
||||
void CTranscoder::Thread(CTranscoder *This)
|
||||
{
|
||||
while ( !This->m_bStopThread )
|
||||
{
|
||||
This->Task();
|
||||
}
|
||||
}
|
||||
|
||||
void CTranscoder::Task(void)
|
||||
{
|
||||
CBuffer Buffer;
|
||||
CIp Ip;
|
||||
uint16 StreamId;
|
||||
uint16 Port;
|
||||
|
||||
// anything coming in from codec server ?
|
||||
//if ( (m_Socket.Receive(&Buffer, &Ip, 20) != -1) && (Ip == m_Ip) )
|
||||
if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
|
||||
{
|
||||
m_LastActivityTime.Now();
|
||||
|
||||
// crack packet
|
||||
if ( IsValidStreamDescrPacket(Buffer, &StreamId, &Port) )
|
||||
{
|
||||
//std::cout << "Transcoder stream " << (int) StreamId << " descr packet " << std::endl;
|
||||
m_bStreamOpened = true;
|
||||
m_StreamidOpenStream = StreamId;
|
||||
m_PortOpenStream = Port;
|
||||
m_SemaphoreOpenStream.Notify();
|
||||
}
|
||||
else if ( IsValidNoStreamAvailablePacket(Buffer) )
|
||||
{
|
||||
m_bStreamOpened = false;
|
||||
m_SemaphoreOpenStream.Notify();
|
||||
}
|
||||
else if ( IsValidKeepAlivePacket(Buffer) )
|
||||
{
|
||||
if ( !m_bConnected )
|
||||
{
|
||||
std::cout << "Transcoder connected at " << Ip << std::endl;
|
||||
}
|
||||
m_bConnected = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// keep client alive
|
||||
if ( m_LastKeepaliveTime.DurationSinceNow() > TRANSCODER_KEEPALIVE_PERIOD )
|
||||
{
|
||||
//
|
||||
HandleKeepalives();
|
||||
|
||||
// update time
|
||||
m_LastKeepaliveTime.Now();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// manage streams
|
||||
|
||||
CCodecStream *CTranscoder::GetStream(CPacketStream *PacketStream, uint8 uiCodecIn)
|
||||
{
|
||||
CBuffer Buffer;
|
||||
|
||||
CCodecStream *stream = NULL;
|
||||
|
||||
// do we need transcoding
|
||||
if ( uiCodecIn != CODEC_NONE )
|
||||
{
|
||||
// are we connected to server
|
||||
if ( m_bConnected )
|
||||
{
|
||||
// yes, post openstream request
|
||||
EncodeOpenstreamPacket(&Buffer, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS);
|
||||
m_Socket.Send(Buffer, m_Ip, TRANSCODER_PORT);
|
||||
|
||||
// wait relpy here
|
||||
if ( m_SemaphoreOpenStream.WaitFor(AMBED_OPENSTREAM_TIMEOUT) )
|
||||
{
|
||||
if ( m_bStreamOpened )
|
||||
{
|
||||
std::cout << "ambed openstream ok" << std::endl;
|
||||
|
||||
// create stream object
|
||||
stream = new CCodecStream(PacketStream, m_StreamidOpenStream, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS);
|
||||
|
||||
// init it
|
||||
if ( stream->Init(m_PortOpenStream) )
|
||||
{
|
||||
// and append to list
|
||||
Lock();
|
||||
m_Streams.push_back(stream);
|
||||
Unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
// send close packet
|
||||
EncodeClosestreamPacket(&Buffer, stream->GetStreamId());
|
||||
m_Socket.Send(Buffer, m_Ip, TRANSCODER_PORT);
|
||||
// and delete
|
||||
delete stream;
|
||||
stream = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "ambed openstream failed (no suitable channel available)" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "ambed openstream timeout" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
void CTranscoder::ReleaseStream(CCodecStream *stream)
|
||||
{
|
||||
CBuffer Buffer;
|
||||
|
||||
if ( stream != NULL )
|
||||
{
|
||||
// look for the stream
|
||||
bool found = false;
|
||||
Lock();
|
||||
{
|
||||
for ( unsigned int i = 0; (i < m_Streams.size()) && !found; i++ )
|
||||
{
|
||||
// compare object pointers
|
||||
if ( (m_Streams[i]) == stream )
|
||||
{
|
||||
// send close packet
|
||||
EncodeClosestreamPacket(&Buffer, m_Streams[i]->GetStreamId());
|
||||
m_Socket.Send(Buffer, m_Ip, TRANSCODER_PORT);
|
||||
|
||||
// display stats
|
||||
if ( m_Streams[i]->GetPingMin() >= 0.0 )
|
||||
{
|
||||
char sz[256];
|
||||
sprintf(sz, "ambed stats (ms) : %.1f/%.1f/%.1f",
|
||||
m_Streams[i]->GetPingMin() * 1000.0,
|
||||
m_Streams[i]->GetPingAve() * 1000.0,
|
||||
m_Streams[i]->GetPingMax() * 1000.0);
|
||||
std::cout << sz << std::endl;
|
||||
}
|
||||
if ( m_Streams[i]->GetTimeoutPackets() > 0 )
|
||||
{
|
||||
char sz[256];
|
||||
sprintf(sz, "ambed %d of %d packets timed out",
|
||||
m_Streams[i]->GetTimeoutPackets(),
|
||||
m_Streams[i]->GetTotalPackets());
|
||||
std::cout << sz << std::endl;
|
||||
}
|
||||
|
||||
// and close it
|
||||
m_Streams[i]->Close();
|
||||
delete m_Streams[i];
|
||||
m_Streams.erase(m_Streams.begin()+i);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// keepalive helpers
|
||||
|
||||
void CTranscoder::HandleKeepalives(void)
|
||||
{
|
||||
CBuffer keepalive;
|
||||
|
||||
// send keepalive
|
||||
EncodeKeepAlivePacket(&keepalive);
|
||||
m_Socket.Send(keepalive, m_Ip, TRANSCODER_PORT);
|
||||
|
||||
// check if still with us
|
||||
if ( m_bConnected && (m_LastActivityTime.DurationSinceNow() >= TRANSCODER_KEEPALIVE_TIMEOUT) )
|
||||
{
|
||||
// no, disconnect
|
||||
m_bConnected = false;
|
||||
std::cout << "Transcoder keepalive timeout" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet decoding helpers
|
||||
|
||||
bool CTranscoder::IsValidKeepAlivePacket(const CBuffer &Buffer)
|
||||
{
|
||||
uint8 tag[] = { 'A','M','B','E','D','P','O','N','G' };
|
||||
|
||||
bool valid = false;
|
||||
if ( (Buffer.size() == 9) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
|
||||
{
|
||||
valid = true;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CTranscoder::IsValidStreamDescrPacket(const CBuffer &Buffer, uint16 *Id, uint16 *Port)
|
||||
{
|
||||
uint8 tag[] = { 'A','M','B','E','D','S','T','D' };
|
||||
|
||||
bool valid = false;
|
||||
if ( (Buffer.size() == 14) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
|
||||
{
|
||||
*Id = *(uint16 *)(&Buffer.data()[8]);
|
||||
*Port = *(uint16 *)(&Buffer.data()[10]);
|
||||
// uint8 CodecIn = Buffer.data()[12];
|
||||
// uint8 CodecOut = Buffer.data()[13];
|
||||
valid = true;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CTranscoder::IsValidNoStreamAvailablePacket(const CBuffer&Buffer)
|
||||
{
|
||||
uint8 tag[] = { 'A','M','B','E','D','B','U','S','Y' };
|
||||
|
||||
return ( (Buffer.size() == 9) && (Buffer.Compare(tag, sizeof(tag)) == 0) );
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet encoding helpers
|
||||
|
||||
void CTranscoder::EncodeKeepAlivePacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 'A','M','B','E','D','P','I','N','G' };
|
||||
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
Buffer->Append((uint8 *)(const char *)g_Reflector.GetCallsign(), CALLSIGN_LEN);
|
||||
}
|
||||
|
||||
void CTranscoder::EncodeOpenstreamPacket(CBuffer *Buffer, uint8 uiCodecIn, uint8 uiCodecOut)
|
||||
{
|
||||
uint8 tag[] = { 'A','M','B','E','D','O','S' };
|
||||
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
Buffer->Append((uint8 *)(const char *)g_Reflector.GetCallsign(), CALLSIGN_LEN);
|
||||
Buffer->Append((uint8)uiCodecIn);
|
||||
Buffer->Append((uint8)uiCodecOut);
|
||||
}
|
||||
|
||||
void CTranscoder::EncodeClosestreamPacket(CBuffer *Buffer, uint16 uiStreamId)
|
||||
{
|
||||
uint8 tag[] = { 'A','M','B','E','D','C','S' };
|
||||
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
Buffer->Append((uint16)uiStreamId);
|
||||
}
|
||||
|
||||
92
cusers.cpp
Normal file
92
cusers.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// cusers.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 13/11/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include "cusers.h"
|
||||
#include "creflector.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
CUsers::CUsers()
|
||||
{
|
||||
m_Users.reserve(LASTHEARD_USERS_MAX_SIZE);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// users management
|
||||
|
||||
void CUsers::AddUser(const CUser &user)
|
||||
{
|
||||
// add
|
||||
m_Users.push_back(user);
|
||||
|
||||
// sort list by descending time (fisrt is youngest)
|
||||
std::sort(m_Users.begin(), m_Users.end());
|
||||
|
||||
// if list size too big, remove oldest
|
||||
if ( m_Users.size() >= (LASTHEARD_USERS_MAX_SIZE-1) )
|
||||
{
|
||||
m_Users.resize(m_Users.size()-1);
|
||||
}
|
||||
|
||||
// notify
|
||||
g_Reflector.OnUsersChanged();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operation
|
||||
|
||||
void CUsers::Hearing(const CCallsign &my, const CCallsign &rpt1, const CCallsign &rpt2)
|
||||
{
|
||||
Hearing(my, rpt1, rpt2, g_Reflector.GetCallsign());
|
||||
}
|
||||
|
||||
void CUsers::Hearing(const CCallsign &my, const CCallsign &rpt1, const CCallsign &rpt2, const CCallsign &xlx)
|
||||
{
|
||||
CUser heard(my, rpt1, rpt2, xlx);
|
||||
|
||||
// first check if we have this user listed yet
|
||||
bool found = false;
|
||||
for ( unsigned int i = 0; (i < m_Users.size()) && !found; i++ )
|
||||
{
|
||||
found = (m_Users[i] == heard);
|
||||
if ( found )
|
||||
{
|
||||
m_Users[i].HeardNow();
|
||||
}
|
||||
}
|
||||
|
||||
// if not found, add user to list
|
||||
// otherwise just re-sort the list
|
||||
if ( !found )
|
||||
{
|
||||
AddUser(heard);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::sort(m_Users.begin(), m_Users.end());
|
||||
}
|
||||
}
|
||||
|
||||
106
cxlxpeer.cpp
Normal file
106
cxlxpeer.cpp
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
//
|
||||
// cxlxpeer.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 10/12/2016.
|
||||
// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include "creflector.h"
|
||||
#include "cxlxpeer.h"
|
||||
#include "cxlxclient.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
|
||||
|
||||
CXlxPeer::CXlxPeer()
|
||||
{
|
||||
}
|
||||
|
||||
CXlxPeer::CXlxPeer(const CCallsign &callsign, const CIp &ip, char *modules, const CVersion &version)
|
||||
: CPeer(callsign, ip, modules, version)
|
||||
{
|
||||
// get protocol revision
|
||||
int protrev = GetProtocolRevision(version);
|
||||
//std::cout << "Adding XLX peer with protocol revision " << protrev << std::endl;
|
||||
|
||||
// and construct all xlx clients
|
||||
for ( unsigned int i = 0; i < ::strlen(modules); i++ )
|
||||
{
|
||||
// create
|
||||
CXlxClient *client = new CXlxClient(callsign, ip, modules[i], protrev);
|
||||
// and append to vector
|
||||
m_Clients.push_back(client);
|
||||
}
|
||||
}
|
||||
|
||||
CXlxPeer::CXlxPeer(const CXlxPeer &peer)
|
||||
: CPeer(peer)
|
||||
{
|
||||
for ( unsigned int i = 0; i < peer.m_Clients.size(); i++ )
|
||||
{
|
||||
CXlxClient *client = new CXlxClient((const CXlxClient &)*(peer.m_Clients[i]));
|
||||
// grow vector capacity if needed
|
||||
if ( m_Clients.capacity() == m_Clients.size() )
|
||||
{
|
||||
m_Clients.reserve(m_Clients.capacity()+10);
|
||||
}
|
||||
// and append
|
||||
m_Clients.push_back(client);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// destructors
|
||||
|
||||
CXlxPeer::~CXlxPeer()
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// status
|
||||
|
||||
bool CXlxPeer::IsAlive(void) const
|
||||
{
|
||||
return (m_LastKeepaliveTime.DurationSinceNow() < XLX_KEEPALIVE_TIMEOUT);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// revision helper
|
||||
|
||||
int CXlxPeer::GetProtocolRevision(const CVersion &version)
|
||||
{
|
||||
int protrev = XLX_PROTOCOL_REVISION_0;
|
||||
|
||||
if ( version.IsEqualOrHigherTo(CVersion(2,2,0)) )
|
||||
{
|
||||
protrev = XLX_PROTOCOL_REVISION_2;
|
||||
}
|
||||
else if ( version.IsEqualOrHigherTo(CVersion(1,4,0)) )
|
||||
{
|
||||
protrev = XLX_PROTOCOL_REVISION_1;
|
||||
}
|
||||
return protrev;
|
||||
}
|
||||
|
||||
766
cxlxprotocol.cpp
Normal file
766
cxlxprotocol.cpp
Normal file
|
|
@ -0,0 +1,766 @@
|
|||
//
|
||||
// cxlxprotocol.cpp
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 28/01/2016.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include "cxlxpeer.h"
|
||||
#include "cbmpeer.h"
|
||||
#include "cxlxprotocol.h"
|
||||
#include "creflector.h"
|
||||
#include "cgatekeeper.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operation
|
||||
|
||||
bool CXlxProtocol::Init(void)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
// base class
|
||||
ok = CProtocol::Init();
|
||||
|
||||
// update the reflector callsign
|
||||
m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"XLX", 3);
|
||||
|
||||
// create our socket
|
||||
ok &= m_Socket.Open(XLX_PORT);
|
||||
if ( !ok )
|
||||
{
|
||||
std::cout << "Error opening socket on port UDP" << XLX_PORT << " on ip " << g_Reflector.GetListenIp() << std::endl;
|
||||
}
|
||||
|
||||
// update time
|
||||
m_LastKeepaliveTime.Now();
|
||||
m_LastPeersLinkTime.Now();
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// task
|
||||
|
||||
void CXlxProtocol::Task(void)
|
||||
{
|
||||
CBuffer Buffer;
|
||||
CIp Ip;
|
||||
CCallsign Callsign;
|
||||
char Modules[NB_MODULES_MAX+1];
|
||||
CVersion Version;
|
||||
CDvHeaderPacket *Header;
|
||||
CDvFramePacket *Frame;
|
||||
CDvLastFramePacket *LastFrame;
|
||||
|
||||
// any incoming packet ?
|
||||
if ( m_Socket.Receive(&Buffer, &Ip, 20) != -1 )
|
||||
{
|
||||
// crack the packet
|
||||
if ( (Frame = IsValidDvFramePacket(Buffer)) != NULL )
|
||||
{
|
||||
//std::cout << "XLX (DExtra) DV frame" << std::endl;
|
||||
|
||||
// handle it
|
||||
OnDvFramePacketIn(Frame, &Ip);
|
||||
}
|
||||
else if ( (Header = IsValidDvHeaderPacket(Buffer)) != NULL )
|
||||
{
|
||||
//std::cout << "XLX (DExtra) DV header:" << std::endl << *Header << std::endl;
|
||||
//std::cout << "XLX (DExtra) DV header on module " << Header->GetRpt2Module() << std::endl;
|
||||
|
||||
// callsign muted?
|
||||
if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip) )
|
||||
{
|
||||
// handle it
|
||||
OnDvHeaderPacketIn(Header, Ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete Header;
|
||||
}
|
||||
}
|
||||
else if ( (LastFrame = IsValidDvLastFramePacket(Buffer)) != NULL )
|
||||
{
|
||||
//std::cout << "XLX (DExtra) DV last frame" << std::endl;
|
||||
|
||||
// handle it
|
||||
OnDvLastFramePacketIn(LastFrame, &Ip);
|
||||
}
|
||||
else if ( IsValidConnectPacket(Buffer, &Callsign, Modules, &Version) )
|
||||
{
|
||||
std::cout << "XLX ("
|
||||
<< Version.GetMajor() << "." << Version.GetMinor() << "." << Version.GetRevision()
|
||||
<< ") connect packet for modules " << Modules
|
||||
<< " from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// callsign authorized?
|
||||
if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_XLX, Modules) )
|
||||
{
|
||||
// acknowledge connecting request
|
||||
// following is version dependent
|
||||
switch ( GetConnectingPeerProtocolRevision(Callsign, Version) )
|
||||
{
|
||||
case XLX_PROTOCOL_REVISION_0:
|
||||
{
|
||||
// already connected ?
|
||||
CPeers *peers = g_Reflector.GetPeers();
|
||||
if ( peers->FindPeer(Callsign, Ip, PROTOCOL_XLX) == NULL )
|
||||
{
|
||||
// acknowledge the request
|
||||
EncodeConnectAckPacket(&Buffer, Modules);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
g_Reflector.ReleasePeers();
|
||||
|
||||
}
|
||||
break;
|
||||
case XLX_PROTOCOL_REVISION_1:
|
||||
case XLX_PROTOCOL_REVISION_2:
|
||||
default:
|
||||
// acknowledge the request
|
||||
EncodeConnectAckPacket(&Buffer, Modules);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// deny the request
|
||||
EncodeConnectNackPacket(&Buffer);
|
||||
m_Socket.Send(Buffer, Ip);
|
||||
}
|
||||
}
|
||||
else if ( IsValidAckPacket(Buffer, &Callsign, Modules, &Version) )
|
||||
{
|
||||
std::cout << "XLX ack packet for modules " << Modules << " from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// callsign authorized?
|
||||
if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_XLX, Modules) )
|
||||
{
|
||||
// already connected ?
|
||||
CPeers *peers = g_Reflector.GetPeers();
|
||||
if ( peers->FindPeer(Callsign, Ip, PROTOCOL_XLX) == NULL )
|
||||
{
|
||||
// create the new peer
|
||||
// this also create one client per module
|
||||
CPeer *peer = CreateNewPeer(Callsign, Ip, Modules, Version);
|
||||
|
||||
// append the peer to reflector peer list
|
||||
// this also add all new clients to reflector client list
|
||||
peers->AddPeer(peer);
|
||||
}
|
||||
g_Reflector.ReleasePeers();
|
||||
}
|
||||
}
|
||||
else if ( IsValidDisconnectPacket(Buffer, &Callsign) )
|
||||
{
|
||||
std::cout << "XLX disconnect packet from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// find peer
|
||||
CPeers *peers = g_Reflector.GetPeers();
|
||||
CPeer *peer = peers->FindPeer(Ip, PROTOCOL_XLX);
|
||||
if ( peer != NULL )
|
||||
{
|
||||
// remove it from reflector peer list
|
||||
// this also remove all concerned clients from reflector client list
|
||||
// and delete them
|
||||
peers->RemovePeer(peer);
|
||||
}
|
||||
g_Reflector.ReleasePeers();
|
||||
}
|
||||
else if ( IsValidNackPacket(Buffer, &Callsign) )
|
||||
{
|
||||
std::cout << "XLX nack packet from " << Callsign << " at " << Ip << std::endl;
|
||||
}
|
||||
else if ( IsValidKeepAlivePacket(Buffer, &Callsign) )
|
||||
{
|
||||
//std::cout << "XLX keepalive packet from " << Callsign << " at " << Ip << std::endl;
|
||||
|
||||
// find peer
|
||||
CPeers *peers = g_Reflector.GetPeers();
|
||||
CPeer *peer = peers->FindPeer(Ip, PROTOCOL_XLX);
|
||||
if ( peer != NULL )
|
||||
{
|
||||
// keep it alive
|
||||
peer->Alive();
|
||||
}
|
||||
g_Reflector.ReleasePeers();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "XLX packet (" << Buffer.size() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// handle end of streaming timeout
|
||||
CheckStreamsTimeout();
|
||||
|
||||
// handle queue from reflector
|
||||
HandleQueue();
|
||||
|
||||
// keep alive
|
||||
if ( m_LastKeepaliveTime.DurationSinceNow() > XLX_KEEPALIVE_PERIOD )
|
||||
{
|
||||
// handle keep alives
|
||||
HandleKeepalives();
|
||||
|
||||
// update time
|
||||
m_LastKeepaliveTime.Now();
|
||||
}
|
||||
|
||||
// peer connections
|
||||
if ( m_LastPeersLinkTime.DurationSinceNow() > XLX_RECONNECT_PERIOD )
|
||||
{
|
||||
// handle remote peers connections
|
||||
HandlePeerLinks();
|
||||
|
||||
// update time
|
||||
m_LastPeersLinkTime.Now();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// queue helper
|
||||
|
||||
void CXlxProtocol::HandleQueue(void)
|
||||
{
|
||||
m_Queue.Lock();
|
||||
while ( !m_Queue.empty() )
|
||||
{
|
||||
// get the packet
|
||||
CPacket *packet = m_Queue.front();
|
||||
m_Queue.pop();
|
||||
|
||||
// check if origin of packet is local
|
||||
// if not, do not stream it out as it will cause
|
||||
// network loop between linked XLX peers
|
||||
if ( packet->IsLocalOrigin() )
|
||||
{
|
||||
// encode it
|
||||
CBuffer buffer;
|
||||
if ( EncodeDvPacket(*packet, &buffer) )
|
||||
{
|
||||
// encode revision dependent version
|
||||
CBuffer bufferLegacy = buffer;
|
||||
if ( packet->IsDvFrame() && (bufferLegacy.size() == 45) )
|
||||
{
|
||||
bufferLegacy.resize(27);
|
||||
}
|
||||
|
||||
// and push it to all our clients linked to the module and who are not streaming in
|
||||
CClients *clients = g_Reflector.GetClients();
|
||||
int index = -1;
|
||||
CClient *client = NULL;
|
||||
while ( (client = clients->FindNextClient(PROTOCOL_XLX, &index)) != NULL )
|
||||
{
|
||||
// is this client busy ?
|
||||
if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) )
|
||||
{
|
||||
// no, send the packet
|
||||
// this is protocol revision dependent
|
||||
switch ( client->GetProtocolRevision() )
|
||||
{
|
||||
case XLX_PROTOCOL_REVISION_0:
|
||||
case XLX_PROTOCOL_REVISION_1:
|
||||
m_Socket.Send(bufferLegacy, client->GetIp());
|
||||
break;
|
||||
case XLX_PROTOCOL_REVISION_2:
|
||||
default:
|
||||
if ( g_Transcoder.IsConnected() )
|
||||
{
|
||||
m_Socket.Send(buffer, client->GetIp());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Socket.Send(bufferLegacy, client->GetIp());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
delete packet;
|
||||
}
|
||||
m_Queue.Unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// keepalive helpers
|
||||
|
||||
void CXlxProtocol::HandleKeepalives(void)
|
||||
{
|
||||
// DExtra protocol sends and monitors keepalives packets
|
||||
// event if the client is currently streaming
|
||||
// so, send keepalives to all
|
||||
CBuffer keepalive;
|
||||
EncodeKeepAlivePacket(&keepalive);
|
||||
|
||||
// iterate on peers
|
||||
CPeers *peers = g_Reflector.GetPeers();
|
||||
int index = -1;
|
||||
CPeer *peer = NULL;
|
||||
while ( (peer = peers->FindNextPeer(PROTOCOL_XLX, &index)) != NULL )
|
||||
{
|
||||
// send keepalive
|
||||
m_Socket.Send(keepalive, peer->GetIp());
|
||||
|
||||
// client busy ?
|
||||
if ( peer->IsAMaster() )
|
||||
{
|
||||
// yes, just tickle it
|
||||
peer->Alive();
|
||||
}
|
||||
// otherwise check if still with us
|
||||
else if ( !peer->IsAlive() )
|
||||
{
|
||||
// no, disconnect
|
||||
CBuffer disconnect;
|
||||
EncodeDisconnectPacket(&disconnect);
|
||||
m_Socket.Send(disconnect, peer->GetIp());
|
||||
|
||||
// remove it
|
||||
std::cout << "XLX peer " << peer->GetCallsign() << " keepalive timeout" << std::endl;
|
||||
peers->RemovePeer(peer);
|
||||
}
|
||||
}
|
||||
g_Reflector.ReleasePeers();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Peers helpers
|
||||
|
||||
void CXlxProtocol::HandlePeerLinks(void)
|
||||
{
|
||||
CBuffer buffer;
|
||||
|
||||
// get the list of peers
|
||||
CPeerCallsignList *list = g_GateKeeper.GetPeerList();
|
||||
CPeers *peers = g_Reflector.GetPeers();
|
||||
|
||||
// check if all our connected peers are still listed by gatekeeper
|
||||
// if not, disconnect
|
||||
int index = -1;
|
||||
CPeer *peer = NULL;
|
||||
while ( (peer = peers->FindNextPeer(PROTOCOL_XLX, &index)) != NULL )
|
||||
{
|
||||
if ( list->FindListItem(peer->GetCallsign()) == NULL )
|
||||
{
|
||||
// send disconnect packet
|
||||
EncodeDisconnectPacket(&buffer);
|
||||
m_Socket.Send(buffer, peer->GetIp());
|
||||
std::cout << "Sending disconnect packet to XLX peer " << peer->GetCallsign() << std::endl;
|
||||
// remove client
|
||||
peers->RemovePeer(peer);
|
||||
}
|
||||
}
|
||||
|
||||
// check if all ours peers listed by gatekeeper are connected
|
||||
// if not, connect or reconnect
|
||||
for ( unsigned int i = 0; i < list->size(); i++ )
|
||||
{
|
||||
CCallsignListItem *item = &((list->data())[i]);
|
||||
if ( peers->FindPeer(item->GetCallsign(), PROTOCOL_XLX) == NULL )
|
||||
{
|
||||
// resolve again peer's IP in case it's a dynamic IP
|
||||
item->ResolveIp();
|
||||
// send connect packet to re-initiate peer link
|
||||
EncodeConnectPacket(&buffer, item->GetModules());
|
||||
m_Socket.Send(buffer, item->GetIp(), XLX_PORT);
|
||||
/*std::cout << "Sending connect packet to XLX peer " << item->GetCallsign() << " @ " << item->GetIp() << " for modules " << item->GetModules() << std::endl;*/
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
g_Reflector.ReleasePeers();
|
||||
g_GateKeeper.ReleasePeerList();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// streams helpers
|
||||
|
||||
bool CXlxProtocol::OnDvHeaderPacketIn(CDvHeaderPacket *Header, const CIp &Ip)
|
||||
{
|
||||
bool newstream = false;
|
||||
CCallsign peer;
|
||||
|
||||
// todo: verify Packet.GetModuleId() is in authorized list of XLX of origin
|
||||
// todo: do the same for DVFrame and DVLAstFrame packets
|
||||
|
||||
// tag packet as remote peer origin
|
||||
Header->SetRemotePeerOrigin();
|
||||
|
||||
// find the stream
|
||||
CPacketStream *stream = GetStream(Header->GetStreamId());
|
||||
if ( stream == NULL )
|
||||
{
|
||||
// no stream open yet, open a new one
|
||||
// find this client
|
||||
CClient *client = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_XLX, Header->GetRpt2Module());
|
||||
if ( client != NULL )
|
||||
{
|
||||
// and try to open the stream
|
||||
if ( (stream = g_Reflector.OpenStream(Header, client)) != NULL )
|
||||
{
|
||||
// keep the handle
|
||||
m_Streams.push_back(stream);
|
||||
newstream = true;
|
||||
}
|
||||
// get origin
|
||||
peer = client->GetCallsign();
|
||||
}
|
||||
// release
|
||||
g_Reflector.ReleaseClients();
|
||||
}
|
||||
else
|
||||
{
|
||||
// stream already open
|
||||
// skip packet, but tickle the stream
|
||||
stream->Tickle();
|
||||
}
|
||||
|
||||
// update last heard
|
||||
g_Reflector.GetUsers()->Hearing(Header->GetMyCallsign(), Header->GetRpt1Callsign(), Header->GetRpt2Callsign(), peer);
|
||||
g_Reflector.ReleaseUsers();
|
||||
|
||||
// delete header if needed
|
||||
if ( !newstream )
|
||||
{
|
||||
delete Header;
|
||||
}
|
||||
|
||||
// done
|
||||
return newstream;
|
||||
}
|
||||
|
||||
void CXlxProtocol::OnDvFramePacketIn(CDvFramePacket *DvFrame, const CIp *Ip)
|
||||
{
|
||||
// tag packet as remote peer origin
|
||||
DvFrame->SetRemotePeerOrigin();
|
||||
|
||||
// anc call base class
|
||||
CDextraProtocol::OnDvFramePacketIn(DvFrame, Ip);
|
||||
}
|
||||
|
||||
void CXlxProtocol::OnDvLastFramePacketIn(CDvLastFramePacket *DvFrame, const CIp *Ip)
|
||||
{
|
||||
// tag packet as remote peer origin
|
||||
DvFrame->SetRemotePeerOrigin();
|
||||
|
||||
// anc call base class
|
||||
CDextraProtocol::OnDvLastFramePacketIn(DvFrame, Ip);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet decoding helpers
|
||||
|
||||
bool CXlxProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign *callsign)
|
||||
{
|
||||
bool valid = false;
|
||||
if (Buffer.size() == 9)
|
||||
{
|
||||
callsign->SetCallsign(Buffer.data(), 8);
|
||||
valid = callsign->IsValid();
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
||||
bool CXlxProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *modules, CVersion *version)
|
||||
{
|
||||
bool valid = false;
|
||||
if ((Buffer.size() == 39) && (Buffer.data()[0] == 'L') && (Buffer.data()[38] == 0))
|
||||
{
|
||||
callsign->SetCallsign((const uint8 *)&(Buffer.data()[1]), 8);
|
||||
::strcpy(modules, (const char *)&(Buffer.data()[12]));
|
||||
valid = callsign->IsValid();
|
||||
*version = CVersion(Buffer.data()[9], Buffer.data()[10], Buffer.data()[11]);
|
||||
for ( unsigned int i = 0; i < ::strlen(modules); i++ )
|
||||
{
|
||||
valid &= IsLetter(modules[i]);
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CXlxProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign)
|
||||
{
|
||||
bool valid = false;
|
||||
if ((Buffer.size() == 10) && (Buffer.data()[0] == 'U') && (Buffer.data()[9] == 0))
|
||||
{
|
||||
callsign->SetCallsign((const uint8 *)&(Buffer.data()[1]), 8);
|
||||
valid = callsign->IsValid();
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CXlxProtocol::IsValidAckPacket(const CBuffer &Buffer, CCallsign *callsign, char *modules, CVersion *version)
|
||||
{
|
||||
bool valid = false;
|
||||
if ((Buffer.size() == 39) && (Buffer.data()[0] == 'A') && (Buffer.data()[38] == 0))
|
||||
{
|
||||
callsign->SetCallsign((const uint8 *)&(Buffer.data()[1]), 8);
|
||||
::strcpy(modules, (const char *)&(Buffer.data()[12]));
|
||||
valid = callsign->IsValid();
|
||||
*version = CVersion(Buffer.data()[9], Buffer.data()[10], Buffer.data()[11]);
|
||||
for ( unsigned int i = 0; i < ::strlen(modules); i++ )
|
||||
{
|
||||
valid &= IsLetter(modules[i]);
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool CXlxProtocol::IsValidNackPacket(const CBuffer &Buffer, CCallsign *callsign)
|
||||
{
|
||||
bool valid = false;
|
||||
if ((Buffer.size() == 10) && (Buffer.data()[0] == 'N') && (Buffer.data()[9] == 0))
|
||||
{
|
||||
callsign->SetCallsign((const uint8 *)&(Buffer.data()[1]), 8);
|
||||
valid = callsign->IsValid();
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
CDvFramePacket *CXlxProtocol::IsValidDvFramePacket(const CBuffer &Buffer)
|
||||
{
|
||||
CDvFramePacket *dvframe = NULL;
|
||||
|
||||
// base class first (protocol revision 1 and lower)
|
||||
dvframe = CDextraProtocol::IsValidDvFramePacket(Buffer);
|
||||
|
||||
// otherwise try protocol revision 2
|
||||
if ( (dvframe == NULL) &&
|
||||
(Buffer.size() == 45) && (Buffer.Compare((uint8 *)"DSVT", 4) == 0) &&
|
||||
(Buffer.data()[4] == 0x20) && (Buffer.data()[8] == 0x20) &&
|
||||
((Buffer.data()[14] & 0x40) == 0) )
|
||||
{
|
||||
// create packet
|
||||
dvframe = new CDvFramePacket(
|
||||
// sid
|
||||
*((uint16 *)&(Buffer.data()[12])),
|
||||
// dstar
|
||||
Buffer.data()[14], &(Buffer.data()[15]), &(Buffer.data()[24]),
|
||||
// dmr
|
||||
Buffer.data()[27], Buffer.data()[28], &(Buffer.data()[29]), &(Buffer.data()[38]));
|
||||
|
||||
// check validity of packet
|
||||
if ( !dvframe->IsValid() )
|
||||
{
|
||||
delete dvframe;
|
||||
dvframe = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return dvframe;
|
||||
}
|
||||
|
||||
CDvLastFramePacket *CXlxProtocol::IsValidDvLastFramePacket(const CBuffer &Buffer)
|
||||
{
|
||||
CDvLastFramePacket *dvframe = NULL;
|
||||
|
||||
// base class first (protocol revision 1 and lower)
|
||||
dvframe = CDextraProtocol::IsValidDvLastFramePacket(Buffer);
|
||||
|
||||
// otherwise try protocol revision 2
|
||||
if ( (dvframe == NULL) &&
|
||||
(Buffer.size() == 45) && (Buffer.Compare((uint8 *)"DSVT", 4) == 0) &&
|
||||
(Buffer.data()[4] == 0x20) && (Buffer.data()[8] == 0x20) &&
|
||||
((Buffer.data()[14] & 0x40) != 0) )
|
||||
{
|
||||
// create packet
|
||||
dvframe = new CDvLastFramePacket(
|
||||
// sid
|
||||
*((uint16 *)&(Buffer.data()[12])),
|
||||
// dstar
|
||||
Buffer.data()[14], &(Buffer.data()[15]), &(Buffer.data()[24]),
|
||||
// dmr
|
||||
Buffer.data()[27], Buffer.data()[28], &(Buffer.data()[29]), &(Buffer.data()[38]));
|
||||
|
||||
// check validity of packet
|
||||
if ( !dvframe->IsValid() )
|
||||
{
|
||||
delete dvframe;
|
||||
dvframe = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return dvframe;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// packet encoding helpers
|
||||
|
||||
void CXlxProtocol::EncodeKeepAlivePacket(CBuffer *Buffer)
|
||||
{
|
||||
Buffer->Set(GetReflectorCallsign());
|
||||
}
|
||||
|
||||
void CXlxProtocol::EncodeConnectPacket(CBuffer *Buffer, const char *Modules)
|
||||
{
|
||||
uint8 tag[] = { 'L' };
|
||||
|
||||
// tag
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
// our callsign
|
||||
Buffer->resize(Buffer->size()+8);
|
||||
g_Reflector.GetCallsign().GetCallsign(Buffer->data()+1);
|
||||
// our version
|
||||
Buffer->Append((uint8)VERSION_MAJOR);
|
||||
Buffer->Append((uint8)VERSION_MINOR);
|
||||
Buffer->Append((uint8)VERSION_REVISION);
|
||||
// the modules we share
|
||||
Buffer->Append(Modules);
|
||||
Buffer->resize(39);
|
||||
}
|
||||
|
||||
void CXlxProtocol::EncodeDisconnectPacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 'U' };
|
||||
|
||||
// tag
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
// our callsign
|
||||
Buffer->resize(Buffer->size()+8);
|
||||
g_Reflector.GetCallsign().GetCallsign(Buffer->data()+1);
|
||||
Buffer->Append((uint8)0);
|
||||
}
|
||||
|
||||
void CXlxProtocol::EncodeConnectAckPacket(CBuffer *Buffer, const char *Modules)
|
||||
{
|
||||
uint8 tag[] = { 'A' };
|
||||
|
||||
// tag
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
// our callsign
|
||||
Buffer->resize(Buffer->size()+8);
|
||||
g_Reflector.GetCallsign().GetCallsign(Buffer->data()+1);
|
||||
// our version
|
||||
Buffer->Append((uint8)VERSION_MAJOR);
|
||||
Buffer->Append((uint8)VERSION_MINOR);
|
||||
Buffer->Append((uint8)VERSION_REVISION);
|
||||
// the modules we share
|
||||
Buffer->Append(Modules);
|
||||
Buffer->resize(39);
|
||||
}
|
||||
|
||||
void CXlxProtocol::EncodeConnectNackPacket(CBuffer *Buffer)
|
||||
{
|
||||
uint8 tag[] = { 'N' };
|
||||
|
||||
// tag
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
// our callsign
|
||||
Buffer->resize(Buffer->size()+8);
|
||||
g_Reflector.GetCallsign().GetCallsign(Buffer->data()+1);
|
||||
Buffer->Append((uint8)0);
|
||||
}
|
||||
|
||||
bool CXlxProtocol::EncodeDvFramePacket(const CDvFramePacket &Packet, CBuffer *Buffer) const
|
||||
{
|
||||
uint8 tag[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 };
|
||||
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
Buffer->Append(Packet.GetStreamId());
|
||||
Buffer->Append((uint8)(Packet.GetDstarPacketId() % 21));
|
||||
Buffer->Append((uint8 *)Packet.GetAmbe(), AMBE_SIZE);
|
||||
Buffer->Append((uint8 *)Packet.GetDvData(), DVDATA_SIZE);
|
||||
|
||||
Buffer->Append((uint8)Packet.GetDmrPacketId());
|
||||
Buffer->Append((uint8)Packet.GetDmrPacketSubid());
|
||||
Buffer->Append((uint8 *)Packet.GetAmbePlus(), AMBEPLUS_SIZE);
|
||||
Buffer->Append((uint8 *)Packet.GetDvSync(), DVSYNC_SIZE);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool CXlxProtocol::EncodeDvLastFramePacket(const CDvLastFramePacket &Packet, CBuffer *Buffer) const
|
||||
{
|
||||
uint8 tag[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 };
|
||||
uint8 dstarambe[] = { 0x55,0xC8,0x7A,0x00,0x00,0x00,0x00,0x00,0x00 };
|
||||
uint8 dstardvdata[] = { 0x25,0x1A,0xC6 };
|
||||
|
||||
Buffer->Set(tag, sizeof(tag));
|
||||
Buffer->Append(Packet.GetStreamId());
|
||||
Buffer->Append((uint8)((Packet.GetPacketId() % 21) | 0x40));
|
||||
Buffer->Append(dstarambe, sizeof(dstarambe));
|
||||
Buffer->Append(dstardvdata, sizeof(dstardvdata));
|
||||
|
||||
|
||||
Buffer->Append((uint8)Packet.GetDmrPacketId());
|
||||
Buffer->Append((uint8)Packet.GetDmrPacketSubid());
|
||||
Buffer->Append((uint8 *)Packet.GetAmbePlus(), AMBEPLUS_SIZE);
|
||||
Buffer->Append((uint8 *)Packet.GetDvSync(), DVSYNC_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// protocol revision helper
|
||||
|
||||
int CXlxProtocol::GetConnectingPeerProtocolRevision(const CCallsign &Callsign, const CVersion &Version)
|
||||
{
|
||||
int protrev;
|
||||
|
||||
// BM ?
|
||||
if ( Callsign.HasSameCallsignWithWildcard(CCallsign("BM*")) )
|
||||
{
|
||||
protrev = CBmPeer::GetProtocolRevision(Version);
|
||||
}
|
||||
// otherwise, assume native xlx
|
||||
else
|
||||
{
|
||||
protrev = CXlxPeer::GetProtocolRevision(Version);
|
||||
}
|
||||
|
||||
// done
|
||||
return protrev;
|
||||
}
|
||||
|
||||
CPeer *CXlxProtocol::CreateNewPeer(const CCallsign &Callsign, const CIp &Ip, char *Modules, const CVersion &Version)
|
||||
{
|
||||
CPeer *peer = NULL;
|
||||
|
||||
// BM ?
|
||||
if ( Callsign.HasSameCallsignWithWildcard(CCallsign("BM*")) )
|
||||
{
|
||||
peer = new CBmPeer(Callsign, Ip, Modules, Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
peer = new CXlxPeer(Callsign, Ip, Modules, Version);
|
||||
}
|
||||
|
||||
// done
|
||||
return peer;
|
||||
}
|
||||
|
||||
197
main.h
Normal file
197
main.h
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
//
|
||||
// main.h
|
||||
// xlxd
|
||||
//
|
||||
// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015.
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of xlxd.
|
||||
//
|
||||
// xlxd is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// xlxd 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#ifndef main_h
|
||||
#define main_h
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <ctime>
|
||||
#include <cctype>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// defines
|
||||
|
||||
// version -----------------------------------------------------
|
||||
|
||||
#define VERSION_MAJOR 2
|
||||
#define VERSION_MINOR 2
|
||||
#define VERSION_REVISION 3
|
||||
|
||||
// global ------------------------------------------------------
|
||||
|
||||
#define RUN_AS_DAEMON
|
||||
#define JSON_MONITOR
|
||||
//#define NO_ERROR_ON_XML_OPEN_FAIL
|
||||
//#define DEBUG_DUMPFILE
|
||||
|
||||
// reflector ---------------------------------------------------
|
||||
|
||||
#define NB_OF_MODULES 10
|
||||
//#define NB_OF_MODULES NB_MODULES_MAX
|
||||
|
||||
// protocols ---------------------------------------------------
|
||||
|
||||
#define NB_OF_PROTOCOLS 6
|
||||
|
||||
#define PROTOCOL_ANY -1
|
||||
#define PROTOCOL_NONE 0
|
||||
#define PROTOCOL_DEXTRA 1
|
||||
#define PROTOCOL_DPLUS 2
|
||||
#define PROTOCOL_DCS 3
|
||||
#define PROTOCOL_XLX 4
|
||||
#define PROTOCOL_DMRPLUS 5
|
||||
#define PROTOCOL_DMRMMDVM 6
|
||||
|
||||
// DExtra
|
||||
#define DEXTRA_PORT 30001 // UDP port
|
||||
#define DEXTRA_KEEPALIVE_PERIOD 3 // in seconds
|
||||
#define DEXTRA_KEEPALIVE_TIMEOUT (DEXTRA_KEEPALIVE_PERIOD*10) // in seconds
|
||||
|
||||
// DPlus
|
||||
#define DPLUS_PORT 20001 // UDP port
|
||||
#define DPLUS_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define DPLUS_KEEPALIVE_TIMEOUT (DPLUS_KEEPALIVE_PERIOD*10) // in seconds
|
||||
|
||||
// DCS
|
||||
#define DCS_PORT 30051 // UDP port
|
||||
#define DCS_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define DCS_KEEPALIVE_TIMEOUT (DCS_KEEPALIVE_PERIOD*30) // in seconds
|
||||
|
||||
// XLX
|
||||
#define XLX_PORT 10002 // UDP port
|
||||
#define XLX_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define XLX_KEEPALIVE_TIMEOUT (XLX_KEEPALIVE_PERIOD*30) // in seconds
|
||||
#define XLX_RECONNECT_PERIOD 5 // in seconds
|
||||
|
||||
// DMRPlus (dongle)
|
||||
#define DMRPLUS_PORT 8880 // UDP port
|
||||
#define DMRPLUS_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define DMRPLUS_KEEPALIVE_TIMEOUT (DMRPLUS_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define DMRPLUS_REFLECTOR_SLOT DMR_SLOT2
|
||||
#define DMRPLUS_REFLECTOR_COLOUR 1
|
||||
|
||||
// DMRMmdvm
|
||||
#define DMRMMDVM_PORT 62030 // UDP port
|
||||
#define DMRMMDVM_KEEPALIVE_PERIOD 10 // in seconds
|
||||
#define DMRMMDVM_KEEPALIVE_TIMEOUT (DMRMMDVM_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define DMRMMDVM_REFLECTOR_SLOT DMR_SLOT2
|
||||
#define DMRMMDVM_REFLECTOR_COLOUR 1
|
||||
|
||||
// Transcoder server --------------------------------------------
|
||||
|
||||
#define TRANSCODER_PORT 10100 // UDP port
|
||||
#define TRANSCODER_KEEPALIVE_PERIOD 5 // in seconds
|
||||
#define TRANSCODER_KEEPALIVE_TIMEOUT 15 // in seconds
|
||||
#define TRANSCODER_AMBEPACKET_TIMEOUT 600 // in ms
|
||||
|
||||
// codec --------------------------------------------------------
|
||||
|
||||
#define CODEC_NONE 0
|
||||
#define CODEC_AMBEPLUS 1 // DStar
|
||||
#define CODEC_AMBE2PLUS 2 // DMR
|
||||
|
||||
|
||||
// DMRid database -----------------------------------------------
|
||||
|
||||
#define DMRIDDB_USE_RLX_SERVER 0 // 1 = use http, 0 = use local file
|
||||
#define DMRIDDB_PATH "/xlxd/dmrid.dat" // local file path
|
||||
#define DMRIDDB_REFRESH_RATE 180 // in minutes
|
||||
|
||||
|
||||
// xml & json reporting -----------------------------------------
|
||||
|
||||
#define LASTHEARD_USERS_MAX_SIZE 100
|
||||
#define XML_UPDATE_PERIOD 10 // in seconds
|
||||
#define JSON_UPDATE_PERIOD 10 // in seconds
|
||||
#define JSON_PORT 10001
|
||||
|
||||
// system paths -------------------------------------------------
|
||||
|
||||
#define XML_PATH "/var/log/xlxd.xml"
|
||||
#define WHITELIST_PATH "/xlxd/xlxd.whitelist"
|
||||
#define BLACKLIST_PATH "/xlxd/xlxd.blacklist"
|
||||
#define INTERLINKLIST_PATH "/xlxd/xlxd.interlink"
|
||||
#define DEBUGDUMP_PATH "/var/log/xlxd.debug"
|
||||
|
||||
// system constants ---------------------------------------------
|
||||
|
||||
#define NB_MODULES_MAX 26
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// typedefs
|
||||
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint32;
|
||||
typedef unsigned int uint;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// macros
|
||||
|
||||
#define MIN(a, b) ((float)(a) < (float)(b))?(a):(b)
|
||||
#define MAX(a, b) ((a) > (b))?(a):(b)
|
||||
#define MAKEWORD(low, high) ((uint16)(((uint8)(low)) | (((uint16)((uint8)(high))) << 8)))
|
||||
#define MAKEDWORD(low, high) ((uint32)(((uint16)(low)) | (((uint32)((uint16)(high))) << 16)))
|
||||
#define LOBYTE(w) ((uint8)(uint16)(w & 0x00FF))
|
||||
#define HIBYTE(w) ((uint8)((((uint16)(w)) >> 8) & 0xFF))
|
||||
#define LOWORD(dw) ((uint16)(uint32)(dw & 0x0000FFFF))
|
||||
#define HIWORD(dw) ((uint16)((((uint32)(dw)) >> 16) & 0xFFFF))
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// global objects
|
||||
|
||||
class CReflector;
|
||||
extern CReflector g_Reflector;
|
||||
|
||||
class CGateKeeper;
|
||||
extern CGateKeeper g_GateKeeper;
|
||||
|
||||
#if (DMRIDDB_USE_RLX_SERVER == 1)
|
||||
class CDmridDirHttp;
|
||||
extern CDmridDirHttp g_DmridDir;
|
||||
#else
|
||||
class CDmridDirFile;
|
||||
extern CDmridDirFile g_DmridDir;
|
||||
#endif
|
||||
|
||||
class CTranscoder;
|
||||
extern CTranscoder g_Transcoder;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
#endif /* main_h */
|
||||
Loading…
Add table
Add a link
Reference in a new issue