1
0
mirror of https://github.com/ShaYmez/xlxd.git synced 2025-06-15 12:02:26 -04:00
xlxd/src/cdcsprotocol.cpp
Nonoo 6b281b9fdb Prevent sending stream close frames back to the sender
The OnDvLastFramePacketIn() function closed the current stream before
HandleQueue() was called, so the stream's client lost it's stream master status
and HandleQueue() sent the client's last (stream closing) frame back to the
sender.

Applying this commit closes the stream only after HandleQueue() has been
called so no unnecessary call end frame reflection happens to the transmitting
client.
2019-02-19 12:39:48 +01:00

561 lines
18 KiB
C++

//
// 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 = NULL;
// 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
{
delete Header;
delete Frame;
Frame = NULL;
}
}
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();
if ( Frame != NULL && Frame->IsLastPacket() )
{
CloseStreamForDvLastFramePacket((CDvLastFramePacket *)Frame, &Ip);
}
// 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;
}