You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
540 lines
13 KiB
540 lines
13 KiB
/*
|
|
* Copyright (C) 2009-2014 by Jonathan Naylor G4KLX
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "RepeaterProtocolHandler.h"
|
|
#include "CCITTChecksumReverse.h"
|
|
#include "DStarDefines.h"
|
|
#include "Utils.h"
|
|
|
|
// #define DUMP_TX
|
|
|
|
const unsigned int BUFFER_LENGTH = 255U;
|
|
|
|
CRepeaterProtocolHandler::CRepeaterProtocolHandler(const wxString& gatewayAddress, unsigned int gatewayPort, const wxString& localAddress, unsigned int localPort, const wxString& name) :
|
|
m_socket(localAddress, localPort),
|
|
m_address(),
|
|
m_port(gatewayPort),
|
|
m_name(name),
|
|
m_outId(0U),
|
|
m_outSeq(0U),
|
|
m_type(NETWORK_NONE),
|
|
m_inId(0U),
|
|
m_buffer(NULL),
|
|
m_length(0U)
|
|
{
|
|
m_address = CUDPReaderWriter::lookup(gatewayAddress);
|
|
|
|
m_buffer = new unsigned char[BUFFER_LENGTH];
|
|
|
|
wxDateTime now = wxDateTime::UNow();
|
|
::srand(now.GetMillisecond());
|
|
}
|
|
|
|
CRepeaterProtocolHandler::~CRepeaterProtocolHandler()
|
|
{
|
|
delete[] m_buffer;
|
|
}
|
|
|
|
bool CRepeaterProtocolHandler::open()
|
|
{
|
|
if (m_address.s_addr == INADDR_NONE)
|
|
return false;
|
|
|
|
return m_socket.open();
|
|
}
|
|
|
|
bool CRepeaterProtocolHandler::writeHeader(const CHeaderData& header)
|
|
{
|
|
unsigned char buffer[50U];
|
|
|
|
buffer[0] = 'D';
|
|
buffer[1] = 'S';
|
|
buffer[2] = 'R';
|
|
buffer[3] = 'P';
|
|
|
|
buffer[4] = 0x20U;
|
|
|
|
// Create a random id for this transmission
|
|
m_outId = (::rand() % 65535U) + 1U;
|
|
|
|
buffer[5] = m_outId / 256U; // Unique session id
|
|
buffer[6] = m_outId % 256U;
|
|
|
|
buffer[7] = 0U;
|
|
|
|
buffer[8] = header.getFlag1();
|
|
buffer[9] = header.getFlag2();
|
|
buffer[10] = header.getFlag3();
|
|
|
|
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[11 + i] = header.getRptCall1().GetChar(i);
|
|
|
|
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[19 + i] = header.getRptCall2().GetChar(i);
|
|
|
|
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[27 + i] = header.getYourCall().GetChar(i);
|
|
|
|
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[35 + i] = header.getMyCall1().GetChar(i);
|
|
|
|
for (unsigned int i = 0U; i < SHORT_CALLSIGN_LENGTH; i++)
|
|
buffer[43 + i] = header.getMyCall2().GetChar(i);
|
|
|
|
// Get the checksum for the header
|
|
CCCITTChecksumReverse csum;
|
|
csum.update(buffer + 8U, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
|
|
csum.result(buffer + 47U);
|
|
|
|
m_outSeq = 0U;
|
|
|
|
#if defined(DUMP_TX)
|
|
CUtils::dump(wxT("Sending Header"), buffer, 49U);
|
|
#endif
|
|
|
|
for (unsigned int i = 0U; i < 2U; i++) {
|
|
bool ret = m_socket.write(buffer, 49U, m_address, m_port);
|
|
if (!ret)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CRepeaterProtocolHandler::writeData(const unsigned char* data, unsigned int length, unsigned int errors, bool end)
|
|
{
|
|
wxASSERT(data != NULL);
|
|
wxASSERT(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES);
|
|
|
|
unsigned char buffer[30U];
|
|
|
|
buffer[0] = 'D';
|
|
buffer[1] = 'S';
|
|
buffer[2] = 'R';
|
|
buffer[3] = 'P';
|
|
|
|
buffer[4] = 0x21U;
|
|
|
|
buffer[5] = m_outId / 256U; // Unique session id
|
|
buffer[6] = m_outId % 256U;
|
|
|
|
// If this is a data sync, reset the sequence to zero
|
|
if (data[9] == 0x55 && data[10] == 0x2D && data[11] == 0x16)
|
|
m_outSeq = 0U;
|
|
|
|
buffer[7] = m_outSeq;
|
|
if (end)
|
|
buffer[7] |= 0x40U; // End of data marker
|
|
|
|
buffer[8] = errors;
|
|
|
|
m_outSeq++;
|
|
if (m_outSeq > 0x14U)
|
|
m_outSeq = 0U;
|
|
|
|
::memcpy(buffer + 9U, data, length);
|
|
|
|
#if defined(DUMP_TX)
|
|
CUtils::dump(wxT("Sending Data"), buffer, length + 9U);
|
|
#endif
|
|
|
|
return m_socket.write(buffer, length + 9U, m_address, m_port);
|
|
}
|
|
|
|
bool CRepeaterProtocolHandler::writeBusyHeader(const CHeaderData& header)
|
|
{
|
|
unsigned char buffer[50U];
|
|
|
|
buffer[0] = 'D';
|
|
buffer[1] = 'S';
|
|
buffer[2] = 'R';
|
|
buffer[3] = 'P';
|
|
|
|
buffer[4] = 0x22U;
|
|
|
|
// Create a random id for this header
|
|
m_outId = (::rand() % 65535U) + 1U;
|
|
|
|
buffer[5] = m_outId / 256U; // Unique session id
|
|
buffer[6] = m_outId % 256U;
|
|
|
|
buffer[7] = 0U;
|
|
|
|
buffer[8] = header.getFlag1();
|
|
buffer[9] = header.getFlag2();
|
|
buffer[10] = header.getFlag3();
|
|
|
|
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[11 + i] = header.getRptCall1().GetChar(i);
|
|
|
|
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[19 + i] = header.getRptCall2().GetChar(i);
|
|
|
|
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[27 + i] = header.getYourCall().GetChar(i);
|
|
|
|
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[35 + i] = header.getMyCall1().GetChar(i);
|
|
|
|
for (unsigned int i = 0U; i < SHORT_CALLSIGN_LENGTH; i++)
|
|
buffer[43 + i] = header.getMyCall2().GetChar(i);
|
|
|
|
// Get the checksum for the header
|
|
CCCITTChecksumReverse csum;
|
|
csum.update(buffer + 8U, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
|
|
csum.result(buffer + 47U);
|
|
|
|
m_outSeq = 0U;
|
|
|
|
#if defined(DUMP_TX)
|
|
CUtils::dump(wxT("Sending Busy Header"), buffer, 49U);
|
|
#endif
|
|
|
|
return m_socket.write(buffer, 49U, m_address, m_port);
|
|
}
|
|
|
|
bool CRepeaterProtocolHandler::writeBusyData(const unsigned char* data, unsigned int length, unsigned int errors, bool end)
|
|
{
|
|
wxASSERT(data != NULL);
|
|
wxASSERT(length == DV_FRAME_LENGTH_BYTES || length == DV_FRAME_MAX_LENGTH_BYTES);
|
|
|
|
unsigned char buffer[30U];
|
|
|
|
buffer[0] = 'D';
|
|
buffer[1] = 'S';
|
|
buffer[2] = 'R';
|
|
buffer[3] = 'P';
|
|
|
|
buffer[4] = 0x23U;
|
|
|
|
buffer[5] = m_outId / 256U; // Unique session id
|
|
buffer[6] = m_outId % 256U;
|
|
|
|
// If this is a data sync, reset the sequence to zero
|
|
if (data[9] == 0x55 && data[10] == 0x2D && data[11] == 0x16)
|
|
m_outSeq = 0U;
|
|
|
|
buffer[7] = m_outSeq;
|
|
if (end)
|
|
buffer[7] |= 0x40U; // End of data marker
|
|
|
|
buffer[8] = errors;
|
|
|
|
m_outSeq++;
|
|
if (m_outSeq > 0x14U)
|
|
m_outSeq = 0U;
|
|
|
|
::memcpy(buffer + 9U, data, length);
|
|
|
|
#if defined(DUMP_TX)
|
|
CUtils::dump(wxT("Sending Busy Data"), buffer, length + 9U);
|
|
#endif
|
|
|
|
return m_socket.write(buffer, length + 9U, m_address, m_port);
|
|
}
|
|
|
|
bool CRepeaterProtocolHandler::writePoll(const wxString& text)
|
|
{
|
|
unsigned char buffer[40U];
|
|
|
|
buffer[0] = 'D';
|
|
buffer[1] = 'S';
|
|
buffer[2] = 'R';
|
|
buffer[3] = 'P';
|
|
|
|
buffer[4] = 0x0A; // Poll with text
|
|
|
|
unsigned int length = text.Length();
|
|
|
|
for (unsigned int i = 0U; i < length; i++)
|
|
buffer[5U + i] = text.GetChar(i);
|
|
|
|
buffer[5U + length] = 0x00;
|
|
|
|
#if defined(DUMP_TX)
|
|
CUtils::dump(wxT("Sending Poll"), buffer, 6U + length);
|
|
#endif
|
|
|
|
return m_socket.write(buffer, 6U + length, m_address, m_port);
|
|
}
|
|
|
|
bool CRepeaterProtocolHandler::writeRegister()
|
|
{
|
|
unsigned char buffer[40U];
|
|
|
|
buffer[0] = 'D';
|
|
buffer[1] = 'S';
|
|
buffer[2] = 'R';
|
|
buffer[3] = 'P';
|
|
|
|
buffer[4] = 0x0B; // Register with name
|
|
|
|
unsigned int length = m_name.Length();
|
|
|
|
for (unsigned int i = 0U; i < length; i++)
|
|
buffer[5U + i] = m_name.GetChar(i);
|
|
|
|
buffer[5U + length] = 0x00;
|
|
|
|
#if defined(DUMP_TX)
|
|
CUtils::dump(wxT("Sending Register"), buffer, 6U + length);
|
|
#endif
|
|
|
|
return m_socket.write(buffer, 6U + length, m_address, m_port);
|
|
}
|
|
|
|
NETWORK_TYPE CRepeaterProtocolHandler::read()
|
|
{
|
|
bool res = true;
|
|
|
|
// Loop until we have no more data from the socket or we have data for the higher layers
|
|
while (res)
|
|
res = readPackets();
|
|
|
|
return m_type;
|
|
}
|
|
|
|
bool CRepeaterProtocolHandler::readPackets()
|
|
{
|
|
m_type = NETWORK_NONE;
|
|
|
|
// No more data?
|
|
in_addr address;
|
|
unsigned int port;
|
|
int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, port);
|
|
if (length <= 0)
|
|
return false;
|
|
|
|
// Check if the data is for us
|
|
if (m_address.s_addr != address.s_addr || m_port != port) {
|
|
wxLogMessage(wxT("Packet received from an invalid source, %08X != %08X and/or %u != %u"), m_address.s_addr, address.s_addr, m_port, port);
|
|
CUtils::dump(wxT("Data"), m_buffer, length);
|
|
return false;
|
|
}
|
|
|
|
m_length = length;
|
|
|
|
// Invalid packet type?
|
|
if (m_buffer[0] == 'D' && m_buffer[1] == 'S' && m_buffer[2] == 'R' && m_buffer[3] == 'P') {
|
|
if (m_buffer[4] == 0x00U) {
|
|
m_type = NETWORK_TEXT;
|
|
return false;
|
|
}
|
|
|
|
if (m_buffer[4] == 0x01U) {
|
|
m_type = NETWORK_TEMPTEXT;
|
|
return false;
|
|
}
|
|
|
|
// Status data 1
|
|
else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x00U) {
|
|
m_type = NETWORK_STATUS1;
|
|
return false;
|
|
}
|
|
|
|
// Status data 2
|
|
else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x01U) {
|
|
m_type = NETWORK_STATUS2;
|
|
return false;
|
|
}
|
|
|
|
// Status data 3
|
|
else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x02U) {
|
|
m_type = NETWORK_STATUS3;
|
|
return false;
|
|
}
|
|
|
|
// Status data 4
|
|
else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x03U) {
|
|
m_type = NETWORK_STATUS4;
|
|
return false;
|
|
}
|
|
|
|
// Status data 5
|
|
else if (m_buffer[4] == 0x04U && m_buffer[5] == 0x04U) {
|
|
m_type = NETWORK_STATUS5;
|
|
return false;
|
|
}
|
|
|
|
// Header data
|
|
else if (m_buffer[4] == 0x20U) {
|
|
wxUint16 id = m_buffer[5] * 256U + m_buffer[6];
|
|
|
|
// Are we listening for headers?
|
|
if (m_inId != 0U)
|
|
return true;
|
|
|
|
m_inId = id; // Take the stream id
|
|
m_type = NETWORK_HEADER;
|
|
return false;
|
|
}
|
|
|
|
// User data
|
|
else if (m_buffer[4] == 0x21U) {
|
|
wxUint16 id = m_buffer[5] * 256U + m_buffer[6];
|
|
|
|
// Check that the stream id matches the valid header, reject otherwise
|
|
if (id != m_inId)
|
|
return true;
|
|
|
|
// Is this the last packet in the stream?
|
|
if ((m_buffer[7] & 0x40) == 0x40)
|
|
m_inId = 0U;
|
|
|
|
m_type = NETWORK_DATA;
|
|
return false;
|
|
}
|
|
|
|
else if (m_buffer[4] == 0x24U) {
|
|
// Silently ignore DD data
|
|
}
|
|
}
|
|
|
|
CUtils::dump(wxT("Unknown packet from the Gateway"), m_buffer, m_length);
|
|
|
|
return true;
|
|
}
|
|
|
|
CHeaderData* CRepeaterProtocolHandler::readHeader()
|
|
{
|
|
if (m_type != NETWORK_HEADER)
|
|
return NULL;
|
|
|
|
// If the checksum is 0xFFFF then we accept the header without testing the checksum
|
|
if (m_buffer[47U] == 0xFFU && m_buffer[48U] == 0xFFU)
|
|
return new CHeaderData(m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES, false);
|
|
|
|
// Header checksum testing is enabled
|
|
CHeaderData* header = new CHeaderData(m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES, true);
|
|
|
|
if (!header->isValid()) {
|
|
CUtils::dump(wxT("Header checksum failure from the Gateway"), m_buffer + 8U, RADIO_HEADER_LENGTH_BYTES);
|
|
delete header;
|
|
return NULL;
|
|
}
|
|
|
|
return header;
|
|
}
|
|
|
|
unsigned int CRepeaterProtocolHandler::readData(unsigned char* buffer, unsigned int length, unsigned char& seqNo)
|
|
{
|
|
if (m_type != NETWORK_DATA)
|
|
return 0U;
|
|
|
|
unsigned int dataLen = m_length - 9U;
|
|
|
|
// Is our buffer too small?
|
|
if (dataLen > length)
|
|
dataLen = length;
|
|
|
|
seqNo = m_buffer[7U];
|
|
|
|
::memcpy(buffer, m_buffer + 9U, dataLen);
|
|
|
|
// Simple sanity checks of the incoming sync bits
|
|
if (seqNo == 0U) {
|
|
// Regenerate sync bytes
|
|
buffer[9U] = DATA_SYNC_BYTES[0U];
|
|
buffer[10U] = DATA_SYNC_BYTES[1U];
|
|
buffer[11U] = DATA_SYNC_BYTES[2U];
|
|
} else if (::memcmp(buffer + 9U, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES) == 0) {
|
|
// Sync bytes appearing where they shouldn't!
|
|
buffer[9U] = 0x70U;
|
|
buffer[10U] = 0x4FU;
|
|
buffer[11U] = 0x93U;
|
|
}
|
|
|
|
return dataLen;
|
|
}
|
|
|
|
void CRepeaterProtocolHandler::readText(wxString& text, LINK_STATUS& status, wxString& reflector)
|
|
{
|
|
if (m_type != NETWORK_TEXT) {
|
|
text = wxT(" ");
|
|
reflector = wxT(" ");
|
|
status = LS_NONE;
|
|
return;
|
|
}
|
|
|
|
text = wxString((char*)(m_buffer + 5U), wxConvLocal, 20U);
|
|
|
|
status = LINK_STATUS(m_buffer[25U]);
|
|
|
|
reflector = wxString((char*)(m_buffer + 26U), wxConvLocal, 8U);
|
|
}
|
|
|
|
void CRepeaterProtocolHandler::readTempText(wxString& text)
|
|
{
|
|
if (m_type != NETWORK_TEMPTEXT) {
|
|
text = wxT(" ");
|
|
return;
|
|
}
|
|
|
|
text = wxString((char*)(m_buffer + 5U), wxConvLocal, 20U);
|
|
}
|
|
|
|
wxString CRepeaterProtocolHandler::readStatus1()
|
|
{
|
|
if (m_type != NETWORK_STATUS1)
|
|
return wxEmptyString;
|
|
|
|
return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U);
|
|
}
|
|
|
|
wxString CRepeaterProtocolHandler::readStatus2()
|
|
{
|
|
if (m_type != NETWORK_STATUS2)
|
|
return wxEmptyString;
|
|
|
|
return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U);
|
|
}
|
|
|
|
wxString CRepeaterProtocolHandler::readStatus3()
|
|
{
|
|
if (m_type != NETWORK_STATUS3)
|
|
return wxEmptyString;
|
|
|
|
return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U);
|
|
}
|
|
|
|
wxString CRepeaterProtocolHandler::readStatus4()
|
|
{
|
|
if (m_type != NETWORK_STATUS4)
|
|
return wxEmptyString;
|
|
|
|
return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U);
|
|
}
|
|
|
|
wxString CRepeaterProtocolHandler::readStatus5()
|
|
{
|
|
if (m_type != NETWORK_STATUS5)
|
|
return wxEmptyString;
|
|
|
|
return wxString((char*)(m_buffer + 6U), wxConvLocal, 20U);
|
|
}
|
|
|
|
void CRepeaterProtocolHandler::reset()
|
|
{
|
|
m_inId = 0U;
|
|
}
|
|
|
|
void CRepeaterProtocolHandler::close()
|
|
{
|
|
m_socket.close();
|
|
}
|