From 7063ce36a02f904ee76fd735ec64a7199c2e7595 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 23 Feb 2025 11:34:27 -0500 Subject: [PATCH 01/34] refactor how netLDUx buffers are built, and ensure buffers are filled with appropriate null IMBE frames; add support to remote grant demand to include whether or not the grant should set the encrypted flag; add warning to notify a user that a misconfiguration for a voice channel exists when a dedicated trunked voice channel has control data enabled; fix display of RF power level during startup; --- src/host/Host.Config.cpp | 2 +- src/host/p25/Control.cpp | 12 +++- src/host/p25/packet/Voice.cpp | 125 +++++++++++++++++++++++++--------- src/host/p25/packet/Voice.h | 12 +++- 4 files changed, 117 insertions(+), 34 deletions(-) diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp index dd444613..b8f8c258 100644 --- a/src/host/Host.Config.cpp +++ b/src/host/Host.Config.cpp @@ -637,7 +637,7 @@ bool Host::createModem() LogInfo(" RX Coarse: %u, Fine: %u", rxCoarse, rxFine); LogInfo(" TX Coarse: %u, Fine: %u", txCoarse, txFine); LogInfo(" RSSI Coarse: %u, Fine: %u", rssiCoarse, rssiFine); - LogInfo(" RF Power Level: %u%", rfPower); + LogInfo(" RF Power Level: %u%%", rfPower); LogInfo(" RX Level: %.1f%%", rxLevel); LogInfo(" CW Id TX Level: %.1f%%", cwIdTXLevel); LogInfo(" DMR TX Level: %.1f%%", dmrTXLevel); diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index d55b9996..f3d54285 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -441,6 +441,12 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw } }); + // throw a warning if we are notifying a CC of our presence (this indicates we're a VC) *AND* we have the control + // enable flag set + if (m_enableControl && m_notifyCC) { + LogWarning(LOG_P25, "We are configured as a dedicated trunked voice channel but, have control data enabled. Don't do this, control data for a dedicated trunked voice channel should be disabled. This can cause unintended behavior."); + } + if (printOptions) { LogInfo(" Silence Threshold: %u (%.1f%%)", m_voice->m_silenceThreshold, float(m_voice->m_silenceThreshold) / 12.33F); LogInfo(" Frame Loss Threshold: %u", m_frameLossThreshold); @@ -1266,6 +1272,7 @@ void Control::processNetwork() bool grantDemand = (buffer[14U] & 0x80U) == 0x80U; bool grantDenial = (buffer[14U] & 0x40U) == 0x40U; + bool grantEncrypt = (buffer[14U] & 0x08U) == 0x08U; bool unitToUnit = (buffer[14U] & 0x01U) == 0x01U; // process network message header @@ -1434,12 +1441,15 @@ void Control::processNetwork() return; } + if (grantEncrypt) + control.setEncrypted(true); // make sure encrypted flag is set + uint8_t serviceOptions = (control.getEmergency() ? 0x80U : 0x00U) + // Emergency Flag (control.getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag (control.getPriority() & 0x07U); // Priority if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR " remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u", srcId, dstId, unitToUnit); + LogMessage(LOG_NET, P25_TSDU_STR " remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u, encrypted = %u", srcId, dstId, unitToUnit, grantEncrypt); } // are we denying the grant? diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index c1bba2d3..8feb11e9 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -75,6 +75,9 @@ void Voice::resetNet() m_netLastLDU1 = lc; //m_netLastFrameType = P25_FT_DATA_UNIT; + m_gotNetLDU1 = false; + m_gotNetLDU2 = false; + m_netFrames = 0U; m_netLost = 0U; m_vocLDU1Count = 0U; @@ -385,7 +388,12 @@ bool Voice::process(uint8_t* data, uint32_t len) // send network grant demand TDU if (m_p25->m_network != nullptr) { if (!m_p25->m_dedicatedControl && m_p25->m_convNetGrantDemand) { - uint8_t controlByte = 0x80U + ((group) ? 0x00U : 0x01U); + uint8_t controlByte = 0x80U; // Grant Demand Flag + if (encrypted) + controlByte |= 0x08U; // Grant Encrypt Flag + if (!group) + controlByte |= 0x01U; // Unit-to-unit Flag + LogMessage(LOG_RF, P25_HDU_STR " remote grant demand, srcId = %u, dstId = %u", srcId, dstId); m_p25->m_network->writeP25TDU(lc, m_rfLSD, controlByte); } @@ -437,7 +445,9 @@ bool Voice::process(uint8_t* data, uint32_t len) // conventional registration or DVRS support? if ((m_p25->m_enableControl && !m_p25->m_dedicatedControl) || m_p25->m_voiceOnControl) { - m_p25->m_control->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, group, true, true); + if (!m_p25->m_affiliations.isGranted(dstId)) { + m_p25->m_control->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, group, false, true); + } // if voice on control; insert grant updates before voice traffic if (m_p25->m_voiceOnControl) { @@ -1241,6 +1251,8 @@ bool Voice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L m_dfsiLC.decodeLDU1(data + count, m_netLDU1 + 204U); count += DFSI_LDU1_VOICE9_FRAME_LENGTH_BYTES; + m_gotNetLDU1 = true; + // these aren't set by the DFSI decoder, so we'll manually // reset them m_dfsiLC.control()->setNetId(control.getNetId()); @@ -1321,6 +1333,8 @@ bool Voice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L m_dfsiLC.decodeLDU2(data + count, m_netLDU2 + 204U); count += DFSI_LDU2_VOICE18_FRAME_LENGTH_BYTES; + m_gotNetLDU2 = true; + if (m_p25->m_enableControl) { lc::LC control = lc::LC(*m_dfsiLC.control()); m_p25->m_affiliations.touchGrant(control.getDstId()); @@ -1431,7 +1445,9 @@ Voice::Voice(Control* p25, bool debug, bool verbose) : m_rfLSD(), m_netLSD(), m_dfsiLC(), + m_gotNetLDU1(false), m_netLDU1(nullptr), + m_gotNetLDU2(false), m_netLDU2(nullptr), m_lastDUID(DUID::TDU), m_lastIMBE(nullptr), @@ -1449,7 +1465,9 @@ Voice::Voice(Control* p25, bool debug, bool verbose) : m_netLDU2 = new uint8_t[9U * 25U]; ::memset(m_netLDU1, 0x00U, 9U * 25U); + resetWithNullAudio(m_netLDU1, false); ::memset(m_netLDU2, 0x00U, 9U * 25U); + resetWithNullAudio(m_netLDU2, false); m_lastIMBE = new uint8_t[RAW_IMBE_LENGTH_BYTES]; ::memcpy(m_lastIMBE, NULL_IMBE, RAW_IMBE_LENGTH_BYTES); @@ -1556,8 +1574,8 @@ void Voice::writeNet_TDU() if (m_p25->m_network != nullptr) m_p25->m_network->resetP25(); - ::memset(m_netLDU1, 0x00U, 9U * 25U); - ::memset(m_netLDU2, 0x00U, 9U * 25U); + resetWithNullAudio(m_netLDU1, false); + resetWithNullAudio(m_netLDU2, false); m_p25->m_netTimeout.stop(); m_p25->m_networkWatchdog.stop(); @@ -1580,9 +1598,10 @@ void Voice::checkNet_LDU1() return; // check for an unflushed LDU1 - if (m_netLDU1[10U] != 0x00U || m_netLDU1[26U] != 0x00U || m_netLDU1[55U] != 0x00U || + if ((m_netLDU1[10U] != 0x00U || m_netLDU1[26U] != 0x00U || m_netLDU1[55U] != 0x00U || m_netLDU1[80U] != 0x00U || m_netLDU1[105U] != 0x00U || m_netLDU1[130U] != 0x00U || - m_netLDU1[155U] != 0x00U || m_netLDU1[180U] != 0x00U || m_netLDU1[204U] != 0x00U) + m_netLDU1[155U] != 0x00U || m_netLDU1[180U] != 0x00U || m_netLDU1[204U] != 0x00U) && + m_gotNetLDU1) writeNet_LDU1(); } @@ -1711,33 +1730,35 @@ void Voice::writeNet_LDU1() (m_netLC.getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag (m_netLC.getPriority() & 0x07U); // Priority - if (!m_p25->m_control->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, group, true)) { - LogError(LOG_NET, P25_HDU_STR " call rejected, network call not granted, dstId = %u", dstId); + if (!m_p25->m_affiliations.isGranted(dstId)) { + if (!m_p25->m_control->writeRF_TSDU_Grant(srcId, dstId, serviceOptions, group, true)) { + LogError(LOG_NET, P25_HDU_STR " call rejected, network call not granted, dstId = %u", dstId); - if ((!m_p25->m_networkWatchdog.isRunning() || m_p25->m_networkWatchdog.hasExpired()) && - m_p25->m_netLastDstId != 0U) { - if (m_p25->m_network != nullptr) - m_p25->m_network->resetP25(); + if ((!m_p25->m_networkWatchdog.isRunning() || m_p25->m_networkWatchdog.hasExpired()) && + m_p25->m_netLastDstId != 0U) { + if (m_p25->m_network != nullptr) + m_p25->m_network->resetP25(); - ::memset(m_netLDU1, 0x00U, 9U * 25U); - ::memset(m_netLDU2, 0x00U, 9U * 25U); + resetWithNullAudio(m_netLDU1, false); + resetWithNullAudio(m_netLDU2, false); - m_p25->m_netTimeout.stop(); - m_p25->m_networkWatchdog.stop(); + m_p25->m_netTimeout.stop(); + m_p25->m_networkWatchdog.stop(); - m_netLC = lc::LC(); - m_netLastLDU1 = lc::LC(); - m_netLastFrameType = FrameType::DATA_UNIT; + m_netLC = lc::LC(); + m_netLastLDU1 = lc::LC(); + m_netLastFrameType = FrameType::DATA_UNIT; - m_p25->m_netState = RS_NET_IDLE; - m_p25->m_netLastDstId = 0U; - m_p25->m_netLastSrcId = 0U; + m_p25->m_netState = RS_NET_IDLE; + m_p25->m_netLastDstId = 0U; + m_p25->m_netLastSrcId = 0U; - if (m_p25->m_rfState == RS_RF_REJECTED) { - m_p25->m_rfState = RS_RF_LISTENING; - } + if (m_p25->m_rfState == RS_RF_REJECTED) { + m_p25->m_rfState = RS_RF_LISTENING; + } - return; + return; + } } } @@ -1933,7 +1954,8 @@ void Voice::writeNet_LDU1() sysId, netId); } - ::memset(m_netLDU1, 0x00U, 9U * 25U); + resetWithNullAudio(m_netLDU1, m_netLC.getAlgId() != P25DEF::ALGO_UNENCRYPT); + m_gotNetLDU1 = false; m_netFrames += 9U; } @@ -1946,10 +1968,12 @@ void Voice::checkNet_LDU2() return; // check for an unflushed LDU2 - if (m_netLDU2[10U] != 0x00U || m_netLDU2[26U] != 0x00U || m_netLDU2[55U] != 0x00U || + if ((m_netLDU2[10U] != 0x00U || m_netLDU2[26U] != 0x00U || m_netLDU2[55U] != 0x00U || m_netLDU2[80U] != 0x00U || m_netLDU2[105U] != 0x00U || m_netLDU2[130U] != 0x00U || - m_netLDU2[155U] != 0x00U || m_netLDU2[180U] != 0x00U || m_netLDU2[204U] != 0x00U) + m_netLDU2[155U] != 0x00U || m_netLDU2[180U] != 0x00U || m_netLDU2[204U] != 0x00U) && + m_gotNetLDU2) { writeNet_LDU2(); + } } /* Helper to write a network P25 LDU2 packet. */ @@ -2022,14 +2046,15 @@ void Voice::writeNet_LDU2() LogMessage(LOG_NET, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", m_netLC.getAlgId(), m_netLC.getKId()); } - ::memset(m_netLDU2, 0x00U, 9U * 25U); + resetWithNullAudio(m_netLDU2, m_netLC.getAlgId() != P25DEF::ALGO_UNENCRYPT); + m_gotNetLDU2 = false; m_netFrames += 9U; } /* Helper to insert IMBE silence frames for missing audio. */ -void Voice::insertMissingAudio(uint8_t *data) +void Voice::insertMissingAudio(uint8_t* data) { if (data[10U] == 0x00U) { ::memcpy(data + 10U, m_lastIMBE, 11U); @@ -2186,6 +2211,44 @@ void Voice::insertEncryptedNullAudio(uint8_t *data) } } +/* Helper to reset IMBE buffer with null frames. */ + +void Voice::resetWithNullAudio(uint8_t* data, bool encrypted) +{ + if (data == nullptr) + return; + + // clear buffer for next sequence + ::memset(data, 0x00U, 9U * 25U); + + // fill with null + if (!encrypted) { + ::memcpy(data + 10U, P25DEF::NULL_IMBE, 11U); + ::memcpy(data + 26U, P25DEF::NULL_IMBE, 11U); + ::memcpy(data + 55U, P25DEF::NULL_IMBE, 11U); + + ::memcpy(data + 80U, P25DEF::NULL_IMBE, 11U); + ::memcpy(data + 105U, P25DEF::NULL_IMBE, 11U); + ::memcpy(data + 130U, P25DEF::NULL_IMBE, 11U); + + ::memcpy(data + 155U, P25DEF::NULL_IMBE, 11U); + ::memcpy(data + 180U, P25DEF::NULL_IMBE, 11U); + ::memcpy(data + 204U, P25DEF::NULL_IMBE, 11U); + } else { + ::memcpy(data + 10U, P25DEF::ENCRYPTED_NULL_IMBE, 11U); + ::memcpy(data + 26U, P25DEF::ENCRYPTED_NULL_IMBE, 11U); + ::memcpy(data + 55U, P25DEF::ENCRYPTED_NULL_IMBE, 11U); + + ::memcpy(data + 80U, P25DEF::ENCRYPTED_NULL_IMBE, 11U); + ::memcpy(data + 105U, P25DEF::ENCRYPTED_NULL_IMBE, 11U); + ::memcpy(data + 130U, P25DEF::ENCRYPTED_NULL_IMBE, 11U); + + ::memcpy(data + 155U, P25DEF::ENCRYPTED_NULL_IMBE, 11U); + ::memcpy(data + 180U, P25DEF::ENCRYPTED_NULL_IMBE, 11U); + ::memcpy(data + 204U, P25DEF::ENCRYPTED_NULL_IMBE, 11U); + } +} + /* Given the last MI, generate the next MI using LFSR. */ void Voice::getNextMI(uint8_t lastMI[9U], uint8_t nextMI[9U]) diff --git a/src/host/p25/packet/Voice.h b/src/host/p25/packet/Voice.h index c1452535..51d7f8e6 100644 --- a/src/host/p25/packet/Voice.h +++ b/src/host/p25/packet/Voice.h @@ -9,7 +9,7 @@ * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) * * Copyright (C) 2016,2017 Jonathan Naylor, G4KLX -* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL +* Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -111,7 +111,9 @@ namespace p25 data::LowSpeedData m_netLSD; dfsi::LC m_dfsiLC; + bool m_gotNetLDU1; uint8_t* m_netLDU1; + bool m_gotNetLDU2; uint8_t* m_netLDU2; defines::DUID::E m_lastDUID; @@ -192,6 +194,14 @@ namespace p25 * @param data Buffer containing frame data. */ void insertEncryptedNullAudio(uint8_t* data); + + /** + * @brief Helper to reset IMBE buffer with null frames. + * @param data Buffer containing frame data. + * @param encrypted Flag indicating whether or not the data is encrypted. + */ + void resetWithNullAudio(uint8_t* data, bool encrypted); + /** * @brief Given the last MI, generate the next MI using LFSR. * @param lastMI Last MI received. From db1d000b21ba2a8dcaa9859ad6b3dea061fb1ee5 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 23 Feb 2025 17:05:48 -0500 Subject: [PATCH 02/34] increase width of "alias" column in tged; --- src/tged/TGListWnd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tged/TGListWnd.h b/src/tged/TGListWnd.h index 4e4fc299..3381f36b 100644 --- a/src/tged/TGListWnd.h +++ b/src/tged/TGListWnd.h @@ -207,7 +207,7 @@ private: // configure list view columns m_listView.addColumn("Name", 25); - m_listView.addColumn("Alias", 20); + m_listView.addColumn("Alias", 40); m_listView.addColumn("TGID", 9); m_listView.addColumn("Slot", 4); m_listView.addColumn("Active", 5); From aedabceac7925b06bc263908422c6de3bc50bfec Mon Sep 17 00:00:00 2001 From: firealarmss <99303085+firealarmss@users.noreply.github.com> Date: Wed, 26 Feb 2025 10:13:15 -0600 Subject: [PATCH 03/34] Add support for USRP (#83) * Add support for USRP UDP transport * Maintain USRP sequence * Oops not sure how I did that * Update DMR to be like P25 * Check for invalid UDP configuration --------- Co-authored-by: firealarmss --- configs/bridge-config.example.yml | 2 + src/bridge/HostBridge.cpp | 242 ++++++++++++++++++++---------- src/bridge/HostBridge.h | 10 ++ 3 files changed, 174 insertions(+), 80 deletions(-) diff --git a/configs/bridge-config.example.yml b/configs/bridge-config.example.yml index 633543ee..75741117 100644 --- a/configs/bridge-config.example.yml +++ b/configs/bridge-config.example.yml @@ -76,6 +76,8 @@ network: # Flag indicating UDP audio should be RTP framed. # NOTE: This flag is only applicable when encoding G.711 uLaw. udpRTPFrames: false + # Flag indicating UDP audio should follow the USRP format. + udpUsrp: false # Source "Radio ID" for transmitted audio frames. sourceId: 1234567 diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 17b541ef..ef52fb79 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -5,6 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL + * Copyright (C) 2025 Caleb, K4PHP * */ #include "Defines.h" @@ -293,6 +294,7 @@ HostBridge::HostBridge(const std::string& confFile) : m_udpNoIncludeLength(false), m_udpUseULaw(false), m_udpRTPFrames(false), + m_udpUsrp(false), m_srcId(p25::defines::WUID_FNE), m_srcIdOverride(0U), m_overrideSrcIdFromMDC(false), @@ -353,7 +355,8 @@ HostBridge::HostBridge(const std::string& confFile) : m_trace(false), m_debug(false), m_rtpSeqNo(0U), - m_rtpTimestamp(INVALID_TS) + m_rtpTimestamp(INVALID_TS), + m_usrpSeqNo(0U) #if defined(_WIN32) , m_encoderState(nullptr), @@ -1019,10 +1022,19 @@ bool HostBridge::createNetwork() m_udpReceivePort = (uint16_t)networkConf["udpReceivePort"].as(34001); m_udpReceiveAddress = networkConf["udpReceiveAddress"].as(); m_udpUseULaw = networkConf["udpUseULaw"].as(false); + m_udpUsrp = networkConf["udpUsrp"].as(false); + if (m_udpUsrp) { + m_udpMetadata = false; // USRP disables metadata due to USRP always having metadata + m_udpRTPFrames = false; // USRP disables RTP + m_udpNoIncludeLength = true; // USRP disables length + m_udpUseULaw = false; // USRP disables ULaw + } + if (m_udpUseULaw) { m_udpNoIncludeLength = networkConf["udpNoIncludeLength"].as(false); m_udpRTPFrames = networkConf["udpRTPFrames"].as(false); + m_udpUsrp = false; // ULaw disables USRP if (m_udpRTPFrames) m_udpNoIncludeLength = true; // RTP disables the length being included } @@ -1096,6 +1108,7 @@ bool HostBridge::createNetwork() LogInfo(" UDP Audio No Length Header: %s", m_udpNoIncludeLength ? "yes" : "no"); LogInfo(" UDP Audio RTP Framed: %s", m_udpRTPFrames ? "yes" : "no"); } + LogInfo(" UDP Audio USRP: %s", m_udpUsrp ? "yes" : "no"); } LogInfo(" Source ID: %u", m_srcId); @@ -1178,31 +1191,40 @@ void HostBridge::processUDPAudio() pcmLength = __GET_UINT32(buffer, 0U); } - if (m_udpRTPFrames) + if (m_udpRTPFrames || m_udpUsrp) pcmLength = MBE_SAMPLES_LENGTH * 2U; UInt8Array __pcm = std::make_unique(pcmLength); uint8_t* pcm = __pcm.get(); + + if (!m_udpUsrp) { + if (m_udpRTPFrames) { + RTPHeader rtpHeader = RTPHeader(); + rtpHeader.decode(buffer); + + if (rtpHeader.getPayloadType() != RTP_G711_PAYLOAD_TYPE) { + LogError(LOG_HOST, "Invalid RTP payload type %u", rtpHeader.getPayloadType()); + return; + } - if (m_udpRTPFrames) { - RTPHeader rtpHeader = RTPHeader(); - rtpHeader.decode(buffer); - - if (rtpHeader.getPayloadType() != RTP_G711_PAYLOAD_TYPE) { - LogError(LOG_HOST, "Invalid RTP payload type %u", rtpHeader.getPayloadType()); - return; - } - - ::memcpy(pcm, buffer + RTP_HEADER_LENGTH_BYTES, MBE_SAMPLES_LENGTH * 2U); - } - else { - if (m_udpNoIncludeLength) { - ::memcpy(pcm, buffer, pcmLength); + ::memcpy(pcm, buffer + RTP_HEADER_LENGTH_BYTES, MBE_SAMPLES_LENGTH * 2U); } else { - ::memcpy(pcm, buffer + 4U, pcmLength); + if (m_udpNoIncludeLength) { + ::memcpy(pcm, buffer, pcmLength); + } + else { + ::memcpy(pcm, buffer + 4U, pcmLength); + } } } + else { + uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH]; + ::memcpy(usrpHeader, buffer, USRP_HEADER_LENGTH); + + if (usrpHeader[15U] == 1U && length > USRP_HEADER_LENGTH) // PTT state true and ensure we did not just receive a USRP header + ::memcpy(pcm, buffer + USRP_HEADER_LENGTH, pcmLength); + } // Utils::dump(1U, "PCM RECV BYTE BUFFER", pcm, pcmLength); @@ -1444,6 +1466,10 @@ void HostBridge::processDMRNetwork(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); uint64_t diff = now - m_rxStartTime; + // send USRP end of transmission + if (m_udpUsrp) + sendUsrpEot(); + LogMessage(LOG_HOST, "P25, call end (T), srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); } @@ -1539,48 +1565,65 @@ void HostBridge::decodeDMRAudioFrame(uint8_t* ambe, uint32_t srcId, uint32_t dst uint32_t length = (MBE_SAMPLES_LENGTH * 2U) + 4U; uint8_t* audioData = nullptr; - if (!m_udpMetadata) { - audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length) - if (m_udpUseULaw) { - length = (MBE_SAMPLES_LENGTH) + 4U; - if (m_udpNoIncludeLength) { - length = MBE_SAMPLES_LENGTH; - ::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH); - } else { - __SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U); - ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH); - } - - // are we sending RTP audio frames? - if (m_udpRTPFrames) { - uint8_t* rtpFrame = generateRTPHeaders(MBE_SAMPLES_LENGTH, m_rtpSeqNo); - if (rtpFrame != nullptr) { - length += RTP_HEADER_LENGTH_BYTES; - uint8_t* newAudioData = new uint8_t[length]; - ::memcpy(newAudioData, rtpFrame, RTP_HEADER_LENGTH_BYTES); - ::memcpy(newAudioData + RTP_HEADER_LENGTH_BYTES, audioData, MBE_SAMPLES_LENGTH); - delete[] audioData; - - audioData = newAudioData; + if (!m_udpUsrp) { + if (!m_udpMetadata) { + audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length) + if (m_udpUseULaw) { + length = (MBE_SAMPLES_LENGTH)+4U; + if (m_udpNoIncludeLength) { + length = MBE_SAMPLES_LENGTH; + ::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH); + } + else { + __SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U); + ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH); } - m_rtpSeqNo++; + // are we sending RTP audio frames? + if (m_udpRTPFrames) { + uint8_t* rtpFrame = generateRTPHeaders(MBE_SAMPLES_LENGTH, m_rtpSeqNo); + if (rtpFrame != nullptr) { + length += RTP_HEADER_LENGTH_BYTES; + uint8_t* newAudioData = new uint8_t[length]; + ::memcpy(newAudioData, rtpFrame, RTP_HEADER_LENGTH_BYTES); + ::memcpy(newAudioData + RTP_HEADER_LENGTH_BYTES, audioData, MBE_SAMPLES_LENGTH); + delete[] audioData; + + audioData = newAudioData; + } + + m_rtpSeqNo++; + } + } + else { + __SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U); + ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH * 2U); } } else { + length = (MBE_SAMPLES_LENGTH * 2U) + 12U; + audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 12U]; // PCM + (4 bytes (PCM length) + 4 bytes (srcId) + 4 bytes (dstId)) __SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U); ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH * 2U); + + // embed destination and source IDs + __SET_UINT32(dstId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 4U)); + __SET_UINT32(srcId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 8U)); } } else { - length = (MBE_SAMPLES_LENGTH * 2U) + 12U; - audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 12U]; // PCM + (4 bytes (PCM length) + 4 bytes (srcId) + 4 bytes (dstId)) - __SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U); - ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH * 2U); - - // embed destination and source IDs - __SET_UINT32(dstId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 4U)); - __SET_UINT32(srcId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 8U)); + uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH]; + + length = (MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH; + audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH]; // PCM + 32 bytes (USRP Header) + + m_usrpSeqNo++; + usrpHeader[15U] = 1; // set PTT state to true + __SET_UINT32(m_usrpSeqNo, usrpHeader, 4U); + + ::memcpy(usrpHeader, "USRP", 4); + ::memcpy(audioData, usrpHeader, USRP_HEADER_LENGTH); // copy USRP header into the UDP payload + ::memcpy(audioData + USRP_HEADER_LENGTH, pcm, MBE_SAMPLES_LENGTH * 2U); } sockaddr_storage addr; @@ -1863,6 +1906,11 @@ void HostBridge::processP25Network(uint8_t* buffer, uint32_t length) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); uint64_t diff = now - m_rxStartTime; + // send USRP end of transmission + if (m_udpUsrp) { + sendUsrpEot(); + } + LogMessage(LOG_HOST, "P25, call end, srcId = %u, dstId = %u, dur = %us", srcId, dstId, diff / 1000U); } @@ -2130,48 +2178,66 @@ void HostBridge::decodeP25AudioFrame(uint8_t* ldu, uint32_t srcId, uint32_t dstI uint32_t length = (MBE_SAMPLES_LENGTH * 2U) + 4U; uint8_t* audioData = nullptr; - if (!m_udpMetadata) { - audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length) - if (m_udpUseULaw) { - length = (MBE_SAMPLES_LENGTH) + 4U; - if (m_udpNoIncludeLength) { - length = MBE_SAMPLES_LENGTH; - ::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH); - } else { - __SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U); - ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH); - } - // are we sending RTP audio frames? - if (m_udpRTPFrames) { - uint8_t* rtpFrame = generateRTPHeaders(MBE_SAMPLES_LENGTH, m_rtpSeqNo); - if (rtpFrame != nullptr) { - length += RTP_HEADER_LENGTH_BYTES; - uint8_t* newAudioData = new uint8_t[length]; - ::memcpy(newAudioData, rtpFrame, RTP_HEADER_LENGTH_BYTES); - ::memcpy(newAudioData + RTP_HEADER_LENGTH_BYTES, audioData, MBE_SAMPLES_LENGTH); - delete[] audioData; - - audioData = newAudioData; + if (!m_udpUsrp) { + if (!m_udpMetadata) { + audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 4U]; // PCM + 4 bytes (PCM length) + if (m_udpUseULaw) { + length = (MBE_SAMPLES_LENGTH)+4U; + if (m_udpNoIncludeLength) { + length = MBE_SAMPLES_LENGTH; + ::memcpy(audioData, pcm, MBE_SAMPLES_LENGTH); + } + else { + __SET_UINT32(MBE_SAMPLES_LENGTH, audioData, 0U); + ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH); } - m_rtpSeqNo++; + // are we sending RTP audio frames? + if (m_udpRTPFrames) { + uint8_t* rtpFrame = generateRTPHeaders(MBE_SAMPLES_LENGTH, m_rtpSeqNo); + if (rtpFrame != nullptr) { + length += RTP_HEADER_LENGTH_BYTES; + uint8_t* newAudioData = new uint8_t[length]; + ::memcpy(newAudioData, rtpFrame, RTP_HEADER_LENGTH_BYTES); + ::memcpy(newAudioData + RTP_HEADER_LENGTH_BYTES, audioData, MBE_SAMPLES_LENGTH); + delete[] audioData; + + audioData = newAudioData; + } + + m_rtpSeqNo++; + } + } + else { + __SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U); + ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH * 2U); } } else { + length = (MBE_SAMPLES_LENGTH * 2U) + 12U; + audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 12U]; // PCM + (4 bytes (PCM length) + 4 bytes (srcId) + 4 bytes (dstId)) __SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U); ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH * 2U); + + // embed destination and source IDs + __SET_UINT32(dstId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 4U)); + __SET_UINT32(srcId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 8U)); } } else { - length = (MBE_SAMPLES_LENGTH * 2U) + 12U; - audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + 12U]; // PCM + (4 bytes (PCM length) + 4 bytes (srcId) + 4 bytes (dstId)) - __SET_UINT32((MBE_SAMPLES_LENGTH * 2U), audioData, 0U); - ::memcpy(audioData + 4U, pcm, MBE_SAMPLES_LENGTH * 2U); - - // embed destination and source IDs - __SET_UINT32(dstId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 4U)); - __SET_UINT32(srcId, audioData, ((MBE_SAMPLES_LENGTH * 2U) + 8U)); + uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH]; + + length = (MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH; + audioData = new uint8_t[(MBE_SAMPLES_LENGTH * 2U) + USRP_HEADER_LENGTH]; // PCM + 32 bytes (USRP Header) + + m_usrpSeqNo++; + usrpHeader[15U] = 1; // set PTT state to true + __SET_UINT32(m_usrpSeqNo, usrpHeader, 4U); + + ::memcpy(usrpHeader, "USRP", 4); + ::memcpy(audioData, usrpHeader, USRP_HEADER_LENGTH); // copy USRP header into the UDP payload + ::memcpy(audioData + USRP_HEADER_LENGTH, pcm, MBE_SAMPLES_LENGTH * 2U); } sockaddr_storage addr; @@ -2341,6 +2407,22 @@ void HostBridge::encodeP25AudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_ m_p25N++; } +/* Helper to send USRP end of transmission */ + +void HostBridge::sendUsrpEot() { + sockaddr_storage addr; + uint32_t addrLen; + + uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH]; + + m_usrpSeqNo = 0U; + ::memcpy(usrpHeader, "USRP", 4); + + if (udp::Socket::lookup(m_udpSendAddress, m_udpSendPort, addr, addrLen) == 0) { + m_udpAudioSocket->write(usrpHeader, USRP_HEADER_LENGTH, addr, addrLen); + } +} + /* Helper to generate the single-tone preamble tone. */ void HostBridge::generatePreambleTone() diff --git a/src/bridge/HostBridge.h b/src/bridge/HostBridge.h index 2bbf9da9..4fb06ce6 100644 --- a/src/bridge/HostBridge.h +++ b/src/bridge/HostBridge.h @@ -56,6 +56,8 @@ #define DECSTATE_SIZE 2048 #define ENCSTATE_SIZE 6144 +#define USRP_HEADER_LENGTH 32 + const uint8_t FULL_RATE_MODE = 0x00U; const uint8_t HALF_RATE_MODE = 0x01U; @@ -162,6 +164,7 @@ private: bool m_udpNoIncludeLength; bool m_udpUseULaw; bool m_udpRTPFrames; + bool m_udpUsrp; uint32_t m_srcId; uint32_t m_srcIdOverride; @@ -245,6 +248,8 @@ private: uint16_t m_rtpSeqNo; uint32_t m_rtpTimestamp; + uint32_t m_usrpSeqNo; + static std::mutex m_audioMutex; static std::mutex m_networkMutex; @@ -446,6 +451,11 @@ private: */ uint8_t* generateRTPHeaders(uint8_t msgLen, uint16_t& rtpSeq); + /** + * @brief Helper to generate USRP end of transmission + */ + void sendUsrpEot(); + /** * @brief Helper to generate the single-tone preamble tone. */ From 151275ce6f7d697a526e81dd7b43c2aebedb4df6 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 27 Feb 2025 23:58:09 -0500 Subject: [PATCH 04/34] implement initial DMR remote grant demand logic (this is mostly untested); --- configs/config.example.yml | 6 +++ src/bridge/HostBridge.cpp | 5 ++ src/common/dmr/data/NetData.cpp | 3 ++ src/common/dmr/data/NetData.h | 5 ++ src/common/network/BaseNetwork.cpp | 2 +- src/host/dmr/Control.cpp | 20 ++++++++ src/host/dmr/Slot.cpp | 60 +++++++++++++++++++++--- src/host/dmr/Slot.h | 8 +++- src/host/dmr/packet/ControlSignaling.cpp | 4 +- src/host/dmr/packet/Data.cpp | 10 ++-- src/host/dmr/packet/Voice.cpp | 22 ++++++--- 11 files changed, 125 insertions(+), 20 deletions(-) diff --git a/configs/config.example.yml b/configs/config.example.yml index 0ac084d6..6a72c049 100644 --- a/configs/config.example.yml +++ b/configs/config.example.yml @@ -129,8 +129,14 @@ protocols: # Flag indicating whether or not the source ID validation before granting disabled. disableGrantSourceIdCheck: false + # Flag indicating whether or not network calls will generate a channel grant. + # (This applies only in conventional operations where channel granting is utilized and RF-only talkgroup + # steering is required.) + disableNetworkGrant: false # Flag indicating whether or not a TGID will be tested for affiliations before being granted. ignoreAffiliationCheck: false + # Flag indicating the host should send a network grant demand for conventional traffic. + convNetGrantDemand: false # Flag indicating whether or not received RF embedded LC data only should be transmitted. embeddedLCOnly: false # Flag indicating whether talker alias data should be dumped to the log. diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index ef52fb79..d2bf6786 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -1687,6 +1687,11 @@ void HostBridge::encodeDMRAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_ dmrData.setSrcId(srcId); dmrData.setDstId(dstId); dmrData.setFLCO(FLCO::GROUP); + if (m_grantDemand) { + dmrData.setControl(0x80U); // DMR remote grant demand flag + } else { + dmrData.setControl(0U); + } dmrData.setN(m_dmrN); dmrData.setSeqNo(m_dmrSeqNo); dmrData.setBER(0U); diff --git a/src/common/dmr/data/NetData.cpp b/src/common/dmr/data/NetData.cpp index 6e07b65a..fe2d33d9 100644 --- a/src/common/dmr/data/NetData.cpp +++ b/src/common/dmr/data/NetData.cpp @@ -30,6 +30,7 @@ NetData::NetData(const NetData& data) : m_srcId(data.m_srcId), m_dstId(data.m_dstId), m_flco(data.m_flco), + m_control(data.m_control), m_n(data.m_n), m_seqNo(data.m_seqNo), m_dataType(data.m_dataType), @@ -48,6 +49,7 @@ NetData::NetData() : m_srcId(0U), m_dstId(0U), m_flco(FLCO::GROUP), + m_control(0U), m_n(0U), m_seqNo(0U), m_dataType(DataType::IDLE), @@ -76,6 +78,7 @@ NetData& NetData::operator=(const NetData& data) m_srcId = data.m_srcId; m_dstId = data.m_dstId; m_flco = data.m_flco; + m_control = data.m_control; m_dataType = data.m_dataType; m_seqNo = data.m_seqNo; m_n = data.m_n; diff --git a/src/common/dmr/data/NetData.h b/src/common/dmr/data/NetData.h index b089ca23..8c753e7d 100644 --- a/src/common/dmr/data/NetData.h +++ b/src/common/dmr/data/NetData.h @@ -85,6 +85,11 @@ namespace dmr */ __PROPERTY(defines::FLCO::E, flco, FLCO); + /** + * @brief + */ + __PROPERTY(uint8_t, control, Control); + /** * @brief */ diff --git a/src/common/network/BaseNetwork.cpp b/src/common/network/BaseNetwork.cpp index cdfa4e16..c34f4a76 100644 --- a/src/common/network/BaseNetwork.cpp +++ b/src/common/network/BaseNetwork.cpp @@ -742,7 +742,7 @@ UInt8Array BaseNetwork::createDMR_Message(uint32_t& length, const uint32_t strea uint32_t slotNo = data.getSlotNo(); - buffer[14U] = 0U; // Control Bits + buffer[14U] = data.getControl(); // Control Bits // Individual slot disabling if (slotNo == 1U && !m_slot1) { diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index a0565223..1e16f7c5 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -176,6 +176,18 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa m_slot1->m_ignoreAffiliationCheck = ignoreAffiliationCheck; m_slot2->m_ignoreAffiliationCheck = ignoreAffiliationCheck; + /* + ** Network Grant Disables + */ + bool disableNetworkGrant = dmrProtocol["disableNetworkGrant"].as(false); + m_slot1->m_disableNetworkGrant = disableNetworkGrant; + m_slot2->m_disableNetworkGrant = disableNetworkGrant; + + bool convNetGrantDemand = dmrProtocol["convNetGrantDemand"].as(false); + m_slot1->m_convNetGrantDemand = convNetGrantDemand; + m_slot2->m_convNetGrantDemand = convNetGrantDemand; + + if (printOptions) { if (enableTSCC) { LogInfo(" TSCC Slot: %u", m_tsccSlotNo); @@ -185,6 +197,9 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa LogInfo(" TSCC Disable Grant Source ID Check: yes"); } } + if (disableNetworkGrant) { + LogInfo(" Disable Network Grants: yes"); + } LogInfo(" Ignore Affiliation Check: %s", ignoreAffiliationCheck ? "yes" : "no"); LogInfo(" Notify Control: %s", notifyCC ? "yes" : "no"); @@ -192,6 +207,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa LogInfo(" Frame Loss Threshold: %u", frameLossThreshold); LogInfo(" Verify Registration: %s", Slot::m_verifyReg ? "yes" : "no"); + LogInfo(" Conventional Network Grant Demand: %s", convNetGrantDemand ? "yes" : "no"); } } @@ -660,6 +676,8 @@ void Control::processNetwork() uint32_t srcId = __GET_UINT16(buffer, 5U); uint32_t dstId = __GET_UINT16(buffer, 8U); + uint8_t controlByte = buffer[14U]; + FLCO::E flco = (buffer[15U] & 0x40U) == 0x40U ? FLCO::PRIVATE : FLCO::GROUP; uint32_t slotNo = (buffer[15U] & 0x80U) == 0x80U ? 2U : 1U; @@ -691,6 +709,8 @@ void Control::processNetwork() data.setDstId(dstId); data.setFLCO(flco); + data.setControl(controlByte); + bool dataSync = (buffer[15U] & 0x20U) == 0x20U; bool voiceSync = (buffer[15U] & 0x10U) == 0x10U; diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index 4cbe363d..249d355a 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -152,6 +152,8 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz m_enableTSCC(false), m_dedicatedTSCC(false), m_ignoreAffiliationCheck(false), + m_disableNetworkGrant(false), + m_convNetGrantDemand(false), m_tsccPayloadDstId(0U), m_tsccPayloadSrcId(0U), m_tsccPayloadGroup(false), @@ -426,9 +428,54 @@ void Slot::processNetwork(const data::NetData& dmrData) DataType::E dataType = dmrData.getDataType(); // ignore non-CSBK data destined for the TSCC slot - if (m_enableTSCC && m_dedicatedTSCC && m_slotNo == m_dmr->m_tsccSlotNo && - dataType != DataType::CSBK) { - return; + if (m_enableTSCC && m_dedicatedTSCC && m_slotNo == m_dmr->m_tsccSlotNo) { + switch (dataType) + { + case DataType::CSBK: + break; + case DataType::VOICE_LC_HEADER: + case DataType::DATA_HEADER: + { + bool grantDemand = (dmrData.getControl() & 0x80U) == 0x80U; + bool unitToUnit = (dmrData.getControl() & 0x01U) == 0x01U; + + if (grantDemand) { + if (m_disableNetworkGrant) { + return; + } + + // if we're non-dedicated control, and if we're not in a listening or idle state, ignore any grant + // demands + if (!m_dedicatedTSCC && (m_rfState != RS_RF_LISTENING || m_netState != RS_NET_IDLE)) { + return; + } + + // validate source RID + if (!acl::AccessControl::validateSrcId(dmrData.getSrcId())) { + return; + } + + // validate the target ID, if the target is a talkgroup + if (!acl::AccessControl::validateTGId(dmrData.getSlotNo(), dmrData.getDstId())) { + return; + } + + if (m_verbose) { + LogMessage(LOG_NET, "DMR Slot %u, remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u", + m_slotNo, dmrData.getSrcId(), dmrData.getDstId(), unitToUnit); + } + + // perform grant response logic + if (dataType == DataType::VOICE_LC_HEADER) + m_control->writeRF_CSBK_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true); + if (dataType == DataType::DATA_HEADER) + m_control->writeRF_CSBK_Data_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true); + } + } + return; + default: + return; + } } switch (dataType) @@ -1203,18 +1250,18 @@ void Slot::notifyCC_TouchGrant(uint32_t dstId) /* Write data frame to the network. */ -void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, uint8_t errors, bool noSequence) +void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, uint8_t control, uint8_t errors, bool noSequence) { assert(data != nullptr); assert(m_rfLC != nullptr); - writeNetwork(data, dataType, m_rfLC->getFLCO(), m_rfLC->getSrcId(), m_rfLC->getDstId(), errors); + writeNetwork(data, dataType, m_rfLC->getFLCO(), m_rfLC->getSrcId(), m_rfLC->getDstId(), control, errors); } /* Write data frame to the network. */ void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, FLCO::E flco, uint32_t srcId, - uint32_t dstId, uint8_t errors, bool noSequence) + uint32_t dstId, uint8_t control, uint8_t errors, bool noSequence) { assert(data != nullptr); @@ -1230,6 +1277,7 @@ void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, FLCO::E flco, dmrData.setSrcId(srcId); dmrData.setDstId(dstId); dmrData.setFLCO(flco); + dmrData.setControl(control); dmrData.setN(m_voice->m_rfN); dmrData.setSeqNo(m_rfSeqNo); dmrData.setBER(errors); diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 185de43f..6f6ff11b 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -384,6 +384,8 @@ namespace dmr bool m_enableTSCC; bool m_dedicatedTSCC; bool m_ignoreAffiliationCheck; + bool m_disableNetworkGrant; + bool m_convNetGrantDemand; uint32_t m_tsccPayloadDstId; uint32_t m_tsccPayloadSrcId; @@ -488,10 +490,11 @@ namespace dmr * @brief Write data frame to the network. * @param[in] data Buffer containing frame data to write to the network. * @param dataType DMR Data Type for this frame. + * @param control Control Byte. * @param errors Number of bit errors detected for this frame. * @param noSequence Flag indicating this frame carries no sequence number. */ - void writeNetwork(const uint8_t* data, defines::DataType::E dataType, uint8_t errors = 0U, bool noSequence = false); + void writeNetwork(const uint8_t* data, defines::DataType::E dataType, uint8_t control, uint8_t errors = 0U, bool noSequence = false); /** * @brief Write data frame to the network. * @param[in] data Buffer containing frame data to write to the network. @@ -499,11 +502,12 @@ namespace dmr * @param flco Full-Link Control Opcode. * @param srcId Source Radio ID. * @param dstId Destination ID. + * @param control Control Byte. * @param errors Number of bit errors detected for this frame. * @param noSequence Flag indicating this frame carries no sequence number. */ void writeNetwork(const uint8_t* data, defines::DataType::E dataType, defines::FLCO::E flco, uint32_t srcId, - uint32_t dstId, uint8_t errors = 0U, bool noSequence = false); + uint32_t dstId, uint8_t control, uint8_t errors = 0U, bool noSequence = false); /** * @brief Helper to write RF end of frame data. diff --git a/src/host/dmr/packet/ControlSignaling.cpp b/src/host/dmr/packet/ControlSignaling.cpp index 304d345f..d3d5665d 100644 --- a/src/host/dmr/packet/ControlSignaling.cpp +++ b/src/host/dmr/packet/ControlSignaling.cpp @@ -379,7 +379,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) if (m_slot->m_duplex) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::CSBK, gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId, 0U, true); + m_slot->writeNetwork(data, DataType::CSBK, gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId, 0U, 0U, true); } return true; @@ -765,7 +765,7 @@ void ControlSignaling::writeNet_CSBK(lc::CSBK* csbk) if (m_slot->m_duplex) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::CSBK, csbk->getGI() ? FLCO::GROUP : FLCO::PRIVATE, csbk->getSrcId(), csbk->getDstId(), 0U, true); + m_slot->writeNetwork(data, DataType::CSBK, csbk->getGI() ? FLCO::GROUP : FLCO::PRIVATE, csbk->getSrcId(), csbk->getDstId(), 0U, 0U, true); } /* diff --git a/src/host/dmr/packet/Data.cpp b/src/host/dmr/packet/Data.cpp index cc00837b..72bf7118 100644 --- a/src/host/dmr/packet/Data.cpp +++ b/src/host/dmr/packet/Data.cpp @@ -105,7 +105,7 @@ bool Data::process(uint8_t* data, uint32_t len) data[0U] = modem::TAG_EOT; data[1U] = 0x00U; - m_slot->writeNetwork(data, DataType::TERMINATOR_WITH_LC); + m_slot->writeNetwork(data, DataType::TERMINATOR_WITH_LC, 0U); if (m_slot->m_duplex) { for (uint32_t i = 0U; i < m_slot->m_hangCount; i++) @@ -250,7 +250,11 @@ bool Data::process(uint8_t* data, uint32_t len) if (m_slot->m_duplex && m_repeatDataPacket) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::DATA_HEADER); + uint8_t controlByte = 0U; + if (m_slot->m_convNetGrantDemand) + controlByte |= 0x80U; // Grant Demand Flag + + m_slot->writeNetwork(data, DataType::DATA_HEADER, controlByte); m_slot->m_rfState = RS_RF_DATA; m_slot->m_rfLastDstId = dstId; @@ -323,7 +327,7 @@ bool Data::process(uint8_t* data, uint32_t len) // convert the Data Sync to be from the BS or MS as needed Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - m_slot->writeNetwork(data, dataType); + m_slot->writeNetwork(data, dataType, 0U); if (m_slot->m_duplex && m_repeatDataPacket) { m_slot->addFrame(data); diff --git a/src/host/dmr/packet/Voice.cpp b/src/host/dmr/packet/Voice.cpp index 224014b9..ba25180a 100644 --- a/src/host/dmr/packet/Voice.cpp +++ b/src/host/dmr/packet/Voice.cpp @@ -199,7 +199,11 @@ bool Voice::process(uint8_t* data, uint32_t len) m_slot->addFrame(data); } - m_slot->writeNetwork(data, DataType::VOICE_LC_HEADER); + uint8_t controlByte = 0U; + if (m_slot->m_convNetGrantDemand) + controlByte |= 0x80U; // Grant Demand Flag + + m_slot->writeNetwork(data, DataType::VOICE_LC_HEADER, controlByte); m_slot->m_rfState = RS_RF_AUDIO; m_slot->m_rfLastDstId = dstId; @@ -246,7 +250,7 @@ bool Voice::process(uint8_t* data, uint32_t len) if (m_slot->m_duplex) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::VOICE_PI_HEADER); + m_slot->writeNetwork(data, DataType::VOICE_PI_HEADER, 0U); if (m_verbose) { LogMessage(LOG_RF, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_slot->m_slotNo, @@ -304,7 +308,7 @@ bool Voice::process(uint8_t* data, uint32_t len) if (m_slot->m_duplex) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::VOICE_SYNC, errors); + m_slot->writeNetwork(data, DataType::VOICE_SYNC, 0U, errors); return true; } @@ -458,7 +462,7 @@ bool Voice::process(uint8_t* data, uint32_t len) data[0U] = modem::TAG_DATA; data[1U] = 0x00U; - m_slot->writeNetwork(data, DataType::VOICE, errors); + m_slot->writeNetwork(data, DataType::VOICE, 0U, errors); if (m_embeddedLCOnly) { // Only send the previously received LC @@ -561,7 +565,11 @@ bool Voice::process(uint8_t* data, uint32_t len) m_slot->addFrame(start); } - m_slot->writeNetwork(start, DataType::VOICE_LC_HEADER); + uint8_t controlByte = 0U; + if (m_slot->m_convNetGrantDemand) + controlByte |= 0x80U; // Grant Demand Flag + + m_slot->writeNetwork(start, DataType::VOICE_LC_HEADER, controlByte); m_rfN = data[1U] & 0x0FU; @@ -610,7 +618,7 @@ bool Voice::process(uint8_t* data, uint32_t len) if (m_slot->m_duplex) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::VOICE, errors); + m_slot->writeNetwork(data, DataType::VOICE, 0U, errors); m_slot->m_rfState = RS_RF_AUDIO; @@ -645,6 +653,8 @@ void Voice::processNetwork(const data::NetData& dmrData) if (m_slot->m_netState == RS_NET_AUDIO) return; + + lc::FullLC fullLC; std::unique_ptr lc = fullLC.decode(data + 2U, DataType::VOICE_LC_HEADER); if (lc == nullptr) { From 172f0f2eb039e2736b3416197030e673ee24db5e Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 28 Feb 2025 00:07:28 -0500 Subject: [PATCH 05/34] missed commit when this was manually merged; --- src/host/dmr/Slot.cpp | 19 ++- src/host/dmr/packet/ControlSignaling.cpp | 204 +++++++++++------------ 2 files changed, 117 insertions(+), 106 deletions(-) diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index 249d355a..9bea018b 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -427,11 +427,22 @@ void Slot::processNetwork(const data::NetData& dmrData) DataType::E dataType = dmrData.getDataType(); + Slot* tscc = m_dmr->getTSCCSlot(); + + bool enableTSCC = false; + if (tscc != nullptr) + enableTSCC = tscc->m_enableTSCC; + bool dedicatedTSCC = false; + if (tscc != nullptr) + dedicatedTSCC = tscc->m_dedicatedTSCC; + // ignore non-CSBK data destined for the TSCC slot - if (m_enableTSCC && m_dedicatedTSCC && m_slotNo == m_dmr->m_tsccSlotNo) { + if (enableTSCC && dedicatedTSCC) { switch (dataType) { case DataType::CSBK: + if (m_slotNo != m_dmr->m_tsccSlotNo) + return; break; case DataType::VOICE_LC_HEADER: case DataType::DATA_HEADER: @@ -446,7 +457,7 @@ void Slot::processNetwork(const data::NetData& dmrData) // if we're non-dedicated control, and if we're not in a listening or idle state, ignore any grant // demands - if (!m_dedicatedTSCC && (m_rfState != RS_RF_LISTENING || m_netState != RS_NET_IDLE)) { + if (!dedicatedTSCC && (m_rfState != RS_RF_LISTENING || m_netState != RS_NET_IDLE)) { return; } @@ -467,9 +478,9 @@ void Slot::processNetwork(const data::NetData& dmrData) // perform grant response logic if (dataType == DataType::VOICE_LC_HEADER) - m_control->writeRF_CSBK_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true); + tscc->m_control->writeRF_CSBK_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true); if (dataType == DataType::DATA_HEADER) - m_control->writeRF_CSBK_Data_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true); + tscc->m_control->writeRF_CSBK_Data_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true); } } return; diff --git a/src/host/dmr/packet/ControlSignaling.cpp b/src/host/dmr/packet/ControlSignaling.cpp index d3d5665d..fbb96099 100644 --- a/src/host/dmr/packet/ControlSignaling.cpp +++ b/src/host/dmr/packet/ControlSignaling.cpp @@ -684,7 +684,7 @@ void ControlSignaling::writeRF_Call_Alrt(uint32_t srcId, uint32_t dstId) /* Initializes a new instance of the ControlSignaling class. */ -ControlSignaling::ControlSignaling(Slot * slot, network::BaseNetwork * network, bool dumpCSBKData, bool debug, bool verbose) : +ControlSignaling::ControlSignaling(Slot* slot, network::BaseNetwork * network, bool dumpCSBKData, bool debug, bool verbose) : m_slot(slot), m_dumpCSBKData(dumpCSBKData), m_verbose(verbose), @@ -802,7 +802,7 @@ void ControlSignaling::writeRF_CSBK_NACK_RSP(uint32_t dstId, uint8_t reason, uin bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp, bool net, bool skip, uint32_t chNo) { - Slot *m_tscc = m_slot->m_dmr->getTSCCSlot(); + Slot* tscc = m_slot->m_dmr->getTSCCSlot(); uint8_t slot = 0U; @@ -819,10 +819,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (!skip) { if (m_slot->m_rfState != RS_RF_LISTENING && m_slot->m_rfState != RS_RF_DATA) { if (!net) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId); + LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", tscc->m_slotNo, dstId); writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); - ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", tscc->m_slotNo, srcId, dstId); m_slot->m_rfState = RS_RF_REJECTED; } @@ -831,10 +831,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (m_slot->m_netState != RS_NET_IDLE && dstId == m_slot->m_netLastDstId) { if (!net) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId); + LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", tscc->m_slotNo, dstId); writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); - ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", tscc->m_slotNo, srcId, dstId); m_slot->m_rfState = RS_RF_REJECTED; } @@ -853,37 +853,37 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } - if (!m_tscc->m_affiliations->isGranted(dstId)) { - ::lookups::TalkgroupRuleGroupVoice groupVoice = m_tscc->m_tidLookup->find(dstId); + if (!tscc->m_affiliations->isGranted(dstId)) { + ::lookups::TalkgroupRuleGroupVoice groupVoice = tscc->m_tidLookup->find(dstId); slot = groupVoice.source().tgSlot(); - if (grp && !m_tscc->m_ignoreAffiliationCheck) { + if (grp && !tscc->m_ignoreAffiliationCheck) { // is this an affiliation required group? - ::lookups::TalkgroupRuleGroupVoice tid = m_tscc->m_tidLookup->find(dstId, slot); + ::lookups::TalkgroupRuleGroupVoice tid = tscc->m_tidLookup->find(dstId, slot); if (tid.config().affiliated()) { - if (!m_tscc->m_affiliations->hasGroupAff(dstId)) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_VOICE_CALL (Group Voice Call) ignored, no group affiliations, dstId = %u", m_tscc->m_slotNo, dstId); + if (!tscc->m_affiliations->hasGroupAff(dstId)) { + LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_VOICE_CALL (Group Voice Call) ignored, no group affiliations, dstId = %u", tscc->m_slotNo, dstId); return false; } } } - if (!grp && !m_tscc->m_ignoreAffiliationCheck) { + if (!grp && !tscc->m_ignoreAffiliationCheck) { // is this the target registered? - if (!m_tscc->m_affiliations->isUnitReg(dstId)) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_VOICE_CALL (Individual Voice Call) ignored, no unit registration, dstId = %u", m_tscc->m_slotNo, dstId); + if (!tscc->m_affiliations->isUnitReg(dstId)) { + LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_VOICE_CALL (Individual Voice Call) ignored, no unit registration, dstId = %u", tscc->m_slotNo, dstId); return false; } } - uint32_t availChNo = m_tscc->m_affiliations->getAvailableChannelForSlot(slot); - if (!m_tscc->m_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) { + uint32_t availChNo = tscc->m_affiliations->getAvailableChannelForSlot(slot); + if (!tscc->m_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) { if (grp) { if (!net) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_VOICE_CALL (Group Voice Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId); + LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_VOICE_CALL (Group Voice Call) queued, no channels available, dstId = %u", tscc->m_slotNo, dstId); writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_QUEUED_RSN_NO_RESOURCE, (grp) ? 1U : 0U); - ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", tscc->m_slotNo, srcId, dstId); m_slot->m_rfState = RS_RF_REJECTED; } @@ -891,10 +891,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } else { if (!net) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_VOICE_CALL (Individual Voice Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId); + LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_VOICE_CALL (Individual Voice Call) queued, no channels available, dstId = %u", tscc->m_slotNo, dstId); writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_QUEUED_RSN_NO_RESOURCE, (grp) ? 1U : 0U); - ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", tscc->m_slotNo, srcId, dstId); m_slot->m_rfState = RS_RF_REJECTED; } @@ -902,24 +902,24 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } else { - if (m_tscc->m_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) { - chNo = m_tscc->m_affiliations->getGrantedCh(dstId); - slot = m_tscc->m_affiliations->getGrantedSlot(dstId); - //m_tscc->m_siteData.setChCnt(m_tscc->m_affiliations->getRFChCnt() + m_tscc->m_affiliations->getGrantedRFChCnt()); + if (tscc->m_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) { + chNo = tscc->m_affiliations->getGrantedCh(dstId); + slot = tscc->m_affiliations->getGrantedSlot(dstId); + //tscc->m_siteData.setChCnt(tscc->m_affiliations->getRFChCnt() + tscc->m_affiliations->getGrantedRFChCnt()); } } } else { - if (!m_tscc->m_disableGrantSrcIdCheck && !net) { + if (!tscc->m_disableGrantSrcIdCheck && !net) { // do collision check between grants to see if a SU is attempting a "grant retry" or if this is a // different source from the original grant - uint32_t grantedSrcId = m_tscc->m_affiliations->getGrantedSrcId(dstId); + uint32_t grantedSrcId = tscc->m_affiliations->getGrantedSrcId(dstId); if (srcId != grantedSrcId) { if (!net) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId); + LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", tscc->m_slotNo, dstId); writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); - ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", tscc->m_slotNo, srcId, dstId); m_slot->m_rfState = RS_RF_REJECTED; } @@ -927,18 +927,18 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } - chNo = m_tscc->m_affiliations->getGrantedCh(dstId); - slot = m_tscc->m_affiliations->getGrantedSlot(dstId); + chNo = tscc->m_affiliations->getGrantedCh(dstId); + slot = tscc->m_affiliations->getGrantedSlot(dstId); - m_tscc->m_affiliations->touchGrant(dstId); + tscc->m_affiliations->touchGrant(dstId); } } else { - if (m_tscc->m_affiliations->isGranted(dstId)) { - chNo = m_tscc->m_affiliations->getGrantedCh(dstId); - slot = m_tscc->m_affiliations->getGrantedSlot(dstId); + if (tscc->m_affiliations->isGranted(dstId)) { + chNo = tscc->m_affiliations->getGrantedCh(dstId); + slot = tscc->m_affiliations->getGrantedSlot(dstId); - m_tscc->m_affiliations->touchGrant(dstId); + tscc->m_affiliations->touchGrant(dstId); } else { return false; @@ -947,13 +947,13 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (grp) { if (!net) { - ::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", tscc->m_slotNo, srcId, dstId); } // callback REST API to permit the granted TG on the specified voice channel - if (m_tscc->m_authoritative && m_tscc->m_supervisor && - m_tscc->m_channelNo != chNo) { - ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (tscc->m_authoritative && tscc->m_supervisor && + tscc->m_channelNo != chNo) { + ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); int state = modem::DVM_STATE::STATE_DMR; @@ -962,10 +962,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ req["slot"].set(slot); int ret = RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - HTTP_PUT, PUT_PERMIT_TG, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug); + HTTP_PUT, PUT_PERMIT_TG, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug); if (ret != network::rest::http::HTTPPayload::StatusType::OK) { - ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot); - m_tscc->m_affiliations->releaseGrant(dstId, false); + ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); + tscc->m_affiliations->releaseGrant(dstId, false); if (!net) { writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); m_slot->m_rfState = RS_RF_REJECTED; @@ -975,7 +975,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } else { - ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot); + ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); } } @@ -989,7 +989,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (m_verbose) { LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", - m_tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); + tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } csbk->setEmergency(emergency); @@ -1001,8 +1001,8 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ writeRF_CSBK_Imm(csbk.get()); // if the channel granted isn't the same as the TSCC; remote activate the payload channel - if (chNo != m_tscc->m_channelNo) { - ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (chNo != tscc->m_channelNo) { + ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); @@ -1013,10 +1013,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ req["voice"].set(voice); RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug); + HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug); } else { - ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot); + ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); } } else { @@ -1025,13 +1025,13 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } else { if (!net) { - ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", tscc->m_slotNo, srcId, dstId); } // callback REST API to permit the granted TG on the specified voice channel - if (m_tscc->m_authoritative && m_tscc->m_supervisor && - m_tscc->m_channelNo != chNo) { - ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (tscc->m_authoritative && tscc->m_supervisor && + tscc->m_channelNo != chNo) { + ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); int state = modem::DVM_STATE::STATE_DMR; @@ -1040,10 +1040,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ req["slot"].set(slot); int ret = RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - HTTP_PUT, PUT_PERMIT_TG, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug); + HTTP_PUT, PUT_PERMIT_TG, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug); if (ret != network::rest::http::HTTPPayload::StatusType::OK) { - ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot); - m_tscc->m_affiliations->releaseGrant(dstId, false); + ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); + tscc->m_affiliations->releaseGrant(dstId, false); if (!net) { writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); m_slot->m_rfState = RS_RF_REJECTED; @@ -1053,7 +1053,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } else { - ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot); + ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to permit TG for use, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); } } @@ -1065,7 +1065,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (m_verbose) { LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", - m_tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); + tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } csbk->setEmergency(emergency); @@ -1077,8 +1077,8 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ writeRF_CSBK_Imm(csbk.get()); // if the channel granted isn't the same as the TSCC; remote activate the payload channel - if (chNo != m_tscc->m_channelNo) { - ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (chNo != tscc->m_channelNo) { + ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); @@ -1089,10 +1089,10 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ req["voice"].set(voice); RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug); + HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug); } else { - ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot); + ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); } } else { @@ -1107,7 +1107,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp, bool net, bool skip, uint32_t chNo) { - Slot *m_tscc = m_slot->m_dmr->getTSCCSlot(); + Slot* tscc = m_slot->m_dmr->getTSCCSlot(); uint8_t slot = 0U; @@ -1124,10 +1124,10 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u if (!skip) { if (m_slot->m_rfState != RS_RF_LISTENING && m_slot->m_rfState != RS_RF_DATA) { if (!net) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, DATA_CALL (Data Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId); + LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, DATA_CALL (Data Call) denied, traffic in progress, dstId = %u", tscc->m_slotNo, dstId); writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); - ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", tscc->m_slotNo, srcId, dstId); m_slot->m_rfState = RS_RF_REJECTED; } @@ -1136,10 +1136,10 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u if (m_slot->m_netState != RS_NET_IDLE && dstId == m_slot->m_netLastDstId) { if (!net) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, DATA_CALL (Data Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId); + LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, DATA_CALL (Data Call) denied, traffic in progress, dstId = %u", tscc->m_slotNo, dstId); writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); - ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", tscc->m_slotNo, srcId, dstId); m_slot->m_rfState = RS_RF_REJECTED; } @@ -1158,18 +1158,18 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u } } - if (!m_tscc->m_affiliations->isGranted(dstId)) { - ::lookups::TalkgroupRuleGroupVoice groupVoice = m_tscc->m_tidLookup->find(dstId); + if (!tscc->m_affiliations->isGranted(dstId)) { + ::lookups::TalkgroupRuleGroupVoice groupVoice = tscc->m_tidLookup->find(dstId); slot = groupVoice.source().tgSlot(); - uint32_t availChNo = m_tscc->m_affiliations->getAvailableChannelForSlot(slot); - if (!m_tscc->m_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) { + uint32_t availChNo = tscc->m_affiliations->getAvailableChannelForSlot(slot); + if (!tscc->m_affiliations->rfCh()->isRFChAvailable() || availChNo == 0U) { if (grp) { if (!net) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_DATA_CALL (Group Data Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId); + LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, GRP_DATA_CALL (Group Data Call) queued, no channels available, dstId = %u", tscc->m_slotNo, dstId); writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_QUEUED_RSN_NO_RESOURCE, (grp) ? 1U : 0U); - ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", tscc->m_slotNo, srcId, dstId); m_slot->m_rfState = RS_RF_REJECTED; } @@ -1177,10 +1177,10 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u } else { if (!net) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_DATA_CALL (Individual Data Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId); + LogWarning(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access, IND_DATA_CALL (Individual Data Call) queued, no channels available, dstId = %u", tscc->m_slotNo, dstId); writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_QUEUED_RSN_NO_RESOURCE, (grp) ? 1U : 0U); - ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", tscc->m_slotNo, srcId, dstId); m_slot->m_rfState = RS_RF_REJECTED; } @@ -1188,26 +1188,26 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u } } else { - if (m_tscc->m_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) { - chNo = m_tscc->m_affiliations->getGrantedCh(dstId); - slot = m_tscc->m_affiliations->getGrantedSlot(dstId); + if (tscc->m_affiliations->grantChSlot(dstId, srcId, slot, GRANT_TIMER_TIMEOUT, grp, net)) { + chNo = tscc->m_affiliations->getGrantedCh(dstId); + slot = tscc->m_affiliations->getGrantedSlot(dstId); - //m_tscc->m_siteData.setChCnt(m_tscc->m_affiliations->getRFChCnt() + m_tscc->m_affiliations->getGrantedRFChCnt()); + //tscc->m_siteData.setChCnt(tscc->m_affiliations->getRFChCnt() + tscc->m_affiliations->getGrantedRFChCnt()); } } } else { - chNo = m_tscc->m_affiliations->getGrantedCh(dstId); - slot = m_tscc->m_affiliations->getGrantedSlot(dstId); + chNo = tscc->m_affiliations->getGrantedCh(dstId); + slot = tscc->m_affiliations->getGrantedSlot(dstId); - m_tscc->m_affiliations->touchGrant(dstId); + tscc->m_affiliations->touchGrant(dstId); } } if (grp) { if (!net) { - ::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", tscc->m_slotNo, srcId, dstId); } writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_ACK_RSN_MSG, (grp) ? 1U : 0U); @@ -1218,7 +1218,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u if (m_verbose) { LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", - m_tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); + tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } csbk->setEmergency(emergency); @@ -1230,8 +1230,8 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u writeRF_CSBK_Imm(csbk.get()); // if the channel granted isn't the same as the TSCC; remote activate the payload channel - if (chNo != m_tscc->m_channelNo) { - ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (chNo != tscc->m_channelNo) { + ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); @@ -1242,10 +1242,10 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u req["voice"].set(voice); RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug); + HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug); } else { - ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot); + ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); } } else { @@ -1254,7 +1254,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u } else { if (!net) { - ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", tscc->m_slotNo, srcId, dstId); } writeRF_CSBK_ACK_RSP(srcId, ReasonCode::TS_ACK_RSN_MSG, (grp) ? 1U : 0U); @@ -1265,7 +1265,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u if (m_verbose) { LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, CSBK, %s, emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", - m_tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); + tscc->m_slotNo, csbk->toString().c_str(), emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } csbk->setEmergency(emergency); @@ -1277,8 +1277,8 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u writeRF_CSBK_Imm(csbk.get()); // if the channel granted isn't the same as the TSCC; remote activate the payload channel - if (chNo != m_tscc->m_channelNo) { - ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->rfCh()->getRFChData(chNo); + if (chNo != tscc->m_channelNo) { + ::lookups::VoiceChData voiceChData = tscc->m_affiliations->rfCh()->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); @@ -1289,10 +1289,10 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u req["voice"].set(voice); RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, m_tscc->m_debug); + HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, voiceChData.ssl(), REST_QUICK_WAIT, tscc->m_debug); } else { - ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot); + ::LogError(LOG_RF, "DMR Slot %u, CSBK, RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", tscc->m_slotNo, chNo, slot); } } else { @@ -1307,7 +1307,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOptions) { - Slot *m_tscc = m_slot->m_dmr->getTSCCSlot(); + Slot* tscc = m_slot->m_dmr->getTSCCSlot(); bool dereg = (serviceOptions & 0x01U) == 0x01U; uint8_t powerSave = (serviceOptions >> 1) & 0x07U; @@ -1318,7 +1318,7 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt csbk->setReason(ReasonCode::TS_DENY_RSN_REG_DENIED); if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, SU power saving unsupported, srcId = %u, serviceOptions = $%02X", m_tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); + LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, SU power saving unsupported, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); } csbk->setSrcId(WUID_REGI); @@ -1334,7 +1334,7 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt if (!dereg) { if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", m_tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); + LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); } // remove dynamic unit registration table entry @@ -1351,14 +1351,14 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt // validate the source RID if (!acl::AccessControl::validateSrcId(srcId)) { - LogWarning(LOG_RF, "DMR Slot %u, CSBK, %s, denial, RID rejection, srcId = %u", m_tscc->m_slotNo, csbk->toString().c_str(), srcId); + LogWarning(LOG_RF, "DMR Slot %u, CSBK, %s, denial, RID rejection, srcId = %u", tscc->m_slotNo, csbk->toString().c_str(), srcId); ::ActivityLog("DMR", true, "unit registration request from %u denied", srcId); csbk->setReason(ReasonCode::TS_DENY_RSN_REG_DENIED); } if (csbk->getReason() == ReasonCode::TS_ACK_RSN_REG) { if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", m_tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); + LogMessage(LOG_RF, "DMR Slot %u, CSBK, %s, srcId = %u, serviceOptions = $%02X", tscc->m_slotNo, csbk->toString().c_str(), srcId, serviceOptions); } ::ActivityLog("DMR", true, "unit registration request from %u", srcId); @@ -1383,10 +1383,10 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt void ControlSignaling::writeRF_CSBK_Grant_LateEntry(uint32_t dstId, uint32_t srcId, bool grp) { - Slot *m_tscc = m_slot->m_dmr->getTSCCSlot(); + Slot* tscc = m_slot->m_dmr->getTSCCSlot(); - uint32_t chNo = m_tscc->m_affiliations->getGrantedCh(dstId); - uint8_t slot = m_tscc->m_affiliations->getGrantedSlot(dstId); + uint32_t chNo = tscc->m_affiliations->getGrantedCh(dstId); + uint8_t slot = tscc->m_affiliations->getGrantedSlot(dstId); if (grp) { std::unique_ptr csbk = std::make_unique(); From e4d2d677fe8f9896d7cc2b004e507146a7216c67 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 28 Feb 2025 00:19:36 -0500 Subject: [PATCH 06/34] last minute fix -- the original implementation was naive and always aborted incorrectly; --- src/host/dmr/Slot.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index 9bea018b..d270b7c0 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -452,23 +452,31 @@ void Slot::processNetwork(const data::NetData& dmrData) if (grantDemand) { if (m_disableNetworkGrant) { - return; + if (m_enableTSCC && m_dedicatedTSCC) // if we *are* the TSCC exit after this + return; + break; } // if we're non-dedicated control, and if we're not in a listening or idle state, ignore any grant // demands if (!dedicatedTSCC && (m_rfState != RS_RF_LISTENING || m_netState != RS_NET_IDLE)) { - return; + if (m_enableTSCC && m_dedicatedTSCC) // if we *are* the TSCC exit after this + return; + break; } // validate source RID if (!acl::AccessControl::validateSrcId(dmrData.getSrcId())) { - return; + if (m_enableTSCC && m_dedicatedTSCC) // if we *are* the TSCC exit after this + return; + break; } // validate the target ID, if the target is a talkgroup if (!acl::AccessControl::validateTGId(dmrData.getSlotNo(), dmrData.getDstId())) { - return; + if (m_enableTSCC && m_dedicatedTSCC) // if we *are* the TSCC exit after this + return; + break; } if (m_verbose) { @@ -483,7 +491,10 @@ void Slot::processNetwork(const data::NetData& dmrData) tscc->m_control->writeRF_CSBK_Data_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true); } } - return; + + if (m_enableTSCC && m_dedicatedTSCC) // if we *are* the TSCC exit after this + return; + break; default: return; } From 71d55b1645a2d4e3a10bf62bc9ddfaa4a7e83e9f Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 28 Feb 2025 09:11:12 -0500 Subject: [PATCH 07/34] correct some late night strangeness when handling whether or not a slot is a TSCC for remote grant demand processing (this is what I get for trying to quickly push out decently old branch code); --- src/host/dmr/Slot.cpp | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index d270b7c0..c028605a 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -436,14 +436,10 @@ void Slot::processNetwork(const data::NetData& dmrData) if (tscc != nullptr) dedicatedTSCC = tscc->m_dedicatedTSCC; - // ignore non-CSBK data destined for the TSCC slot + // check if this host instance is TSCC enabled or not -- if it is, handle processing network grant demands if (enableTSCC && dedicatedTSCC) { switch (dataType) { - case DataType::CSBK: - if (m_slotNo != m_dmr->m_tsccSlotNo) - return; - break; case DataType::VOICE_LC_HEADER: case DataType::DATA_HEADER: { @@ -452,30 +448,22 @@ void Slot::processNetwork(const data::NetData& dmrData) if (grantDemand) { if (m_disableNetworkGrant) { - if (m_enableTSCC && m_dedicatedTSCC) // if we *are* the TSCC exit after this - return; break; } // if we're non-dedicated control, and if we're not in a listening or idle state, ignore any grant // demands if (!dedicatedTSCC && (m_rfState != RS_RF_LISTENING || m_netState != RS_NET_IDLE)) { - if (m_enableTSCC && m_dedicatedTSCC) // if we *are* the TSCC exit after this - return; break; } // validate source RID if (!acl::AccessControl::validateSrcId(dmrData.getSrcId())) { - if (m_enableTSCC && m_dedicatedTSCC) // if we *are* the TSCC exit after this - return; break; } // validate the target ID, if the target is a talkgroup if (!acl::AccessControl::validateTGId(dmrData.getSlotNo(), dmrData.getDstId())) { - if (m_enableTSCC && m_dedicatedTSCC) // if we *are* the TSCC exit after this - return; break; } @@ -491,12 +479,20 @@ void Slot::processNetwork(const data::NetData& dmrData) tscc->m_control->writeRF_CSBK_Data_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true); } } - - if (m_enableTSCC && m_dedicatedTSCC) // if we *are* the TSCC exit after this - return; break; default: - return; + break; + } + + // if *this slot* is the TSCC slot, stop processing after this point + if (m_enableTSCC && m_dedicatedTSCC) + { + if (dataType != DataType::CSBK) + return; + else { + if (m_slotNo != m_dmr->m_tsccSlotNo) + return; + } } } From ad8713fbe8e328b6c86a5ea11e58053d03055fa1 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 28 Feb 2025 09:24:18 -0500 Subject: [PATCH 08/34] issue a P_CLEAR proper at the end of a network call; --- src/host/dmr/Slot.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index c028605a..4cc7ad00 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -1163,15 +1163,15 @@ void Slot::processFrameLoss() m_slotNo, m_rfFrames, m_rfBits, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); // release trunked grant (if necessary) - Slot *m_tscc = m_dmr->getTSCCSlot(); - if (m_tscc != nullptr) { - if (m_tscc->m_enableTSCC && m_rfLC != nullptr) { - m_tscc->m_affiliations->releaseGrant(m_rfLC->getDstId(), false); + Slot* tscc = m_dmr->getTSCCSlot(); + if (tscc != nullptr) { + if (tscc->m_enableTSCC && m_rfLC != nullptr) { + tscc->m_affiliations->releaseGrant(m_rfLC->getDstId(), false); } clearTSCCActivated(); - if (!m_tscc->m_enableTSCC) { + if (!tscc->m_enableTSCC) { notifyCC_ReleaseGrant(m_rfLC->getDstId()); } } @@ -1397,6 +1397,20 @@ void Slot::writeEndNet(bool writeEnd) } } + // release trunked grant (if necessary) + Slot* tscc = m_dmr->getTSCCSlot(); + if (tscc != nullptr) { + if (tscc->m_enableTSCC && m_rfLC != nullptr) { + tscc->m_affiliations->releaseGrant(m_rfLC->getDstId(), false); + } + + clearTSCCActivated(); + + if (!tscc->m_enableTSCC) { + notifyCC_ReleaseGrant(m_rfLC->getDstId()); + } + } + m_data->m_pduDataOffset = 0U; if (m_network != nullptr) From 633ff29f550dd84378eb5b939b0227f24a4ea3bc Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 28 Feb 2025 09:31:21 -0500 Subject: [PATCH 09/34] fix typo; --- src/host/dmr/Slot.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index 4cc7ad00..ab9d31d4 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -1400,14 +1400,14 @@ void Slot::writeEndNet(bool writeEnd) // release trunked grant (if necessary) Slot* tscc = m_dmr->getTSCCSlot(); if (tscc != nullptr) { - if (tscc->m_enableTSCC && m_rfLC != nullptr) { - tscc->m_affiliations->releaseGrant(m_rfLC->getDstId(), false); + if (tscc->m_enableTSCC && m_netLC != nullptr) { + tscc->m_affiliations->releaseGrant(m_netLC->getDstId(), false); } clearTSCCActivated(); if (!tscc->m_enableTSCC) { - notifyCC_ReleaseGrant(m_rfLC->getDstId()); + notifyCC_ReleaseGrant(m_netLC->getDstId()); } } From cbce2fbdf043b21ee1624359985cfea16f03bf6f Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 28 Feb 2025 10:28:48 -0500 Subject: [PATCH 10/34] update copyright dates; --- src/common/dmr/data/NetData.cpp | 2 +- src/common/dmr/data/NetData.h | 2 +- src/host/dmr/Control.cpp | 2 +- src/host/dmr/Control.h | 2 +- src/host/dmr/Slot.cpp | 2 +- src/host/dmr/Slot.h | 2 +- src/host/dmr/packet/Data.cpp | 2 +- src/host/dmr/packet/Voice.cpp | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/common/dmr/data/NetData.cpp b/src/common/dmr/data/NetData.cpp index fe2d33d9..3514c53b 100644 --- a/src/common/dmr/data/NetData.cpp +++ b/src/common/dmr/data/NetData.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016 Jonathan Naylor, G4KLX - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" diff --git a/src/common/dmr/data/NetData.h b/src/common/dmr/data/NetData.h index 8c753e7d..ecb7bde7 100644 --- a/src/common/dmr/data/NetData.h +++ b/src/common/dmr/data/NetData.h @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016 Jonathan Naylor, G4KLX - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ /** diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index 1e16f7c5..fa4ab77d 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" diff --git a/src/host/dmr/Control.h b/src/host/dmr/Control.h index 3d1e56d1..a42e21d4 100644 --- a/src/host/dmr/Control.h +++ b/src/host/dmr/Control.h @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX - * Copyright (C) 2017,2020-2023 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017,2020-2025 Bryan Biedenkapp, N2PLL * */ /** diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index ab9d31d4..a6727202 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 6f6ff11b..9b36921a 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ /** diff --git a/src/host/dmr/packet/Data.cpp b/src/host/dmr/packet/Data.cpp index 72bf7118..0a79aabc 100644 --- a/src/host/dmr/packet/Data.cpp +++ b/src/host/dmr/packet/Data.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" diff --git a/src/host/dmr/packet/Voice.cpp b/src/host/dmr/packet/Voice.cpp index ba25180a..dbc18767 100644 --- a/src/host/dmr/packet/Voice.cpp +++ b/src/host/dmr/packet/Voice.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" From 1d7f3fdd6ed2ab3a2cd72a433bb1c8af8a459d8f Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 28 Feb 2025 10:35:16 -0500 Subject: [PATCH 11/34] document control byte values for DMR and P25; --- docs/TN.1000 - FNE Network.adoc | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/docs/TN.1000 - FNE Network.adoc b/docs/TN.1000 - FNE Network.adoc index a7d452b2..0bec863c 100644 --- a/docs/TN.1000 - FNE Network.adoc +++ b/docs/TN.1000 - FNE Network.adoc @@ -657,6 +657,21 @@ The payload for a DMR protocol data message is formatted: | BER | RSSI | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +[discrete] +==== Control Flags +[cols="2,1,2"] +|=== +|Name |Flag |Description + +|Grant Demand +|$80 (0x80) +|This control flag indicates the packet contains a remote grant demand request. + +|Unit to Unit +|$01 (0x01) +|This control flag indicates any included control request is a unit-to-unit request. +|=== + === 3.3 P25 Protocol Data For both reception and transmission of P25 protocol data on the network, simple packets are formed using the Protocol function and P25 sub-function. @@ -703,6 +718,29 @@ All messages (with the exception of PDUs) carry this message header: | | +-+-+-+-+-+-+-+-+ +[discrete] +==== Control Flags +[cols="2,1,2"] +|=== +|Name |Flag |Description + +|Grant Demand +|$80 (0x80) +|This control flag indicates the packet contains a remote grant demand request. + +|Grant Denial +|$40 (0x40) +|This control flag indicates any included grant demand request is to be denied. + +|Encrypted +|$08 (0x08) +|This control flag indicates any included control request is for encrypted traffic to follow. + +|Unit to Unit +|$01 (0x01) +|This control flag indicates any included control request is a unit-to-unit request. +|=== + ==== 3.3.2 PDU Message Header [listing] Below is the representation of the data layout for the P25 frame From 3e0b7e3cfe9541fc01a1df52a1627cd526ad1d8f Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 28 Feb 2025 10:39:36 -0500 Subject: [PATCH 12/34] update documentation some more to be clear and to reference pertinant sections; --- docs/TN.1000 - FNE Network.adoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/TN.1000 - FNE Network.adoc b/docs/TN.1000 - FNE Network.adoc index 0bec863c..370bf0f0 100644 --- a/docs/TN.1000 - FNE Network.adoc +++ b/docs/TN.1000 - FNE Network.adoc @@ -1222,4 +1222,8 @@ Once connected, a Peer-Linked CFNE will transparently pass peer list, peer statu On DVM, for P25 a special network TDU exists called a "grant demand". The "grant demand" TDUs set a flag in the control bit of the P25 network header (bit $80) to flag that the TDU is meant to trigger a channel grant on a end-point. +For DMR, "grant demands", are carryed along side the VOICE_LC_HEADER or DATA_HEADER packets with the control bytes set. Like, P25, the "grant demand" set a flag in the control bit of the DMR network header (bit $80) to flag that the packet is meant to trigger a channel grant on a end-point. + Because these grants are considered "network sourced" end-points should issue a grant locally and not repeat that grant to the network. + +(See Section 3.3.1 for P25, and Section 3.2 for DMR) From 63e2a6a57930df7379a61fd15a7e35be2131a951 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 28 Feb 2025 10:40:23 -0500 Subject: [PATCH 13/34] horrible no good very bad attempt at English; --- docs/TN.1000 - FNE Network.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/TN.1000 - FNE Network.adoc b/docs/TN.1000 - FNE Network.adoc index 370bf0f0..c6a25b58 100644 --- a/docs/TN.1000 - FNE Network.adoc +++ b/docs/TN.1000 - FNE Network.adoc @@ -1222,7 +1222,7 @@ Once connected, a Peer-Linked CFNE will transparently pass peer list, peer statu On DVM, for P25 a special network TDU exists called a "grant demand". The "grant demand" TDUs set a flag in the control bit of the P25 network header (bit $80) to flag that the TDU is meant to trigger a channel grant on a end-point. -For DMR, "grant demands", are carryed along side the VOICE_LC_HEADER or DATA_HEADER packets with the control bytes set. Like, P25, the "grant demand" set a flag in the control bit of the DMR network header (bit $80) to flag that the packet is meant to trigger a channel grant on a end-point. +For DMR, "grant demands", are carried along side the VOICE_LC_HEADER or DATA_HEADER packets with the control bytes set. Like, P25, the "grant demand" set a flag in the control bit of the DMR network header (bit $80) to flag that the packet is meant to trigger a channel grant on a end-point. Because these grants are considered "network sourced" end-points should issue a grant locally and not repeat that grant to the network. From e38488f602635235017970ba9b7763a08058a9a8 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 1 Mar 2025 12:14:53 -0500 Subject: [PATCH 14/34] fix potential bridge memory leak when using USRP audio; add support for bridge to reset and start a new call when using UDP metadata *and* overriding the source ID; --- configs/bridge-config.example.yml | 3 +++ src/bridge/HostBridge.cpp | 30 +++++++++++++++++++++++++----- src/bridge/HostBridge.h | 1 + 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/configs/bridge-config.example.yml b/configs/bridge-config.example.yml index 75741117..0cb68fc6 100644 --- a/configs/bridge-config.example.yml +++ b/configs/bridge-config.example.yml @@ -87,6 +87,9 @@ network: # Flag indicating the source "Radio ID" will be overridden from the received # UDP SRC ID. overrideSourceIdFromUDP: false + # Flag indicating if the source "Radio ID" is being overriden, and changes mid-call, bridge should + # terminate previous call an initiate a new one. (This applies only when both udpMetadata and overrideSourceIdFromUDP is set.) + resetCallForSourceIdChange: false # Talkgroup ID for transmitted/received audio frames. destinationId: 1 # Slot for received/transmitted audio frames. diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index d2bf6786..fe6859cd 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -299,6 +299,7 @@ HostBridge::HostBridge(const std::string& confFile) : m_srcIdOverride(0U), m_overrideSrcIdFromMDC(false), m_overrideSrcIdFromUDP(false), + m_resetCallForSourceIdChange(false), m_dstId(1U), m_slot(1U), m_identity(), @@ -1045,9 +1046,15 @@ bool HostBridge::createNetwork() m_srcId = (uint32_t)networkConf["sourceId"].as(p25::defines::WUID_FNE); m_overrideSrcIdFromMDC = networkConf["overrideSourceIdFromMDC"].as(false); m_overrideSrcIdFromUDP = networkConf["overrideSourceIdFromUDP"].as(false); + m_resetCallForSourceIdChange = networkConf["resetCallForSourceIdChange"].as(false); m_dstId = (uint32_t)networkConf["destinationId"].as(1U); m_slot = (uint8_t)networkConf["slot"].as(1U); + if (!m_udpMetadata && m_resetCallForSourceIdChange) + m_resetCallForSourceIdChange = false; // only applies to UDP audio with metadata + if (!m_overrideSrcIdFromUDP && m_resetCallForSourceIdChange) + m_resetCallForSourceIdChange = false; // only applies to UDP audio when overriding source ID + bool encrypted = networkConf["encrypted"].as(false); std::string key = networkConf["presharedKey"].as(); uint8_t presharedKey[AES_WRAPPED_PCKT_KEY_LEN]; @@ -1116,6 +1123,9 @@ bool HostBridge::createNetwork() LogInfo(" DMR Slot: %u", m_slot); LogInfo(" Override Source ID from MDC: %s", m_overrideSrcIdFromMDC ? "yes" : "no"); LogInfo(" Override Source ID from UDP Audio: %s", m_overrideSrcIdFromUDP ? "yes" : "no"); + if (m_resetCallForSourceIdChange) { + LogInfo(" Reset Call if Source ID Changes from UDP Audio: %s", m_resetCallForSourceIdChange ? "yes" : "no"); + } if (debug) { LogInfo(" Debug: yes"); @@ -1224,17 +1234,27 @@ void HostBridge::processUDPAudio() if (usrpHeader[15U] == 1U && length > USRP_HEADER_LENGTH) // PTT state true and ensure we did not just receive a USRP header ::memcpy(pcm, buffer + USRP_HEADER_LENGTH, pcmLength); + + delete[] usrpHeader; } // Utils::dump(1U, "PCM RECV BYTE BUFFER", pcm, pcmLength); - m_udpSrcId = m_srcId; + m_udpDstId = m_dstId; + if (m_udpMetadata) { - if (m_overrideSrcIdFromUDP) - m_udpSrcId = __GET_UINT32(buffer, pcmLength + 8U); - } + if (m_overrideSrcIdFromUDP) { + uint32_t udpSrcId = __GET_UINT32(buffer, pcmLength + 8U); - m_udpDstId = m_dstId; + // if the UDP source ID now doesn't match the current call ID, reset call states + if (m_resetCallForSourceIdChange && (udpSrcId != m_udpSrcId)) + callEnd(m_udpSrcId, m_dstId); + + m_udpSrcId = udpSrcId; + } + } else { + m_udpSrcId = m_srcId; + } std::lock_guard lock(m_audioMutex); diff --git a/src/bridge/HostBridge.h b/src/bridge/HostBridge.h index 4fb06ce6..52422e88 100644 --- a/src/bridge/HostBridge.h +++ b/src/bridge/HostBridge.h @@ -170,6 +170,7 @@ private: uint32_t m_srcIdOverride; bool m_overrideSrcIdFromMDC; bool m_overrideSrcIdFromUDP; + bool m_resetCallForSourceIdChange; uint32_t m_dstId; uint8_t m_slot; From 086730320de07d36ea6884f65e3f2989a910ea2a Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 2 Mar 2025 10:58:41 -0500 Subject: [PATCH 15/34] code cleanup; --- src/bridge/HostBridge.cpp | 20 ++++---------------- src/common/lookups/LookupTable.h | 2 +- src/common/lookups/TalkgroupRulesLookup.h | 2 +- src/common/network/udp/Socket.cpp | 1 + src/common/p25/SiteData.h | 4 ++-- src/fne/HostFNE.cpp | 6 +++--- src/host/Host.cpp | 4 ++-- src/host/p25/Control.cpp | 4 ++-- 8 files changed, 16 insertions(+), 27 deletions(-) diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index fe6859cd..3137880c 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -658,8 +658,7 @@ int HostBridge::run() // -- Network Clocking -- // ------------------------------------------------------ - if (m_network != nullptr) - { + if (m_network != nullptr) { std::lock_guard lock(HostBridge::m_networkMutex); m_network->clock(ms); } @@ -2434,11 +2433,12 @@ void HostBridge::encodeP25AudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_ /* Helper to send USRP end of transmission */ -void HostBridge::sendUsrpEot() { +void HostBridge::sendUsrpEot() +{ sockaddr_storage addr; uint32_t addrLen; - uint8_t* usrpHeader = new uint8_t[USRP_HEADER_LENGTH]; + uint8_t usrpHeader[USRP_HEADER_LENGTH]; m_usrpSeqNo = 0U; ::memcpy(usrpHeader, "USRP", 4); @@ -2621,18 +2621,12 @@ void* HostBridge::threadAudioProcess(void* arg) ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE - StopWatch stopWatch; - stopWatch.start(); - while (!g_killed) { if (!bridge->m_running) { Thread::sleep(1U); continue; } - uint32_t ms = stopWatch.elapsed(); - stopWatch.start(); - // scope is intentional { std::lock_guard lock(m_audioMutex); @@ -2779,18 +2773,12 @@ void* HostBridge::threadNetworkProcess(void* arg) ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE - StopWatch stopWatch; - stopWatch.start(); - while (!g_killed) { if (!bridge->m_running) { Thread::sleep(1U); continue; } - uint32_t ms = stopWatch.elapsed(); - stopWatch.start(); - uint32_t length = 0U; bool netReadRet = false; if (bridge->m_txMode == TX_MODE_DMR) { diff --git a/src/common/lookups/LookupTable.h b/src/common/lookups/LookupTable.h index 6eabc41c..5fadd79c 100644 --- a/src/common/lookups/LookupTable.h +++ b/src/common/lookups/LookupTable.h @@ -182,7 +182,7 @@ namespace lookups * @brief Helper to set the reload time of this lookup table. * @param reloadTime Lookup time in seconds. */ - void setReloadTime(uint32_t reloadTime) { m_reloadTime = 0U; } + void setReloadTime(uint32_t reloadTime) { m_reloadTime = reloadTime; } protected: std::string m_filename; diff --git a/src/common/lookups/TalkgroupRulesLookup.h b/src/common/lookups/TalkgroupRulesLookup.h index 05bbb883..16aaa91d 100644 --- a/src/common/lookups/TalkgroupRulesLookup.h +++ b/src/common/lookups/TalkgroupRulesLookup.h @@ -603,7 +603,7 @@ namespace lookups * @brief Helper to set the reload time of this lookup table. * @param reloadTime Lookup time in seconds. */ - void setReloadTime(uint32_t reloadTime) { m_reloadTime = 0U; } + void setReloadTime(uint32_t reloadTime) { m_reloadTime = reloadTime; } private: std::string m_rulesFile; diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index 952046e0..d0971a2d 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -333,6 +333,7 @@ bool Socket::write(const uint8_t* buffer, uint32_t length, const sockaddr_storag cryptedLen += alignment; // reallocate buffer and copy + delete[] cryptoBuffer; cryptoBuffer = new uint8_t[cryptedLen]; ::memset(cryptoBuffer, 0x00U, cryptedLen); ::memcpy(cryptoBuffer, buffer, length); diff --git a/src/common/p25/SiteData.h b/src/common/p25/SiteData.h index 904b5ba5..899cb861 100644 --- a/src/common/p25/SiteData.h +++ b/src/common/p25/SiteData.h @@ -122,13 +122,13 @@ namespace p25 m_netId = dist(mt); // netId clamping - netId = P25Utils::netId(netId); + m_netId = P25Utils::netId(netId); dist = std::uniform_int_distribution(0x01, 0xFFEU); m_sysId = dist(mt); // sysId clamping - sysId = P25Utils::sysId(sysId); + m_sysId = P25Utils::sysId(sysId); } m_rfssId = rfssId; diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index d1292106..65fc2b9b 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -281,18 +281,18 @@ int HostFNE::run() } if (m_tidLookup != nullptr) { + m_tidLookup->setReloadTime(0U); // no reload m_tidLookup->stop(); - delete m_tidLookup; } if (m_ridLookup != nullptr) { + m_ridLookup->setReloadTime(0U); // no reload m_ridLookup->stop(); - delete m_ridLookup; } if (m_peerListLookup != nullptr) { + m_peerListLookup->setReloadTime(0U); // no reload m_peerListLookup->stop(); - delete m_peerListLookup; } #if !defined(_WIN32) if (m_tun != nullptr) { diff --git a/src/host/Host.cpp b/src/host/Host.cpp index 6c8edf6f..639118bf 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -1666,12 +1666,12 @@ void Host::setState(uint8_t state) } if (m_tidLookup != nullptr) { + m_tidLookup->setReloadTime(0U); m_tidLookup->stop(); - //delete m_tidLookup; } if (m_ridLookup != nullptr) { + m_tidLookup->setReloadTime(0U); m_ridLookup->stop(); - //delete m_ridLookup; } } else { diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index f3d54285..f4f6a5c1 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -1317,7 +1317,7 @@ void Control::processNetwork() } if (!m_dedicatedControl) - ret = m_data->processNetwork(data.get(), frameLength, blockLength); + m_data->processNetwork(data.get(), frameLength, blockLength); return; } @@ -1414,7 +1414,7 @@ void Control::processNetwork() break; } - ret = m_voice->processNetwork(data.get(), frameLength, control, lsd, duid, frameType); + m_voice->processNetwork(data.get(), frameLength, control, lsd, duid, frameType); break; case DUID::TDU: From c68b99dc61b7b36cac312ede1b7327293b22c813 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 3 Mar 2025 10:16:29 -0500 Subject: [PATCH 16/34] silence frame queue read errors after 5 consecutive errors (this is to prevent log spam); --- src/common/network/FrameQueue.cpp | 10 +++++++++- src/common/network/RawFrameQueue.cpp | 11 ++++++++++- src/common/network/RawFrameQueue.h | 3 +++ src/host/network/Network.cpp | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/common/network/FrameQueue.cpp b/src/common/network/FrameQueue.cpp index 3c855629..170f7434 100644 --- a/src/common/network/FrameQueue.cpp +++ b/src/common/network/FrameQueue.cpp @@ -57,7 +57,13 @@ UInt8Array FrameQueue::read(int& messageLength, sockaddr_storage& address, uint3 ::memset(buffer, 0x00U, DATA_PACKET_LENGTH); int length = m_socket->read(buffer, DATA_PACKET_LENGTH, address, addrLen); if (length < 0) { - LogError(LOG_NET, "Failed reading data from the network"); + if (m_failedReadCnt <= MAX_FAILED_READ_CNT_LOGGING) + LogError(LOG_NET, "Failed reading data from the network, failedCnt = %u", m_failedReadCnt); + else { + if (m_failedReadCnt == MAX_FAILED_READ_CNT_LOGGING + 1U) + LogError(LOG_NET, "Failed reading data from the network -- exceeded 5 read errors, probable connection issue, silencing further errors"); + } + m_failedReadCnt++; return nullptr; } @@ -65,6 +71,8 @@ UInt8Array FrameQueue::read(int& messageLength, sockaddr_storage& address, uint3 if (m_debug) Utils::dump(1U, "Network Packet", buffer, length); + m_failedReadCnt = 0U; + if (length < RTP_HEADER_LENGTH_BYTES + RTP_EXTENSION_HEADER_LENGTH_BYTES) { LogError(LOG_NET, "FrameQueue::read(), message received from network is malformed! %u bytes != %u bytes", RTP_HEADER_LENGTH_BYTES + RTP_EXTENSION_HEADER_LENGTH_BYTES, length); diff --git a/src/common/network/RawFrameQueue.cpp b/src/common/network/RawFrameQueue.cpp index e6e7ea0f..1f85d1d8 100644 --- a/src/common/network/RawFrameQueue.cpp +++ b/src/common/network/RawFrameQueue.cpp @@ -33,6 +33,7 @@ std::mutex RawFrameQueue::m_flushMutex; RawFrameQueue::RawFrameQueue(udp::Socket* socket, bool debug) : m_socket(socket), m_buffers(), + m_failedReadCnt(0U), m_debug(debug) { /* stub */ @@ -56,7 +57,13 @@ UInt8Array RawFrameQueue::read(int& messageLength, sockaddr_storage& address, ui ::memset(buffer, 0x00U, DATA_PACKET_LENGTH); int length = m_socket->read(buffer, DATA_PACKET_LENGTH, address, addrLen); if (length < 0) { - LogError(LOG_NET, "Failed reading data from the network"); + if (m_failedReadCnt <= MAX_FAILED_READ_CNT_LOGGING) + LogError(LOG_NET, "Failed reading data from the network, failedCnt = %u", m_failedReadCnt); + else { + if (m_failedReadCnt == MAX_FAILED_READ_CNT_LOGGING + 1U) + LogError(LOG_NET, "Failed reading data from the network -- exceeded 5 read errors, probable connection issue, silencing further errors"); + } + m_failedReadCnt++; return nullptr; } @@ -64,6 +71,8 @@ UInt8Array RawFrameQueue::read(int& messageLength, sockaddr_storage& address, ui if (m_debug) Utils::dump(1U, "Network Packet", buffer, length); + m_failedReadCnt = 0U; + // copy message messageLength = length; UInt8Array message = std::unique_ptr(new uint8_t[length]); diff --git a/src/common/network/RawFrameQueue.h b/src/common/network/RawFrameQueue.h index 2393c986..f0d6671f 100644 --- a/src/common/network/RawFrameQueue.h +++ b/src/common/network/RawFrameQueue.h @@ -29,6 +29,7 @@ namespace network // --------------------------------------------------------------------------- const uint32_t DATA_PACKET_LENGTH = 8192U; + const uint8_t MAX_FAILED_READ_CNT_LOGGING = 5U; // --------------------------------------------------------------------------- // Class Declaration @@ -96,6 +97,8 @@ namespace network static std::mutex m_flushMutex; udp::BufferVector m_buffers; + uint32_t m_failedReadCnt; + bool m_debug; private: diff --git a/src/host/network/Network.cpp b/src/host/network/Network.cpp index 0a5699bb..2f84900b 100644 --- a/src/host/network/Network.cpp +++ b/src/host/network/Network.cpp @@ -669,7 +669,7 @@ bool Network::open() LogMessage(LOG_NET, "PEER %u opening network", m_peerId); if (udp::Socket::lookup(m_address, m_port, m_addr, m_addrLen) != 0) { - LogMessage(LOG_NET, "Could not lookup the address of the master"); + LogMessage(LOG_NET, "!!! Could not lookup the address of the master!"); return false; } From 64bcb562287845e2ae6a811da3d304158ef3cac9 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 3 Mar 2025 12:06:27 -0500 Subject: [PATCH 17/34] enhance debug logging to incorporate both file and line numbers (when provided by the compiler) and optionally implement LogDebugEx macro to also incorporate the caller identifying the function name; correct LogDebug uses in some cases that should have been LogMessage; --- src/bridge/HostBridge.cpp | 22 ++--- src/common/AESCrypto.cpp | 12 +-- src/common/Log.cpp | 73 +++++++++++++-- src/common/Log.h | 32 ++++--- src/common/RingBuffer.h | 4 +- src/common/Utils.cpp | 12 +-- src/common/edac/AMBEFEC.cpp | 4 +- src/common/edac/CRC.cpp | 30 +++---- src/common/edac/RS634717.cpp | 6 +- src/common/edac/Trellis.cpp | 8 +- src/common/edac/rs/RS.h | 26 +++--- src/common/lookups/PeerListLookup.cpp | 6 +- src/common/lookups/RadioIdLookup.cpp | 9 +- src/common/lookups/TalkgroupRulesLookup.cpp | 8 +- src/common/network/FrameQueue.cpp | 6 +- src/common/network/rest/RequestDispatcher.h | 4 +- src/common/network/rest/http/HTTPHeaders.h | 4 +- src/common/network/rest/http/HTTPLexer.cpp | 2 +- src/common/network/rest/http/HTTPPayload.cpp | 8 +- src/common/nxdn/channel/LICH.cpp | 4 +- src/common/p25/dfsi/LC.cpp | 8 +- src/common/p25/lc/AMBT.cpp | 4 +- src/fne/HostFNE.cpp | 24 ++--- src/fne/network/RESTAPI.cpp | 6 +- src/host/Host.DMR.cpp | 24 ++--- src/host/Host.NXDN.cpp | 12 +-- src/host/Host.P25.cpp | 12 +-- src/host/Host.cpp | 24 ++--- src/host/dmr/Slot.cpp | 12 +-- src/host/dmr/lookups/DMRAffiliationLookup.cpp | 2 +- src/host/modem/Modem.cpp | 90 +++++++++---------- src/host/modem/ModemV24.cpp | 10 +-- .../modem/port/specialized/V24UDPPort.cpp | 8 +- src/host/network/Network.cpp | 12 +-- src/host/network/RESTAPI.cpp | 12 +-- src/host/nxdn/Control.cpp | 2 +- src/host/nxdn/packet/ControlSignaling.cpp | 2 +- src/host/p25/Control.cpp | 6 +- src/host/p25/packet/ControlSignaling.cpp | 6 +- src/sysview/HostWS.cpp | 4 +- src/sysview/NodeStatusWnd.h | 4 +- src/sysview/SysViewMain.cpp | 4 +- 42 files changed, 316 insertions(+), 252 deletions(-) diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 3137880c..39bceda2 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -2488,7 +2488,7 @@ uint8_t* HostBridge::generateRTPHeaders(uint8_t msgLen, uint16_t& rtpSeq) if (timestamp != INVALID_TS) { timestamp += (RTP_GENERIC_CLOCK_RATE / 50); if (m_debug) - LogDebug(LOG_NET, "HostBridge::generateRTPHeaders() RTP, previous TS = %u, TS = %u, rtpSeq = %u", m_rtpTimestamp, timestamp, rtpSeq); + LogDebugEx(LOG_NET, "HostBridge::generateRTPHeaders()", "RTP, previous TS = %u, TS = %u, rtpSeq = %u", m_rtpTimestamp, timestamp, rtpSeq); m_rtpTimestamp = timestamp; } @@ -2507,7 +2507,7 @@ uint8_t* HostBridge::generateRTPHeaders(uint8_t msgLen, uint16_t& rtpSeq) if (timestamp == INVALID_TS) { if (m_debug) - LogDebug(LOG_NET, "HostBridge::generateRTPHeaders() RTP, initial TS = %u, rtpSeq = %u", header.getTimestamp(), rtpSeq); + LogDebugEx(LOG_NET, "HostBridge::generateRTPHeaders()", "RTP, initial TS = %u, rtpSeq = %u", header.getTimestamp(), rtpSeq); m_rtpTimestamp = header.getTimestamp(); } @@ -2608,7 +2608,7 @@ void* HostBridge::threadAudioProcess(void* arg) HostBridge* bridge = static_cast(th->obj); if (bridge == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -2616,7 +2616,7 @@ void* HostBridge::threadAudioProcess(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -2737,7 +2737,7 @@ void* HostBridge::threadAudioProcess(void* arg) Thread::sleep(1U); } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -2760,7 +2760,7 @@ void* HostBridge::threadNetworkProcess(void* arg) HostBridge* bridge = static_cast(th->obj); if (bridge == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -2768,7 +2768,7 @@ void* HostBridge::threadNetworkProcess(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -2800,7 +2800,7 @@ void* HostBridge::threadNetworkProcess(void* arg) Thread::sleep(1U); } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -2823,7 +2823,7 @@ void* HostBridge::threadCallWatchdog(void* arg) HostBridge* bridge = static_cast(th->obj); if (bridge == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -2831,7 +2831,7 @@ void* HostBridge::threadCallWatchdog(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -2885,7 +2885,7 @@ void* HostBridge::threadCallWatchdog(void* arg) Thread::sleep(5U); } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/common/AESCrypto.cpp b/src/common/AESCrypto.cpp index 1638304c..fc582076 100644 --- a/src/common/AESCrypto.cpp +++ b/src/common/AESCrypto.cpp @@ -266,7 +266,7 @@ AES::AES(const AESKeyLength keyLength) { uint8_t* AES::encryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[]) { if (inLen % BLOCK_BYTES_LEN != 0) { - LogDebug(LOG_HOST, "AES::encryptECB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); + LogDebugEx(LOG_HOST, "AES::encryptECB()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); return nullptr; } @@ -289,7 +289,7 @@ uint8_t* AES::encryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[] uint8_t* AES::decryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[]) { if (inLen % BLOCK_BYTES_LEN != 0) { - LogDebug(LOG_HOST, "AES::decryptECB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); + LogDebugEx(LOG_HOST, "AES::decryptECB()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); return nullptr; } @@ -312,7 +312,7 @@ uint8_t* AES::decryptECB(const uint8_t in[], uint32_t inLen, const uint8_t key[] uint8_t* AES::encryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t* iv) { if (inLen % BLOCK_BYTES_LEN != 0) { - LogDebug(LOG_HOST, "AES::encryptCBC() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); + LogDebugEx(LOG_HOST, "AES::encryptCBC()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); return nullptr; } @@ -339,7 +339,7 @@ uint8_t* AES::encryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[] uint8_t* AES::decryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t *iv) { if (inLen % BLOCK_BYTES_LEN != 0) { - LogDebug(LOG_HOST, "AES::decryptCBC() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); + LogDebugEx(LOG_HOST, "AES::decryptCBC()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); return nullptr; } @@ -366,7 +366,7 @@ uint8_t* AES::decryptCBC(const uint8_t in[], uint32_t inLen, const uint8_t key[] uint8_t* AES::encryptCFB(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t *iv) { if (inLen % BLOCK_BYTES_LEN != 0) { - LogDebug(LOG_HOST, "AES::encryptCFB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); + LogDebugEx(LOG_HOST, "AES::encryptCFB()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); return nullptr; } @@ -394,7 +394,7 @@ uint8_t* AES::encryptCFB(const uint8_t in[], uint32_t inLen, const uint8_t key[] uint8_t* AES::decryptCFB(const uint8_t in[], uint32_t inLen, const uint8_t key[], const uint8_t *iv) { if (inLen % BLOCK_BYTES_LEN != 0) { - LogDebug(LOG_HOST, "AES::decryptCFB() Plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); + LogDebugEx(LOG_HOST, "AES::decryptCFB()", "plaintext length must be divisible by %u, inLen = %u", BLOCK_BYTES_LEN, inLen); return nullptr; } diff --git a/src/common/Log.cpp b/src/common/Log.cpp index 7399a8b8..01553284 100644 --- a/src/common/Log.cpp +++ b/src/common/Log.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016 Jonathan Naylor, G4KLX - * Copyright (C) 2018-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2018-2025 Bryan Biedenkapp, N2PLL * */ #include "Log.h" @@ -201,7 +201,7 @@ void LogFinalise() /* Writes a new entry to the diagnostics log. */ -void Log(uint32_t level, const char *module, const char* fmt, ...) +void Log(uint32_t level, const char *module, const char* file, const int lineNo, const char* func, const char* fmt, ...) { assert(fmt != nullptr); #if defined(CATCH2_TEST_COMPILATION) @@ -217,22 +217,83 @@ void Log(uint32_t level, const char *module, const char* fmt, ...) ::gettimeofday(&nowMillis, NULL); if (module != nullptr) { - ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s) ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module); + // level 1 is DEBUG + if (level == 1U) { + // if we have a file and line number -- add that to the log entry + if (file != nullptr && lineNo > 0) { + // if we have a function name add that to the log entry + if (func != nullptr) { + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s)[%s:%u][%s] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module, file, lineNo, func); + } + else { + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s)[%s:%u] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module, file, lineNo); + } + } else { + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s) ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module); + } + } else { + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu (%s) ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, module); + } } else { - ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); + // level 1 is DEBUG + if (level == 1U) { + // if we have a file and line number -- add that to the log entry + if (file != nullptr && lineNo > 0) { + // if we have a function name add that to the log entry + if (func != nullptr) { + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu [%s:%u][%s] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, file, lineNo, func); + } + else { + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu [%s:%u] ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U, file, lineNo); + } + } else { + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); + } + } else { + ::sprintf(buffer, "%c: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", LEVELS[level], tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, nowMillis.tv_usec / 1000U); + } } } else { if (module != nullptr) { - ::sprintf(buffer, "%c: (%s) ", LEVELS[level], module); + // level 1 is DEBUG + if (level == 1U) { + // if we have a file and line number -- add that to the log entry + if (file != nullptr && lineNo > 0) { + // if we have a function name add that to the log entry + if (func != nullptr) { + ::sprintf(buffer, "%c: (%s)[%s:%u][%s] ", LEVELS[level], module, file, lineNo, func); + } + else { + ::sprintf(buffer, "%c: (%s)[%s:%u] ", LEVELS[level], module, file, lineNo); + } + } + else { + ::sprintf(buffer, "%c: (%s) ", LEVELS[level], module); + } + } else { + ::sprintf(buffer, "%c: (%s) ", LEVELS[level], module); + } } else { if (level >= 9999U) { ::sprintf(buffer, "U: "); } else { - ::sprintf(buffer, "%c: ", LEVELS[level]); + // if we have a file and line number -- add that to the log entry + if (file != nullptr && lineNo > 0) { + // if we have a function name add that to the log entry + if (func != nullptr) { + ::sprintf(buffer, "%c: [%s:%u][%s] ", LEVELS[level], file, lineNo, func); + } + else { + ::sprintf(buffer, "%c: [%s:%u] ", LEVELS[level], file, lineNo); + } + } + else { + ::sprintf(buffer, "%c: ", LEVELS[level]); + } } } } diff --git a/src/common/Log.h b/src/common/Log.h index 62c7a7bc..2087b808 100644 --- a/src/common/Log.h +++ b/src/common/Log.h @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016 Jonathan Naylor, G4KLX - * Copyright (C) 2018-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2018-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -62,7 +62,16 @@ * * This is a variable argument function. */ -#define LogDebug(_module, fmt, ...) Log(1U, _module, fmt, ##__VA_ARGS__) +#define LogDebug(_module, fmt, ...) Log(1U, _module, __FILE__, __LINE__, nullptr, fmt, ##__VA_ARGS__) +/** + * @brief Macro helper to create a debug log entry. + * @param _module Name of module generating log entry. + * @param _func Name of function generating log entry. + * @param fmt String format. + * + * This is a variable argument function. + */ +#define LogDebugEx(_module, _func, fmt, ...) Log(1U, _module, __FILE__, __LINE__, _func, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a message log entry. * @param _module Name of module generating log entry. @@ -70,7 +79,7 @@ * * This is a variable argument function. */ -#define LogMessage(_module, fmt, ...) Log(2U, _module, fmt, ##__VA_ARGS__) +#define LogMessage(_module, fmt, ...) Log(2U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a informational log entry. * @param _module Name of module generating log entry. @@ -79,7 +88,7 @@ * This is a variable argument function. LogInfo() does not use a module * name when creating a log entry. */ -#define LogInfo(fmt, ...) Log(3U, nullptr, fmt, ##__VA_ARGS__) +#define LogInfo(fmt, ...) Log(3U, nullptr, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a informational log entry with module name. * @param _module Name of module generating log entry. @@ -87,7 +96,7 @@ * * This is a variable argument function. */ -#define LogInfoEx(_module, fmt, ...) Log(3U, _module, fmt, ##__VA_ARGS__) +#define LogInfoEx(_module, fmt, ...) Log(3U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a warning log entry. * @param _module Name of module generating log entry. @@ -95,7 +104,7 @@ * * This is a variable argument function. */ -#define LogWarning(_module, fmt, ...) Log(4U, _module, fmt, ##__VA_ARGS__) +#define LogWarning(_module, fmt, ...) Log(4U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a error log entry. * @param _module Name of module generating log entry. @@ -103,7 +112,7 @@ * * This is a variable argument function. */ -#define LogError(_module, fmt, ...) Log(5U, _module, fmt, ##__VA_ARGS__) +#define LogError(_module, fmt, ...) Log(5U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) /** * @brief Macro helper to create a fatal log entry. * @param _module Name of module generating log entry. @@ -111,7 +120,7 @@ * * This is a variable argument function. */ -#define LogFatal(_module, fmt, ...) Log(6U, _module, fmt, ##__VA_ARGS__) +#define LogFatal(_module, fmt, ...) Log(6U, _module, nullptr, 0, nullptr, fmt, ##__VA_ARGS__) // --------------------------------------------------------------------------- // Externs @@ -186,11 +195,14 @@ extern HOST_SW_API void LogFinalise(); * @brief Writes a new entry to the diagnostics log. * @param level Log level for entry. * @param module Name of module generating log entry. + * @param file Name of source code file generating log entry. + * @param line Line number in source code file generating log entry. + * @param func Name of function generating log entry. * @param fmt String format. * - * This is a variable argument function. + * This is a variable argument function. This shouldn't be called directly, utilize the LogXXXX macros above, instead. */ -extern HOST_SW_API void Log(uint32_t level, const char* module, const char* fmt, ...); +extern HOST_SW_API void Log(uint32_t level, const char* module, const char* file, const int lineNo, const char* func, const char* fmt, ...); /** @} */ #endif // __LOG_H__ diff --git a/src/common/RingBuffer.h b/src/common/RingBuffer.h index c390c7f4..c8076935 100644 --- a/src/common/RingBuffer.h +++ b/src/common/RingBuffer.h @@ -82,7 +82,7 @@ public: m_iPtr = 0U; } #if DEBUG_RINGBUFFER - LogDebug(LOG_HOST, "RingBuffer::addData(%s): iPtr_Before = %u, iPtr_After = %u, oPtr = %u, len = %u, len_Written = %u", m_name, iPtr_BeforeWrite, m_iPtr, m_oPtr, m_length, (m_iPtr - iPtr_BeforeWrite)); + LogDebugEx(LOG_HOST, "RingBuffer::addData()", "(%s): iPtr_Before = %u, iPtr_After = %u, oPtr = %u, len = %u, len_Written = %u", m_name, iPtr_BeforeWrite, m_iPtr, m_oPtr, m_length, (m_iPtr - iPtr_BeforeWrite)); #endif return true; } @@ -109,7 +109,7 @@ public: m_oPtr = 0U; } #if DEBUG_RINGBUFFER - LogDebug(LOG_HOST, "RingBuffer::getData(%s): iPtr = %u, oPtr_Before = %u, oPtr_After = %u, len = %u, len_Read = %u", m_name, m_iPtr, oPtr_BeforeRead, m_oPtr, m_length, (m_oPtr - oPtr_BeforeRead)); + LogDebugEx(LOG_HOST, "RingBuffer::getData()", "(%s): iPtr = %u, oPtr_Before = %u, oPtr_After = %u, len = %u, len_Read = %u", m_name, m_iPtr, oPtr_BeforeRead, m_oPtr, m_length, (m_oPtr - oPtr_BeforeRead)); #endif return true; } diff --git a/src/common/Utils.cpp b/src/common/Utils.cpp index 74cfbe3e..12ac2e8d 100644 --- a/src/common/Utils.cpp +++ b/src/common/Utils.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2009,2014,2015,2016 Jonathan Naylor, G4KLX - * Copyright (C) 2018-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2018-2025 Bryan Biedenkapp, N2PLL * */ #include "Utils.h" @@ -72,7 +72,7 @@ void Utils::dump(int level, const std::string& title, const uint8_t* data, uint3 { assert(data != nullptr); - ::Log(level, "DUMP", "%s (len %u)", title.c_str(), length); + ::Log(level, "DUMP", nullptr, 0, nullptr, "%s (len %u)", title.c_str(), length); uint32_t offset = 0U; @@ -103,7 +103,7 @@ void Utils::dump(int level, const std::string& title, const uint8_t* data, uint3 output += '*'; #endif - ::Log(level, "DUMP", "%04X: %s", offset, output.c_str()); + ::Log(level, "DUMP", nullptr, 0, nullptr, "%04X: %s", offset, output.c_str()); offset += 16U; @@ -143,7 +143,7 @@ void Utils::symbols(const std::string& title, const uint8_t* data, uint32_t leng { assert(data != nullptr); - ::Log(2U, "SYMBOLS", "%s (len %u)", title.c_str(), length); + ::Log(2U, "SYMBOLS", nullptr, 0, nullptr, "%s (len %u)", title.c_str(), length); uint32_t offset = 0U; uint32_t count = 0U; @@ -158,7 +158,7 @@ void Utils::symbols(const std::string& title, const uint8_t* data, uint32_t leng microslotHeader += temp; } - ::Log(2U, "SYMBOLS", "MCR: %s", microslotHeader.c_str()); + ::Log(2U, "SYMBOLS", nullptr, 0, nullptr, "MCR: %s", microslotHeader.c_str()); uint32_t bufLen = length; while (bufLen > 0U) { @@ -188,7 +188,7 @@ void Utils::symbols(const std::string& title, const uint8_t* data, uint32_t leng symOffset += 9; } - ::Log(2U, "SYMBOLS", "%03u: %s", count, output.c_str()); + ::Log(2U, "SYMBOLS", nullptr, 0, nullptr, "%03u: %s", count, output.c_str()); offset += 18U; count += 2U; diff --git a/src/common/edac/AMBEFEC.cpp b/src/common/edac/AMBEFEC.cpp index 4fc3f393..17c20f40 100644 --- a/src/common/edac/AMBEFEC.cpp +++ b/src/common/edac/AMBEFEC.cpp @@ -571,7 +571,7 @@ uint32_t AMBEFEC::regenerate(uint32_t& a, uint32_t& b, uint32_t& c) const if (!valid) { uint32_t errsA = Utils::countBits32(data ^ a); #if DEBUG_AMBEFEC - LogDebug(LOG_HOST, "AMBEFEC::regnerate() invalid A block, errsA = %u, a = %6X, b = %6X, c = %6X", errsA, a, b, c); + LogDebugEx(LOG_HOST, "AMBEFEC::regnerate()", "invalid A block, errsA = %u, a = %6X, b = %6X, c = %6X", errsA, a, b, c); #endif a = 0xF00292U; b = 0x0E0B20U; @@ -596,7 +596,7 @@ uint32_t AMBEFEC::regenerate(uint32_t& a, uint32_t& b, uint32_t& c) const v = b ^ old_b; uint32_t errsB = Utils::countBits32(v); #if DEBUG_AMBEFEC - LogDebug(LOG_HOST, "AMBEFEC::regnerate() errsA = %u, a = %6X, errsB = %u, b = %6X, c = %6X", errsA, a, errsB, b, c); + LogDebugEx(LOG_HOST, "AMBEFEC::regnerate()", "errsA = %u, a = %6X, errsB = %u, b = %6X, c = %6X", errsA, a, errsB, b, c); #endif if (errsA >= 4U || ((errsA + errsB) >= 6U && errsA >= 2U)) { a = 0xF00292U; diff --git a/src/common/edac/CRC.cpp b/src/common/edac/CRC.cpp index a415d986..b4f6a852 100644 --- a/src/common/edac/CRC.cpp +++ b/src/common/edac/CRC.cpp @@ -202,7 +202,7 @@ bool CRC::checkCCITT162(const uint8_t *in, uint32_t length) #if DEBUG_CRC_CHECK uint16_t inCrc = (in[length - 2U] << 8) | (in[length - 1U] << 0); - LogDebug(LOG_HOST, "CRC::checkCCITT162(), crc = $%04X, in = $%04X, len = %u", crc16, inCrc, length); + LogDebugEx(LOG_HOST, "CRC::checkCCITT162()", "crc = $%04X, in = $%04X, len = %u", crc16, inCrc, length); #endif return crc8[0U] == in[length - 1U] && crc8[1U] == in[length - 2U]; @@ -228,7 +228,7 @@ void CRC::addCCITT162(uint8_t* in, uint32_t length) crc16 = ~crc16; #if DEBUG_CRC_ADD - LogDebug(LOG_HOST, "CRC::addCCITT162(), crc = $%04X, len = %u", crc16, length); + LogDebugEx(LOG_HOST, "CRC::addCCITT162()", "crc = $%04X, len = %u", crc16, length); #endif in[length - 1U] = crc8[0U]; @@ -256,7 +256,7 @@ bool CRC::checkCCITT161(const uint8_t *in, uint32_t length) #if DEBUG_CRC_CHECK uint16_t inCrc = (in[length - 2U] << 8) | (in[length - 1U] << 0); - LogDebug(LOG_HOST, "CRC::checkCCITT161(), crc = $%04X, in = $%04X, len = %u", crc16, inCrc, length); + LogDebugEx(LOG_HOST, "CRC::checkCCITT161()", "crc = $%04X, in = $%04X, len = %u", crc16, inCrc, length); #endif return crc8[0U] == in[length - 2U] && crc8[1U] == in[length - 1U]; @@ -282,7 +282,7 @@ void CRC::addCCITT161(uint8_t* in, uint32_t length) crc16 = ~crc16; #if DEBUG_CRC_ADD - LogDebug(LOG_HOST, "CRC::addCCITT161(), crc = $%04X, len = %u", crc16, length); + LogDebugEx(LOG_HOST, "CRC::addCCITT161()", "crc = $%04X, len = %u", crc16, length); #endif in[length - 2U] = crc8[0U]; @@ -314,7 +314,7 @@ bool CRC::checkCRC32(const uint8_t *in, uint32_t length) #if DEBUG_CRC_CHECK uint32_t inCrc = (in[length - 4U] << 24) | (in[length - 3U] << 16) | (in[length - 2U] << 8) | (in[length - 1U] << 0); - LogDebug(LOG_HOST, "CRC::checkCRC32(), crc = $%08X, in = $%08X, len = %u", crc32, inCrc, length); + LogDebugEx(LOG_HOST, "CRC::checkCRC32()", "crc = $%08X, in = $%08X, len = %u", crc32, inCrc, length); #endif return crc8[0U] == in[length - 1U] && crc8[1U] == in[length - 2U] && crc8[2U] == in[length - 3U] && crc8[3U] == in[length - 4U]; @@ -344,7 +344,7 @@ void CRC::addCRC32(uint8_t* in, uint32_t length) crc32 &= 0xFFFFFFFFU; #if DEBUG_CRC_ADD - LogDebug(LOG_HOST, "CRC::addCRC32(), crc = $%08X, len = %u", crc32, length); + LogDebugEx(LOG_HOST, "CRC::addCRC32()", "crc = $%08X, len = %u", crc32, length); #endif in[length - 1U] = crc8[0U]; @@ -365,7 +365,7 @@ uint8_t CRC::crc8(const uint8_t *in, uint32_t length) crc = CRC8_TABLE[crc ^ in[i]]; #if DEBUG_CRC_CHECK - LogDebug(LOG_HOST, "CRC::crc8(), crc = $%02X, len = %u", crc, length); + LogDebugEx(LOG_HOST, "CRC::crc8()", "crc = $%02X, len = %u", crc, length); #endif return crc; @@ -389,7 +389,7 @@ bool CRC::checkCRC6(const uint8_t* in, uint32_t bitLength) #if DEBUG_CRC_CHECK uint32_t inCrc = temp[0U]; - LogDebug(LOG_HOST, "CRC::checkCRC6(), crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength); + LogDebugEx(LOG_HOST, "CRC::checkCRC6()", "crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength); #endif return crc == temp[0U]; @@ -411,7 +411,7 @@ uint8_t CRC::addCRC6(uint8_t* in, uint32_t bitLength) } #if DEBUG_CRC_ADD - LogDebug(LOG_HOST, "CRC::addCRC6(), crc = $%04X, bitlen = %u", crc[0U], bitLength); + LogDebugEx(LOG_HOST, "CRC::addCRC6()", "crc = $%04X, bitlen = %u", crc[0U], bitLength); #endif return crc[0U]; } @@ -438,7 +438,7 @@ bool CRC::checkCRC12(const uint8_t* in, uint32_t bitLength) #if DEBUG_CRC_CHECK uint16_t inCrc = (temp2[0U] << 8) | (temp2[1U] << 0); - LogDebug(LOG_HOST, "CRC:checkCRC12(), crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength); + LogDebugEx(LOG_HOST, "CRC:checkCRC12()", "crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength); #endif return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U]; @@ -463,7 +463,7 @@ uint16_t CRC::addCRC12(uint8_t* in, uint32_t bitLength) } #if DEBUG_CRC_ADD - LogDebug(LOG_HOST, "CRC::addCRC12(), crc = $%04X, bitlen = %u", crc, bitLength); + LogDebugEx(LOG_HOST, "CRC::addCRC12()", "crc = $%04X, bitlen = %u", crc, bitLength); #endif return crc; } @@ -490,7 +490,7 @@ bool CRC::checkCRC15(const uint8_t* in, uint32_t bitLength) #if DEBUG_CRC_CHECK uint16_t inCrc = (temp2[0U] << 8) | (temp2[1U] << 0); - LogDebug(LOG_HOST, "CRC:checkCRC15(), crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength); + LogDebugEx(LOG_HOST, "CRC:checkCRC15()", "crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength); #endif return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U]; @@ -515,7 +515,7 @@ uint16_t CRC::addCRC15(uint8_t* in, uint32_t bitLength) } #if DEBUG_CRC_ADD - LogDebug(LOG_HOST, "CRC::addCRC15(), crc = $%04X, bitlen = %u", crc, bitLength); + LogDebugEx(LOG_HOST, "CRC::addCRC15()", "crc = $%04X, bitlen = %u", crc, bitLength); #endif return crc; } @@ -542,7 +542,7 @@ bool CRC::checkCRC16(const uint8_t* in, uint32_t bitLength) #if DEBUG_CRC_CHECK uint16_t inCrc = (temp2[0U] << 8) | (temp2[1U] << 0); - LogDebug(LOG_HOST, "CRC:checkCRC16(), crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength); + LogDebugEx(LOG_HOST, "CRC:checkCRC16()", "crc = $%04X, in = $%04X, bitlen = %u", crc, inCrc, bitLength); #endif return temp1[0U] == temp2[0U] && temp1[1U] == temp2[1U]; @@ -567,7 +567,7 @@ uint16_t CRC::addCRC16(uint8_t* in, uint32_t bitLength) } #if DEBUG_CRC_ADD - LogDebug(LOG_HOST, "CRC::addCRC16(), crc = $%04X, bitlen = %u", crc, bitLength); + LogDebugEx(LOG_HOST, "CRC::addCRC16()", "crc = $%04X, bitlen = %u", crc, bitLength); #endif return crc; } diff --git a/src/common/edac/RS634717.cpp b/src/common/edac/RS634717.cpp index cc8240a3..4ad9b2cb 100644 --- a/src/common/edac/RS634717.cpp +++ b/src/common/edac/RS634717.cpp @@ -155,7 +155,7 @@ bool RS634717::decode241213(uint8_t* data) int ec = rs241213.decode(codeword); #if DEBUG_RS - LogDebug(LOG_HOST, "RS634717::decode241213(), errors = %d", ec); + LogDebugEx(LOG_HOST, "RS634717::decode241213()", "errors = %d", ec); #endif offset = 0U; for (uint32_t i = 0U; i < 12U; i++, offset += 6) @@ -205,7 +205,7 @@ bool RS634717::decode24169(uint8_t* data) int ec = rs24169.decode(codeword); #if DEBUG_RS - LogDebug(LOG_HOST, "RS634717::decode24169(), errors = %d\n", ec); + LogDebugEx(LOG_HOST, "RS634717::decode24169()", "errors = %d\n", ec); #endif offset = 0U; for (uint32_t i = 0U; i < 16U; i++, offset += 6) @@ -255,7 +255,7 @@ bool RS634717::decode362017(uint8_t* data) int ec = rs634717.decode(codeword); #if DEBUG_RS - LogDebug(LOG_HOST, "RS634717::decode362017(), errors = %d\n", ec); + LogDebugEx(LOG_HOST, "RS634717::decode362017()", "errors = %d\n", ec); #endif offset = 0U; for (uint32_t i = 0U; i < 20U; i++, offset += 6) diff --git a/src/common/edac/Trellis.cpp b/src/common/edac/Trellis.cpp index 2ab37994..c5992c17 100644 --- a/src/common/edac/Trellis.cpp +++ b/src/common/edac/Trellis.cpp @@ -453,7 +453,7 @@ void Trellis::dibitsToBits(const uint8_t* dibits, uint8_t* payload) const bool Trellis::fixCode34(uint8_t* points, uint32_t failPos, uint8_t* payload) const { #if DEBUG_TRELLIS - ::LogDebug(LOG_HOST, "Trellis::fixCode34() failPos = %u, val = %01X", failPos, points[failPos]); + ::LogDebugEx(LOG_HOST, "Trellis::fixCode34()", "failPos = %u, val = %01X", failPos, points[failPos]); #endif for (unsigned j = 0U; j < 20U; j++) { uint32_t bestPos = 0U; @@ -466,7 +466,7 @@ bool Trellis::fixCode34(uint8_t* points, uint32_t failPos, uint8_t* payload) con uint32_t pos = checkCode34(points, tribits); if (pos == 999U) { #if DEBUG_TRELLIS - ::LogDebug(LOG_HOST, "Trellis::fixCode34() fixed, failPos = %u, pos = %u, val = %01X", failPos, bestPos, bestVal); + ::LogDebugEx(LOG_HOST, "Trellis::fixCode34()", "fixed, failPos = %u, pos = %u, val = %01X", failPos, bestPos, bestVal); #endif tribitsToBits(tribits, payload); return true; @@ -519,7 +519,7 @@ uint32_t Trellis::checkCode34(const uint8_t* points, uint8_t* tribits) const bool Trellis::fixCode12(uint8_t* points, uint32_t failPos, uint8_t* payload) const { #if DEBUG_TRELLIS - ::LogDebug(LOG_HOST, "Trellis::fixCode12() failPos = %u, val = %01X", failPos, points[failPos]); + ::LogDebugEx(LOG_HOST, "Trellis::fixCode12()", "failPos = %u, val = %01X", failPos, points[failPos]); #endif for (unsigned j = 0U; j < 20U; j++) { uint32_t bestPos = 0U; @@ -532,7 +532,7 @@ bool Trellis::fixCode12(uint8_t* points, uint32_t failPos, uint8_t* payload) con uint32_t pos = checkCode12(points, dibits); if (pos == 999U) { #if DEBUG_TRELLIS - ::LogDebug(LOG_HOST, "Trellis::fixCode12() fixed, failPos = %u, pos = %u, val = %01X", failPos, bestPos, bestVal); + ::LogDebugEx(LOG_HOST, "Trellis::fixCode12()", "fixed, failPos = %u, pos = %u, val = %01X", failPos, bestPos, bestVal); #endif dibitsToBits(dibits, payload); return true; diff --git a/src/common/edac/rs/RS.h b/src/common/edac/rs/RS.h index 51f83365..54673317 100755 --- a/src/common/edac/rs/RS.h +++ b/src/common/edac/rs/RS.h @@ -338,7 +338,7 @@ namespace edac return; } #if DEBUG_RS - LogDebug(LOG_HOST, "reed_solomon_tabs::reed_solomon_tabs() RS(%d,*): initialized for %d symbols size, %d modulo table", SIZE, NN, MODS); + LogDebugEx(LOG_HOST, "reed_solomon_tabs::reed_solomon_tabs()", "RS(%d,*): initialized for %d symbols size, %d modulo table", SIZE, NN, MODS); #endif // Generate Galois field lookup tables index_of[0] = A0; // log(zero) = -inf @@ -478,7 +478,7 @@ namespace edac return; } #if DEBUG_RS - LogDebug(LOG_HOST, "reed_solomon::reed_solomon() RS(%d,%d): initialized for %d roots", SIZE, LOAD, NROOTS); + LogDebugEx(LOG_HOST, "reed_solomon::reed_solomon()", "RS(%d,%d): initialized for %d roots", SIZE, LOAD, NROOTS); #endif std::array tmppoly; // uninitialized @@ -896,25 +896,25 @@ namespace edac } if (count != no_eras) { - LogDebug(LOG_HOST, "reed_solomon::decode(): count = %d, no_eras = %d, lambda(x) is WRONG", count, no_eras); + LogDebugEx(LOG_HOST, "reed_solomon::decode()", "count = %d, no_eras = %d, lambda(x) is WRONG", count, no_eras); count = -1; goto finish; } if (count) { std::stringstream ss; - ss << "reed_solomon::decode(): Erasure positions as determined by roots of Eras Loc Poly: "; + ss << "Erasure positions as determined by roots of Eras Loc Poly: "; for (int i = 0; i < count; i++) { ss << loc[i] << ' '; } - LogDebug(LOG_HOST, "%s", ss.str().c_str()); + LogDebugEx(LOG_HOST, "reed_solomon::decode()", "%s", ss.str().c_str()); ss.clear(); - ss << "reed_solomon::decode(): Erasure positions as determined by roots of eras_pos array: "; + ss << "Erasure positions as determined by roots of eras_pos array: "; for (int i = 0; i < no_eras; i++) { ss << eras_pos[i] << ' '; } - LogDebug(LOG_HOST, "%s", ss.str().c_str()); + LogDebugEx(LOG_HOST, "reed_solomon::decode()", "%s", ss.str().c_str()); } #endif @@ -1052,7 +1052,7 @@ namespace edac #if DEBUG_RS if (den == 0) { - LogDebug(LOG_HOST, "reed_solomon::decode(): ERROR: denominator = 0"); + LogDebugEx(LOG_HOST, "reed_solomon::decode()", "ERROR: denominator = 0"); count = -1; goto finish; } @@ -1066,9 +1066,9 @@ namespace edac // correction location outside of the data and parity we've been provided! #if DEBUG_RS std::stringstream ss; - ss << "reed_solomon::decode(): ERROR: RS(" << SIZE <<"," << LOAD << ") computed error location: " << loc[j] << + ss << "ERROR: RS(" << SIZE <<"," << LOAD << ") computed error location: " << loc[j] << " within " << pad << " pad symbols, not within " << LOAD - pad << " data or " << NROOTS << " parity"; - LogDebug(LOG_HOST, "%s", ss.str().c_str()); + LogDebugEx(LOG_HOST, "reed_solomon::decode()", "%s", ss.str().c_str()); #endif count = -1; goto finish; @@ -1098,7 +1098,7 @@ namespace edac finish: #if DEBUG_RS if (count > NROOTS) { - LogDebug(LOG_HOST, "reed_solomon::decode(): ERROR: number of corrections %d exceeds NROOTS %d", count, NROOTS); + LogDebugEx(LOG_HOST, "reed_solomon::decode()", "ERROR: number of corrections %d exceeds NROOTS %d", count, NROOTS); } if (count > 0) { @@ -1114,8 +1114,8 @@ finish: } std::stringstream ss; - ss << "reed_solomon::decode(): e)rase, E)rror; count = " << count << ": " << std::endl << errors; - LogDebug(LOG_HOST, "%s", ss.str().c_str()); + ss << "e)rase, E)rror; count = " << count << ": " << std::endl << errors; + LogDebugEx(LOG_HOST, "reed_solomon::decode()", "%s", ss.str().c_str()); } #endif if (eras_pos != NULL) { diff --git a/src/common/lookups/PeerListLookup.cpp b/src/common/lookups/PeerListLookup.cpp index f64d131c..454e07c3 100644 --- a/src/common/lookups/PeerListLookup.cpp +++ b/src/common/lookups/PeerListLookup.cpp @@ -221,7 +221,7 @@ bool PeerListLookup::load() m_table[id] = PeerId(id, alias, password, peerLink, false); // Log depending on what was loaded - LogDebug(LOG_HOST, "Loaded peer ID %u%s into peer ID lookup table, %s%s", id, + LogMessage(LOG_HOST, "Loaded peer ID %u%s into peer ID lookup table, %s%s", id, (!alias.empty() ? (" (" + alias + ")").c_str() : ""), (!password.empty() ? "using unique peer password" : "using master password"), (peerLink) ? ", Peer-Link Enabled" : ""); @@ -242,8 +242,6 @@ bool PeerListLookup::load() bool PeerListLookup::save() { - LogDebug(LOG_HOST, "Saving peer lookup file to %s", m_filename.c_str()); - if (m_filename.empty()) { return false; } @@ -254,6 +252,8 @@ bool PeerListLookup::save() return false; } + LogMessage(LOG_HOST, "Saving peer lookup file to %s", m_filename.c_str()); + // Counter for lines written unsigned int lines = 0; diff --git a/src/common/lookups/RadioIdLookup.cpp b/src/common/lookups/RadioIdLookup.cpp index 86797533..4a02c3c6 100644 --- a/src/common/lookups/RadioIdLookup.cpp +++ b/src/common/lookups/RadioIdLookup.cpp @@ -195,11 +195,6 @@ bool RadioIdLookup::load() } m_table[id] = RadioId(radioEnabled, false, alias, ipAddress); - /*if (alias != "") { - LogDebug(LOG_HOST, "Loaded RID %u (%s) into RID lookup table", id, parsed[2].c_str()); - } else { - LogDebug(LOG_HOST, "Loaded RID %u into RID lookup table", id); - }*/ } } @@ -218,8 +213,6 @@ bool RadioIdLookup::load() bool RadioIdLookup::save() { - LogDebug(LOG_HOST, "Saving RID lookup file to %s", m_filename.c_str()); - if (m_filename.empty()) { return false; } @@ -230,6 +223,8 @@ bool RadioIdLookup::save() return false; } + LogMessage(LOG_HOST, "Saving RID lookup file to %s", m_filename.c_str()); + // Counter for lines written unsigned int lines = 0; diff --git a/src/common/lookups/TalkgroupRulesLookup.cpp b/src/common/lookups/TalkgroupRulesLookup.cpp index 93240fb5..1d477648 100644 --- a/src/common/lookups/TalkgroupRulesLookup.cpp +++ b/src/common/lookups/TalkgroupRulesLookup.cpp @@ -358,10 +358,10 @@ bool TalkgroupRulesLookup::save() for (auto entry : m_groupVoice) { yaml::Node& gv = groupVoiceList.push_back(); entry.getYaml(gv); - //LogDebug(LOG_HOST, "Added TGID %s to yaml TG list", gv["name"].as().c_str()); + //LogDebugEx(LOG_HOST, "TalkgroupRulesLookup::save()", "Added TGID %s to yaml TG list", gv["name"].as().c_str()); } - //LogDebug(LOG_HOST, "Got final GroupVoiceList YAML size of %u", groupVoiceList.size()); + //LogDebugEx(LOG_HOST, "TalkgroupRulesLookup::save()", "Got final GroupVoiceList YAML size of %u", groupVoiceList.size()); // Set the new rules newRules["groupVoice"] = groupVoiceList; @@ -373,7 +373,7 @@ bool TalkgroupRulesLookup::save() } try { - //LogDebug(LOG_HOST, "Saving TGID file to %s", m_rulesFile.c_str()); + LogMessage(LOG_HOST, "Saving talkgroup rules file to %s", m_rulesFile.c_str()); yaml::Serialize(newRules, m_rulesFile.c_str()); LogDebug(LOG_HOST, "Saved TGID config file to %s", m_rulesFile.c_str()); } @@ -383,4 +383,4 @@ bool TalkgroupRulesLookup::save() } return true; -} \ No newline at end of file +} diff --git a/src/common/network/FrameQueue.cpp b/src/common/network/FrameQueue.cpp index 170f7434..2502a154 100644 --- a/src/common/network/FrameQueue.cpp +++ b/src/common/network/FrameQueue.cpp @@ -211,7 +211,7 @@ uint8_t* FrameQueue::generateMessage(const uint8_t* message, uint32_t length, ui if (timestamp != INVALID_TS) { timestamp += (RTP_GENERIC_CLOCK_RATE / 133); if (m_debug) - LogDebug(LOG_NET, "FrameQueue::generateMessage() RTP streamId = %u, previous TS = %u, TS = %u, rtpSeq = %u", streamId, m_streamTimestamps[streamId], timestamp, rtpSeq); + LogDebugEx(LOG_NET, "FrameQueue::generateMessage()", "RTP streamId = %u, previous TS = %u, TS = %u, rtpSeq = %u", streamId, m_streamTimestamps[streamId], timestamp, rtpSeq); m_streamTimestamps[streamId] = timestamp; } } @@ -232,7 +232,7 @@ uint8_t* FrameQueue::generateMessage(const uint8_t* message, uint32_t length, ui if (streamId != 0U && timestamp == INVALID_TS && rtpSeq != RTP_END_OF_CALL_SEQ) { if (m_debug) - LogDebug(LOG_NET, "FrameQueue::generateMessage() RTP streamId = %u, initial TS = %u, rtpSeq = %u", streamId, header.getTimestamp(), rtpSeq); + LogDebugEx(LOG_NET, "FrameQueue::generateMessage()", "RTP streamId = %u, initial TS = %u, rtpSeq = %u", streamId, header.getTimestamp(), rtpSeq); m_streamTimestamps[streamId] = header.getTimestamp(); } @@ -241,7 +241,7 @@ uint8_t* FrameQueue::generateMessage(const uint8_t* message, uint32_t length, ui auto entry = m_streamTimestamps.find(streamId); if (entry != m_streamTimestamps.end()) { if (m_debug) - LogDebug(LOG_NET, "FrameQueue::generateMessage() RTP streamId = %u, rtpSeq = %u", streamId, rtpSeq); + LogDebugEx(LOG_NET, "FrameQueue::generateMessage()", "RTP streamId = %u, rtpSeq = %u", streamId, rtpSeq); m_streamTimestamps.erase(streamId); } } diff --git a/src/common/network/rest/RequestDispatcher.h b/src/common/network/rest/RequestDispatcher.h index b2a87e95..a751100a 100644 --- a/src/common/network/rest/RequestDispatcher.h +++ b/src/common/network/rest/RequestDispatcher.h @@ -322,9 +322,9 @@ namespace network void handleRequest(const Request& request, Reply& reply) { for (auto header : request.headers.headers()) - ::LogDebug(LOG_REST, "DebugRequestDispatcher::handleRequest() header = %s, value = %s", header.name.c_str(), header.value.c_str()); + ::LogDebugEx(LOG_REST, "DebugRequestDispatcher::handleRequest()", "header = %s, value = %s", header.name.c_str(), header.value.c_str()); - ::LogDebug(LOG_REST, "DebugRequestDispatcher::handleRequest() content = %s", request.content.c_str()); + ::LogDebugEx(LOG_REST, "DebugRequestDispatcher::handleRequest()", "content = %s", request.content.c_str()); } }; diff --git a/src/common/network/rest/http/HTTPHeaders.h b/src/common/network/rest/http/HTTPHeaders.h index 74c24b81..94c5fef3 100644 --- a/src/common/network/rest/http/HTTPHeaders.h +++ b/src/common/network/rest/http/HTTPHeaders.h @@ -97,7 +97,7 @@ namespace network */ void add(const std::string& name, const std::string& value) { - //::LogDebug(LOG_REST, "HTTPHeaders::add(), header = %s, value = %s", name.c_str(), value.c_str()); + //::LogDebugEx(LOG_REST, "HTTPHeaders::add()", "header = %s, value = %s", name.c_str(), value.c_str()); for (auto& header : m_headers) { if (::strtolower(header.name) == ::strtolower(name)) { header.value = value; @@ -107,7 +107,7 @@ namespace network m_headers.push_back(Header(name, value)); //for (auto header : m_headers) - // ::LogDebug(LOG_REST, "HTTPHeaders::add() m_headers.header = %s, m_headers.value = %s", header.name.c_str(), header.value.c_str()); + // ::LogDebugEx(LOG_REST, "HTTPHeaders::add()", "m_headers.header = %s, m_headers.value = %s", header.name.c_str(), header.value.c_str()); } /** * @brief Helper to remove a HTTP header. diff --git a/src/common/network/rest/http/HTTPLexer.cpp b/src/common/network/rest/http/HTTPLexer.cpp index fcfe36f7..668d1820 100644 --- a/src/common/network/rest/http/HTTPLexer.cpp +++ b/src/common/network/rest/http/HTTPLexer.cpp @@ -358,7 +358,7 @@ HTTPLexer::ResultType HTTPLexer::consume(HTTPPayload& req, char input) case EXPECTING_NEWLINE_3: if (input == '\n') { for (auto header : m_headers) { - //::LogDebug(LOG_REST, "HTTPLexer::consume(), header = %s, value = %s", header.name.c_str(), header.value.c_str()); + //::LogDebugEx(LOG_REST, "HTTPLexer::consume()", "header = %s, value = %s", header.name.c_str(), header.value.c_str()); req.headers.add(header.name, header.value); } diff --git a/src/common/network/rest/http/HTTPPayload.cpp b/src/common/network/rest/http/HTTPPayload.cpp index 2fce5ed9..ca2fb0f0 100644 --- a/src/common/network/rest/http/HTTPPayload.cpp +++ b/src/common/network/rest/http/HTTPPayload.cpp @@ -279,7 +279,7 @@ std::vector HTTPPayload::toBuffers() // copy URI and erase zero terminator uri.erase(std::find(uri.begin(), uri.end(), '\0'), uri.end()); #if DEBUG_HTTP_PAYLOAD - ::LogDebug(LOG_REST, "HTTPPayload::toBuffers() method = %s, uri = %s", method.c_str(), uri.c_str()); + ::LogDebugEx(LOG_REST, "HTTPPayload::toBuffers()", "method = %s, uri = %s", method.c_str(), uri.c_str()); #endif buffers.push_back(asio::buffer(method)); buffers.push_back(asio::buffer(misc_strings::request_method_separator)); @@ -295,7 +295,7 @@ std::vector HTTPPayload::toBuffers() for (std::size_t i = 0; i < headers.size(); ++i) { HTTPHeaders::Header& h = headers.m_headers[i]; #if DEBUG_HTTP_PAYLOAD - ::LogDebug(LOG_REST, "HTTPPayload::toBuffers() header = %s, value = %s", h.name.c_str(), h.value.c_str()); + ::LogDebugEx(LOG_REST, "HTTPPayload::toBuffers()", "header = %s, value = %s", h.name.c_str(), h.value.c_str()); #endif buffers.push_back(asio::buffer(h.name)); @@ -309,9 +309,9 @@ std::vector HTTPPayload::toBuffers() buffers.push_back(asio::buffer(content)); #if DEBUG_HTTP_PAYLOAD - ::LogDebug(LOG_REST, "HTTPPayload::toBuffers() content = %s", content.c_str()); + ::LogDebugEx(LOG_REST, "HTTPPayload::toBuffers()", "content = %s", content.c_str()); for (auto buffer : buffers) - Utils::dump("buffer[]", (uint8_t*)buffer.data(), buffer.size()); + Utils::dump("[HTTPPayload::toBuffers()] buffer[]", (uint8_t*)buffer.data(), buffer.size()); #endif return buffers; diff --git a/src/common/nxdn/channel/LICH.cpp b/src/common/nxdn/channel/LICH.cpp index fbb308ea..9798ae24 100644 --- a/src/common/nxdn/channel/LICH.cpp +++ b/src/common/nxdn/channel/LICH.cpp @@ -86,7 +86,7 @@ bool LICH::decode(const uint8_t* data) m_lich = lich[0U]; #if DEBUG_NXDN_LICH - LogDebug(LOG_NXDN, "LICH::decode(), m_lich = %02X", m_lich); + LogDebugEx(LOG_NXDN, "LICH::decode()", "m_lich = %02X", m_lich); #endif bool newParity = getParity(); @@ -121,7 +121,7 @@ void LICH::encode(uint8_t* data) m_lich |= (m_outbound ? 0x01U : 0x00U) << 1; #if DEBUG_NXDN_LICH - LogDebug(LOG_NXDN, "LICH::encode(), m_lich = %02X", m_lich); + LogDebugEx(LOG_NXDN, "LICH::encode()", "m_lich = %02X", m_lich); #endif bool parity = getParity(); diff --git a/src/common/p25/dfsi/LC.cpp b/src/common/p25/dfsi/LC.cpp index 4463546d..70d5b15e 100644 --- a/src/common/p25/dfsi/LC.cpp +++ b/src/common/p25/dfsi/LC.cpp @@ -405,8 +405,8 @@ void LC::encodeLDU1(uint8_t* data, const uint8_t* imbe) } #if DEBUG_P25_DFSI - LogDebug(LOG_P25, "LC::encodeLDU1(), frameType = $%02X", m_frameType); - Utils::dump(2U, "LC::encodeLDU1(), DFSI LDU1 Frame", dfsiFrame, frameLength); + LogDebugEx(LOG_P25, "LC::encodeLDU1()", "frameType = $%02X", m_frameType); + Utils::dump(2U, "[LC::encodeLDU1()] DFSI LDU1 Frame", dfsiFrame, frameLength); #endif ::memcpy(data, dfsiFrame, frameLength); @@ -646,8 +646,8 @@ void LC::encodeLDU2(uint8_t* data, const uint8_t* imbe) } #if DEBUG_P25_DFSI - LogDebug(LOG_P25, "LC::encodeLDU2(), frameType = $%02X", m_frameType); - Utils::dump(2U, "LC::encodeLDU2(), DFSI LDU2 Frame", dfsiFrame, frameLength); + LogDebugEx(LOG_P25, "LC::encodeLDU2()", "frameType = $%02X", m_frameType); + Utils::dump(2U, "[LC::encodeLDU2()] DFSI LDU2 Frame", dfsiFrame, frameLength); #endif ::memcpy(data, dfsiFrame, frameLength); diff --git a/src/common/p25/lc/AMBT.cpp b/src/common/p25/lc/AMBT.cpp index c9059404..36fb0f91 100644 --- a/src/common/p25/lc/AMBT.cpp +++ b/src/common/p25/lc/AMBT.cpp @@ -114,8 +114,8 @@ bool AMBT::decode(const data::DataHeader& dataHeader, const data::DataBlock* blo } if (m_verbose) { - LogDebug(LOG_P25, "AMBT::decode(), mfId = $%02X, lco = $%02X, ambt8 = $%02X, ambt9 = $%02X", m_mfId, m_lco, dataHeader.getAMBTField8(), dataHeader.getAMBTField9()); - Utils::dump(2U, "AMBT::decode(), pduUserData", pduUserData, P25_PDU_UNCONFIRMED_LENGTH_BYTES * dataHeader.getBlocksToFollow()); + LogDebugEx(LOG_P25, "AMBT::decode()", "mfId = $%02X, lco = $%02X, ambt8 = $%02X, ambt9 = $%02X", m_mfId, m_lco, dataHeader.getAMBTField8(), dataHeader.getAMBTField9()); + Utils::dump(2U, "[AMBT::decode()] pduUserData", pduUserData, P25_PDU_UNCONFIRMED_LENGTH_BYTES * dataHeader.getBlocksToFollow()); } return true; diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 65fc2b9b..95789346 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -608,7 +608,7 @@ void* HostFNE::threadMasterNetwork(void* arg) HostFNE* fne = static_cast(th->obj); if (fne == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -616,7 +616,7 @@ void* HostFNE::threadMasterNetwork(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -628,7 +628,7 @@ void* HostFNE::threadMasterNetwork(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -651,7 +651,7 @@ void* HostFNE::threadDiagNetwork(void* arg) HostFNE* fne = static_cast(th->obj); if (fne == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -664,7 +664,7 @@ void* HostFNE::threadDiagNetwork(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -676,7 +676,7 @@ void* HostFNE::threadDiagNetwork(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -842,7 +842,7 @@ void* HostFNE::threadVirtualNetworking(void* arg) HostFNE* fne = static_cast(th->obj); if (fne == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -855,7 +855,7 @@ void* HostFNE::threadVirtualNetworking(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -887,7 +887,7 @@ void* HostFNE::threadVirtualNetworking(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -906,7 +906,7 @@ void* HostFNE::threadVirtualNetworkingClock(void* arg) HostFNE* fne = static_cast(th->obj); if (fne == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -919,7 +919,7 @@ void* HostFNE::threadVirtualNetworkingClock(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -947,7 +947,7 @@ void* HostFNE::threadVirtualNetworkingClock(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/fne/network/RESTAPI.cpp b/src/fne/network/RESTAPI.cpp index 3b30f09d..9c13b47a 100644 --- a/src/fne/network/RESTAPI.cpp +++ b/src/fne/network/RESTAPI.cpp @@ -653,7 +653,7 @@ bool RESTAPI::validateAuth(const HTTPPayload& request, HTTPPayload& reply) std::string host = request.headers.find("RemoteHost"); std::string headerToken = request.headers.find("X-DVM-Auth-Token"); #if DEBUG_HTTP_PAYLOAD - ::LogDebug(LOG_REST, "RESTAPI::validateAuth() token, host = %s, token = %s", host.c_str(), headerToken.c_str()); + ::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "token, host = %s, token = %s", host.c_str(), headerToken.c_str()); #endif if (headerToken.empty()) { errorPayload(reply, "no authentication token", HTTPPayload::UNAUTHORIZED); @@ -662,11 +662,11 @@ bool RESTAPI::validateAuth(const HTTPPayload& request, HTTPPayload& reply) for (auto& token : m_authTokens) { #if DEBUG_HTTP_PAYLOAD - ::LogDebug(LOG_REST, "RESTAPI::validateAuth() valid list, host = %s, token = %s", token.first.c_str(), std::to_string(token.second).c_str()); + ::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "valid list, host = %s, token = %s", token.first.c_str(), std::to_string(token.second).c_str()); #endif if (token.first.compare(host) == 0) { #if DEBUG_HTTP_PAYLOAD - ::LogDebug(LOG_REST, "RESTAPI::validateAuth() storedToken = %s, passedToken = %s", std::to_string(token.second).c_str(), headerToken.c_str()); + ::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "storedToken = %s, passedToken = %s", std::to_string(token.second).c_str(), headerToken.c_str()); #endif if (std::to_string(token.second).compare(headerToken) == 0) { return true; diff --git a/src/host/Host.DMR.cpp b/src/host/Host.DMR.cpp index f2106790..16c83e36 100644 --- a/src/host/Host.DMR.cpp +++ b/src/host/Host.DMR.cpp @@ -49,7 +49,7 @@ void* Host::threadDMRReader1(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -57,7 +57,7 @@ void* Host::threadDMRReader1(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -159,7 +159,7 @@ void* Host::threadDMRReader1(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -182,7 +182,7 @@ void* Host::threadDMRWriter1(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -190,7 +190,7 @@ void* Host::threadDMRWriter1(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -288,7 +288,7 @@ void* Host::threadDMRWriter1(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -311,7 +311,7 @@ void* Host::threadDMRReader2(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -319,7 +319,7 @@ void* Host::threadDMRReader2(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -420,7 +420,7 @@ void* Host::threadDMRReader2(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -443,7 +443,7 @@ void* Host::threadDMRWriter2(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -451,7 +451,7 @@ void* Host::threadDMRWriter2(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -549,7 +549,7 @@ void* Host::threadDMRWriter2(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/host/Host.NXDN.cpp b/src/host/Host.NXDN.cpp index 35d4bcec..b290a5e8 100644 --- a/src/host/Host.NXDN.cpp +++ b/src/host/Host.NXDN.cpp @@ -36,7 +36,7 @@ void* Host::threadNXDNReader(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -44,7 +44,7 @@ void* Host::threadNXDNReader(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -108,7 +108,7 @@ void* Host::threadNXDNReader(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -131,7 +131,7 @@ void* Host::threadNXDNWriter(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -139,7 +139,7 @@ void* Host::threadNXDNWriter(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -224,7 +224,7 @@ void* Host::threadNXDNWriter(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/host/Host.P25.cpp b/src/host/Host.P25.cpp index 5af51fb7..6311fc06 100644 --- a/src/host/Host.P25.cpp +++ b/src/host/Host.P25.cpp @@ -37,7 +37,7 @@ void* Host::threadP25Reader(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -45,7 +45,7 @@ void* Host::threadP25Reader(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -150,7 +150,7 @@ void* Host::threadP25Reader(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -173,7 +173,7 @@ void* Host::threadP25Writer(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -181,7 +181,7 @@ void* Host::threadP25Writer(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -309,7 +309,7 @@ void* Host::threadP25Writer(void* arg) } } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/host/Host.cpp b/src/host/Host.cpp index 639118bf..96750a5e 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -1697,7 +1697,7 @@ void* Host::threadModem(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -1705,7 +1705,7 @@ void* Host::threadModem(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -1734,7 +1734,7 @@ void* Host::threadModem(void* arg) Thread::sleep(m_idleTickDelay); } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -1757,7 +1757,7 @@ void* Host::threadWatchdog(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -1765,7 +1765,7 @@ void* Host::threadWatchdog(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -1949,7 +1949,7 @@ void* Host::threadWatchdog(void* arg) Thread::sleep(m_idleTickDelay); } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -1972,7 +1972,7 @@ void* Host::threadSiteData(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -1980,7 +1980,7 @@ void* Host::threadSiteData(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -2018,7 +2018,7 @@ void* Host::threadSiteData(void* arg) Thread::sleep(m_idleTickDelay); } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } @@ -2041,7 +2041,7 @@ void* Host::threadPresence(void* arg) Host* host = static_cast(th->obj); if (host == nullptr) { g_killed = true; - LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + LogError(LOG_HOST, "[FAIL] %s", threadName.c_str()); } if (g_killed) { @@ -2049,7 +2049,7 @@ void* Host::threadPresence(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -2125,7 +2125,7 @@ void* Host::threadPresence(void* arg) Thread::sleep(m_idleTickDelay); } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index a6727202..d27b83e5 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -1103,7 +1103,7 @@ void Slot::addFrame(const uint8_t *data, bool net, bool imm) fifoSpace = m_modem->getDMRSpace2(); } - //LogDebug(LOG_DMR, "Slot %u, addFrame() fifoSpace = %u", m_slotNo, fifoSpace); + //LogDebugEx(LOG_DMR, "Slot::addFrame()", "Slot %u, fifoSpace = %u", m_slotNo, fifoSpace); // is this immediate data? if (imm) { @@ -1506,7 +1506,7 @@ void Slot::writeRF_ControlData(uint16_t frameCnt, uint8_t n) bool grp = m_affiliations->isGroup(dstId); if (m_debug) { - LogDebug(LOG_DMR, "writeRF_ControlData, frameCnt = %u, seq = %u, late entry, dstId = %u, srcId = %u", frameCnt, n, dstId, srcId); + LogDebugEx(LOG_DMR, "Slot::writeRF_ControlData()", "frameCnt = %u, seq = %u, late entry, dstId = %u, srcId = %u", frameCnt, n, dstId, srcId); } m_control->writeRF_CSBK_Grant_LateEntry(dstId, srcId, grp); @@ -1709,8 +1709,8 @@ void Slot::setShortLC_TSCC(SiteData siteData, uint16_t counter) lc[3U] = (uint8_t)((lcValue >> 0) & 0xFFU); lc[4U] = edac::CRC::crc8(lc, 4U); - //LogDebug(LOG_DMR, "setShortLC_TSCC, netId = %02X, siteId = %02X", siteData.netId(), siteData.siteId()); - //Utils::dump(1U, "shortLC_TSCC", lc, 5U); + //LogDebugEx(LOG_DMR, "Slot::setShortLC_TSCC()", "netId = %02X, siteId = %02X", siteData.netId(), siteData.siteId()); + //Utils::dump(1U, "[Slot::shortLC_TSCC()]", lc, 5U); uint8_t sLC[9U]; @@ -1769,8 +1769,8 @@ void Slot::setShortLC_Payload(SiteData siteData, uint16_t counter) lc[3U] = (uint8_t)((lcValue >> 0) & 0xFFU); lc[4U] = edac::CRC::crc8(lc, 4U); - //LogDebug(LOG_DMR, "setShortLC_Payload, netId = %02X, siteId = %02X", siteData.netId(), siteData.siteId()); - //Utils::dump(1U, "setShortLC_Payload", lc, 5U); + //LogDebugEx(LOG_DMR, "Slot::setShortLC_Payload()", "netId = %02X, siteId = %02X", siteData.netId(), siteData.siteId()); + //Utils::dump(1U, "[Slot::setShortLC_Payload()]", lc, 5U); uint8_t sLC[9U]; diff --git a/src/host/dmr/lookups/DMRAffiliationLookup.cpp b/src/host/dmr/lookups/DMRAffiliationLookup.cpp index 37b1c540..8a486922 100644 --- a/src/host/dmr/lookups/DMRAffiliationLookup.cpp +++ b/src/host/dmr/lookups/DMRAffiliationLookup.cpp @@ -36,7 +36,7 @@ DMRAffiliationLookup::~DMRAffiliationLookup() = default; bool DMRAffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTimeout, bool grp, bool netGranted) { - ::LogDebug(LOG_HOST, "%s, DMRAffiliationLookup::grantCh() use grantChSlot() BUGBUG"); + ::LogDebugEx(LOG_HOST, "%s", "DMRAffiliationLookup::grantCh()", "use grantChSlot() BUGBUG"); return false; } diff --git a/src/host/modem/Modem.cpp b/src/host/modem/Modem.cpp index 53bbbd35..4e217546 100644 --- a/src/host/modem/Modem.cpp +++ b/src/host/modem/Modem.cpp @@ -581,7 +581,7 @@ void Modem::clock(uint32_t ms) m_rxDMRQueue1.addData(m_buffer + 3U, m_length - 3U); if (m_trace) - Utils::dump(1U, "Modem::clock() RX DMR Data 1", m_buffer + 3U, m_length - 3U); + Utils::dump(1U, "[Modem::clock()] RX DMR Data 1", m_buffer + 3U, m_length - 3U); } } break; @@ -607,7 +607,7 @@ void Modem::clock(uint32_t ms) m_rxDMRQueue2.addData(m_buffer + 3U, m_length - 3U); if (m_trace) - Utils::dump(1U, "Modem::clock() RX DMR Data 2", m_buffer + 3U, m_length - 3U); + Utils::dump(1U, "[Modem::clock()] RX DMR Data 2", m_buffer + 3U, m_length - 3U); } } break; @@ -669,7 +669,7 @@ void Modem::clock(uint32_t ms) m_rxP25Queue.addData(m_buffer + (cmdOffset + 1U), m_length - (cmdOffset + 1U)); if (m_trace) - Utils::dump(1U, "Modem::clock() RX P25 Data", m_buffer + (cmdOffset + 1U), m_length - (cmdOffset + 1U)); + Utils::dump(1U, "[Modem::clock()] RX P25 Data", m_buffer + (cmdOffset + 1U), m_length - (cmdOffset + 1U)); } } break; @@ -712,7 +712,7 @@ void Modem::clock(uint32_t ms) m_rxNXDNQueue.addData(m_buffer + 3U, m_length - 3U); if (m_trace) - Utils::dump(1U, "Modem::clock() RX NXDN Data", m_buffer + 3U, m_length - 3U); + Utils::dump(1U, "[Modem::clock()] RX NXDN Data", m_buffer + 3U, m_length - 3U); } } break; @@ -834,9 +834,9 @@ void Modem::clock(uint32_t ms) m_p25Space = m_buffer[10U] * (P25DEF::P25_LDU_FRAME_LENGTH_BYTES); if (m_dumpModemStatus) { - LogDebug(LOG_MODEM, "Modem::clock(), CMD_GET_STATUS, isHotspot = %u, dmr = %u / %u, p25 = %u / %u, nxdn = %u / %u, modemState = %u, tx = %u, adcOverflow = %u, rxOverflow = %u, txOverflow = %u, dacOverflow = %u, dmrSpace1 = %u, dmrSpace2 = %u, p25Space = %u, nxdnSpace = %u", + LogDebugEx(LOG_MODEM, "Modem::clock()", "CMD_GET_STATUS, isHotspot = %u, dmr = %u / %u, p25 = %u / %u, nxdn = %u / %u, modemState = %u, tx = %u, adcOverflow = %u, rxOverflow = %u, txOverflow = %u, dacOverflow = %u, dmrSpace1 = %u, dmrSpace2 = %u, p25Space = %u, nxdnSpace = %u", m_isHotspot, dmrEnable, m_dmrEnabled, p25Enable, m_p25Enabled, nxdnEnable, m_nxdnEnabled, m_modemState, m_tx, adcOverflow, rxOverflow, txOverflow, dacOverflow, m_dmrSpace1, m_dmrSpace2, m_p25Space, m_nxdnSpace); - LogDebug(LOG_MODEM, "Modem::clock(), CMD_GET_STATUS, rxDMRData1 size = %u, len = %u, free = %u; rxDMRData2 size = %u, len = %u, free = %u, rxP25Data size = %u, len = %u, free = %u, rxNXDNData size = %u, len = %u, free = %u", + LogDebugEx(LOG_MODEM, "Modem::clock()", "CMD_GET_STATUS, rxDMRData1 size = %u, len = %u, free = %u; rxDMRData2 size = %u, len = %u, free = %u, rxP25Data size = %u, len = %u, free = %u, rxNXDNData size = %u, len = %u, free = %u", m_rxDMRQueue1.length(), m_rxDMRQueue1.dataSize(), m_rxDMRQueue1.freeSpace(), m_rxDMRQueue2.length(), m_rxDMRQueue2.dataSize(), m_rxDMRQueue2.freeSpace(), m_rxP25Queue.length(), m_rxP25Queue.dataSize(), m_rxP25Queue.freeSpace(), m_rxNXDNQueue.length(), m_rxNXDNQueue.dataSize(), m_rxNXDNQueue.freeSpace()); } @@ -907,7 +907,7 @@ void Modem::clock(uint32_t ms) void Modem::close() { - LogDebug(LOG_MODEM, "Closing the modem"); + LogMessage(LOG_MODEM, "Closing the modem"); m_port->close(); m_gotModemStatus = false; @@ -928,7 +928,7 @@ uint32_t Modem::peekDMRFrame1Length() uint8_t len = 0U; m_rxDMRQueue1.peek(&len, 1U); #if DEBUG_MODEM - LogDebug(LOG_MODEM, "Modem::peekDMRFrame1Length() len = %u, dataSize = %u", len, m_rxDMRQueue1.dataSize()); + LogDebugEx(LOG_MODEM, "Modem::peekDMRFrame1Length()", "len = %u, dataSize = %u", len, m_rxDMRQueue1.dataSize()); #endif // this ensures we never get in a situation where we have length stuck on the queue if (m_rxDMRQueue1.dataSize() == 1U && len > m_rxDMRQueue1.dataSize()) { @@ -982,7 +982,7 @@ uint32_t Modem::peekDMRFrame2Length() uint8_t len = 0U; m_rxDMRQueue2.peek(&len, 1U); #if DEBUG_MODEM - LogDebug(LOG_MODEM, "Modem::peekDMRFrame2Length() len = %u, dataSize = %u", len, m_rxDMRQueue2.dataSize()); + LogDebugEx(LOG_MODEM, "Modem::peekDMRFrame2Length()", "len = %u, dataSize = %u", len, m_rxDMRQueue2.dataSize()); #endif // this ensures we never get in a situation where we have length stuck on the queue if (m_rxDMRQueue2.dataSize() == 1U && len > m_rxDMRQueue2.dataSize()) { @@ -1040,7 +1040,7 @@ uint32_t Modem::peekP25FrameLength() uint16_t len = 0U; len = (length[0U] << 8) + length[1U]; #if DEBUG_MODEM - LogDebug(LOG_MODEM, "Modem::peekP25FrameLength() len = %u, dataSize = %u", len, m_rxP25Queue.dataSize()); + LogDebugEx(LOG_MODEM, "Modem::peekP25FrameLength()", "len = %u, dataSize = %u", len, m_rxP25Queue.dataSize()); #endif // this ensures we never get in a situation where we have length stuck on the queue if (m_rxP25Queue.dataSize() == 2U && len > m_rxP25Queue.dataSize()) { @@ -1098,7 +1098,7 @@ uint32_t Modem::peekNXDNFrameLength() uint8_t len = 0U; m_rxNXDNQueue.peek(&len, 1U); #if DEBUG_MODEM - LogDebug(LOG_MODEM, "Modem::peekNXDNFrameLength() len = %u, dataSize = %u", len, m_rxNXDNQueue.dataSize()); + LogDebugEx(LOG_MODEM, "Modem::peekNXDNFrameLength()", "len = %u, dataSize = %u", len, m_rxNXDNQueue.dataSize()); #endif // this ensures we never get in a situation where we have length stuck on the queue if (m_rxNXDNQueue.dataSize() == 1U && len > m_rxNXDNQueue.dataSize()) { @@ -1222,7 +1222,7 @@ void Modem::clearDMRFrame1() buffer[1U] = 3U; buffer[2U] = CMD_DMR_CLEAR1; #if DEBUG_MODEM - Utils::dump(1U, "Modem::clearDMRFrame1(), Written", buffer, 3U); + Utils::dump(1U, "[Modem::clearDMRFrame1()] Written", buffer, 3U); #endif write(buffer, 3U); Thread::sleep(5); // 5ms delay @@ -1238,7 +1238,7 @@ void Modem::clearDMRFrame2() buffer[1U] = 3U; buffer[2U] = CMD_DMR_CLEAR2; #if DEBUG_MODEM - Utils::dump(1U, "Modem::clearDMRFrame2(), Written", buffer, 3U); + Utils::dump(1U, "[Modem::clearDMRFrame2()] Written", buffer, 3U); #endif write(buffer, 3U); Thread::sleep(5); // 5ms delay @@ -1254,7 +1254,7 @@ void Modem::clearP25Frame() buffer[1U] = 3U; buffer[2U] = CMD_P25_CLEAR; #if DEBUG_MODEM - Utils::dump(1U, "Modem::clearP25Data(), Written", buffer, 3U); + Utils::dump(1U, "[Modem::clearP25Data()] Written", buffer, 3U); #endif write(buffer, 3U); Thread::sleep(5); // 5ms delay @@ -1270,7 +1270,7 @@ void Modem::clearNXDNFrame() buffer[1U] = 3U; buffer[2U] = CMD_NXDN_CLEAR; #if DEBUG_MODEM - Utils::dump(1U, "Modem::clearNXDNFrame(), Written", buffer, 3U); + Utils::dump(1U, "[Modem::clearNXDNFrame()] Written", buffer, 3U); #endif write(buffer, 3U); Thread::sleep(5); // 5ms delay @@ -1382,7 +1382,7 @@ bool Modem::writeDMRFrame1(const uint8_t* data, uint32_t length) return false; if (length > MAX_LENGTH) { LogError(LOG_MODEM, "Modem::writeDMRData1(); request data to write >%u?, len = %u", MAX_LENGTH, length); - Utils::dump(1U, "Modem::writeDMRData1(); Attmpted Data", data, length); + Utils::dump(1U, "[Modem::writeDMRData1()] Attmpted Data", data, length); return false; } @@ -1399,9 +1399,9 @@ bool Modem::writeDMRFrame1(const uint8_t* data, uint32_t length) // write or buffer DMR slot 1 data to air interface if (m_dmrSpace1 >= length) { if (m_debug) - LogDebug(LOG_MODEM, "Modem::writeDMRData1(); immediate write (len %u)", length); + LogDebugEx(LOG_MODEM, "Modem::writeDMRData1()", "immediate write (len %u)", length); if (m_trace) - Utils::dump(1U, "Modem::writeDMRData1() Immediate TX DMR Data 1", buffer + 3U, length - 1U); + Utils::dump(1U, "[Modem::writeDMRData1()] Immediate TX DMR Data 1", buffer + 3U, length - 1U); int ret = write(buffer, len); if (ret != int(len)) { @@ -1453,9 +1453,9 @@ bool Modem::writeDMRFrame2(const uint8_t* data, uint32_t length) // write or buffer DMR slot 2 data to air interface if (m_dmrSpace2 >= length) { if (m_debug) - LogDebug(LOG_MODEM, "Modem::writeDMRData2(); immediate write (len %u)", length); + LogDebugEx(LOG_MODEM, "Modem::writeDMRData2()", "immediate write (len %u)", length); if (m_trace) - Utils::dump(1U, "Modem::writeDMRData2() Immediate TX DMR Data 2", buffer + 3U, length - 1U); + Utils::dump(1U, "[Modem::writeDMRData2()] Immediate TX DMR Data 2", buffer + 3U, length - 1U); int ret = write(buffer, len); if (ret != int(len)) { @@ -1493,7 +1493,7 @@ bool Modem::writeP25Frame(const uint8_t* data, uint32_t length) return false; if (length > MAX_LENGTH) { LogError(LOG_MODEM, "Modem::writeP25Data(); request data to write >%u?, len = %u", MAX_LENGTH, length); - Utils::dump(1U, "Modem::writeP25Data(); Attmpted Data", data, length); + Utils::dump(1U, "[Modem::writeP25Data()] Attmpted Data", data, length); return false; } @@ -1519,9 +1519,9 @@ bool Modem::writeP25Frame(const uint8_t* data, uint32_t length) // write or buffer P25 data to air interface if (m_p25Space >= length) { if (m_debug) - LogDebug(LOG_MODEM, "Modem::writeP25Data(); immediate write (len %u)", length); + LogDebugEx(LOG_MODEM, "Modem::writeP25Data()", "immediate write (len %u)", length); if (m_trace) - Utils::dump(1U, "Modem::writeP25Data() Immediate TX P25 Data", buffer + 3U, length - 3U); + Utils::dump(1U, "[Modem::writeP25Data()] Immediate TX P25 Data", buffer + 3U, length - 3U); int ret = write(buffer, len); if (ret != int(len)) { @@ -1556,7 +1556,7 @@ bool Modem::writeNXDNFrame(const uint8_t* data, uint32_t length) return false; if (length > MAX_LENGTH) { LogError(LOG_MODEM, "Modem::writeNXDNData(); request data to write >%u?, len = %u", MAX_LENGTH, length); - Utils::dump(1U, "Modem::writeNXDNData(); Attmpted Data", data, length); + Utils::dump(1U, "[Modem::writeNXDNData()] Attmpted Data", data, length); return false; } @@ -1573,9 +1573,9 @@ bool Modem::writeNXDNFrame(const uint8_t* data, uint32_t length) // write or buffer NXDN data to air interface if (m_nxdnSpace >= length) { if (m_debug) - LogDebug(LOG_MODEM, "Modem::writeNXDNData(); immediate write (len %u)", length); + LogDebugEx(LOG_MODEM, "Modem::writeNXDNData()", "immediate write (len %u)", length); if (m_trace) - Utils::dump(1U, "Modem::writeNXDNData() Immediate TX NXDN Data", buffer + 3U, length - 1U); + Utils::dump(1U, "[Modem::writeNXDNData()] Immediate TX NXDN Data", buffer + 3U, length - 1U); int ret = write(buffer, len); if (ret != int(len)) { @@ -1613,7 +1613,7 @@ bool Modem::writeDMRStart(bool tx) buffer[2U] = CMD_DMR_START; buffer[3U] = tx ? 0x01U : 0x00U; #if DEBUG_MODEM - Utils::dump(1U, "Modem::writeDMRStart(), Written", buffer, 4U); + Utils::dump(1U, "[Modem::writeDMRStart()] Written", buffer, 4U); #endif return write(buffer, 4U) == 4; } @@ -1644,7 +1644,7 @@ bool Modem::writeDMRShortLC(const uint8_t* lc) buffer[10U] = lc[7U]; buffer[11U] = lc[8U]; #if DEBUG_MODEM - Utils::dump(1U, "Modem::writeDMRShortLC(), Written", buffer, 12U); + Utils::dump(1U, "[Modem::writeDMRShortLC()] Written", buffer, 12U); #endif return write(buffer, 12U) == 12; } @@ -1665,7 +1665,7 @@ bool Modem::writeDMRAbort(uint32_t slotNo) buffer[2U] = CMD_DMR_ABORT; buffer[3U] = slotNo; #if DEBUG_MODEM - Utils::dump(1U, "Modem::writeDMRAbort(), Written", buffer, 4U); + Utils::dump(1U, "[Modem::writeDMRAbort()] Written", buffer, 4U); #endif return write(buffer, 4U) == 4; } @@ -1689,7 +1689,7 @@ bool Modem::setDMRIgnoreCACH_AT(uint8_t slotNo) // are we on a protocol version 3 firmware? if (m_protoVer >= 3U) { #if DEBUG_MODEM - Utils::dump(1U, "Modem::setDMRIgnoreCACH_AT(), Written", buffer, 4U); + Utils::dump(1U, "[Modem::setDMRIgnoreCACH_AT()] Written", buffer, 4U); #endif return write(buffer, 4U) == 4; } else { @@ -1734,7 +1734,7 @@ bool Modem::setState(DVM_STATE state) buffer[2U] = CMD_SET_MODE; buffer[3U] = state; #if DEBUG_MODEM - Utils::dump(1U, "Modem::setState(), Written", buffer, 4U); + Utils::dump(1U, "[Modem::setState()] Written", buffer, 4U); #endif return write(buffer, 4U) == 4; } @@ -1876,7 +1876,7 @@ bool Modem::getStatus() buffer[1U] = 3U; buffer[2U] = CMD_GET_STATUS; - //LogDebug(LOG_MODEM, "getStatus(), polling modem status"); + //LogDebugEx(LOG_MODEM, "Modem::getStatus()", "polling modem status"); return write(buffer, 3U) == 3; } @@ -2372,7 +2372,7 @@ RESP_TYPE_DVM Modem::getResponse() { m_rspDoubleLength = false; - //LogDebug(LOG_MODEM, "Modem::getResponse(), checking if we have data"); + //LogDebugEx(LOG_MODEM, "Modem::getResponse()", "checking if we have data"); // get the start of the frame or nothing at all if (m_rspState == RESP_START) { @@ -2384,13 +2384,13 @@ RESP_TYPE_DVM Modem::getResponse() } if (ret == 0) { - //LogDebug(LOG_MODEM, "Modem::getResponse(), no data available"); + //LogDebugEx(LOG_MODEM, "Modem::getResponse()", "no data available"); return RTM_TIMEOUT; } if (m_buffer[0U] != DVM_SHORT_FRAME_START && m_buffer[0U] != DVM_LONG_FRAME_START) { - //LogError(LOG_MODEM, "Modem::getResponse(), illegal response, first byte not a frame start; byte = %02X", m_buffer[0U]); + //LogErrorEx(LOG_MODEM, "Modem::getResponse()", "illegal response, first byte not a frame start; byte = %02X", m_buffer[0U]); ::memset(m_buffer, 0x00U, BUFFER_LENGTH); m_rspState = RESP_START; return RTM_ERROR; @@ -2400,12 +2400,12 @@ RESP_TYPE_DVM Modem::getResponse() m_rspDoubleLength = true; } - //LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_START"); + //LogDebugEx(LOG_MODEM, "Modem::getResponse()", "RESP_START"); m_rspState = RESP_LENGTH1; } - //LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame length 1/2, rspDoubleLength = %u", m_rspDoubleLength); + //LogDebugEx(LOG_MODEM, "Modem::getResponse()", "getting frame length 1/2, rspDoubleLength = %u", m_rspDoubleLength); // get the length of the frame, 1/2 if (m_rspState == RESP_LENGTH1) { int ret = m_port->read(m_buffer + 1U, 1U); @@ -2432,12 +2432,12 @@ RESP_TYPE_DVM Modem::getResponse() m_length = m_buffer[1U]; } - //LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_LENGTH1, len = %u", m_length); + //LogDebugEx(LOG_MODEM, "Modem::getResponse()", "RESP_LENGTH1, len = %u", m_length); m_rspOffset = 2U; } - //LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame length 2/2"); + //LogDebugEx(LOG_MODEM, "Modem::getResponse()", "getting frame length 2/2"); // get the length of the frame, 2/2 if (m_rspState == RESP_LENGTH2) { int ret = m_port->read(m_buffer + 2U, 1U); @@ -2453,13 +2453,13 @@ RESP_TYPE_DVM Modem::getResponse() m_length = (m_length + (m_buffer[2U] & 0xFFU)); m_rspState = RESP_TYPE; - //LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_LENGTH2, len = %u", m_length); + //LogDebugEx(LOG_MODEM, "Modem::getResponse()", "RESP_LENGTH2, len = %u", m_length); m_rspDoubleLength = true; m_rspOffset = 3U; } - //LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame type"); + //LogDebugEx(LOG_MODEM, "Modem::getResponse()", "getting frame type"); // get the frame type if (m_rspState == RESP_TYPE) { int ret = m_port->read(m_buffer + m_rspOffset, 1U); @@ -2474,18 +2474,18 @@ RESP_TYPE_DVM Modem::getResponse() m_rspType = (DVM_COMMANDS)m_buffer[m_rspOffset]; - //LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_TYPE, len = %u, type = %u", m_length, m_rspType); + //LogDebugEx(LOG_MODEM, "Modem::getResponse()", "RESP_TYPE, len = %u, type = %u", m_length, m_rspType); m_rspState = RESP_DATA; m_rspOffset++; } - //LogDebug(LOG_MODEM, "Modem::getResponse(), getting frame data"); + //LogDebugEx(LOG_MODEM, "Modem::getResponse()", "getting frame data"); // get the frame data if (m_rspState == RESP_DATA) { if (m_respTrace) - LogDebug(LOG_MODEM, "Modem::getResponse(), RESP_DATA, len = %u, offset = %u, type = %02X", m_length, m_rspOffset, m_rspType); + LogDebugEx(LOG_MODEM, "Modem::getResponse()", "RESP_DATA, len = %u, offset = %u, type = %02X", m_length, m_rspOffset, m_rspType); while (m_rspOffset < m_length) { int ret = m_port->read(m_buffer + m_rspOffset, m_length - m_rspOffset); @@ -2503,7 +2503,7 @@ RESP_TYPE_DVM Modem::getResponse() } if (m_respTrace) - Utils::dump(1U, "Modem::getResponse() Buffer", m_buffer, m_length); + Utils::dump(1U, "[Modem::getResponse()] Buffer", m_buffer, m_length); } m_rspState = RESP_START; diff --git a/src/host/modem/ModemV24.cpp b/src/host/modem/ModemV24.cpp index 6a355ac8..c2e41e89 100644 --- a/src/host/modem/ModemV24.cpp +++ b/src/host/modem/ModemV24.cpp @@ -320,9 +320,9 @@ void ModemV24::clock(uint32_t ms) m_p25Space = m_buffer[10U] * (P25DEF::P25_LDU_FRAME_LENGTH_BYTES); if (m_dumpModemStatus) { - LogDebug(LOG_MODEM, "ModemV24::clock(), CMD_GET_STATUS, isHotspot = %u, v24Connected = %u, dmr = %u / %u, p25 = %u / %u, nxdn = %u / %u, modemState = %u, tx = %u, adcOverflow = %u, rxOverflow = %u, txOverflow = %u, dacOverflow = %u, dmrSpace1 = %u, dmrSpace2 = %u, p25Space = %u, nxdnSpace = %u", + LogDebugEx(LOG_MODEM, "ModemV24::clock()", "CMD_GET_STATUS, isHotspot = %u, v24Connected = %u, dmr = %u / %u, p25 = %u / %u, nxdn = %u / %u, modemState = %u, tx = %u, adcOverflow = %u, rxOverflow = %u, txOverflow = %u, dacOverflow = %u, dmrSpace1 = %u, dmrSpace2 = %u, p25Space = %u, nxdnSpace = %u", m_isHotspot, m_v24Connected, dmrEnable, m_dmrEnabled, p25Enable, m_p25Enabled, nxdnEnable, m_nxdnEnabled, m_modemState, m_tx, adcOverflow, rxOverflow, txOverflow, dacOverflow, m_dmrSpace1, m_dmrSpace2, m_p25Space, m_nxdnSpace); - LogDebug(LOG_MODEM, "ModemV24::clock(), CMD_GET_STATUS, rxDMRData1 size = %u, len = %u, free = %u; rxDMRData2 size = %u, len = %u, free = %u, rxP25Data size = %u, len = %u, free = %u, rxNXDNData size = %u, len = %u, free = %u", + LogDebugEx(LOG_MODEM, "ModemV24::clock()", "CMD_GET_STATUS, rxDMRData1 size = %u, len = %u, free = %u; rxDMRData2 size = %u, len = %u, free = %u, rxP25Data size = %u, len = %u, free = %u, rxNXDNData size = %u, len = %u, free = %u", m_rxDMRQueue1.length(), m_rxDMRQueue1.dataSize(), m_rxDMRQueue1.freeSpace(), m_rxDMRQueue2.length(), m_rxDMRQueue2.dataSize(), m_rxDMRQueue2.freeSpace(), m_rxP25Queue.length(), m_rxP25Queue.dataSize(), m_rxP25Queue.freeSpace(), m_rxNXDNQueue.length(), m_rxNXDNQueue.dataSize(), m_rxNXDNQueue.freeSpace()); } @@ -409,7 +409,7 @@ void ModemV24::clock(uint32_t ms) void ModemV24::close() { - LogDebug(LOG_MODEM, "Closing the modem"); + LogMessage(LOG_MODEM, "Closing the modem"); m_port->close(); m_gotModemStatus = false; @@ -1553,9 +1553,9 @@ void ModemV24::queueP25Frame(uint8_t* data, uint16_t len, SERIAL_TX_TYPE msgType assert(len > 0U); if (m_debug) - LogDebug(LOG_MODEM, "ModemV24::queueP25Frame() msgType = $%02X", msgType); + LogDebugEx(LOG_MODEM, "ModemV24::queueP25Frame()", "msgType = $%02X", msgType); if (m_trace) - Utils::dump(1U, "ModemV24::queueP25Frame() data", data, len); + Utils::dump(1U, "[ModemV24::queueP25Frame()] data", data, len); // get current time in ms uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); diff --git a/src/host/modem/port/specialized/V24UDPPort.cpp b/src/host/modem/port/specialized/V24UDPPort.cpp index c4776305..e48711da 100644 --- a/src/host/modem/port/specialized/V24UDPPort.cpp +++ b/src/host/modem/port/specialized/V24UDPPort.cpp @@ -759,7 +759,7 @@ uint8_t* V24UDPPort::generateMessage(const uint8_t* message, uint32_t length, ui if (timestamp != INVALID_TS) { timestamp += (RTP_GENERIC_CLOCK_RATE / 133); if (m_debug) - LogDebug(LOG_NET, "V24UDPPort::generateMessage() RTP streamId = %u, previous TS = %u, TS = %u, rtpSeq = %u", streamId, m_timestamp, timestamp, rtpSeq); + LogDebugEx(LOG_NET, "V24UDPPort::generateMessage()", "RTP streamId = %u, previous TS = %u, TS = %u, rtpSeq = %u", streamId, m_timestamp, timestamp, rtpSeq); m_timestamp = timestamp; } @@ -779,20 +779,20 @@ uint8_t* V24UDPPort::generateMessage(const uint8_t* message, uint32_t length, ui if (streamId != 0U && timestamp == INVALID_TS && rtpSeq != RTP_END_OF_CALL_SEQ) { if (m_debug) - LogDebug(LOG_NET, "V24UDPPort::generateMessage() RTP streamId = %u, initial TS = %u, rtpSeq = %u", streamId, header.getTimestamp(), rtpSeq); + LogDebugEx(LOG_NET, "V24UDPPort::generateMessage()", "RTP streamId = %u, initial TS = %u, rtpSeq = %u", streamId, header.getTimestamp(), rtpSeq); m_timestamp = header.getTimestamp(); } if (streamId != 0U && rtpSeq == RTP_END_OF_CALL_SEQ) { m_timestamp = INVALID_TS; if (m_debug) - LogDebug(LOG_NET, "V24UDPPort::generateMessage() RTP streamId = %u, rtpSeq = %u", streamId, rtpSeq); + LogDebugEx(LOG_NET, "V24UDPPort::generateMessage()", "RTP streamId = %u, rtpSeq = %u", streamId, rtpSeq); } ::memcpy(buffer + RTP_HEADER_LENGTH_BYTES, message, length); if (m_debug) - Utils::dump(1U, "V24UDPPort::generateMessage() Buffered Message", buffer, bufferLen); + Utils::dump(1U, "[V24UDPPort::generateMessage()] Buffered Message", buffer, bufferLen); if (outBufferLen != nullptr) { *outBufferLen = bufferLen; diff --git a/src/host/network/Network.cpp b/src/host/network/Network.cpp index 2f84900b..b3b54526 100644 --- a/src/host/network/Network.cpp +++ b/src/host/network/Network.cpp @@ -226,7 +226,7 @@ void Network::clock(uint32_t ms) } if (m_debug) { - LogDebug(LOG_NET, "RTP, peerId = %u, seq = %u, streamId = %u, func = %02X, subFunc = %02X", fneHeader.getPeerId(), rtpHeader.getSequence(), + LogDebugEx(LOG_NET, "Network::clock()", "RTP, peerId = %u, seq = %u, streamId = %u, func = %02X, subFunc = %02X", fneHeader.getPeerId(), rtpHeader.getSequence(), fneHeader.getStreamId(), fneHeader.getFunction(), fneHeader.getSubFunction()); } @@ -280,7 +280,7 @@ void Network::clock(uint32_t ms) } if (m_debug) - Utils::dump(1U, "Network Received, DMR", buffer.get(), length); + Utils::dump(1U, "[Network::clock()] Network Received, DMR", buffer.get(), length); if (length > 255) LogError(LOG_NET, "DMR Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, m_pktSeq, length); @@ -308,7 +308,7 @@ void Network::clock(uint32_t ms) } if (m_debug) - Utils::dump(1U, "Network Received, P25", buffer.get(), length); + Utils::dump(1U, "[Network::clock()] Network Received, P25", buffer.get(), length); if (length > 255) LogError(LOG_NET, "P25 Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, m_pktSeq, length); @@ -336,7 +336,7 @@ void Network::clock(uint32_t ms) } if (m_debug) - Utils::dump(1U, "Network Received, NXDN", buffer.get(), length); + Utils::dump(1U, "[Network::clock()] Network Received, NXDN", buffer.get(), length); if (length > 255) LogError(LOG_NET, "NXDN Stream %u, frame oversized? this shouldn't happen, pktSeq = %u, len = %u", streamId, m_pktSeq, length); @@ -548,7 +548,7 @@ void Network::clock(uint32_t ms) { switch (m_status) { case NET_STAT_WAITING_LOGIN: - LogDebug(LOG_NET, "PEER %u RPTL ACK, performing login exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); + LogMessage(LOG_NET, "PEER %u RPTL ACK, performing login exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); ::memcpy(m_salt, buffer.get() + 6U, sizeof(uint32_t)); writeAuthorisation(); @@ -558,7 +558,7 @@ void Network::clock(uint32_t ms) m_retryTimer.start(); break; case NET_STAT_WAITING_AUTHORISATION: - LogDebug(LOG_NET, "PEER %u RPTK ACK, performing configuration exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); + LogMessage(LOG_NET, "PEER %u RPTK ACK, performing configuration exchange, remotePeerId = %u", m_peerId, rtpHeader.getSSRC()); writeConfig(); diff --git a/src/host/network/RESTAPI.cpp b/src/host/network/RESTAPI.cpp index 67e03f7c..828477dd 100644 --- a/src/host/network/RESTAPI.cpp +++ b/src/host/network/RESTAPI.cpp @@ -349,7 +349,7 @@ bool RESTAPI::validateAuth(const HTTPPayload& request, HTTPPayload& reply) std::string host = request.headers.find("RemoteHost"); std::string headerToken = request.headers.find("X-DVM-Auth-Token"); #if DEBUG_HTTP_PAYLOAD - ::LogDebug(LOG_REST, "RESTAPI::validateAuth() token, host = %s, token = %s", host.c_str(), headerToken.c_str()); + ::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "token, host = %s, token = %s", host.c_str(), headerToken.c_str()); #endif if (headerToken.empty()) { errorPayload(reply, "no authentication token", HTTPPayload::UNAUTHORIZED); @@ -358,11 +358,11 @@ bool RESTAPI::validateAuth(const HTTPPayload& request, HTTPPayload& reply) for (auto& token : m_authTokens) { #if DEBUG_HTTP_PAYLOAD - ::LogDebug(LOG_REST, "RESTAPI::validateAuth() valid list, host = %s, token = %s", token.first.c_str(), std::to_string(token.second).c_str()); + ::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "valid list, host = %s, token = %s", token.first.c_str(), std::to_string(token.second).c_str()); #endif if (token.first.compare(host) == 0) { #if DEBUG_HTTP_PAYLOAD - ::LogDebug(LOG_REST, "RESTAPI::validateAuth() storedToken = %s, passedToken = %s", std::to_string(token.second).c_str(), headerToken.c_str()); + ::LogDebugEx(LOG_REST, "RESTAPI::validateAuth()", "storedToken = %s, passedToken = %s", std::to_string(token.second).c_str(), headerToken.c_str()); #endif if (std::to_string(token.second).compare(headerToken) == 0) { return true; @@ -1006,7 +1006,7 @@ void RESTAPI::restAPI_PutRegisterCCVC(const HTTPPayload& request, HTTPPayload& r uint32_t peerId = req["peerId"].get(); - // LogDebug(LOG_REST, "restAPI_PutRegisterCCVC(): callback, channelNo = %u, peerId = %u", channelNo, peerId); + // LogDebugEx(LOG_REST, "RESTAPI::restAPI_PutRegisterCCVC()", "callback, channelNo = %u, peerId = %u", channelNo, peerId); // validate restAddress is a string within the JSON blob if (!req["restAddress"].is()) { @@ -1087,7 +1087,7 @@ void RESTAPI::restAPI_PutReleaseGrant(const HTTPPayload& request, HTTPPayload& r return; } - // LogDebug(LOG_REST, "restAPI_PutReleaseGrant(): callback, state = %u, dstId = %u", state, dstId); + // LogDebugEx(LOG_REST, "RESTAPI::restAPI_PutReleaseGrant()", "callback, state = %u, dstId = %u", state, dstId); switch (state) { case STATE_DMR: @@ -1179,7 +1179,7 @@ void RESTAPI::restAPI_PutTouchGrant(const HTTPPayload& request, HTTPPayload& rep return; } - // LogDebug(LOG_REST, "restAPI_PutTouchGrant(): callback, state = %u, dstId = %u", state, dstId); + // LogDebugEx(LOG_REST, "RESTAPI::restAPI_PutTouchGrant()", "callback, state = %u, dstId = %u", state, dstId); switch (state) { case STATE_DMR: diff --git a/src/host/nxdn/Control.cpp b/src/host/nxdn/Control.cpp index bd1ccbbe..311a9e58 100644 --- a/src/host/nxdn/Control.cpp +++ b/src/host/nxdn/Control.cpp @@ -877,7 +877,7 @@ void Control::addFrame(const uint8_t *data, bool net, bool imm) uint32_t fifoSpace = m_modem->getNXDNSpace(); - //LogDebug(LOG_NXDN, "addFrame() fifoSpace = %u", fifoSpace); + //LogDebugEx(LOG_NXDN, "Control::addFrame()", "fifoSpace = %u", fifoSpace); // is this immediate data? if (imm) { diff --git a/src/host/nxdn/packet/ControlSignaling.cpp b/src/host/nxdn/packet/ControlSignaling.cpp index 80fc82bf..28babb81 100644 --- a/src/host/nxdn/packet/ControlSignaling.cpp +++ b/src/host/nxdn/packet/ControlSignaling.cpp @@ -398,7 +398,7 @@ void ControlSignaling::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adj do { if (m_debug) { - LogDebug(LOG_NXDN, "writeRF_ControlData, frameCnt = %u, seq = %u", frameCnt, n); + LogDebugEx(LOG_NXDN, "ControlSignaling::writeRF_ControlData()", "frameCnt = %u, seq = %u", frameCnt, n); } switch (n) diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index f4f6a5c1..1c125252 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -1194,7 +1194,7 @@ void Control::addFrame(const uint8_t* data, uint32_t length, bool net, bool imm) uint32_t fifoSpace = m_modem->getP25Space(); - //LogDebug(LOG_P25, "addFrame() fifoSpace = %u", fifoSpace); + // LogDebugEx(LOG_P25, "Control::addFrame()", "fifoSpace = %u", fifoSpace); // is this immediate data? if (imm) { @@ -1689,10 +1689,6 @@ void Control::writeRF_Nulls() data[0U] = modem::TAG_EOT; data[1U] = 0x00U; - if (m_debug) { - LogDebug(LOG_P25, "writeRF_Nulls()"); - } - addFrame(data, NULLS_LENGTH_BYTES + 2U); } diff --git a/src/host/p25/packet/ControlSignaling.cpp b/src/host/p25/packet/ControlSignaling.cpp index 1e9f36f2..b9d1c4b4 100644 --- a/src/host/p25/packet/ControlSignaling.cpp +++ b/src/host/p25/packet/ControlSignaling.cpp @@ -1784,7 +1784,7 @@ void ControlSignaling::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adj } if (m_debug) { - LogDebug(LOG_P25, "writeRF_ControlData, mbfCnt = %u, frameCnt = %u, seq = %u, adjSS = %u", m_mbfCnt, frameCnt, n, adjSS); + LogDebugEx(LOG_P25, "ControlSignaling::writeRF_ControlData()", "mbfCnt = %u, frameCnt = %u, seq = %u, adjSS = %u", m_mbfCnt, frameCnt, n, adjSS); } // bryanb: this is just a simple counter because we treat the SYNC_BCST as unlocked @@ -1877,7 +1877,7 @@ void ControlSignaling::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adj queueRF_TSBK_Ctrl(TSBKO::OSP_RFSS_STS_BCAST); queueRF_TSBK_Ctrl(TSBKO::OSP_NET_STS_BCAST); if (m_debug) { - LogDebug(LOG_P25, "writeRF_ControlData, have 1 pad 2, mbfCnt = %u", m_mbfCnt); + LogDebugEx(LOG_P25, "ControlSignaling::writeRF_ControlData()]", "have 1 pad 2, mbfCnt = %u", m_mbfCnt); } } @@ -1892,7 +1892,7 @@ void ControlSignaling::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adj } if (m_debug) { - LogDebug(LOG_P25, "writeRF_ControlData, have 2 pad 1, mbfCnt = %u", m_mbfCnt); + LogDebugEx(LOG_P25, "ControlSignaling::writeRF_ControlData()", "have 2 pad 1, mbfCnt = %u", m_mbfCnt); } } diff --git a/src/sysview/HostWS.cpp b/src/sysview/HostWS.cpp index fe60e38e..d8ea7591 100644 --- a/src/sysview/HostWS.cpp +++ b/src/sysview/HostWS.cpp @@ -533,7 +533,7 @@ void* HostWS::threadWebSocket(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -542,7 +542,7 @@ void* HostWS::threadWebSocket(void* arg) ws->m_wsServer.start_accept(); ws->m_wsServer.run(); - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/sysview/NodeStatusWnd.h b/src/sysview/NodeStatusWnd.h index 8bf42a87..01572da8 100644 --- a/src/sysview/NodeStatusWnd.h +++ b/src/sysview/NodeStatusWnd.h @@ -887,7 +887,7 @@ private: return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -909,7 +909,7 @@ private: } wnd->m_threadStopped = true; - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } diff --git a/src/sysview/SysViewMain.cpp b/src/sysview/SysViewMain.cpp index 5ed79fd0..cf099344 100644 --- a/src/sysview/SysViewMain.cpp +++ b/src/sysview/SysViewMain.cpp @@ -286,7 +286,7 @@ void* threadNetworkPump(void* arg) return nullptr; } - LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[ OK ] %s", threadName.c_str()); #ifdef _GNU_SOURCE ::pthread_setname_np(th->thread, threadName.c_str()); #endif // _GNU_SOURCE @@ -985,7 +985,7 @@ void* threadNetworkPump(void* arg) Thread::sleep(1U); } - LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + LogMessage(LOG_HOST, "[STOP] %s", threadName.c_str()); delete th; } From f051434e7b6047d1336e0724dfabf2b660cceb81 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 3 Mar 2025 12:13:43 -0500 Subject: [PATCH 18/34] adjust lookup table logging slightly; --- src/common/lookups/PeerListLookup.cpp | 2 +- src/common/lookups/RadioIdLookup.cpp | 2 +- src/common/lookups/TalkgroupRulesLookup.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/lookups/PeerListLookup.cpp b/src/common/lookups/PeerListLookup.cpp index 454e07c3..5552a928 100644 --- a/src/common/lookups/PeerListLookup.cpp +++ b/src/common/lookups/PeerListLookup.cpp @@ -234,7 +234,7 @@ bool PeerListLookup::load() if (size == 0U) return false; - LogInfoEx(LOG_HOST, "Loaded %lu peers into list", size); + LogInfoEx(LOG_HOST, "Loaded %lu entries into peer list lookup table", size); return true; } diff --git a/src/common/lookups/RadioIdLookup.cpp b/src/common/lookups/RadioIdLookup.cpp index 4a02c3c6..e823407b 100644 --- a/src/common/lookups/RadioIdLookup.cpp +++ b/src/common/lookups/RadioIdLookup.cpp @@ -204,7 +204,7 @@ bool RadioIdLookup::load() if (size == 0U) return false; - LogInfoEx(LOG_HOST, "Loaded %u entries into lookup table", size); + LogInfoEx(LOG_HOST, "Loaded %lu entries into radio ID lookup table", size); return true; } diff --git a/src/common/lookups/TalkgroupRulesLookup.cpp b/src/common/lookups/TalkgroupRulesLookup.cpp index 1d477648..ae78c2d8 100644 --- a/src/common/lookups/TalkgroupRulesLookup.cpp +++ b/src/common/lookups/TalkgroupRulesLookup.cpp @@ -335,7 +335,7 @@ bool TalkgroupRulesLookup::load() if (size == 0U) return false; - LogInfoEx(LOG_HOST, "Loaded %u entries into lookup table", size); + LogInfoEx(LOG_HOST, "Loaded %lu entries into talkgroup rules table", size); return true; } From 4025db82004d68f0705bf5ea4bc1aa3b71a73c25 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 3 Mar 2025 12:16:22 -0500 Subject: [PATCH 19/34] clarify more lookup table log entries; --- src/host/network/Network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/host/network/Network.cpp b/src/host/network/Network.cpp index b3b54526..2a79bbb3 100644 --- a/src/host/network/Network.cpp +++ b/src/host/network/Network.cpp @@ -440,7 +440,7 @@ void Network::clock(uint32_t ms) offs += 5U; } - LogMessage(LOG_NET, "Activated %u TGs; loaded %u entries into lookup table", len, m_tidLookup->groupVoice().size()); + LogMessage(LOG_NET, "Activated %u TGs; loaded %u entries into talkgroup rules table", len, m_tidLookup->groupVoice().size()); // save if saving from network is enabled if (m_saveLookup && len > 0) { @@ -471,7 +471,7 @@ void Network::clock(uint32_t ms) offs += 5U; } - LogMessage(LOG_NET, "Deactivated %u TGs; loaded %u entries into lookup table", len, m_tidLookup->groupVoice().size()); + LogMessage(LOG_NET, "Deactivated %u TGs; loaded %u entries into talkgroup rules table", len, m_tidLookup->groupVoice().size()); // save if saving from network is enabled if (m_saveLookup && len > 0) { From ef1a0df4969a4f2149c7e636c87c135d8241903e Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 3 Mar 2025 20:51:27 -0500 Subject: [PATCH 20/34] refactor FNE RTP packet handling, this change better handles a peer end-point transmitting multiple varied RTP streams; refactor promiscuous handling of RTP streams on the host network API; refactor handling non-promiscuous handling of RTP streams on the host network API; bump project version from 4.11F to 4.11G to reflect these larger changes; correct bad use of magic numbers when dealing with signal values; --- src/bridge/BridgeMain.cpp | 12 +- src/common/Defines.h | 2 +- src/common/network/BaseNetwork.cpp | 7 +- src/fne/FNEMain.cpp | 12 +- src/fne/HostFNE.cpp | 8 +- src/fne/network/DiagNetwork.cpp | 39 ++-- src/fne/network/FNENetwork.cpp | 185 ++++++++---------- src/fne/network/FNENetwork.h | 185 +++++++++++++----- src/fne/network/PeerNetwork.cpp | 14 ++ src/fne/network/PeerNetwork.h | 17 ++ src/fne/network/callhandler/TagDMRData.cpp | 2 +- src/fne/network/callhandler/TagNXDNData.cpp | 2 +- src/fne/network/callhandler/TagP25Data.cpp | 12 +- .../callhandler/packetdata/DMRPacketData.cpp | 2 +- .../callhandler/packetdata/P25PacketData.cpp | 4 +- src/host/HostMain.cpp | 12 +- src/host/network/Network.cpp | 123 +++++++++--- 17 files changed, 412 insertions(+), 226 deletions(-) diff --git a/src/bridge/BridgeMain.cpp b/src/bridge/BridgeMain.cpp index 0ca43790..ea03b492 100644 --- a/src/bridge/BridgeMain.cpp +++ b/src/bridge/BridgeMain.cpp @@ -32,6 +32,10 @@ using namespace lookups; // Global Variables // --------------------------------------------------------------------------- +#ifndef SIGHUP +#define SIGHUP 1 +#endif + int g_signal = 0; std::string g_progExe = std::string(__EXE_NAME__); std::string g_iniFile = std::string(DEFAULT_CONF_FILE); @@ -293,15 +297,15 @@ int main(int argc, char** argv) ret = bridge->run(); delete bridge; - if (g_signal == 2) + if (g_signal == SIGINT) ::LogInfoEx(LOG_HOST, "Exited on receipt of SIGINT"); - if (g_signal == 15) + if (g_signal == SIGTERM) ::LogInfoEx(LOG_HOST, "Exited on receipt of SIGTERM"); - if (g_signal == 1) + if (g_signal == SIGHUP) ::LogInfoEx(LOG_HOST, "Restarting on receipt of SIGHUP"); - } while (g_signal == 1); + } while (g_signal == SIGHUP); ::LogFinalise(); ::ActivityLogFinalise(); diff --git a/src/common/Defines.h b/src/common/Defines.h index 01190b25..56d6fb1e 100644 --- a/src/common/Defines.h +++ b/src/common/Defines.h @@ -109,7 +109,7 @@ typedef unsigned long long ulong64_t; #define VERSION_MAJOR "04" #define VERSION_MINOR "11" -#define VERSION_REV "F" +#define VERSION_REV "G" #define __NETVER__ "DVM_R" VERSION_MAJOR VERSION_REV VERSION_MINOR #define __VER__ VERSION_MAJOR "." VERSION_MINOR VERSION_REV " (R" VERSION_MAJOR VERSION_REV VERSION_MINOR " " __GIT_VER__ ")" diff --git a/src/common/network/BaseNetwork.cpp b/src/common/network/BaseNetwork.cpp index c34f4a76..3cdb384d 100644 --- a/src/common/network/BaseNetwork.cpp +++ b/src/common/network/BaseNetwork.cpp @@ -544,12 +544,7 @@ bool BaseNetwork::writeP25TDU(const p25::lc::LC& control, const p25::data::LowSp return false; } - uint16_t seq = pktSeq(resetSeq); - if (controlByte == 0x00U) { - seq = RTP_END_OF_CALL_SEQ; - } - - return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, seq, m_p25StreamId); + return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, m_p25StreamId); } /* Writes P25 TSDU frame data to the network. */ diff --git a/src/fne/FNEMain.cpp b/src/fne/FNEMain.cpp index 765c6cbb..7a7ba851 100644 --- a/src/fne/FNEMain.cpp +++ b/src/fne/FNEMain.cpp @@ -32,6 +32,10 @@ using namespace lookups; // Global Variables // --------------------------------------------------------------------------- +#ifndef SIGHUP +#define SIGHUP 1 +#endif + int g_signal = 0; std::string g_progExe = std::string(__EXE_NAME__); std::string g_iniFile = std::string(DEFAULT_CONF_FILE); @@ -209,15 +213,15 @@ int main(int argc, char** argv) ret = fne->run(); delete fne; - if (g_signal == 2) + if (g_signal == SIGINT) ::LogInfoEx(LOG_HOST, "[STOP] dvmfne:main SIGINT"); - if (g_signal == 15) + if (g_signal == SIGTERM) ::LogInfoEx(LOG_HOST, "[STOP] dvmfne:main SIGTERM"); - if (g_signal == 1) + if (g_signal == SIGHUP) ::LogInfoEx(LOG_HOST, "[RSTR] dvmfne:main SIGHUP"); - } while (g_signal == 1); + } while (g_signal == SIGHUP); ::LogFinalise(); ::ActivityLogFinalise(); diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 95789346..f9818af8 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2023,2024,2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" @@ -972,7 +972,7 @@ void HostFNE::processPeer(network::PeerNetwork* peerNetwork) if (ret) { uint32_t peerId = peerNetwork->getPeerId(); uint32_t slotNo = (data[15U] & 0x80U) == 0x80U ? 2U : 1U; - uint32_t streamId = peerNetwork->getDMRStreamId(slotNo); + uint32_t streamId = peerNetwork->getRxDMRStreamId(slotNo); m_network->dmrTrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId, true); } @@ -985,7 +985,7 @@ void HostFNE::processPeer(network::PeerNetwork* peerNetwork) UInt8Array data = peerNetwork->readP25(ret, length); if (ret) { uint32_t peerId = peerNetwork->getPeerId(); - uint32_t streamId = peerNetwork->getP25StreamId(); + uint32_t streamId = peerNetwork->getRxP25StreamId(); m_network->p25TrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId, true); } @@ -998,7 +998,7 @@ void HostFNE::processPeer(network::PeerNetwork* peerNetwork) UInt8Array data = peerNetwork->readNXDN(ret, length); if (ret) { uint32_t peerId = peerNetwork->getPeerId(); - uint32_t streamId = peerNetwork->getNXDNStreamId(); + uint32_t streamId = peerNetwork->getRxNXDNStreamId(); m_network->nxdnTrafficHandler()->processFrame(data.get(), length, peerId, peerNetwork->pktLastSeq(), streamId, true); } diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index c623e41d..9ee6c16b 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL * */ #include "fne/Defines.h" @@ -177,20 +177,17 @@ void* DiagNetwork::threadedNetworkRx(void* arg) if (connection != nullptr) { if (pktSeq == RTP_END_OF_CALL_SEQ) { - connection->pktLastSeq(pktSeq); - connection->pktNextSeq(0U); + connection->eraseStreamPktSeq(streamId); // attempt to erase packet sequence for the stream } else { - if ((connection->currStreamId() == streamId) && (pktSeq != connection->pktNextSeq()) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U))) { - LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; %u != %u", peerId, connection->identity().c_str(), - streamId, pktSeq, connection->pktNextSeq()); + if (connection->hasStreamPktSeq(streamId)) { + uint16_t currPkt = connection->getStreamPktSeq(streamId); + if ((pktSeq != currPkt) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U)) && pktSeq != 0U) { + LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; %u != %u", peerId, connection->identity().c_str(), + streamId, pktSeq, currPkt); + } } - connection->currStreamId(streamId); - connection->pktLastSeq(pktSeq); - connection->pktNextSeq(pktSeq + 1); - if (connection->pktNextSeq() > (RTP_END_OF_CALL_SEQ - 1U)) { - connection->pktNextSeq(0U); - } + connection->incStreamPktSeq(streamId, pktSeq + 1U); } } @@ -255,10 +252,6 @@ void* DiagNetwork::threadedNetworkRx(void* arg) for (auto peer : network->m_peers) { if (peer.second != nullptr) { if (peer.second->isSysView()) { - uint32_t peerStreamId = peer.second->currStreamId(); - if (streamId == 0U) { - streamId = peerStreamId; - } sockaddr_storage addr = peer.second->socketStorage(); uint32_t addrLen = peer.second->sockStorageLen(); @@ -284,7 +277,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(pktPeerId, TAG_TRANSFER_ACT_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(pktPeerId, streamId, TAG_TRANSFER_ACT_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -322,7 +315,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, TAG_TRANSFER_DIAG_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, streamId, TAG_TRANSFER_DIAG_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -341,10 +334,6 @@ void* DiagNetwork::threadedNetworkRx(void* arg) for (auto peer : network->m_peers) { if (peer.second != nullptr) { if (peer.second->isSysView()) { - uint32_t peerStreamId = peer.second->currStreamId(); - if (streamId == 0U) { - streamId = peerStreamId; - } sockaddr_storage addr = peer.second->socketStorage(); uint32_t addrLen = peer.second->sockStorageLen(); @@ -374,13 +363,13 @@ void* DiagNetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(pktPeerId, TAG_TRANSFER_STATUS, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(pktPeerId, streamId, TAG_TRANSFER_STATUS, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } } else { - network->writePeerNAK(peerId, TAG_TRANSFER, NET_CONN_NAK_ILLEGAL_PACKET); + network->writePeerNAK(peerId, streamId, TAG_TRANSFER, NET_CONN_NAK_ILLEGAL_PACKET); Utils::dump("unknown transfer opcode from the peer", req->buffer, req->length); } } @@ -420,7 +409,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, TAG_PEER_LINK, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, streamId, TAG_PEER_LINK, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 4c16dd79..b9a64c17 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL * */ #include "fne/Defines.h" @@ -403,8 +403,9 @@ void FNENetwork::close() uint8_t buffer[1U]; ::memset(buffer, 0x00U, 1U); + uint32_t streamId = createStreamId(); for (auto peer : m_peers) { - writePeer(peer.first, { NET_FUNC::MST_CLOSING, NET_SUBFUNC::NOP }, buffer, 1U, (uint16_t)0U, 0U); + writePeer(peer.first, { NET_FUNC::MST_CLOSING, NET_SUBFUNC::NOP }, buffer, 1U, RTP_END_OF_CALL_SEQ, streamId, false); } } @@ -459,21 +460,18 @@ void* FNENetwork::threadedNetworkRx(void* arg) // only reset packet sequences if we're a PROTOCOL or RPTC function if ((req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) || (req->fneHeader.getFunction() == NET_FUNC::RPTC)) { - connection->pktLastSeq(pktSeq); - connection->pktNextSeq(0U); + connection->eraseStreamPktSeq(streamId); // attempt to erase packet sequence for the stream } } else { - if ((connection->currStreamId() == streamId) && (pktSeq != connection->pktNextSeq()) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U)) && pktSeq != 0U) { - LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; %u != %u", peerId, connection->identity().c_str(), - streamId, pktSeq, connection->pktNextSeq()); + if (connection->hasStreamPktSeq(streamId)) { + uint16_t currPkt = connection->getStreamPktSeq(streamId); + if ((pktSeq != currPkt) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U)) && pktSeq != 0U) { + LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; %u != %u", peerId, connection->identity().c_str(), + streamId, pktSeq, currPkt); + } } - connection->currStreamId(streamId); - connection->pktLastSeq(pktSeq); - connection->pktNextSeq(pktSeq + 1); - if (connection->pktNextSeq() > (RTP_END_OF_CALL_SEQ - 1U)) { - connection->pktNextSeq(0U); - } + connection->incStreamPktSeq(streamId, pktSeq + 1U); } } @@ -510,7 +508,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) network->m_tagDMR->processFrame(req->buffer, req->length, peerId, req->rtpHeader.getSequence(), streamId); } } else { - network->writePeerNAK(peerId, TAG_DMR_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); + network->writePeerNAK(peerId, streamId, TAG_DMR_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); } } } @@ -533,7 +531,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) network->m_tagP25->processFrame(req->buffer, req->length, peerId, req->rtpHeader.getSequence(), streamId); } } else { - network->writePeerNAK(peerId, TAG_P25_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); + network->writePeerNAK(peerId, streamId, TAG_P25_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); } } } @@ -556,7 +554,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) network->m_tagNXDN->processFrame(req->buffer, req->length, peerId, req->rtpHeader.getSequence(), streamId); } } else { - network->writePeerNAK(peerId, TAG_NXDN_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); + network->writePeerNAK(peerId, streamId, TAG_NXDN_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); } } } @@ -588,9 +586,8 @@ void* FNENetwork::threadedNetworkRx(void* arg) FNEPeerConnection* connection = new FNEPeerConnection(peerId, req->address, req->addrLen); connection->lastPing(now); - connection->currStreamId(streamId); - network->setupRepeaterLogin(peerId, connection); + network->setupRepeaterLogin(peerId, streamId, connection); // check if the peer is in the peer ACL list if (network->m_peerListLookup->getACL()) { @@ -625,10 +622,9 @@ void* FNENetwork::threadedNetworkRx(void* arg) connection = new FNEPeerConnection(peerId, req->address, req->addrLen); connection->lastPing(now); - connection->currStreamId(streamId); network->erasePeerAffiliations(peerId); - network->setupRepeaterLogin(peerId, connection); + network->setupRepeaterLogin(peerId, streamId, connection); // check if the peer is in the peer ACL list if (network->m_peerListLookup->getACL()) { @@ -745,7 +741,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) if (validHash) { connection->connectionState(NET_STAT_WAITING_CONFIG); - network->writePeerACK(peerId); + network->writePeerACK(peerId, streamId); LogInfoEx(LOG_NET, "PEER %u RPTK ACK, completed the login exchange", peerId); network->m_peers[peerId] = connection; } @@ -822,7 +818,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) buffer[0U] = 0x80U; } - network->writePeerACK(peerId, buffer, 1U); + network->writePeerACK(peerId, streamId, buffer, 1U); LogInfoEx(LOG_NET, "PEER %u RPTC ACK, completed the configuration exchange", peerId); json::object peerConfig = connection->config(); @@ -943,7 +939,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) if (dt < now) { LogInfoEx(LOG_NET, "PEER %u (%s) updating ACL list, dt = %u, now = %u", peerId, connection->identity().c_str(), dt, now); - if (connection->pktLastSeq() == RTP_END_OF_CALL_SEQ) { + if (connection->streamCount() <= 1) { network->peerACLUpdate(peerId); } connection->lastACLUpdate(now); @@ -963,7 +959,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) payload[7U] = (uint8_t)((now >> 0) & 0xFFU); network->m_peers[peerId] = connection; - network->writePeerCommand(peerId, { NET_FUNC::PONG, NET_SUBFUNC::NOP }, payload, 8U); + network->writePeerCommand(peerId, { NET_FUNC::PONG, NET_SUBFUNC::NOP }, payload, 8U, streamId, false); if (network->m_reportPeerPing) { LogInfoEx(LOG_NET, "PEER %u (%s) ping, pingsReceived = %u, lastPing = %u, now = %u", peerId, connection->identity().c_str(), @@ -971,7 +967,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, TAG_REPEATER_PING); + network->writePeerNAK(peerId, streamId, TAG_REPEATER_PING); } } } @@ -1001,7 +997,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) if (network->m_tagDMR != nullptr) { network->m_tagDMR->processGrantReq(srcId, dstId, slot, unitToUnit, peerId, req->rtpHeader.getSequence(), streamId); } else { - network->writePeerNAK(peerId, TAG_DMR_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); + network->writePeerNAK(peerId, streamId, TAG_DMR_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); } } break; @@ -1010,7 +1006,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) if (network->m_tagP25 != nullptr) { network->m_tagP25->processGrantReq(srcId, dstId, unitToUnit, peerId, req->rtpHeader.getSequence(), streamId); } else { - network->writePeerNAK(peerId, TAG_P25_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); + network->writePeerNAK(peerId, streamId, TAG_P25_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); } } break; @@ -1019,18 +1015,18 @@ void* FNENetwork::threadedNetworkRx(void* arg) if (network->m_tagNXDN != nullptr) { network->m_tagNXDN->processGrantReq(srcId, dstId, unitToUnit, peerId, req->rtpHeader.getSequence(), streamId); } else { - network->writePeerNAK(peerId, TAG_NXDN_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); + network->writePeerNAK(peerId, streamId, TAG_NXDN_DATA, NET_CONN_NAK_MODE_NOT_ENABLED); } } break; default: - network->writePeerNAK(peerId, TAG_REPEATER_GRANT, NET_CONN_NAK_ILLEGAL_PACKET); + network->writePeerNAK(peerId, streamId, TAG_REPEATER_GRANT, NET_CONN_NAK_ILLEGAL_PACKET); Utils::dump("unknown state for grant request from the peer", req->buffer, req->length); break; } } else { - network->writePeerNAK(peerId, TAG_REPEATER_GRANT, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, streamId, TAG_REPEATER_GRANT, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -1074,7 +1070,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, TAG_TRANSFER_ACT_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, streamId, TAG_TRANSFER_ACT_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -1112,7 +1108,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, TAG_TRANSFER_DIAG_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, streamId, TAG_TRANSFER_DIAG_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -1122,7 +1118,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) // main traffic port status transfers aren't supported for performance reasons } else { - network->writePeerNAK(peerId, TAG_TRANSFER, NET_CONN_NAK_ILLEGAL_PACKET); + network->writePeerNAK(peerId, streamId, TAG_TRANSFER, NET_CONN_NAK_ILLEGAL_PACKET); Utils::dump("unknown transfer opcode from the peer", req->buffer, req->length); } } @@ -1138,7 +1134,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str()); - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } // validate peer (simple validation really) @@ -1161,7 +1157,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -1174,7 +1170,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str()); - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } // validate peer (simple validation really) @@ -1195,7 +1191,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -1208,7 +1204,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str()); - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } // validate peer (simple validation really) @@ -1229,7 +1225,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -1242,7 +1238,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str()); - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } // validate peer (simple validation really) @@ -1263,7 +1259,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -1279,7 +1275,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) lookups::AffiliationLookup* aff = network->m_peerAffiliations[peerId]; if (aff == nullptr) { LogError(LOG_NET, "PEER %u (%s) has uninitialized affiliations lookup?", peerId, connection->identity().c_str()); - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_INVALID); } if (aff != nullptr) { @@ -1311,7 +1307,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -1356,13 +1352,13 @@ void* FNENetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } } else { - network->writePeerNAK(peerId, TAG_ANNOUNCE, NET_CONN_NAK_ILLEGAL_PACKET); + network->writePeerNAK(peerId, streamId, TAG_ANNOUNCE, NET_CONN_NAK_ILLEGAL_PACKET); Utils::dump("unknown announcement opcode from the peer", req->buffer, req->length); } } @@ -1545,7 +1541,7 @@ std::string FNENetwork::resolvePeerIdentity(uint32_t peerId) /* Helper to complete setting up a repeater login request. */ -void FNENetwork::setupRepeaterLogin(uint32_t peerId, FNEPeerConnection* connection) +void FNENetwork::setupRepeaterLogin(uint32_t peerId, uint32_t streamId, FNEPeerConnection* connection) { std::uniform_int_distribution dist(DVM_RAND_MIN, DVM_RAND_MAX); connection->salt(dist(m_random)); @@ -1560,7 +1556,7 @@ void FNENetwork::setupRepeaterLogin(uint32_t peerId, FNEPeerConnection* connecti ::memset(salt, 0x00U, 4U); __SET_UINT32(connection->salt(), salt, 0U); - writePeerACK(peerId, salt, 4U); + writePeerACK(peerId, streamId, salt, 4U); LogInfoEx(LOG_NET, "PEER %u RPTL ACK, challenge response sent for login", peerId); } @@ -1607,26 +1603,24 @@ void* FNENetwork::threadedACLUpdate(void* arg) FNEPeerConnection* connection = network->m_peers[req->peerId]; if (connection != nullptr) { + uint32_t aclStreamId = network->createStreamId(); + // if the connection is an external peer, and peer is participating in peer link, // send the peer proper configuration data if (connection->isExternalPeer() && connection->isPeerLink()) { LogInfoEx(LOG_NET, "PEER %u (%s) sending Peer-Link ACL list updates", req->peerId, peerIdentity.c_str()); - network->writeWhitelistRIDs(req->peerId, true); - network->writeTGIDs(req->peerId, true); - - connection->pktLastSeq(RTP_END_OF_CALL_SEQ - 1U); - network->writePeerList(req->peerId); + network->writeWhitelistRIDs(req->peerId, aclStreamId, true); + network->writeTGIDs(req->peerId, aclStreamId, true); + network->writePeerList(req->peerId, aclStreamId); } else { LogInfoEx(LOG_NET, "PEER %u (%s) sending ACL list updates", req->peerId, peerIdentity.c_str()); - network->writeWhitelistRIDs(req->peerId, false); - network->writeBlacklistRIDs(req->peerId); - network->writeTGIDs(req->peerId, false); - - connection->pktLastSeq(RTP_END_OF_CALL_SEQ - 1U); - network->writeDeactiveTGIDs(req->peerId); + network->writeWhitelistRIDs(req->peerId, aclStreamId, false); + network->writeBlacklistRIDs(req->peerId, aclStreamId); + network->writeTGIDs(req->peerId, aclStreamId, false); + network->writeDeactiveTGIDs(req->peerId, aclStreamId); } } @@ -1638,7 +1632,7 @@ void* FNENetwork::threadedACLUpdate(void* arg) /* Helper to send the list of whitelisted RIDs to the specified peer. */ -void FNENetwork::writeWhitelistRIDs(uint32_t peerId, bool isExternalPeer) +void FNENetwork::writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool isExternalPeer) { uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); @@ -1743,7 +1737,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, bool isExternalPeer) offs += PEER_LINK_BLOCK_SIZE; writePeer(peerId, { NET_FUNC::PEER_LINK, NET_SUBFUNC::PL_RID_LIST }, - payload, bufSize, 0U, false, true, true); + payload, bufSize, 0U, streamId, false, true, true); } connection->lastPing(now); @@ -1813,7 +1807,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, bool isExternalPeer) } writePeerCommand(peerId, { NET_FUNC::MASTER, NET_SUBFUNC::MASTER_SUBFUNC_WL_RID }, - payload, bufSize, true); + payload, bufSize, streamId, true); } connection->lastPing(now); @@ -1822,7 +1816,7 @@ void FNENetwork::writeWhitelistRIDs(uint32_t peerId, bool isExternalPeer) /* Helper to send the list of whitelisted RIDs to the specified peer. */ -void FNENetwork::writeBlacklistRIDs(uint32_t peerId) +void FNENetwork::writeBlacklistRIDs(uint32_t peerId, uint32_t streamId) { uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); @@ -1887,7 +1881,7 @@ void FNENetwork::writeBlacklistRIDs(uint32_t peerId) } writePeerCommand(peerId, { NET_FUNC::MASTER, NET_SUBFUNC::MASTER_SUBFUNC_BL_RID }, - payload, bufSize, true); + payload, bufSize, streamId, true); } connection->lastPing(now); @@ -1896,7 +1890,7 @@ void FNENetwork::writeBlacklistRIDs(uint32_t peerId) /* Helper to send the list of active TGIDs to the specified peer. */ -void FNENetwork::writeTGIDs(uint32_t peerId, bool isExternalPeer) +void FNENetwork::writeTGIDs(uint32_t peerId, uint32_t streamId, bool isExternalPeer) { uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); @@ -2005,7 +1999,7 @@ void FNENetwork::writeTGIDs(uint32_t peerId, bool isExternalPeer) offs += PEER_LINK_BLOCK_SIZE; writePeer(peerId, { NET_FUNC::PEER_LINK, NET_SUBFUNC::PL_TALKGROUP_LIST }, - payload, bufSize, 0U, false, true, true); + payload, bufSize, 0U, streamId, false, true, true); } connection->lastPing(now); @@ -2086,12 +2080,12 @@ void FNENetwork::writeTGIDs(uint32_t peerId, bool isExternalPeer) } writePeerCommand(peerId, { NET_FUNC::MASTER, NET_SUBFUNC::MASTER_SUBFUNC_ACTIVE_TGS }, - payload, 4U + (tgidList.size() * 5U), true); + payload, 4U + (tgidList.size() * 5U), streamId, true); } /* Helper to send the list of deactivated TGIDs to the specified peer. */ -void FNENetwork::writeDeactiveTGIDs(uint32_t peerId) +void FNENetwork::writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId) { if (!m_tidLookup->sendTalkgroups()) { return; @@ -2147,12 +2141,12 @@ void FNENetwork::writeDeactiveTGIDs(uint32_t peerId) } writePeerCommand(peerId, { NET_FUNC::MASTER, NET_SUBFUNC::MASTER_SUBFUNC_DEACTIVE_TGS }, - payload, 4U + (tgidList.size() * 5U), true); + payload, 4U + (tgidList.size() * 5U), streamId, true); } /* Helper to send the list of peers to the specified peer. */ -void FNENetwork::writePeerList(uint32_t peerId) +void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) { if (!m_tidLookup->sendTalkgroups()) { return; @@ -2260,7 +2254,7 @@ void FNENetwork::writePeerList(uint32_t peerId) offs += PEER_LINK_BLOCK_SIZE; writePeer(peerId, { NET_FUNC::PEER_LINK, NET_SUBFUNC::PL_PEER_LIST }, - payload, bufSize, 0U, false, true, true); + payload, bufSize, 0U, streamId, false, true, true); } connection->lastPing(now); @@ -2269,22 +2263,26 @@ void FNENetwork::writePeerList(uint32_t peerId) return; } -/* Helper to send a data message to the specified peer. */ +/* Helper to send a data message to the specified peer with a explicit packet sequence. */ bool FNENetwork::writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data, - uint32_t length, uint16_t pktSeq, uint32_t streamId, bool queueOnly, bool directWrite) const + uint32_t length, uint16_t pktSeq, uint32_t streamId, bool queueOnly, bool incPktSeq, bool directWrite) const { + if (streamId == 0U) { + LogError(LOG_NET, "PEER %u, trying to send data with a streamId of 0? BUGBUG", peerId); + } + auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; }); if (it != m_peers.end()) { FNEPeerConnection* connection = m_peers.at(peerId); if (connection != nullptr) { - uint32_t peerStreamId = connection->currStreamId(); - if (streamId == 0U) { - streamId = peerStreamId; - } sockaddr_storage addr = connection->socketStorage(); uint32_t addrLen = connection->sockStorageLen(); + if (incPktSeq) { + pktSeq = connection->incStreamPktSeq(streamId, pktSeq); + } + if (directWrite) return m_frameQueue->write(data, length, streamId, peerId, m_peerId, opcode, pktSeq, addr, addrLen); else { @@ -2299,31 +2297,10 @@ bool FNENetwork::writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const return false; } -/* Helper to send a data message to the specified peer. */ - -bool FNENetwork::writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data, - uint32_t length, uint32_t streamId, bool queueOnly, bool incPktSeq, bool directWrite) const -{ - auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; }); - if (it != m_peers.end()) { - FNEPeerConnection* connection = m_peers.at(peerId); - if (connection != nullptr) { - if (incPktSeq) { - connection->pktLastSeq(connection->pktLastSeq() + 1); - } - uint16_t pktSeq = connection->pktLastSeq(); - - return writePeer(peerId, opcode, data, length, pktSeq, streamId, queueOnly, directWrite); - } - } - - return false; -} - /* Helper to send a command message to the specified peer. */ bool FNENetwork::writePeerCommand(uint32_t peerId, FrameQueue::OpcodePair opcode, - const uint8_t* data, uint32_t length, bool incPktSeq) const + const uint8_t* data, uint32_t length, uint32_t streamId, bool incPktSeq) const { assert(peerId > 0); @@ -2335,12 +2312,12 @@ bool FNENetwork::writePeerCommand(uint32_t peerId, FrameQueue::OpcodePair opcode } uint32_t len = length + 6U; - return writePeer(peerId, opcode, buffer, len, 0U, false, incPktSeq, true); + return writePeer(peerId, opcode, buffer, len, RTP_END_OF_CALL_SEQ, streamId, false, incPktSeq, true); } /* Helper to send a ACK response to the specified peer. */ -bool FNENetwork::writePeerACK(uint32_t peerId, const uint8_t* data, uint32_t length) +bool FNENetwork::writePeerACK(uint32_t peerId, uint32_t streamId, const uint8_t* data, uint32_t length) { uint8_t buffer[DATA_PACKET_LENGTH]; ::memset(buffer, 0x00U, DATA_PACKET_LENGTH); @@ -2351,7 +2328,8 @@ bool FNENetwork::writePeerACK(uint32_t peerId, const uint8_t* data, uint32_t len ::memcpy(buffer + 6U, data, length); } - return writePeer(peerId, { NET_FUNC::ACK, NET_SUBFUNC::NOP }, buffer, length + 10U, RTP_END_OF_CALL_SEQ, false, true); + return writePeer(peerId, { NET_FUNC::ACK, NET_SUBFUNC::NOP }, buffer, length + 10U, RTP_END_OF_CALL_SEQ, streamId, + false); } /* Helper to log a warning specifying which NAK reason is being sent a peer. */ @@ -2393,7 +2371,7 @@ void FNENetwork::logPeerNAKReason(uint32_t peerId, const char* tag, NET_CONN_NAK /* Helper to send a NAK response to the specified peer. */ -bool FNENetwork::writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REASON reason) +bool FNENetwork::writePeerNAK(uint32_t peerId, uint32_t streamId, const char* tag, NET_CONN_NAK_REASON reason) { assert(peerId > 0); assert(tag != nullptr); @@ -2405,7 +2383,8 @@ bool FNENetwork::writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REA __SET_UINT16B((uint16_t)reason, buffer, 10U); // Reason logPeerNAKReason(peerId, tag, reason); - return writePeer(peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, buffer, 10U, RTP_END_OF_CALL_SEQ, false, true); + return writePeer(peerId, { NET_FUNC::NAK, NET_SUBFUNC::NOP }, buffer, 10U, RTP_END_OF_CALL_SEQ, streamId, + false); } /* Helper to send a NAK response to the specified peer. */ diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 8a2721f5..bd8f8630 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2023-2025 Bryan Biedenkapp, N2PLL * */ /** @@ -103,7 +103,6 @@ namespace network FNEPeerConnection() : m_id(0U), m_ccPeerId(0U), - m_currStreamId(0U), m_socketStorage(), m_sockStorageLen(0U), m_address(), @@ -119,8 +118,8 @@ namespace network m_isSysView(false), m_isPeerLink(false), m_config(), - m_pktLastSeq(RTP_END_OF_CALL_SEQ), - m_pktNextSeq(1U) + m_streamSeqMutex(), + m_streamSeqNos() { /* stub */ } @@ -133,7 +132,6 @@ namespace network FNEPeerConnection(uint32_t id, sockaddr_storage& socketStorage, uint32_t sockStorageLen) : m_id(id), m_ccPeerId(0U), - m_currStreamId(0U), m_socketStorage(socketStorage), m_sockStorageLen(sockStorageLen), m_address(udp::Socket::address(socketStorage)), @@ -149,8 +147,8 @@ namespace network m_isSysView(false), m_isPeerLink(false), m_config(), - m_pktLastSeq(RTP_END_OF_CALL_SEQ), - m_pktNextSeq(1U) + m_streamSeqMutex(), + m_streamSeqNos() { assert(id > 0U); assert(sockStorageLen > 0U); @@ -158,6 +156,118 @@ namespace network assert(m_port > 0U); } + /** + * @brief Helper to return the current count of mapped RTP streams. + * @returns size_t + */ + size_t streamCount() + { + return m_streamSeqNos.size(); + } + + /** + * @brief Helper to determine if the stream ID has a stored RTP sequence. + * @param streamId Stream ID. + * @returns bool + */ + bool hasStreamPktSeq(uint64_t streamId) + { + bool ret = false; + m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60)); + + // determine if the stream has a current sequence no and return + { + auto it = m_streamSeqNos.find(streamId); + if (it == m_streamSeqNos.end()) { + ret = false; + } + else { + ret = true; + } + } + + m_streamSeqMutex.unlock(); + + return ret; + } + + /** + * @brief Helper to get the stored RTP sequence for the given stream ID. + * @param streamId Stream ID. + * @returns uint16_t + */ + uint16_t getStreamPktSeq(uint64_t streamId) + { + m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60)); + + // find the current sequence no and return + uint32_t pktSeq = 0U; + { + auto it = m_streamSeqNos.find(streamId); + if (it == m_streamSeqNos.end()) { + pktSeq = RTP_END_OF_CALL_SEQ; + } else { + pktSeq = m_streamSeqNos[streamId]; + } + } + + m_streamSeqMutex.unlock(); + + return pktSeq; + } + + /** + * @brief Helper to increment the stored RTP sequence for the given stream ID. + * @param streamId Stream ID. + * @param initialSeq Initial sequence number to set. + * @returns uint16_t + */ + uint16_t incStreamPktSeq(uint64_t streamId, uint16_t initialSeq) + { + m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60)); + + // find the current sequence no, increment and return + uint32_t pktSeq = 0U; + { + auto it = m_streamSeqNos.find(streamId); + if (it == m_streamSeqNos.end()) { + m_streamSeqNos.insert({streamId, initialSeq}); + } else { + pktSeq = m_streamSeqNos[streamId]; + + ++pktSeq; + if (pktSeq > RTP_END_OF_CALL_SEQ) { + pktSeq = 0U; + } + + m_streamSeqNos[streamId] = pktSeq; + } + } + + m_streamSeqMutex.unlock(); + + return pktSeq; + } + /** + * @brief Helper to erase the stored RTP sequence for the given stream ID. + * @param streamId Stream ID. + * @returns uint16_t + */ + void eraseStreamPktSeq(uint64_t streamId) + { + m_streamSeqMutex.try_lock_for(std::chrono::milliseconds(60)); + + // find the sequence no and erase + { + auto entry = m_streamSeqNos.find(streamId); + if (entry != m_streamSeqNos.end()) { + m_streamSeqNos.erase(streamId); + } + } + + m_streamSeqMutex.unlock(); + } + public: /** * @brief Peer ID. @@ -173,11 +283,6 @@ namespace network */ __PROPERTY_PLAIN(uint32_t, ccPeerId); - /** - * @brief Current Stream ID. - */ - __PROPERTY_PLAIN(uint32_t, currStreamId); - /** * @brief Unix socket storage containing the connected address. */ @@ -246,14 +351,9 @@ namespace network */ __PROPERTY_PLAIN(json::object, config); - /** - * @brief Last received RTP sequence. - */ - __PROPERTY_PLAIN(uint16_t, pktLastSeq); - /** - * @brief Calculated next RTP sequence. - */ - __PROPERTY_PLAIN(uint16_t, pktNextSeq); + private: + std::timed_mutex m_streamSeqMutex; + std::unordered_map m_streamSeqNos; }; // --------------------------------------------------------------------------- @@ -526,9 +626,10 @@ namespace network /** * @brief Helper to complete setting up a repeater login request. * @param peerId Peer ID. + * @param streamId Stream ID for the login sequence. * @param connection Instance of the FNEPeerConnection class. */ - void setupRepeaterLogin(uint32_t peerId, FNEPeerConnection* connection); + void setupRepeaterLogin(uint32_t peerId, uint32_t streamId, FNEPeerConnection* connection); /** * @brief Helper to send the ACL lists to the specified peer in a separate thread. @@ -545,33 +646,38 @@ namespace network /** * @brief Helper to send the list of whitelisted RIDs to the specified peer. * @param peerId Peer ID. + * @param streamId Stream ID for this message. * @param sendISSI Flag indicating the RID transfer is to an external peer via ISSI. */ - void writeWhitelistRIDs(uint32_t peerId, bool sendISSI); + void writeWhitelistRIDs(uint32_t peerId, uint32_t streamId, bool sendISSI); /** * @brief Helper to send the list of blacklisted RIDs to the specified peer. * @param peerId Peer ID. + * @param streamId Stream ID for this message. */ - void writeBlacklistRIDs(uint32_t peerId); + void writeBlacklistRIDs(uint32_t peerId, uint32_t streamId); /** * @brief Helper to send the list of active TGIDs to the specified peer. * @param peerId Peer ID. + * @param streamId Stream ID for this message. * @param sendISSI Flag indicating the TGID transfer is to an external peer via ISSI. */ - void writeTGIDs(uint32_t peerId, bool sendISSI); + void writeTGIDs(uint32_t peerId, uint32_t streamId, bool sendISSI); /** * @brief Helper to send the list of deactivated TGIDs to the specified peer. * @param peerId Peer ID. + * @param streamId Stream ID for this message. */ - void writeDeactiveTGIDs(uint32_t peerId); + void writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId); /** * @brief Helper to send the list of peers to the specified peer. * @param peerId Peer ID. + * @param streamId Stream ID for this message. */ - void writePeerList(uint32_t peerId); + void writePeerList(uint32_t peerId, uint32_t streamId); /** - * @brief Helper to send a data message to the specified peer. + * @brief Helper to send a data message to the specified peer with a explicit packet sequence. * @param peerId Peer ID. * @param opcode FNE network opcode pair. * @param[in] data Buffer containing message to send to peer. @@ -579,23 +685,11 @@ namespace network * @param pktSeq RTP packet sequence for this message. * @param streamId Stream ID for this message. * @param queueOnly Flag indicating this message should be queued for transmission. - * @param directWrite Flag indicating this message should be immediately directly written. - */ - bool writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, - uint16_t pktSeq, uint32_t streamId, bool queueOnly = false, bool directWrite = false) const; - /** - * @brief Helper to send a data message to the specified peer. - * @param peerId Peer ID. - * @param opcode FNE network opcode pair. - * @param[in] data Buffer containing message to send to peer. - * @param length Length of buffer. - * @param streamId Stream ID for this message. - * @param queueOnly Flag indicating this message should be queued for transmission. * @param incPktSeq Flag indicating the message should increment the packet sequence after transmission. * @param directWrite Flag indicating this message should be immediately directly written. */ bool writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, - uint32_t streamId, bool queueOnly = false, bool incPktSeq = false, bool directWrite = false) const; + uint16_t pktSeq, uint32_t streamId, bool queueOnly, bool incPktSeq = false, bool directWrite = false) const; /** * @brief Helper to send a command message to the specified peer. @@ -603,18 +697,20 @@ namespace network * @param opcode FNE network opcode pair. * @param[in] data Buffer containing message to send to peer. * @param length Length of buffer. + * @param streamId Stream ID for this message. * @param incPktSeq Flag indicating the message should increment the packet sequence after transmission. */ - bool writePeerCommand(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data = nullptr, uint32_t length = 0U, - bool incPktSeq = false) const; + bool writePeerCommand(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint8_t* data, uint32_t length, + uint32_t streamId, bool incPktSeq) const; /** * @brief Helper to send a ACK response to the specified peer. * @param peerId Peer ID. + * @param streamId Stream ID for this message. * @param[in] data Buffer containing response data to send to peer. * @param length Length of buffer. */ - bool writePeerACK(uint32_t peerId, const uint8_t* data = nullptr, uint32_t length = 0U); + bool writePeerACK(uint32_t peerId, uint32_t streamId, const uint8_t* data = nullptr, uint32_t length = 0U); /** * @brief Helper to log a warning specifying which NAK reason is being sent a peer. @@ -626,10 +722,11 @@ namespace network /** * @brief Helper to send a NAK response to the specified peer. * @param peerId Peer ID. + * @param streamId Stream ID for this message. * @param tag Tag. * @param reason NAK reason. */ - bool writePeerNAK(uint32_t peerId, const char* tag, NET_CONN_NAK_REASON reason = NET_CONN_NAK_GENERAL_FAILURE); + bool writePeerNAK(uint32_t peerId, uint32_t streamId, const char* tag, NET_CONN_NAK_REASON reason = NET_CONN_NAK_GENERAL_FAILURE); /** * @brief Helper to send a NAK response to the specified peer. * @param peerId Peer ID. diff --git a/src/fne/network/PeerNetwork.cpp b/src/fne/network/PeerNetwork.cpp index 5054daf5..a991cdc2 100644 --- a/src/fne/network/PeerNetwork.cpp +++ b/src/fne/network/PeerNetwork.cpp @@ -58,6 +58,20 @@ void PeerNetwork::setPeerLookups(lookups::PeerListLookup* pidLookup) m_pidLookup = pidLookup; } +/* Gets the received DMR stream ID. */ + +uint32_t PeerNetwork::getRxDMRStreamId(uint32_t slotNo) const +{ + assert(slotNo == 1U || slotNo == 2U); + + if (slotNo == 1U) { + return m_rxDMRStreamId[0U]; + } + else { + return m_rxDMRStreamId[1U]; + } +} + /* Checks if the passed peer ID is blocked from sending to this peer. */ bool PeerNetwork::checkBlockedPeer(uint32_t peerId) diff --git a/src/fne/network/PeerNetwork.h b/src/fne/network/PeerNetwork.h index 047fedb6..69a64340 100644 --- a/src/fne/network/PeerNetwork.h +++ b/src/fne/network/PeerNetwork.h @@ -63,6 +63,23 @@ namespace network */ void setPeerLookups(lookups::PeerListLookup* pidLookup); + /** + * @brief Gets the received DMR stream ID. + * @param slotNo DMR slot to get stream ID for. + * @return uint32_t Stream ID for the given DMR slot. + */ + uint32_t getRxDMRStreamId(uint32_t slotNo) const; + /** + * @brief Gets the received P25 stream ID. + * @return uint32_t Stream ID. + */ + uint32_t getRxP25StreamId() const { return m_rxP25StreamId; } + /** + * @brief Gets the received NXDN stream ID. + * @return uint32_t Stream ID. + */ + uint32_t getRxNXDNStreamId() const { return m_rxNXDNStreamId; } + /** * @brief Gets the blocked traffic peer ID table. * @returns std::vector List of peer IDs this peer network cannot send traffic to. diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index e926bb10..2fff63f5 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -982,7 +982,7 @@ void TagDMRData::write_CSBK(uint32_t peerId, uint8_t slot, lc::CSBK* csbk) } if (peerId > 0U) { - m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false, true); + m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false); } else { // repeat traffic to the connected peers if (m_network->m_peers.size() > 0U) { diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 996b0d18..368112a3 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -759,5 +759,5 @@ void TagNXDNData::write_Message(uint32_t peerId, lc::RCCH* rcch) } uint32_t streamId = m_network->createStreamId(); - m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false, true); + m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false); } diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 9f33d9d3..eaf9f61c 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -494,12 +494,14 @@ void TagP25Data::playbackParrot() if (message != nullptr) { if (m_network->m_parrotOnlyOriginating) { LogMessage(LOG_NET, "P25, Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", pkt.peerId, srcId, dstId); - m_network->writePeer(pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, 0U, false); + m_network->writePeer(pkt.peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, + RTP_END_OF_CALL_SEQ, m_network->createStreamId(), false); } else { // repeat traffic to the connected peers for (auto peer : m_network->m_peers) { LogMessage(LOG_NET, "P25, Parrot Grant Demand, peer = %u, srcId = %u, dstId = %u", peer.first, srcId, dstId); - m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, 0U, false); + m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, + RTP_END_OF_CALL_SEQ, m_network->createStreamId(), false); } } } @@ -1349,7 +1351,8 @@ void TagP25Data::write_TSDU(uint32_t peerId, lc::TSBK* tsbk) uint32_t streamId = m_network->createStreamId(); if (peerId > 0U) { - m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, false, true); + m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, + RTP_END_OF_CALL_SEQ, streamId, false); } else { // repeat traffic to the connected peers if (m_network->m_peers.size() > 0U) { @@ -1360,7 +1363,8 @@ void TagP25Data::write_TSDU(uint32_t peerId, lc::TSBK* tsbk) m_network->m_frameQueue->flushQueue(); } - m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, RTP_END_OF_CALL_SEQ, streamId, true); + m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, + RTP_END_OF_CALL_SEQ, streamId, true); if (m_network->m_debug) { LogDebug(LOG_NET, "P25, peer = %u, len = %u, streamId = %u", peer.first, messageLength, streamId); diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp index 528eca05..8a351cc0 100644 --- a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ #include "fne/Defines.h" diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index 6a0b860b..23f69e74 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -4,7 +4,7 @@ * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * */ #include "fne/Defines.h" @@ -1045,7 +1045,7 @@ bool P25PacketData::writeNetwork(uint32_t peerId, network::PeerNetwork* peerNet, if (peerNet != nullptr) { return peerNet->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq, streamId); } else { - return m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq, streamId, false, true); + return m_network->writePeer(peerId, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq, streamId, false); } } diff --git a/src/host/HostMain.cpp b/src/host/HostMain.cpp index f5bd93fa..d3945669 100644 --- a/src/host/HostMain.cpp +++ b/src/host/HostMain.cpp @@ -36,6 +36,10 @@ using namespace lookups; // Global Variables // --------------------------------------------------------------------------- +#ifndef SIGHUP +#define SIGHUP 1 +#endif + int g_signal = 0; bool g_calibrate = false; bool g_setup = false; @@ -302,15 +306,15 @@ int main(int argc, char** argv) delete host; } - if (g_signal == 2) + if (g_signal == SIGINT) ::LogInfoEx(LOG_HOST, "[STOP] dvmhost:main SIGINT"); - if (g_signal == 15) + if (g_signal == SIGTERM) ::LogInfoEx(LOG_HOST, "[STOP] dvmhost:main SIGTERM"); - if (g_signal == 1) + if (g_signal == SIGHUP) ::LogInfoEx(LOG_HOST, "[RSTR] dvmhost:main SIGHUP"); - } while (g_signal == 1); + } while (g_signal == SIGHUP); ::LogFinalise(); ::ActivityLogFinalise(); diff --git a/src/host/network/Network.cpp b/src/host/network/Network.cpp index 2a79bbb3..cba5624f 100644 --- a/src/host/network/Network.cpp +++ b/src/host/network/Network.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX - * Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL + * Copyright (C) 2017-2025 Bryan Biedenkapp, N2PLL * */ #include "Defines.h" @@ -263,20 +263,47 @@ void Network::clock(uint32_t ms) if (fneHeader.getSubFunction() == NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR) { // Encapsulated DMR data frame if (m_enabled && m_dmrEnabled) { uint32_t slotNo = (buffer[15U] & 0x80U) == 0x80U ? 2U : 1U; - if (m_rxDMRStreamId[slotNo] == 0U) { + + if (m_debug) { + LogDebug(LOG_NET, "DMR Slot %u, peer = %u, len = %u, pktSeq = %u, streamId = %u", + slotNo, peerId, length, rtpHeader.getSequence(), streamId); + } + + if (m_promiscuousPeer) { m_rxDMRStreamId[slotNo] = streamId; m_pktLastSeq = m_pktSeq; } else { - if (m_rxDMRStreamId[slotNo] == streamId) { - if (m_pktSeq != 0U && m_pktLastSeq != 0U) { - if (m_pktSeq >= 1U && ((m_pktSeq != m_pktLastSeq + 1) && (m_pktSeq - 1 != m_pktLastSeq + 1))) { - LogWarning(LOG_NET, "DMR Stream %u out-of-sequence; %u != %u", streamId, m_pktSeq, m_pktLastSeq + 1); - } + if (m_rxDMRStreamId[slotNo] == 0U) { + if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { + m_rxDMRStreamId[slotNo] = 0U; } - + else { + m_rxDMRStreamId[slotNo] = streamId; + } + m_pktLastSeq = m_pktSeq; } + else { + if (m_rxDMRStreamId[slotNo] == streamId) { + if (m_pktSeq != 0U && m_pktLastSeq != 0U) { + if (m_pktSeq >= 1U && ((m_pktSeq != m_pktLastSeq + 1) && (m_pktSeq - 1 != m_pktLastSeq + 1))) { + LogWarning(LOG_NET, "DMR Stream %u out-of-sequence; %u != %u", streamId, m_pktSeq, m_pktLastSeq + 1); + } + } + + m_pktLastSeq = m_pktSeq; + } + + if (m_rxDMRStreamId[slotNo] != streamId && (rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ)) { + //LogDebug(LOG_NET, "DMR Incorrect Stream; %u != %u", streamId, m_rxDMRStreamId[slotNo]); + break; + } + + if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { + m_rxDMRStreamId[slotNo] = 0U; + } + } } if (m_debug) @@ -291,20 +318,46 @@ void Network::clock(uint32_t ms) } else if (fneHeader.getSubFunction() == NET_SUBFUNC::PROTOCOL_SUBFUNC_P25) { // Encapsulated P25 data frame if (m_enabled && m_p25Enabled) { - if (m_rxP25StreamId == 0U) { + if (m_debug) { + LogDebug(LOG_NET, "P25, peer = %u, len = %u, pktSeq = %u, streamId = %u", + peerId, length, rtpHeader.getSequence(), streamId); + } + + if (m_promiscuousPeer) { m_rxP25StreamId = streamId; m_pktLastSeq = m_pktSeq; } else { - if (m_rxP25StreamId == streamId) { - if (m_pktSeq != 0U && m_pktLastSeq != 0U) { - if (m_pktSeq >= 1U && ((m_pktSeq != m_pktLastSeq + 1) && (m_pktSeq - 1 != m_pktLastSeq + 1))) { - LogWarning(LOG_NET, "P25 Stream %u out-of-sequence; %u != %u", streamId, m_pktSeq, m_pktLastSeq + 1); - } + if (m_rxP25StreamId == 0U) { + if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { + m_rxP25StreamId = 0U; } - + else { + m_rxP25StreamId = streamId; + } + m_pktLastSeq = m_pktSeq; } + else { + if (m_rxP25StreamId == streamId) { + if (m_pktSeq != 0U && m_pktLastSeq != 0U) { + if (m_pktSeq >= 1U && ((m_pktSeq != m_pktLastSeq + 1) && (m_pktSeq - 1 != m_pktLastSeq + 1))) { + LogWarning(LOG_NET, "P25 Stream %u out-of-sequence; %u != %u", streamId, m_pktSeq, m_pktLastSeq + 1); + } + } + + m_pktLastSeq = m_pktSeq; + } + + if (m_rxP25StreamId != streamId && (rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ)) { + //LogDebug(LOG_NET, "P25 Incorrect Stream; %u != %u", streamId, m_rxP25StreamId); + break; + } + + if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { + m_rxP25StreamId = 0U; + } + } } if (m_debug) @@ -319,20 +372,46 @@ void Network::clock(uint32_t ms) } else if (fneHeader.getSubFunction() == NET_SUBFUNC::PROTOCOL_SUBFUNC_NXDN) { // Encapsulated NXDN data frame if (m_enabled && m_nxdnEnabled) { - if (m_rxNXDNStreamId == 0U) { + if (m_debug) { + LogDebug(LOG_NET, "NXDN, peer = %u, len = %u, pktSeq = %u, streamId = %u", + peerId, length, rtpHeader.getSequence(), streamId); + } + + if (m_promiscuousPeer) { m_rxNXDNStreamId = streamId; m_pktLastSeq = m_pktSeq; } else { - if (m_rxNXDNStreamId == streamId) { - if (m_pktSeq != 0U && m_pktLastSeq != 0U) { - if (m_pktSeq >= 1U && ((m_pktSeq != m_pktLastSeq + 1) && (m_pktSeq - 1 != m_pktLastSeq + 1))) { - LogWarning(LOG_NET, "NXDN Stream %u out-of-sequence; %u != %u", streamId, m_pktSeq, m_pktLastSeq + 1); - } + if (m_rxNXDNStreamId == 0U) { + if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { + m_rxNXDNStreamId = 0U; } - + else { + m_rxNXDNStreamId = streamId; + } + m_pktLastSeq = m_pktSeq; } + else { + if (m_rxNXDNStreamId == streamId) { + if (m_pktSeq != 0U && m_pktLastSeq != 0U) { + if (m_pktSeq >= 1U && ((m_pktSeq != m_pktLastSeq + 1) && (m_pktSeq - 1 != m_pktLastSeq + 1))) { + LogWarning(LOG_NET, "NXDN Stream %u out-of-sequence; %u != %u", streamId, m_pktSeq, m_pktLastSeq + 1); + } + } + + m_pktLastSeq = m_pktSeq; + } + + if (m_rxNXDNStreamId != streamId && (rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ)) { + //LogDebug(LOG_NET, "NXDN Incorrect Stream; %u != %u", streamId, m_rxNXDNStreamId); + break; + } + + if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { + m_rxNXDNStreamId = 0U; + } + } } if (m_debug) From 20a096ab493f3b3f55c3f8c07ce421f33b09c286 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 3 Mar 2025 22:14:13 -0500 Subject: [PATCH 21/34] remove stream ID handling from DiagNetwork (these are always sent with a stream ID of 0 anyway...); revert Rx stream ID to stream ID validation check in Network; --- src/fne/network/DiagNetwork.cpp | 43 +++++++-------------------------- src/fne/network/FNENetwork.cpp | 2 +- src/host/network/Network.cpp | 17 +------------ 3 files changed, 11 insertions(+), 51 deletions(-) diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 9ee6c16b..adbc047d 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -162,7 +162,6 @@ void* DiagNetwork::threadedNetworkRx(void* arg) if (req->length > 0) { uint32_t peerId = req->fneHeader.getPeerId(); - uint32_t streamId = req->fneHeader.getStreamId(); std::stringstream peerName; peerName << peerId << ":diag-rx-pckt"; @@ -170,30 +169,6 @@ void* DiagNetwork::threadedNetworkRx(void* arg) ::pthread_setname_np(req->thread, peerName.str().c_str()); #endif // _GNU_SOURCE - // update current peer packet sequence and stream ID - if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end()) && streamId != 0U) { - FNEPeerConnection* connection = network->m_peers[peerId]; - uint16_t pktSeq = req->rtpHeader.getSequence(); - - if (connection != nullptr) { - if (pktSeq == RTP_END_OF_CALL_SEQ) { - connection->eraseStreamPktSeq(streamId); // attempt to erase packet sequence for the stream - } else { - if (connection->hasStreamPktSeq(streamId)) { - uint16_t currPkt = connection->getStreamPktSeq(streamId); - if ((pktSeq != currPkt) && (pktSeq != (RTP_END_OF_CALL_SEQ - 1U)) && pktSeq != 0U) { - LogWarning(LOG_NET, "PEER %u (%s) stream %u out-of-sequence; %u != %u", peerId, connection->identity().c_str(), - streamId, pktSeq, currPkt); - } - } - - connection->incStreamPktSeq(streamId, pktSeq + 1U); - } - } - - network->m_peers[peerId] = connection; - } - // process incoming message frame opcodes switch (req->fneHeader.getFunction()) { case NET_FUNC::TRANSFER: @@ -255,7 +230,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg) sockaddr_storage addr = peer.second->socketStorage(); uint32_t addrLen = peer.second->sockStorageLen(); - network->m_frameQueue->write(req->buffer, req->length, streamId, pktPeerId, network->m_peerId, + network->m_frameQueue->write(req->buffer, req->length, network->createStreamId(), pktPeerId, network->m_peerId, { NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_ACTIVITY }, RTP_END_OF_CALL_SEQ, addr, addrLen); } } else { @@ -270,14 +245,14 @@ void* DiagNetwork::threadedNetworkRx(void* arg) if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isPeerLink()) { peer.second->writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_ACTIVITY }, - req->buffer, req->length, RTP_END_OF_CALL_SEQ, streamId, false, true, pktPeerId); + req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false, true, pktPeerId); } } } } } else { - network->writePeerNAK(pktPeerId, streamId, TAG_TRANSFER_ACT_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(pktPeerId, network->createStreamId(), TAG_TRANSFER_ACT_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -315,7 +290,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, streamId, TAG_TRANSFER_DIAG_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, network->createStreamId(), TAG_TRANSFER_DIAG_LOG, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } @@ -341,7 +316,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg) LogDebug(LOG_NET, "SysView, srcPeer = %u, dstPeer = %u, peer status message, len = %u", pktPeerId, peer.first, req->length); } - network->m_frameQueue->write(req->buffer, req->length, streamId, pktPeerId, network->m_peerId, + network->m_frameQueue->write(req->buffer, req->length, network->createStreamId(), pktPeerId, network->m_peerId, { NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_STATUS }, RTP_END_OF_CALL_SEQ, addr, addrLen); } } else { @@ -355,7 +330,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg) if (peer.second != nullptr) { if (peer.second->isEnabled() && peer.second->isPeerLink()) { peer.second->writeMaster({ NET_FUNC::TRANSFER, NET_SUBFUNC::TRANSFER_SUBFUNC_STATUS }, - req->buffer, req->length, RTP_END_OF_CALL_SEQ, streamId, false, true, pktPeerId); + req->buffer, req->length, RTP_END_OF_CALL_SEQ, 0U, false, true, pktPeerId); } } } @@ -363,13 +338,13 @@ void* DiagNetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(pktPeerId, streamId, TAG_TRANSFER_STATUS, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(pktPeerId, network->createStreamId(), TAG_TRANSFER_STATUS, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } } else { - network->writePeerNAK(peerId, streamId, TAG_TRANSFER, NET_CONN_NAK_ILLEGAL_PACKET); + network->writePeerNAK(peerId, network->createStreamId(), TAG_TRANSFER, NET_CONN_NAK_ILLEGAL_PACKET); Utils::dump("unknown transfer opcode from the peer", req->buffer, req->length); } } @@ -409,7 +384,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg) } } else { - network->writePeerNAK(peerId, streamId, TAG_PEER_LINK, NET_CONN_NAK_FNE_UNAUTHORIZED); + network->writePeerNAK(peerId, 0U, TAG_PEER_LINK, NET_CONN_NAK_FNE_UNAUTHORIZED); } } } diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index b9a64c17..0db5cb47 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -2269,7 +2269,7 @@ bool FNENetwork::writePeer(uint32_t peerId, FrameQueue::OpcodePair opcode, const uint32_t length, uint16_t pktSeq, uint32_t streamId, bool queueOnly, bool incPktSeq, bool directWrite) const { if (streamId == 0U) { - LogError(LOG_NET, "PEER %u, trying to send data with a streamId of 0? BUGBUG", peerId); + LogError(LOG_NET, "BUGBUG: PEER %u, trying to send data with a streamId of 0?", peerId); } auto it = std::find_if(m_peers.begin(), m_peers.end(), [&](PeerMapPair x) { return x.first == peerId; }); diff --git a/src/host/network/Network.cpp b/src/host/network/Network.cpp index cba5624f..d51e3ab2 100644 --- a/src/host/network/Network.cpp +++ b/src/host/network/Network.cpp @@ -295,17 +295,12 @@ void Network::clock(uint32_t ms) m_pktLastSeq = m_pktSeq; } - if (m_rxDMRStreamId[slotNo] != streamId && (rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ)) { - //LogDebug(LOG_NET, "DMR Incorrect Stream; %u != %u", streamId, m_rxDMRStreamId[slotNo]); - break; - } - if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { m_rxDMRStreamId[slotNo] = 0U; } } } - + if (m_debug) Utils::dump(1U, "[Network::clock()] Network Received, DMR", buffer.get(), length); if (length > 255) @@ -349,11 +344,6 @@ void Network::clock(uint32_t ms) m_pktLastSeq = m_pktSeq; } - if (m_rxP25StreamId != streamId && (rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ)) { - //LogDebug(LOG_NET, "P25 Incorrect Stream; %u != %u", streamId, m_rxP25StreamId); - break; - } - if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { m_rxP25StreamId = 0U; } @@ -403,11 +393,6 @@ void Network::clock(uint32_t ms) m_pktLastSeq = m_pktSeq; } - if (m_rxNXDNStreamId != streamId && (rtpHeader.getSequence() != RTP_END_OF_CALL_SEQ)) { - //LogDebug(LOG_NET, "NXDN Incorrect Stream; %u != %u", streamId, m_rxNXDNStreamId); - break; - } - if (rtpHeader.getSequence() == RTP_END_OF_CALL_SEQ) { m_rxNXDNStreamId = 0U; } From d87c1f76c7af24e9cfb2e1feaa28f6a37cca83f4 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 3 Mar 2025 22:54:01 -0500 Subject: [PATCH 22/34] typo; --- src/fne/network/FNENetwork.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 0db5cb47..cbe35d0f 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -479,7 +479,7 @@ void* FNENetwork::threadedNetworkRx(void* arg) } // if we don't have a stream ID and are receiving call data -- throw an error and discard - if (streamId == 0 && req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) { + if (streamId == 0U && req->fneHeader.getFunction() == NET_FUNC::PROTOCOL) { std::string peerIdentity = network->resolvePeerIdentity(peerId); LogError(LOG_NET, "PEER %u (%s) malformed packet (no stream ID for a call?)", peerId, peerIdentity.c_str()); From 6e551a568aaf72396b90df9397f1e0d4a74d52cd Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 3 Mar 2025 23:03:29 -0500 Subject: [PATCH 23/34] fix incorrect usage of slotNo, in this case slotNo refers to the index into the stream ID array, not the actual slot number; --- src/host/network/Network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/host/network/Network.cpp b/src/host/network/Network.cpp index d51e3ab2..9d04f3dd 100644 --- a/src/host/network/Network.cpp +++ b/src/host/network/Network.cpp @@ -262,11 +262,11 @@ void Network::clock(uint32_t ms) { if (fneHeader.getSubFunction() == NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR) { // Encapsulated DMR data frame if (m_enabled && m_dmrEnabled) { - uint32_t slotNo = (buffer[15U] & 0x80U) == 0x80U ? 2U : 1U; + uint32_t slotNo = (buffer[15U] & 0x80U) == 0x80U ? 1U : 0U; // this is the raw index for the stream ID array if (m_debug) { LogDebug(LOG_NET, "DMR Slot %u, peer = %u, len = %u, pktSeq = %u, streamId = %u", - slotNo, peerId, length, rtpHeader.getSequence(), streamId); + slotNo + 1U, peerId, length, rtpHeader.getSequence(), streamId); } if (m_promiscuousPeer) { From beeebe3587656c41acc2cc25cca8ffb7788b454d Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 3 Mar 2025 23:26:13 -0500 Subject: [PATCH 24/34] fix issue with FNE diagnostic logging; --- src/fne/network/DiagNetwork.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index adbc047d..02a56930 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -275,7 +275,7 @@ void* DiagNetwork::threadedNetworkRx(void* arg) bool currState = g_disableTimeDisplay; g_disableTimeDisplay = true; - ::Log(9999U, nullptr, "%.9u (%8s) %s", peerId, connection->identity().c_str(), payload.c_str()); + ::Log(9999U, nullptr, nullptr, 0U, nullptr, "%.9u (%8s) %s", peerId, connection->identity().c_str(), payload.c_str()); g_disableTimeDisplay = currState; // report diagnostic log to InfluxDB From 6fd3c68de3b77d77bc71d3bb3cd1c241f7f1fd1c Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 4 Mar 2025 09:21:42 -0500 Subject: [PATCH 25/34] allow call end to cleanup a stream ID associated to a given call stream; --- src/fne/network/FNENetwork.cpp | 13 +++++++++++++ src/fne/network/FNENetwork.h | 7 +++++++ src/fne/network/callhandler/TagDMRData.cpp | 1 + src/fne/network/callhandler/TagNXDNData.cpp | 1 + src/fne/network/callhandler/TagP25Data.cpp | 1 + 5 files changed, 23 insertions(+) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index cbe35d0f..dc980bc9 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -1392,6 +1392,19 @@ bool FNENetwork::checkU2UDroppedPeer(uint32_t peerId) return false; } +/* Erases a stream ID from the given peer ID connection. */ + +void FNENetwork::eraseStreamPktSeq(uint32_t peerId, uint32_t streamId) +{ + if (peerId > 0 && (m_peers.find(peerId) != m_peers.end())) { + FNEPeerConnection* connection = m_peers[peerId]; + if (connection != nullptr) { + std::lock_guard lock(m_peerMutex); + connection->eraseStreamPktSeq(streamId); + } + } +} + /* Helper to create a peer on the peers affiliations list. */ void FNENetwork::createPeerAffiliations(uint32_t peerId, std::string peerName) diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index bd8f8630..2ea46091 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -597,6 +597,13 @@ namespace network */ bool checkU2UDroppedPeer(uint32_t peerId); + /** + * @brief Erases a stream ID from the given peer ID connection. + * @param peerId Peer ID. + * @param streamId Stream ID. + */ + void eraseStreamPktSeq(uint32_t peerId, uint32_t streamId); + /** * @brief Helper to create a peer on the peers affiliations list. * @param peerId Peer ID. diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 2fff63f5..d9f4197b 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -197,6 +197,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId .request(m_network->m_influxServer); } + m_network->eraseStreamPktSeq(peerId, streamId); m_network->m_callInProgress = false; } } diff --git a/src/fne/network/callhandler/TagNXDNData.cpp b/src/fne/network/callhandler/TagNXDNData.cpp index 368112a3..422d7fd9 100644 --- a/src/fne/network/callhandler/TagNXDNData.cpp +++ b/src/fne/network/callhandler/TagNXDNData.cpp @@ -150,6 +150,7 @@ bool TagNXDNData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerI .request(m_network->m_influxServer); } + m_network->eraseStreamPktSeq(peerId, streamId); m_network->m_callInProgress = false; } } diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index eaf9f61c..d0b08722 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -223,6 +223,7 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId .request(m_network->m_influxServer); } + m_network->eraseStreamPktSeq(peerId, streamId); m_network->m_callInProgress = false; } } From 8823fc09b6521682adf580237bc84d8d58c0cbd5 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Wed, 5 Mar 2025 21:41:18 -0500 Subject: [PATCH 26/34] correct erroneous log messages; fix remote UDP mode handler (this was mishandling frames in some situations); update hotspot and modem submodules; --- src/common/p25/data/DataHeader.cpp | 2 +- src/fw/hotspot | 2 +- src/fw/modem | 2 +- src/host/Host.cpp | 26 +++++++++++++++++++---- src/host/modem/Modem.cpp | 34 +++++++++++++++--------------- 5 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/common/p25/data/DataHeader.cpp b/src/common/p25/data/DataHeader.cpp index 5baa3e09..923ded10 100644 --- a/src/common/p25/data/DataHeader.cpp +++ b/src/common/p25/data/DataHeader.cpp @@ -353,7 +353,7 @@ void DataHeader::encodeExtAddr(uint8_t* data, bool noTrellis) header[1U] = m_exSap & 0x3FU; // Service Access Point header[1U] |= 0xC0U; - header[2U] = m_mfId; // Mfg Id. + //header[2U] = m_mfId; // Mfg Id. header[3U] = (m_srcLlId >> 16) & 0xFFU; // Source Logical Link ID header[4U] = (m_srcLlId >> 8) & 0xFFU; diff --git a/src/fw/hotspot b/src/fw/hotspot index c45ec691..718093ae 160000 --- a/src/fw/hotspot +++ b/src/fw/hotspot @@ -1 +1 @@ -Subproject commit c45ec691a04741443f390354a366798db841900c +Subproject commit 718093aea3a5a6c80f7fceb1bbbc940168b8d98c diff --git a/src/fw/modem b/src/fw/modem index ca0882a6..f8358ed3 160000 --- a/src/fw/modem +++ b/src/fw/modem @@ -1 +1 @@ -Subproject commit ca0882a6de99c0dc05eac5de14a5e096c77232ff +Subproject commit f8358ed3118c28d35617dc2ba9209b2ed20e541b diff --git a/src/host/Host.cpp b/src/host/Host.cpp index 96750a5e..09d418f4 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -1519,6 +1519,10 @@ bool Host::rmtPortModemHandler(Modem* modem, uint32_t ms, modem::RESP_TYPE_DVM r if (modem->getTrace()) Utils::dump(1U, "TX Remote Data", buffer, len); + // never send less then 3 bytes + if (len < 3U) + return false; + // send entire modem packet over the remote port m_modemRemotePort->write(buffer, len); } @@ -1540,10 +1544,24 @@ bool Host::rmtPortModemHandler(Modem* modem, uint32_t ms, modem::RESP_TYPE_DVM r return true; } - uint8_t len = data[1U]; - int ret = modem->write(data, len); - if (ret != int(len)) - LogError(LOG_MODEM, "Error writing remote data"); + uint32_t pktLength = 0U; + switch (data[0U]) { + case DVM_SHORT_FRAME_START: + pktLength = data[1U]; + break; + case DVM_LONG_FRAME_START: + pktLength = ((data[1U] & 0xFFU) << 8) + (data[2U] & 0xFFU); + break; + default: + LogError(LOG_MODEM, "Invalid start of modem frame!"); + break; + } + + if (pktLength > 0U) { + int ret = modem->write(data, pktLength); + if (ret != int(pktLength)) + LogError(LOG_MODEM, "Error writing remote data"); + } } // handled modem response diff --git a/src/host/modem/Modem.cpp b/src/host/modem/Modem.cpp index 4e217546..b3e6d500 100644 --- a/src/host/modem/Modem.cpp +++ b/src/host/modem/Modem.cpp @@ -1381,8 +1381,8 @@ bool Modem::writeDMRFrame1(const uint8_t* data, uint32_t length) if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) return false; if (length > MAX_LENGTH) { - LogError(LOG_MODEM, "Modem::writeDMRData1(); request data to write >%u?, len = %u", MAX_LENGTH, length); - Utils::dump(1U, "[Modem::writeDMRData1()] Attmpted Data", data, length); + LogError(LOG_MODEM, "Modem::writeDMRFrame1(); request data to write >%u?, len = %u", MAX_LENGTH, length); + Utils::dump(1U, "[Modem::writeDMRFrame1()] Attmpted Data", data, length); return false; } @@ -1399,9 +1399,9 @@ bool Modem::writeDMRFrame1(const uint8_t* data, uint32_t length) // write or buffer DMR slot 1 data to air interface if (m_dmrSpace1 >= length) { if (m_debug) - LogDebugEx(LOG_MODEM, "Modem::writeDMRData1()", "immediate write (len %u)", length); + LogDebugEx(LOG_MODEM, "Modem::writeDMRFrame1()", "immediate write (len %u)", length); if (m_trace) - Utils::dump(1U, "[Modem::writeDMRData1()] Immediate TX DMR Data 1", buffer + 3U, length - 1U); + Utils::dump(1U, "[Modem::writeDMRFrame1()] Immediate TX DMR Data 1", buffer + 3U, length - 1U); int ret = write(buffer, len); if (ret != int(len)) { @@ -1435,8 +1435,8 @@ bool Modem::writeDMRFrame2(const uint8_t* data, uint32_t length) if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) return false; if (length > MAX_LENGTH) { - LogError(LOG_MODEM, "Modem::writeDMRData2(); request data to write >%u?, len = %u", MAX_LENGTH, length); - Utils::dump(1U, "Modem::writeDMRData2(); Attmpted Data", data, length); + LogError(LOG_MODEM, "Modem::writeDMRFrame2(); request data to write >%u?, len = %u", MAX_LENGTH, length); + Utils::dump(1U, "Modem::writeDMRFrame2(); Attmpted Data", data, length); return false; } @@ -1453,9 +1453,9 @@ bool Modem::writeDMRFrame2(const uint8_t* data, uint32_t length) // write or buffer DMR slot 2 data to air interface if (m_dmrSpace2 >= length) { if (m_debug) - LogDebugEx(LOG_MODEM, "Modem::writeDMRData2()", "immediate write (len %u)", length); + LogDebugEx(LOG_MODEM, "Modem::writeDMRFrame2()", "immediate write (len %u)", length); if (m_trace) - Utils::dump(1U, "[Modem::writeDMRData2()] Immediate TX DMR Data 2", buffer + 3U, length - 1U); + Utils::dump(1U, "[Modem::writeDMRFrame2()] Immediate TX DMR Data 2", buffer + 3U, length - 1U); int ret = write(buffer, len); if (ret != int(len)) { @@ -1492,8 +1492,8 @@ bool Modem::writeP25Frame(const uint8_t* data, uint32_t length) if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) return false; if (length > MAX_LENGTH) { - LogError(LOG_MODEM, "Modem::writeP25Data(); request data to write >%u?, len = %u", MAX_LENGTH, length); - Utils::dump(1U, "[Modem::writeP25Data()] Attmpted Data", data, length); + LogError(LOG_MODEM, "Modem::writeP25Frame(); request data to write >%u?, len = %u", MAX_LENGTH, length); + Utils::dump(1U, "[Modem::writeP25Frame()] Attmpted Data", data, length); return false; } @@ -1514,14 +1514,14 @@ bool Modem::writeP25Frame(const uint8_t* data, uint32_t length) ::memcpy(buffer + 4U, data + 1U, length - 1U); } - uint8_t len = length + 2U; + uint32_t len = length + 2U; // write or buffer P25 data to air interface if (m_p25Space >= length) { if (m_debug) - LogDebugEx(LOG_MODEM, "Modem::writeP25Data()", "immediate write (len %u)", length); + LogDebugEx(LOG_MODEM, "Modem::writeP25Frame()", "immediate write (len %u)", length); if (m_trace) - Utils::dump(1U, "[Modem::writeP25Data()] Immediate TX P25 Data", buffer + 3U, length - 3U); + Utils::dump(1U, "[Modem::writeP25Frame()] Immediate TX P25 Data", buffer + 3U, length - 3U); int ret = write(buffer, len); if (ret != int(len)) { @@ -1555,8 +1555,8 @@ bool Modem::writeNXDNFrame(const uint8_t* data, uint32_t length) if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) return false; if (length > MAX_LENGTH) { - LogError(LOG_MODEM, "Modem::writeNXDNData(); request data to write >%u?, len = %u", MAX_LENGTH, length); - Utils::dump(1U, "[Modem::writeNXDNData()] Attmpted Data", data, length); + LogError(LOG_MODEM, "Modem::writeNXDNFrame(); request data to write >%u?, len = %u", MAX_LENGTH, length); + Utils::dump(1U, "[Modem::writeNXDNFrame()] Attmpted Data", data, length); return false; } @@ -1573,9 +1573,9 @@ bool Modem::writeNXDNFrame(const uint8_t* data, uint32_t length) // write or buffer NXDN data to air interface if (m_nxdnSpace >= length) { if (m_debug) - LogDebugEx(LOG_MODEM, "Modem::writeNXDNData()", "immediate write (len %u)", length); + LogDebugEx(LOG_MODEM, "Modem::writeNXDNFrame()", "immediate write (len %u)", length); if (m_trace) - Utils::dump(1U, "[Modem::writeNXDNData()] Immediate TX NXDN Data", buffer + 3U, length - 1U); + Utils::dump(1U, "[Modem::writeNXDNFrame()] Immediate TX NXDN Data", buffer + 3U, length - 1U); int ret = write(buffer, len); if (ret != int(len)) { From b9830263db4aceb7cdac14af61d5a7619593d908 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Thu, 6 Mar 2025 15:11:45 -0500 Subject: [PATCH 27/34] fix dstId not being appropriately set; --- src/bridge/HostBridge.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index 39bceda2..0ff8dc8f 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -1246,8 +1246,10 @@ void HostBridge::processUDPAudio() uint32_t udpSrcId = __GET_UINT32(buffer, pcmLength + 8U); // if the UDP source ID now doesn't match the current call ID, reset call states - if (m_resetCallForSourceIdChange && (udpSrcId != m_udpSrcId)) + if (m_resetCallForSourceIdChange && (udpSrcId != m_udpSrcId)) { callEnd(m_udpSrcId, m_dstId); + m_udpDstId = m_dstId; + } m_udpSrcId = udpSrcId; } From 4617a817a1ea66f3e0fc86de81cb0daaf037ef23 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 7 Mar 2025 12:45:07 -0500 Subject: [PATCH 28/34] update modem submodule; --- src/fw/modem | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fw/modem b/src/fw/modem index f8358ed3..0a4c7844 160000 --- a/src/fw/modem +++ b/src/fw/modem @@ -1 +1 @@ -Subproject commit f8358ed3118c28d35617dc2ba9209b2ed20e541b +Subproject commit 0a4c78445fb0c6929a3f351f3f4e693ec0b6d969 From cb335cffed71c7642922da5661424b374bc40a46 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 8 Mar 2025 21:02:15 -0500 Subject: [PATCH 29/34] add some exception checking around some cases where vector types are used; --- src/common/lookups/AffiliationLookup.cpp | 4 +- src/common/lookups/ChannelLookup.cpp | 14 ++- src/common/lookups/ChannelLookup.h | 3 +- src/common/network/udp/Socket.cpp | 113 ++++++++++++----------- 4 files changed, 74 insertions(+), 60 deletions(-) diff --git a/src/common/lookups/AffiliationLookup.cpp b/src/common/lookups/AffiliationLookup.cpp index e4dde41c..e8b784df 100644 --- a/src/common/lookups/AffiliationLookup.cpp +++ b/src/common/lookups/AffiliationLookup.cpp @@ -305,7 +305,9 @@ bool AffiliationLookup::grantCh(uint32_t dstId, uint32_t srcId, uint32_t grantTi std::lock_guard lock(m_mutex); uint32_t chNo = m_chLookup->getFirstRFChannel(); - m_chLookup->removeRFCh(chNo); + if (!m_chLookup->removeRFCh(chNo)) { + return false; + } m_grantChTable[dstId] = chNo; m_grantSrcIdTable[dstId] = srcId; diff --git a/src/common/lookups/ChannelLookup.cpp b/src/common/lookups/ChannelLookup.cpp index b8a8b311..f803702a 100644 --- a/src/common/lookups/ChannelLookup.cpp +++ b/src/common/lookups/ChannelLookup.cpp @@ -71,12 +71,18 @@ bool ChannelLookup::addRFCh(uint32_t chNo, bool force) /* Helper to remove a RF channel. */ -void ChannelLookup::removeRFCh(uint32_t chNo) +bool ChannelLookup::removeRFCh(uint32_t chNo) { if (chNo == 0U) { - return; + return false; } - auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo); - m_rfChTable.erase(it); + try { + auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo); + m_rfChTable.erase(it); + } catch (...) { + return false; + } + + return true; } diff --git a/src/common/lookups/ChannelLookup.h b/src/common/lookups/ChannelLookup.h index afe3dcb7..7a979352 100644 --- a/src/common/lookups/ChannelLookup.h +++ b/src/common/lookups/ChannelLookup.h @@ -206,8 +206,9 @@ namespace lookups /** * @brief Helper to remove a RF channel. * @param chNo Channel Number. + * @returns bool True, if channel remove, otherwise false. */ - void removeRFCh(uint32_t chNo); + bool removeRFCh(uint32_t chNo); /** * @brief Helper to determine if there are any RF channels available.. * @returns bool True, if any RF channels are available for use, otherwise false. diff --git a/src/common/network/udp/Socket.cpp b/src/common/network/udp/Socket.cpp index d0971a2d..58b2a603 100644 --- a/src/common/network/udp/Socket.cpp +++ b/src/common/network/udp/Socket.cpp @@ -467,64 +467,69 @@ bool Socket::write(BufferVector& buffers, ssize_t* lenWritten) noexcept continue; } - // are we crypto wrapped? - if (m_isCryptoWrapped && m_presharedKey != nullptr) { - uint32_t cryptedLen = length * sizeof(uint8_t); - uint8_t* cryptoBuffer = buffers[i]->buffer; - - // do we need to pad the original buffer to be block aligned? - if (cryptedLen % crypto::AES::BLOCK_BYTES_LEN != 0) { - uint32_t alignment = crypto::AES::BLOCK_BYTES_LEN - (cryptedLen % crypto::AES::BLOCK_BYTES_LEN); - cryptedLen += alignment; - - // reallocate buffer and copy - cryptoBuffer = new uint8_t[cryptedLen]; - ::memset(cryptoBuffer, 0x00U, cryptedLen); - ::memcpy(cryptoBuffer, buffers.at(i)->buffer, length); - } - - // encrypt - uint8_t* crypted = m_aes->encryptECB(cryptoBuffer, cryptedLen, m_presharedKey); - delete[] cryptoBuffer; - - if (crypted == nullptr) { - --size; - continue; + try { + // are we crypto wrapped? + if (m_isCryptoWrapped && m_presharedKey != nullptr) { + uint32_t cryptedLen = length * sizeof(uint8_t); + uint8_t* cryptoBuffer = buffers[i]->buffer; + + // do we need to pad the original buffer to be block aligned? + if (cryptedLen % crypto::AES::BLOCK_BYTES_LEN != 0) { + uint32_t alignment = crypto::AES::BLOCK_BYTES_LEN - (cryptedLen % crypto::AES::BLOCK_BYTES_LEN); + cryptedLen += alignment; + + // reallocate buffer and copy + cryptoBuffer = new uint8_t[cryptedLen]; + ::memset(cryptoBuffer, 0x00U, cryptedLen); + ::memcpy(cryptoBuffer, buffers.at(i)->buffer, length); + } + + // encrypt + uint8_t* crypted = m_aes->encryptECB(cryptoBuffer, cryptedLen, m_presharedKey); + delete[] cryptoBuffer; + + if (crypted == nullptr) { + --size; + continue; + } + + // Utils::dump(1U, "Socket::write() crypted", crypted, cryptedLen); + + // finalize + UInt8Array __outBuf = std::make_unique(cryptedLen + 2U); + uint8_t* out = __outBuf.get(); + ::memcpy(out + 2U, crypted, cryptedLen); + __SET_UINT16B(AES_WRAPPED_PCKT_MAGIC, out, 0U); + + // cleanup buffers and replace with new + delete[] crypted; + //delete buffers[i]->buffer; + + // this should never happen... + if (buffers[i] == nullptr) { + --size; + continue; + } + + buffers[i]->buffer = new uint8_t[cryptedLen + 2U]; + ::memcpy(buffers[i]->buffer, out, cryptedLen + 2U); + buffers[i]->length = cryptedLen + 2U; } - // Utils::dump(1U, "Socket::write() crypted", crypted, cryptedLen); - - // finalize - UInt8Array __outBuf = std::make_unique(cryptedLen + 2U); - uint8_t* out = __outBuf.get(); - ::memcpy(out + 2U, crypted, cryptedLen); - __SET_UINT16B(AES_WRAPPED_PCKT_MAGIC, out, 0U); - - // cleanup buffers and replace with new - delete[] crypted; - //delete buffers[i]->buffer; - - // this should never happen... - if (buffers[i] == nullptr) { - --size; - continue; - } + chunks[i].iov_len = buffers.at(i)->length; + chunks[i].iov_base = buffers.at(i)->buffer; + sent += buffers.at(i)->length; - buffers[i]->buffer = new uint8_t[cryptedLen + 2U]; - ::memcpy(buffers[i]->buffer, out, cryptedLen + 2U); - buffers[i]->length = cryptedLen + 2U; + headers[i].msg_hdr.msg_name = (void*)&buffers.at(i)->address; + headers[i].msg_hdr.msg_namelen = buffers.at(i)->addrLen; + headers[i].msg_hdr.msg_iov = &chunks[i]; + headers[i].msg_hdr.msg_iovlen = 1; + headers[i].msg_hdr.msg_control = 0; + headers[i].msg_hdr.msg_controllen = 0; + } + catch (...) { + --size; } - - chunks[i].iov_len = buffers.at(i)->length; - chunks[i].iov_base = buffers.at(i)->buffer; - sent += buffers.at(i)->length; - - headers[i].msg_hdr.msg_name = (void*)&buffers.at(i)->address; - headers[i].msg_hdr.msg_namelen = buffers.at(i)->addrLen; - headers[i].msg_hdr.msg_iov = &chunks[i]; - headers[i].msg_hdr.msg_iovlen = 1; - headers[i].msg_hdr.msg_control = 0; - headers[i].msg_hdr.msg_controllen = 0; } bool skip = false; From b1f10a87ab56c733c27c00d7b772ad18270d69f9 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 9 Mar 2025 11:28:03 -0400 Subject: [PATCH 30/34] add some helper routines to the peer list lookup; --- src/common/lookups/PeerListLookup.cpp | 14 ++++++++++++++ src/common/lookups/PeerListLookup.h | 19 +++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/common/lookups/PeerListLookup.cpp b/src/common/lookups/PeerListLookup.cpp index 5552a928..f4112499 100644 --- a/src/common/lookups/PeerListLookup.cpp +++ b/src/common/lookups/PeerListLookup.cpp @@ -152,6 +152,20 @@ PeerListLookup::Mode PeerListLookup::getMode() const return m_mode; } +/* Gets the entire peer ID table. */ + +std::vector PeerListLookup::tableAsList() const +{ + std::vector ret = std::vector(); + + std::lock_guard lock(m_mutex); + for (auto entry : m_table) { + ret.push_back(entry.second); + } + + return ret; +} + // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- diff --git a/src/common/lookups/PeerListLookup.h b/src/common/lookups/PeerListLookup.h index b1b318ba..494ec13f 100644 --- a/src/common/lookups/PeerListLookup.h +++ b/src/common/lookups/PeerListLookup.h @@ -110,19 +110,19 @@ namespace lookups /** * @brief Peer ID. */ - __READONLY_PROPERTY_PLAIN(uint32_t, peerId); + __PROPERTY_PLAIN(uint32_t, peerId); /** * @breif Peer Alias */ - __READONLY_PROPERTY_PLAIN(std::string, peerAlias); + __PROPERTY_PLAIN(std::string, peerAlias); /** * @brief Per Peer Password. */ - __READONLY_PROPERTY_PLAIN(std::string, peerPassword); + __PROPERTY_PLAIN(std::string, peerPassword); /** * @brief Flag indicating if the peer participates in peer link and should be sent configuration. */ - __READONLY_PROPERTY_PLAIN(bool, peerLink); + __PROPERTY_PLAIN(bool, peerLink); /** * @brief Flag indicating if the peer is default. */ @@ -221,6 +221,17 @@ namespace lookups */ Mode getMode() const; + /** + * @brief Gets the entire peer ID table. + * @returns std::unordered_map + */ + std::unordered_map table() const { return m_table; } + /** + * @brief Gets the entire peer ID table. + * @returns std::vector + */ + std::vector tableAsList() const; + protected: bool m_acl; From 69f8fbe884bc94f5ab255b81fedfba55bf0a74d6 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sun, 9 Mar 2025 15:45:35 -0400 Subject: [PATCH 31/34] add better logic to handle forcing peer-link updates; ensure ACL's go out regardless of stream if too much time has passed; --- src/fne/network/FNENetwork.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index dc980bc9..d989c9cc 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -339,6 +339,24 @@ void FNENetwork::clock(uint32_t ms) } } + // send ACL updates forcibly to any Peer-Link peers + for (auto peer : m_peers) { + uint32_t id = peer.first; + FNEPeerConnection* connection = peer.second; + if (connection != nullptr) { + if (connection->connected() && connection->isPeerLink()) { + // does this peer need an ACL update? + uint64_t dt = connection->lastACLUpdate() + (m_updateLookupTime * 1000); + if (dt < now) { + LogInfoEx(LOG_NET, "PEER %u (%s) updating ACL list, dt = %u, now = %u", id, connection->identity().c_str(), + dt, now); + peerACLUpdate(id); + connection->lastACLUpdate(now); + } + } + } + } + m_maintainenceTimer.start(); } @@ -939,10 +957,11 @@ void* FNENetwork::threadedNetworkRx(void* arg) if (dt < now) { LogInfoEx(LOG_NET, "PEER %u (%s) updating ACL list, dt = %u, now = %u", peerId, connection->identity().c_str(), dt, now); - if (connection->streamCount() <= 1) { + dt = connection->lastACLUpdate() + ((network->m_updateLookupTime * 1000) * 2); + if (connection->streamCount() <= 1 || (dt < now)) { network->peerACLUpdate(peerId); + connection->lastACLUpdate(now); } - connection->lastACLUpdate(now); } uint8_t payload[8U]; @@ -2161,10 +2180,6 @@ void FNENetwork::writeDeactiveTGIDs(uint32_t peerId, uint32_t streamId) void FNENetwork::writePeerList(uint32_t peerId, uint32_t streamId) { - if (!m_tidLookup->sendTalkgroups()) { - return; - } - uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); // sending PEER_LINK style RID list to external peers From 78f034511f516db5fa6dc2ed9b264656583b3f3b Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 10 Mar 2025 00:40:16 -0400 Subject: [PATCH 32/34] initial support for loading KFDtool EKC files (this is currently unused but is a future stepping-stone for encryption key handling); --- configs/fne-config.example.yml | 13 + src/fne/CMakeLists.txt | 1 + src/fne/CryptoContainer.cpp | 571 +++++++ src/fne/CryptoContainer.h | 246 +++ src/fne/HostFNE.cpp | 17 + src/fne/HostFNE.h | 3 + src/fne/xml/rapidxml.h | 2619 ++++++++++++++++++++++++++++++++ src/fne/xml/rapidxml_print.h | 467 ++++++ src/fne/xml/rapidxml_utils.h | 141 ++ 9 files changed, 4078 insertions(+) create mode 100644 src/fne/CryptoContainer.cpp create mode 100644 src/fne/CryptoContainer.h create mode 100644 src/fne/xml/rapidxml.h create mode 100644 src/fne/xml/rapidxml_print.h create mode 100644 src/fne/xml/rapidxml_utils.h diff --git a/configs/fne-config.example.yml b/configs/fne-config.example.yml index 8fdbdb2b..d2d93531 100644 --- a/configs/fne-config.example.yml +++ b/configs/fne-config.example.yml @@ -117,6 +117,19 @@ master: # Flag indicating whether TSBK/CSBK/RCCH messages will be logged to InfluxDB. influxLogRawData: false + # + # Crypto Container Configuration + # + crypto_container: + # Flag indicating whether or not crypto services are enabled. + enabled: false + # Full path to the talkgroup rules file. + file: key_container.ekc + # Container password. + password: "PASSWORD" + # Amount of time between updates of crypto container file. (minutes) + time: 30 + # # Talkgroup Rules Configuration # diff --git a/src/fne/CMakeLists.txt b/src/fne/CMakeLists.txt index ab778b31..c7fdd654 100644 --- a/src/fne/CMakeLists.txt +++ b/src/fne/CMakeLists.txt @@ -18,6 +18,7 @@ file(GLOB dvmfne_SRC "src/fne/network/influxdb/*.h" "src/fne/network/*.h" "src/fne/network/*.cpp" + "src/fne/xml/*.h" "src/fne/*.h" "src/fne/*.cpp" ) diff --git a/src/fne/CryptoContainer.cpp b/src/fne/CryptoContainer.cpp new file mode 100644 index 00000000..2dbdffd8 --- /dev/null +++ b/src/fne/CryptoContainer.cpp @@ -0,0 +1,571 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Converged FNE Software + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "common/AESCrypto.h" +#include "common/Log.h" +#include "common/Timer.h" +#include "common/Utils.h" +#include "common/zlib/zlib.h" +#include "xml/rapidxml.h" +#include "CryptoContainer.h" + +#include +#include +#include +#include +#include + +#if defined(ENABLE_TCP_SSL) +#include +#include +#endif + +using namespace crypto; + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +#define CHUNK 16384 + +// --------------------------------------------------------------------------- +// Global Functions +// --------------------------------------------------------------------------- + +/** + * @brief Calculates the length of a decoded base64 string. + * @param b64input String containing the base64 encoded data. + * @returns int Length of buffer to contain base64 encoded data. + */ +int calcDecodeLength(const char* b64input) +{ + int len = strlen(b64input); + int padding = 0; + + // last two chars are = + if (b64input[len-1] == '=' && b64input[len-2] == '=') + padding = 2; + else if (b64input[len-1] == '=') // last char is = + padding = 1; + + return (int)len * 0.75 - padding; +} + +/** + * @brief Decodes a base64 encoded string. + * @param b64message String containing the base64 encoded data. + * @param buffer Buffer pointer to place encoded data. + * @returns int + */ +int base64Decode(char* b64message, uint8_t** buffer) +{ + int decodeLen = calcDecodeLength(b64message), len = 0; + + *buffer = (uint8_t*)malloc(decodeLen + 1); + FILE* stream = ::fmemopen(b64message, ::strlen(b64message), "r"); + + BIO* b64 = BIO_new(BIO_f_base64()); + BIO* bio = BIO_new_fp(stream, BIO_NOCLOSE); + bio = BIO_push(b64, bio); + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // do not use newlines to flush buffer + len = BIO_read(bio, *buffer, ::strlen(b64message)); + + // can test here if len == decodeLen - if not, then return an error + (*buffer)[len] = '\0'; + + BIO_free_all(bio); + ::fclose(stream); + + return decodeLen; +} + +/** + * @brief + * @param buffer + * @param len + * @param target + * @return int + */ +int findFirstChar(const uint8_t* buffer, uint32_t len, char target) +{ + for (uint32_t i = 0U; i < len; i++) { + if (buffer[i] == target) { + return (int)i; + } + } + + return -1; +} + +/** + * @brief + * @param buffer + * @param len + * @param target + * @return int + */ +int findLastChar(const uint8_t* buffer, uint32_t len, char target) +{ + if (buffer == nullptr) { + return -1; + } + + int lastIndex = -1; + for (uint32_t i = 0U; i < len; i++) { + if (buffer[i] == target) { + lastIndex = i; + } + } + return lastIndex; +} + +// --------------------------------------------------------------------------- +// Static Class Members +// --------------------------------------------------------------------------- + +std::mutex CryptoContainer::m_mutex; + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the CryptoContainer class. */ + +CryptoContainer::CryptoContainer(const std::string& filename, const std::string& password, uint32_t reloadTime, bool enabled) : Thread(), + m_file(filename), + m_password(password), + m_reloadTime(reloadTime), +#if !defined(ENABLE_TCP_SSL) + m_enabled(false), +#else + m_enabled(enabled), +#endif // !ENABLE_TCP_SSL + m_stop(false), + m_keys() +{ + /* stub */ +} + +/* Finalizes a instance of the CryptoContainer class. */ + +CryptoContainer::~CryptoContainer() = default; + +/* Thread entry point. This function is provided to run the thread for the lookup table. */ + +void CryptoContainer::entry() +{ + if (m_reloadTime == 0U) { + return; + } + + Timer timer(1U, 60U * m_reloadTime); + timer.start(); + + while (!m_stop) { + sleep(1000U); + + timer.clock(); + if (timer.hasExpired()) { + load(); + timer.start(); + } + } +} + +/* Stops and unloads this lookup table. */ + +void CryptoContainer::stop(bool noDestroy) +{ + if (!m_enabled) + return; + + if (m_reloadTime == 0U) { + if (!noDestroy) + delete this; + return; + } + + m_stop = true; + + wait(); +} + +/* Reads the lookup table from the specified lookup table file. */ + +bool CryptoContainer::read() +{ + if (!m_enabled) + return false; + + bool ret = load(); + + if (m_reloadTime > 0U) + run(); + setName("fne:crypto-lookup-tbl"); + + return ret; +} + +/* Clears all entries from the lookup table. */ + +void CryptoContainer::clear() +{ + std::lock_guard lock(m_mutex); + m_keys.clear(); +} + +/* Adds a new entry to the lookup table by the specified unique ID. */ + +void CryptoContainer::addEntry(KeyItem key) +{ + if (key.isInvalid()) + return; + + KeyItem entry = key; + uint32_t id = entry.id(); + uint32_t kId = entry.kId(); + + std::lock_guard lock(m_mutex); + auto it = std::find_if(m_keys.begin(), m_keys.end(), + [&](KeyItem x) + { + return x.id() == id && x.kId() == kId; + }); + if (it != m_keys.end()) { + m_keys[it - m_keys.begin()] = entry; + } + else { + m_keys.push_back(entry); + } +} + +/* Erases an existing entry from the lookup table by the specified unique ID. */ + +void CryptoContainer::eraseEntry(uint32_t id) +{ + std::lock_guard lock(m_mutex); + auto it = std::find_if(m_keys.begin(), m_keys.end(), [&](KeyItem x) { return x.id() == id; }); + if (it != m_keys.end()) { + m_keys.erase(it); + } +} + +/* Finds a table entry in this lookup table. */ + +KeyItem CryptoContainer::find(uint32_t kId) +{ + KeyItem entry; + + std::lock_guard lock(m_mutex); + auto it = std::find_if(m_keys.begin(), m_keys.end(), + [&](KeyItem x) + { + return x.kId() == kId; + }); + if (it != m_keys.end()) { + entry = *it; + } else { + entry = KeyItem(); + } + + return entry; +} + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +/* Loads the table from the passed lookup table file. */ + +bool CryptoContainer::load() +{ +#if !defined(ENABLE_TCP_SSL) + return false; +#else + if (!m_enabled) { + return false; + } + if (m_file.length() <= 0) { + return false; + } + if (m_password.length() <= 0) { + return false; + } + + FILE* ekcFile = ::fopen(m_file.c_str(), "rb"); + if (ekcFile == nullptr) { + LogError(LOG_HOST, "Cannot open the crypto container file - %s", m_file.c_str()); + return false; + } + + // inflate file + // compression structures + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + // set input data + strm.avail_in = 0U; + strm.next_in = Z_NULL; + + // initialize decompression + int ret = inflateInit2(&strm, 16 + MAX_WBITS); + if (ret != Z_OK) { + LogError(LOG_HOST, "Error initializing ZLIB, ret = %d", ret); + ::fclose(ekcFile); + return false; + } + + // skip 4 bytes (C# adds a header on the GZIP stream for the decompressed length) + ::fseek(ekcFile, 4, SEEK_SET); + + // decompress data + std::vector decompressedData; + uint8_t inBuffer[CHUNK]; + uint8_t outBuffer[CHUNK]; + do { + strm.avail_in = fread(inBuffer, 1, CHUNK, ekcFile); + if (::ferror(ekcFile)) { + inflateEnd(&strm); + ::fclose(ekcFile); + return false; + } + + if (strm.avail_in == 0) + break; + strm.next_in = inBuffer; + + uint32_t have = 0U; + do { + strm.avail_out = CHUNK; + strm.next_out = outBuffer; + + ret = inflate(&strm, Z_NO_FLUSH); + if (ret == Z_DATA_ERROR) { + // deflate stream invalid + LogError(LOG_HOST, "Error decompressing EKC: %s", (strm.msg == NULL) ? "compressed data error" : strm.msg); + inflateEnd(&strm); + ::fclose(ekcFile); + return false; + } + + if (ret == Z_STREAM_ERROR || ret < 0) { + LogError(LOG_HOST, "Error decompressing EKC, ret = %d", ret); + inflateEnd(&strm); + ::fclose(ekcFile); + return false; + } + + have = CHUNK - strm.avail_out; + decompressedData.insert(decompressedData.end(), outBuffer, outBuffer + have); + } while (strm.avail_out == 0); + } while (ret != Z_STREAM_END); + + // cleanup + inflateEnd(&strm); + ::fclose(ekcFile); + + try { + // ensure zero termination + decompressedData.push_back(0U); + + uint8_t* decompressed = decompressedData.data(); + + // parse outer container DOM + enum { PARSE_FLAGS = rapidxml::parse_full }; + rapidxml::xml_document<> ekcOuterContainer; + ekcOuterContainer.parse(reinterpret_cast(decompressed)); + + rapidxml::xml_node<>* outerRoot = ekcOuterContainer.first_node("OuterContainer"); + if (outerRoot != nullptr) { + // get EKC version + std::string version = ""; + rapidxml::xml_attribute<>* versionAttr = outerRoot->first_attribute("version"); + if (versionAttr != nullptr) + version = std::string(versionAttr->value()); + + // validate EKC version is set and is 1.0 + if (version == "") { + ::LogError(LOG_HOST, "Error opening EKC: incorrect version, expected 1.0 got none"); + return false; + } + + if (version != "1.0") { + ::LogError(LOG_HOST, "Error opening EKC: incorrect version, expected 1.0 got %s", version.c_str()); + return false; + } + + // get key derivation node + rapidxml::xml_node<>* keyDerivation = outerRoot->first_node("KeyDerivation"); + if (keyDerivation == nullptr) { + ::LogError(LOG_HOST, "Error opening EKC: failed to process XML"); + return false; + } + // retreive and parse salt + uint8_t* salt = nullptr; + rapidxml::xml_node<>* saltNode = keyDerivation->first_node("Salt"); + if (saltNode == nullptr) { + ::LogError(LOG_HOST, "Error opening EKC: failed to process XML"); + return false; + } + int8_t saltBufLen = base64Decode(saltNode->value(), &salt); + + // retrieve interation count + int32_t iterationCount = 0; + rapidxml::xml_node<>* iterNode = keyDerivation->first_node("IterationCount"); + if (iterNode == nullptr) { + ::LogError(LOG_HOST, "Error opening EKC: failed to process XML"); + return false; + } + iterationCount = ::strtoul(iterNode->value(), NULL, 10); + + // retrieve key length + int32_t keyLength = 0; + rapidxml::xml_node<>* keyLenNode = keyDerivation->first_node("KeyLength"); + if (keyLenNode == nullptr) { + ::LogError(LOG_HOST, "Error opening EKC: failed to process XML"); + return false; + } + keyLength = ::strtoul(keyLenNode->value(), NULL, 10); + + // generate crypto key to decrypt inner container + uint8_t key[EVP_MAX_KEY_LENGTH]; + ::memset(key, 0x00U, EVP_MAX_KEY_LENGTH); + uint8_t iv[EVP_MAX_IV_LENGTH]; + ::memset(iv, 0x00U, EVP_MAX_IV_LENGTH); + + uint8_t keyIv[EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH]; + ::memset(keyIv, 0x00U, EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH); + if (PKCS5_PBKDF2_HMAC(m_password.c_str(), m_password.size(), salt, saltBufLen, iterationCount, EVP_sha512(), keyLength + EVP_MAX_IV_LENGTH, keyIv)) { + ::memcpy(key, keyIv, keyLength); + ::memcpy(iv, keyIv + keyLength, EVP_MAX_IV_LENGTH); + } + + // get inner container encrypted data + // bryanb: annoying levels of XML encapsulation... + rapidxml::xml_node<>* encryptedDataNode = outerRoot->first_node("EncryptedData"); + if (encryptedDataNode == nullptr) { + ::LogError(LOG_HOST, "Error opening EKC: failed to process XML"); + return false; + } + rapidxml::xml_node<>* cipherDataNode = encryptedDataNode->first_node("CipherData"); + if (cipherDataNode == nullptr) { + ::LogError(LOG_HOST, "Error opening EKC: failed to process XML"); + return false; + } + rapidxml::xml_node<>* cipherValue = cipherDataNode->first_node("CipherValue"); + if (cipherValue == nullptr) { + ::LogError(LOG_HOST, "Error opening EKC: failed to process XML"); + return false; + } + + uint8_t* innerContainerCrypted = nullptr; + int innerContainerLen = base64Decode(cipherValue->value(), &innerContainerCrypted); + + // decrypt inner container + AES aes = AES(AESKeyLength::AES_256); + uint8_t* innerContainer = aes.decryptCBC(innerContainerCrypted, innerContainerLen, key, iv); + + /* + ** bryanb: this is probably slightly error prone... + */ + + int xmlFirstTagChar = findFirstChar(innerContainer, innerContainerLen, '<'); + int xmlLastTagChar = findLastChar(innerContainer, innerContainerLen, '>'); + // zero all bytes after the last > character + ::memset(innerContainer + xmlLastTagChar + 1U, 0x00U, innerContainerLen - xmlLastTagChar); + + rapidxml::xml_document<> ekcInnerContainer; + ekcInnerContainer.parse(reinterpret_cast(innerContainer + xmlFirstTagChar)); + + rapidxml::xml_node<>* innerRoot = ekcInnerContainer.first_node("InnerContainer"); + if (innerRoot != nullptr) { + // clear table + clear(); + + std::lock_guard lock(m_mutex); + + // get keys node + rapidxml::xml_node<>* keys = innerRoot->first_node("Keys"); + if (keys != nullptr) { + uint32_t i = 0U; + for (rapidxml::xml_node<>* keyNode = keys->first_node("KeyItem"); keyNode; keyNode = keyNode->next_sibling()) { + KeyItem key = KeyItem(); + key.id(i); + + // get name + rapidxml::xml_node<>* nameNode = keyNode->first_node("Name"); + if (nameNode == nullptr) { + continue; + } + key.name(nameNode->value()); + + // get keyset ID + rapidxml::xml_node<>* keysetIdNode = keyNode->first_node("KeysetId"); + if (keysetIdNode == nullptr) { + continue; + } + key.keysetId(::strtoul(keysetIdNode->value(), NULL, 10)); + + // get SLN + rapidxml::xml_node<>* slnNode = keyNode->first_node("Sln"); + if (slnNode == nullptr) { + continue; + } + key.sln(::strtoul(slnNode->value(), NULL, 10)); + + // get algorithm ID + rapidxml::xml_node<>* algIdNode = keyNode->first_node("AlgorithmId"); + if (algIdNode == nullptr) { + continue; + } + key.algId(::strtoul(algIdNode->value(), NULL, 10)); + + // get key ID + rapidxml::xml_node<>* kIdNode = keyNode->first_node("KeyId"); + if (kIdNode == nullptr) { + continue; + } + key.kId(::strtoul(kIdNode->value(), NULL, 10)); + + // get key material + rapidxml::xml_node<>* keyMatNode = keyNode->first_node("Key"); + if (keyMatNode == nullptr) { + continue; + } + key.keyMaterial(keyMatNode->value()); + + ::LogInfoEx(LOG_HOST, "Key NAME: %s SLN: %u ALGID: $%02X, KID: $%04X", key.name().c_str(), key.sln(), key.algId(), key.kId()); + + m_keys.push_back(key); + i++; + } + } + } + } + } catch(const std::exception& e) { + ::LogError(LOG_HOST, "Error opening EKC: %s", e.what()); + return false; + } + + if (m_keys.size() == 0U) { + ::LogError(LOG_HOST, "No encryption keys defined!"); + return false; + } + + size_t size = m_keys.size(); + if (size == 0U) + return false; + + LogInfoEx(LOG_HOST, "Loaded %lu entries into crypto lookup table", size); + + return true; +#endif // !ENABLE_TCP_SSL +} diff --git a/src/fne/CryptoContainer.h b/src/fne/CryptoContainer.h new file mode 100644 index 00000000..79a172cd --- /dev/null +++ b/src/fne/CryptoContainer.h @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Converged FNE Software + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file CryptoContainer.h + * @ingroup fne + * @file CryptoContainer.cpp + * @ingroup fne + */ +#if !defined(__CRYPTO_CONTAINER_H__) +#define __CRYPTO_CONTAINER_H__ + +#include "common/Defines.h" +#include "common/Thread.h" +#include "common/Utils.h" + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief Represents an key item. + * @ingroup fne + */ +class HOST_SW_API KeyItem { +public: + /** + * @brief Initializes a new instance of the KeyItem class. + */ + KeyItem() : + m_id(0U), + m_name(), + m_keysetId(0U), + m_sln(0U), + m_algId(0U), + m_kId(0U), + m_keyMaterial() + { + /* stub */ + } + + /** + * @brief Equals operator. Copies this KeyItem to another KeyItem. + * @param data Instance of KeyItem to copy. + */ + virtual KeyItem& operator= (const KeyItem& data) + { + if (this != &data) { + m_id = data.m_id; + m_name = data.m_name; + m_keysetId = data.m_keysetId; + m_sln = data.m_sln; + m_algId = data.m_algId; + m_kId = data.m_kId; + + m_keyMaterial = data.m_keyMaterial; + } + + return *this; + } + + /** + * @brief Helper to quickly determine if a key item entry is valid. + * @returns bool True, if key item entry is valid, otherwise false. + */ + bool isInvalid() const + { + if (m_sln == 0U) + return true; + if (m_algId == 0U) + return true; + if (m_kId == 0U) + return true; + if (m_keyMaterial.size() == 0U) + return true; + return false; + } + + /** + * @brief Gets the encryption key. + * @param[out] key Buffer containing the key. + */ + void getKey(uint8_t* key) const + { + assert(key != nullptr); + + const char* rawKey = m_keyMaterial.c_str(); + ::memset(key, 0x00U, 32U); + + for (uint8_t i = 0U; i < 32U; i++) { + char t[4] = {rawKey[0], rawKey[1], 0}; + key[i] = (uint8_t)::strtoul(t, NULL, 16); + if (i + 2U > m_keyMaterial.size()) + break; + rawKey += 2 * sizeof(char); + } + } + +public: + /** + * @brief + */ + __PROPERTY_PLAIN(uint32_t, id); + /** + * @brief + */ + __PROPERTY_PLAIN(std::string, name); + + /** + * @brief + */ + __PROPERTY_PLAIN(uint32_t, keysetId); + /** + * @brief + */ + __PROPERTY_PLAIN(uint32_t, sln); + + /** + * @brief Encryption algorithm ID. + */ + __PROPERTY_PLAIN(uint8_t, algId); + /** + * @brief Encryption key ID. + */ + __PROPERTY_PLAIN(uint32_t, kId); + + /** + * @brief Encryption key material. + */ + __PROPERTY_PLAIN(std::string, keyMaterial); +}; + +// --------------------------------------------------------------------------- +// Class Declaration +// --------------------------------------------------------------------------- + +/** + * @brief Implements a threading lookup table class that contains routing + * rules information. + * @ingroup lookups_tgid + */ +class HOST_SW_API CryptoContainer : public Thread { +public: + /** + * @brief Initializes a new instance of the CryptoContainer class. + * @param filename Full-path to the crypto container file. + * @param password Crypto container file access password. + * @param reloadTime Interval of time to reload the crypto container. + * @param enabled Flag indicating if crypto container is enabled. + */ + CryptoContainer(const std::string& filename, const std::string& password, uint32_t reloadTime, bool enabled); + /** + * @brief Finalizes a instance of the CryptoContainer class. + */ + ~CryptoContainer() override; + + /** + * @brief Thread entry point. This function is provided to run the thread + * for the lookup table. + */ + void entry() override; + + /** + * @brief Stops and unloads this lookup table. + * @param noDestroy Flag indicating the lookup table should remain resident in memory after stopping. + */ + void stop(bool noDestroy = false); + /** + * @brief Reads the lookup table from the specified lookup table file. + * @returns bool True, if lookup table was read, otherwise false. + */ + bool read(); + /** + * @brief Reads the lookup table from the specified lookup table file. + * @returns bool True, if lookup table was read, otherwise false. + */ + bool reload() { return load(); } + /** + * @brief Clears all entries from the lookup table. + */ + void clear(); + + /** + * @brief Adds a new entry to the lookup table. + * @param key Key Item. + */ + void addEntry(KeyItem key); + /** + * @brief Erases an existing entry from the lookup table by the specified unique ID. + * @param id Unique ID to erase. + */ + void eraseEntry(uint32_t id); + /** + * @brief Finds a table entry in this lookup table. + * @param kId Unique identifier for table entry. + * @returns KeyItem Table entry. + */ + virtual KeyItem find(uint32_t kId); + + /** + * @brief Helper to return the flag indicating whether or not the crypto container is enabled. + * @returns bool + */ + bool isEnabled() const { return m_enabled; } + + /** + * @brief Helper to set the reload time of this lookup table. + * @param reloadTime Lookup time in seconds. + */ + void setReloadTime(uint32_t reloadTime) { m_reloadTime = reloadTime; } + +private: + std::string m_file; + std::string m_password; + uint32_t m_reloadTime; + + bool m_enabled; + bool m_stop; + + static std::mutex m_mutex; + + /** + * @brief Loads the table from the passed lookup table file. + * @return True, if lookup table was loaded, otherwise false. + */ + bool load(); + +public: + /** + * @brief List of keys. + */ + __PROPERTY_PLAIN(std::vector, keys); +}; + +#endif // __CRYPTO_CONTAINER_H__ diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index f9818af8..ad1ccb50 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -64,6 +64,7 @@ HostFNE::HostFNE(const std::string& confFile) : m_ridLookup(nullptr), m_tidLookup(nullptr), m_peerListLookup(nullptr), + m_cryptoLookup(nullptr), m_peerNetworks(), m_pingTime(5U), m_maxMissedPings(5U), @@ -353,6 +354,12 @@ bool HostFNE::readParams() std::string talkgroupConfig = talkgroupRules["file"].as(); uint32_t talkgroupConfigReload = talkgroupRules["time"].as(30U); + yaml::Node cryptoContainer = masterConf["crypto_container"]; + bool cryptoContainerEnabled = cryptoContainer["enabled"].as(false); + std::string cryptoContainerEKC = cryptoContainer["file"].as(); + std::string cryptoContainerPassword = cryptoContainer["password"].as(); + uint32_t cryptoContainerReload = cryptoContainer["time"].as(30U); + std::string peerListLookupFile = systemConf["peer_acl"]["file"].as(); bool peerListLookupEnable = systemConf["peer_acl"]["enabled"].as(false); std::string peerListModeStr = systemConf["peer_acl"]["mode"].as("whitelist"); @@ -385,6 +392,16 @@ bool HostFNE::readParams() m_peerListLookup = new PeerListLookup(peerListLookupFile, peerListMode, peerListConfigReload, peerListLookupEnable); m_peerListLookup->read(); + // try to load peer whitelist/blacklist + LogInfo("Crypto Container Lookups"); + LogInfo(" Enabled: %s", cryptoContainerEnabled ? "yes" : "no"); + LogInfo(" File: %s", cryptoContainerEKC.length() > 0U ? cryptoContainerEKC.c_str() : "None"); + if (cryptoContainerReload > 0U) + LogInfo(" Reload: %u mins", cryptoContainerReload); + + m_cryptoLookup = new CryptoContainer(cryptoContainerEKC, cryptoContainerPassword, cryptoContainerReload, cryptoContainerEnabled); + m_cryptoLookup->read(); + return true; } diff --git a/src/fne/HostFNE.h b/src/fne/HostFNE.h index 77e983de..8cd60bd9 100644 --- a/src/fne/HostFNE.h +++ b/src/fne/HostFNE.h @@ -27,6 +27,7 @@ #include "network/DiagNetwork.h" #include "network/PeerNetwork.h" #include "network/RESTAPI.h" +#include "CryptoContainer.h" #include #include @@ -104,6 +105,8 @@ private: lookups::TalkgroupRulesLookup* m_tidLookup; lookups::PeerListLookup* m_peerListLookup; + CryptoContainer* m_cryptoLookup; + std::unordered_map m_peerNetworks; uint32_t m_pingTime; diff --git a/src/fne/xml/rapidxml.h b/src/fne/xml/rapidxml.h new file mode 100644 index 00000000..77c09ac9 --- /dev/null +++ b/src/fne/xml/rapidxml.h @@ -0,0 +1,2619 @@ +// SPDX-License-Identifier: MIT +/* + * Digital Voice Modem - Common Library + * MIT Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2006, 2009 Marcin Kalicinski + * Copyright (C) 2019 https://github.com/Fe-Bell/RapidXML + * + */ +/** + * @defgroup xml Extensible Markup Langauge + * @brief Defines and implements XML handling. + * @ingroup common + * + * @file rapidxml.h + * @ingroup xml + */ +#if !defined(__RAPIDXML_H__) +#define __RAPIDXML_H__ + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Copyright (C) 2019 https://github.com/Fe-Bell/RapidXML +// Version 1.17 +// Revision $DateTime: 2024/06/15 19:38:51 $ +//! \file rapidxml.h This file contains rapidxml parser and DOM implementation + +// If standard library is disabled, user must provide implementations of required functions and typedefs +#if !defined(RAPIDXML_NO_STDLIB) + #include // For std::size_t + #include // For assert + #include // For placement new +#endif + +// On MSVC, disable "conditional expression is constant" warning (level 4). +// This warning is almost impossible to avoid with certain types of templated code +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) // Conditional expression is constant +#endif + +/////////////////////////////////////////////////////////////////////////// +// RAPIDXML_PARSE_ERROR + +#if defined(RAPIDXML_NO_EXCEPTIONS) + +#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } + +namespace rapidxml +{ + //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, + //! this function is called to notify user about the error. + //! It must be defined by the user. + //!

+ //! This function cannot return. If it does, the results are undefined. + //!

+ //! A very simple definition might look like that: + //!
+    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
+    //! {
+    //!     std::cout << "Parse error: " << what << "\n";
+    //!     std::abort();
+    //! }
+    //! 
+ //! \param what Human readable description of the error. + //! \param where Pointer to character data where error was detected. + void parse_error_handler(const char *what, void *where); +} + +#else + +#include // For std::exception + +#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) + +namespace rapidxml +{ + + //! Parse error exception. + //! This exception is thrown by the parser when an error occurs. + //! Use what() function to get human-readable error message. + //! Use where() function to get a pointer to position within source text where error was detected. + //!

+ //! If throwing exceptions by the parser is undesirable, + //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. + //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. + //! This function must be defined by the user. + //!

+ //! This class derives from std::exception class. + class parse_error: public std::exception + { + + public: + + //! Constructs parse error + parse_error(const char *what, void *where) + : m_what(what) + , m_where(where) + { + } + + //! Gets human readable description of error. + //! \return Pointer to null terminated description of the error. + virtual const char *what() const throw() + { + return m_what; + } + + //! Gets pointer to character data where error happened. + //! Ch should be the same as char type of xml_document that produced the error. + //! \return Pointer to location within the parsed string where error occured. + template + Ch *where() const + { + return reinterpret_cast(m_where); + } + + private: + + const char *m_what; + void *m_where; + + }; +} + +#endif + +/////////////////////////////////////////////////////////////////////////// +// Pool sizes + +#ifndef RAPIDXML_STATIC_POOL_SIZE + // Size of static memory block of memory_pool. + // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. + #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_DYNAMIC_POOL_SIZE + // Size of dynamic memory block of memory_pool. + // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. + #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_ALIGNMENT + // Memory allocation alignment. + // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. + // All memory allocations for nodes, attributes and strings will be aligned to this value. + // This must be a power of 2 and at least 1, otherwise memory_pool will not work. + #define RAPIDXML_ALIGNMENT sizeof(void *) +#endif + +namespace rapidxml +{ + // Forward declarations + template class xml_node; + template class xml_attribute; + template class xml_document; + + //! Enumeration listing all node types produced by the parser. + //! Use xml_node::type() function to query node type. + enum class node_type + { + node_document, //!< A document node. Name and value are empty. + node_element, //!< An element node. Name contains element name. Value contains text of first data node. + node_data, //!< A data node. Name is empty. Value contains data text. + node_cdata, //!< A CDATA node. Name is empty. Value contains data text. + node_comment, //!< A comment node. Name is empty. Value contains comment text. + node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. + node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. + node_pi //!< A PI node. Name contains target. Value contains instructions. + }; + + /////////////////////////////////////////////////////////////////////// + // Parsing flags + + //! Parse flag instructing the parser to not create data nodes. + //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_data_nodes = 0x1; + + //! Parse flag instructing the parser to not use text of first data node as a value of parent element. + //! Can be combined with other flags by use of | operator. + //! Note that child data nodes of element node take precendence over its value when printing. + //! That is, if element has one or more child data nodes and a value, the value will be ignored. + //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. + //!

+ //! See xml_document::parse() function. + const int parse_no_element_values = 0x2; + + //! Parse flag instructing the parser to not place zero terminators after strings in the source text. + //! By default zero terminators are placed, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_string_terminators = 0x4; + + //! Parse flag instructing the parser to not translate entities in the source text. + //! By default entities are translated, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_entity_translation = 0x8; + + //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. + //! By default, UTF-8 handling is enabled. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_utf8 = 0x10; + + //! Parse flag instructing the parser to create XML declaration node. + //! By default, declaration node is not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_declaration_node = 0x20; + + //! Parse flag instructing the parser to create comments nodes. + //! By default, comment nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_comment_nodes = 0x40; + + //! Parse flag instructing the parser to create DOCTYPE node. + //! By default, doctype node is not created. + //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_doctype_node = 0x80; + + //! Parse flag instructing the parser to create PI nodes. + //! By default, PI nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_pi_nodes = 0x100; + + //! Parse flag instructing the parser to validate closing tag names. + //! If not set, name inside closing tag is irrelevant to the parser. + //! By default, closing tags are not validated. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_validate_closing_tags = 0x200; + + //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. + //! By default, whitespace is not trimmed. + //! This flag does not cause the parser to modify source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_trim_whitespace = 0x400; + + //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. + //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. + //! By default, whitespace is not normalized. + //! If this flag is specified, source text will be modified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_normalize_whitespace = 0x800; + + // Compound flags + + //! Parse flags which represent default behaviour of the parser. + //! This is always equal to 0, so that all other flags can be simply ored together. + //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. + //! This also means that meaning of each flag is a negation of the default setting. + //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, + //! and using the flag will disable it. + //!

+ //! See xml_document::parse() function. + const int parse_default = 0; + + //! A combination of parse flags that forbids any modifications of the source text. + //! This also results in faster parsing. However, note that the following will occur: + //!
    + //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • + //!
  • entities will not be translated
  • + //!
  • whitespace will not be normalized
  • + //!
+ //! See xml_document::parse() function. + const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; + + //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. + //!

+ //! See xml_document::parse() function. + const int parse_fastest = parse_non_destructive | parse_no_data_nodes; + + //! A combination of parse flags resulting in largest amount of data being extracted. + //! This usually results in slowest parsing. + //!

+ //! See xml_document::parse() function. + const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; + + /////////////////////////////////////////////////////////////////////// + // Internals + + //! \cond internal + namespace internal + { + + // Struct that contains lookup tables for the parser + // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). + template + struct lookup_tables + { + static const unsigned char lookup_whitespace[256]; // Whitespace table + static const unsigned char lookup_node_name[256]; // Node name table + static const unsigned char lookup_text[256]; // Text table + static const unsigned char lookup_text_pure_no_ws[256]; // Text table + static const unsigned char lookup_text_pure_with_ws[256]; // Text table + static const unsigned char lookup_attribute_name[256]; // Attribute name table + static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes + static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes + static const unsigned char lookup_digits[256]; // Digits + static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters + }; + + // Find length of the string + template + inline std::size_t measure(const Ch *p) + { + const Ch *tmp = p; + while (*tmp) + ++tmp; + return static_cast(tmp - p); + } + + // Compare strings for equality + template + inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) + { + if (size1 != size2) + return false; + if (case_sensitive) + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (*p1 != *p2) + return false; + } + else + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) + return false; + } + return true; + } + } + //! \endcond + + /////////////////////////////////////////////////////////////////////// + // Memory pool + + //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. + //! In most cases, you will not need to use this class directly. + //! However, if you need to create nodes manually or modify names/values of nodes, + //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. + //! Not only is this faster than allocating them by using new operator, + //! but also their lifetime will be tied to the lifetime of document, + //! possibly simplyfing memory management. + //!

+ //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. + //! You can also call allocate_string() function to allocate strings. + //! Such strings can then be used as names or values of nodes without worrying about their lifetime. + //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, + //! or when the pool is destroyed. + //!

+ //! It is also possible to create a standalone memory_pool, and use it + //! to allocate nodes, whose lifetime will not be tied to any document. + //!

+ //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. + //! Until static memory is exhausted, no dynamic memory allocations are done. + //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, + //! by using global new[] and delete[] operators. + //! This behaviour can be changed by setting custom allocation routines. + //! Use set_allocator() function to set them. + //!

+ //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. + //! This value defaults to the size of pointer on target architecture. + //!

+ //! To obtain absolutely top performance from the parser, + //! it is important that all nodes are allocated from a single, contiguous block of memory. + //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. + //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT + //! to obtain best wasted memory to performance compromise. + //! To do it, define their values before rapidxml.hpp file is included. + //! \param Ch Character type of created nodes. + template + class memory_pool + { + + public: + + //! \cond internal + typedef void *(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory + typedef void (free_func)(void *); // Type of user-defined function used to free memory + //! \endcond + + //! Constructs empty pool with default allocator functions. + memory_pool() + : m_alloc_func(0) + , m_free_func(0) + { + init(); + } + + //! Destroys pool and frees all the memory. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Nodes allocated from the pool are no longer valid. + ~memory_pool() + { + clear(); + } + + //! Allocates a new node from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param type Type of node to create. + //! \param name Name to assign to the node, or 0 to assign no name. + //! \param value Value to assign to the node, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated node. This pointer will never be NULL. + xml_node *allocate_node(node_type type, + const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_node)); + xml_node *node = new(memory) xml_node(type); + if (name) + { + if (name_size > 0) + node->name(name, name_size); + else + node->name(name); + } + if (value) + { + if (value_size > 0) + node->value(value, value_size); + else + node->value(value); + } + return node; + } + + //! Allocates a new attribute from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param name Name to assign to the attribute, or 0 to assign no name. + //! \param value Value to assign to the attribute, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated attribute. This pointer will never be NULL. + xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_attribute)); + xml_attribute *attribute = new(memory) xml_attribute; + if (name) + { + if (name_size > 0) + attribute->name(name, name_size); + else + attribute->name(name); + } + if (value) + { + if (value_size > 0) + attribute->value(value, value_size); + else + attribute->value(value); + } + return attribute; + } + + //! Allocates a char array of given size from the pool, and optionally copies a given string to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param source String to initialize the allocated memory with, or 0 to not initialize it. + //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. + //! \return Pointer to allocated char array. This pointer will never be NULL. + Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) + { + assert(source || size); // Either source or size (or both) must be specified + if (size == 0) + size = internal::measure(source) + 1; + Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); + if (source) + for (std::size_t i = 0; i < size; ++i) + result[i] = source[i]; + return result; + } + + //! Clones an xml_node and its hierarchy of child nodes and attributes. + //! Nodes and attributes are allocated from this memory pool. + //! Names and values are not cloned, they are shared between the clone and the source. + //! Result node can be optionally specified as a second parameter, + //! in which case its contents will be replaced with cloned source node. + //! This is useful when you want to clone entire document. + //! \param source Node to clone. + //! \param result Node to put results in, or 0 to automatically allocate result node + //! \return Pointer to cloned node. This pointer will never be NULL. + xml_node *clone_node(const xml_node *source, xml_node *result = 0) + { + // Prepare result node + if (result) + { + result->remove_all_attributes(); + result->remove_all_nodes(); + result->type(source->type()); + } + else + result = allocate_node(source->type()); + + // Clone name and value + result->name(source->name(), source->name_size()); + result->value(source->value(), source->value_size()); + + // Clone child nodes and attributes + for (xml_node *child = source->first_node(); child; child = child->next_sibling()) + result->append_node(clone_node(child)); + for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) + result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); + + return result; + } + + //! Clears the pool. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Any nodes or strings allocated from the pool will no longer be valid. + void clear() + { + while (m_begin != m_static_memory) + { + char *previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; + if (m_free_func) + m_free_func(m_begin); + else + delete[] m_begin; + m_begin = previous_begin; + } + init(); + } + + //! Sets or resets the user-defined memory allocation functions for the pool. + //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. + //! Allocation function must not return invalid pointer on failure. It should either throw, + //! stop the program, or use longjmp() function to pass control to other place of program. + //! If it returns invalid pointer, results are undefined. + //!

+ //! User defined allocation functions must have the following forms: + //!
+ //!
void *allocate(std::size_t size); + //!
void free(void *pointer); + //!

+ //! \param af Allocation function, or 0 to restore default function + //! \param ff Free function, or 0 to restore default function + void set_allocator(alloc_func *af, free_func *ff) + { + assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet + m_alloc_func = af; + m_free_func = ff; + } + + private: + + struct header + { + char *previous_begin; + }; + + void init() + { + m_begin = m_static_memory; + m_ptr = align(m_begin); + m_end = m_static_memory + sizeof(m_static_memory); + } + + char *align(char *ptr) + { + std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); + return ptr + alignment; + } + + char *allocate_raw(std::size_t size) + { + // Allocate + void *memory; + if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] + { + memory = m_alloc_func(size); + assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp + } + else + { + memory = new char[size]; +#ifdef RAPIDXML_NO_EXCEPTIONS + if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc + RAPIDXML_PARSE_ERROR("out of memory", 0); +#endif + } + return static_cast(memory); + } + + void *allocate_aligned(std::size_t size) + { + // Calculate aligned pointer + char *result = align(m_ptr); + + // If not enough memory left in current pool, allocate a new pool + if (result + size > m_end) + { + // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) + std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; + if (pool_size < size) + pool_size = size; + + // Allocate + std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation + char *raw_memory = allocate_raw(alloc_size); + + // Setup new pool in allocated memory + char *pool = align(raw_memory); + header *new_header = reinterpret_cast
(pool); + new_header->previous_begin = m_begin; + m_begin = raw_memory; + m_ptr = pool + sizeof(header); + m_end = raw_memory + alloc_size; + + // Calculate aligned pointer again using new pool + result = align(m_ptr); + } + + // Update pool and return aligned pointer + m_ptr = result + size; + return result; + } + + char *m_begin; // Start of raw memory making up current pool + char *m_ptr; // First free byte in current pool + char *m_end; // One past last available byte in current pool + char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory + alloc_func *m_alloc_func; // Allocator function, or 0 if default is to be used + free_func *m_free_func; // Free function, or 0 if default is to be used + }; + + /////////////////////////////////////////////////////////////////////////// + // XML base + + //! Base class for xml_node and xml_attribute implementing common functions: + //! name(), name_size(), value(), value_size() and parent(). + //! \param Ch Character type to use + template + class xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + // Construct a base with empty name, value and parent + xml_base() : m_name(0), m_value(0), m_name_size(0), m_value_size(0), m_parent(0) + { + + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets name of the node. + //! Interpretation of name depends on type of node. + //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use name_size() function to determine length of the name. + //! \return Name of node, or empty string if node has no name. + Ch *name() const + { + return m_name ? m_name : nullstr(); + } + + //! Gets size of node name, not including terminator character. + //! This function works correctly irrespective of whether name is or is not zero terminated. + //! \return Size of node name, in characters. + std::size_t name_size() const + { + return m_name ? m_name_size : 0; + } + + //! Gets value of node. + //! Interpretation of value depends on type of node. + //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use value_size() function to determine length of the value. + //! \return Value of node, or empty string if node has no value. + Ch *value() const + { + return m_value ? m_value : nullstr(); + } + + //! Gets size of node value, not including terminator character. + //! This function works correctly irrespective of whether value is or is not zero terminated. + //! \return Size of node value, in characters. + std::size_t value_size() const + { + return m_value ? m_value_size : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets name of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of name must be specified separately, because name does not have to be zero terminated. + //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //! \param name Name of node to set. Does not have to be zero terminated. + //! \param size Size of name, in characters. This does not include zero terminator, if one is present. + void name(const Ch *name, std::size_t size) + { + m_name = const_cast(name); + m_name_size = size; + } + + //! Sets name of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). + //! \param name Name of node to set. Must be zero terminated. + void name(const Ch *name) + { + this->name(name, internal::measure(name)); + } + + //! Sets value of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of value must be specified separately, because it does not have to be zero terminated. + //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //!

+ //! If an element has a child node of type node_type::node_data, it will take precedence over element value when printing. + //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. + //! \param value value of node to set. Does not have to be zero terminated. + //! \param size Size of value, in characters. This does not include zero terminator, if one is present. + void value(const Ch *value, std::size_t size) + { + m_value = const_cast(value); + m_value_size = size; + } + + //! Sets value of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). + //! \param value Vame of node to set. Must be zero terminated. + void value(const Ch *value) + { + this->value(value, internal::measure(value)); + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets node parent. + //! \return Pointer to parent node, or 0 if there is no parent. + xml_node *parent() const + { + return m_parent; + } + + protected: + + // Return empty string + static Ch *nullstr() + { + static Ch zero = Ch('\0'); + return &zero; + } + + Ch *m_name; // Name of node, or 0 if no name + Ch *m_value; // Value of node, or 0 if no value + std::size_t m_name_size; // Length of node name, or undefined of no name + std::size_t m_value_size; // Length of node value, or undefined if no value + xml_node *m_parent; // Pointer to parent node, or 0 if none + + }; + + //! Class representing attribute node of XML document. + //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). + //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. + //! Thus, this text must persist in memory for the lifetime of attribute. + //! \param Ch Character type to use. + template + class xml_attribute: public xml_base + { + + friend class xml_node; + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty attribute with the specified type. + //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. + xml_attribute() : m_prev_attribute(nullptr), m_next_attribute(nullptr) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which attribute is a child. + //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. + xml_document *document() const + { + if (xml_node *node = this->parent()) + { + while (node->parent()) + node = node->parent(); + return node->type() == node_type::node_document ? static_cast *>(node) : 0; + } + else + return 0; + } + + //! Gets previous attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_prev_attribute : 0; + } + + //! Gets next attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_next_attribute : 0; + } + + private: + + xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero + xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML node + + //! Class representing a node of XML document. + //! Each node may have associated name and value strings, which are available through name() and value() functions. + //! Interpretation of name and value depends on type of the node. + //! Type of node can be determined by using type() function. + //!

+ //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. + //! Thus, this text must persist in the memory for the lifetime of node. + //! \param Ch Character type to use. + template + class xml_node: public xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty node with the specified type. + //! Consider using memory_pool of appropriate document to allocate nodes manually. + //! \param type Type of node to construct. + xml_node(node_type type) : m_type(type), m_first_node(0), m_last_node(nullptr), m_first_attribute(0), m_last_attribute(nullptr), m_prev_sibling(nullptr), m_next_sibling(nullptr) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets type of node. + //! \return Type of node. + node_type type() const + { + return m_type; + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which node is a child. + //! \return Pointer to document that contains this node, or 0 if there is no parent document. + xml_document *document() const + { + xml_node *node = const_cast *>(this); + while (node->parent()) + node = node->parent(); + return node->type() == node_type::node_document ? static_cast *>(node) : 0; + } + + //! Gets first child node, optionally matching node name. + //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_first_node; child; child = child->next_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_first_node; + } + + //! Gets last child node, optionally matching node name. + //! Behaviour is undefined if node has no children. + //! Use first_node() to test if node has children. + //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(m_first_node); // Cannot query for last child if node has no children + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_last_node; child; child = child->previous_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_last_node; + } + + //! Gets previous sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_prev_sibling; + } + + //! Gets next sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_next_sibling; + } + + //! Gets first attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute; + } + + //! Gets last attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute ? m_last_attribute : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets type of node. + //! \param type Type of node to set. + void type(node_type type) + { + m_type = type; + } + + /////////////////////////////////////////////////////////////////////////// + // Node manipulation + + //! Prepends a new child node. + //! The prepended child becomes the first child, and all existing children are moved one position back. + //! \param child Node to prepend. + void prepend_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_type::node_document); + if (first_node()) + { + child->m_next_sibling = m_first_node; + m_first_node->m_prev_sibling = child; + } + else + { + child->m_next_sibling = 0; + m_last_node = child; + } + m_first_node = child; + child->m_parent = this; + child->m_prev_sibling = 0; + } + + //! Appends a new child node. + //! The appended child becomes the last child. + //! \param child Node to append. + void append_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_type::node_document); + if (first_node()) + { + child->m_prev_sibling = m_last_node; + m_last_node->m_next_sibling = child; + } + else + { + child->m_prev_sibling = 0; + m_first_node = child; + } + m_last_node = child; + child->m_parent = this; + child->m_next_sibling = 0; + } + + //! Inserts a new child node at specified place inside the node. + //! All children after and including the specified node are moved one position back. + //! \param where Place where to insert the child, or 0 to insert at the back. + //! \param child Node to insert. + void insert_node(xml_node *where, xml_node *child) + { + assert(!where || where->parent() == this); + assert(child && !child->parent() && child->type() != node_type::node_document); + if (where == m_first_node) + prepend_node(child); + else if (where == 0) + append_node(child); + else + { + child->m_prev_sibling = where->m_prev_sibling; + child->m_next_sibling = where; + where->m_prev_sibling->m_next_sibling = child; + where->m_prev_sibling = child; + child->m_parent = this; + } + } + + //! Removes first child node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_first_node() + { + assert(first_node()); + xml_node *child = m_first_node; + m_first_node = child->m_next_sibling; + if (child->m_next_sibling) + child->m_next_sibling->m_prev_sibling = 0; + else + m_last_node = 0; + child->m_parent = 0; + } + + //! Removes last child of the node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_last_node() + { + assert(first_node()); + xml_node *child = m_last_node; + if (child->m_prev_sibling) + { + m_last_node = child->m_prev_sibling; + child->m_prev_sibling->m_next_sibling = 0; + } + else + m_first_node = 0; + child->m_parent = 0; + } + + //! Removes specified child from the node + // \param where Pointer to child to be removed. + void remove_node(xml_node *where) + { + assert(where && where->parent() == this); + assert(first_node()); + if (where == m_first_node) + remove_first_node(); + else if (where == m_last_node) + remove_last_node(); + else + { + where->m_prev_sibling->m_next_sibling = where->m_next_sibling; + where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; + where->m_parent = 0; + } + } + + //! Removes all child nodes (but not attributes). + void remove_all_nodes() + { + for (xml_node *node = first_node(); node; node = node->m_next_sibling) + node->m_parent = 0; + m_first_node = 0; + } + + //! Prepends a new attribute to the node. + //! \param attribute Attribute to prepend. + void prepend_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_next_attribute = m_first_attribute; + m_first_attribute->m_prev_attribute = attribute; + } + else + { + attribute->m_next_attribute = 0; + m_last_attribute = attribute; + } + m_first_attribute = attribute; + attribute->m_parent = this; + attribute->m_prev_attribute = 0; + } + + //! Appends a new attribute to the node. + //! \param attribute Attribute to append. + void append_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_prev_attribute = m_last_attribute; + m_last_attribute->m_next_attribute = attribute; + } + else + { + attribute->m_prev_attribute = 0; + m_first_attribute = attribute; + } + m_last_attribute = attribute; + attribute->m_parent = this; + attribute->m_next_attribute = 0; + } + + //! Inserts a new attribute at specified place inside the node. + //! All attributes after and including the specified attribute are moved one position back. + //! \param where Place where to insert the attribute, or 0 to insert at the back. + //! \param attribute Attribute to insert. + void insert_attribute(xml_attribute *where, xml_attribute *attribute) + { + assert(!where || where->parent() == this); + assert(attribute && !attribute->parent()); + if (where == m_first_attribute) + prepend_attribute(attribute); + else if (where == 0) + append_attribute(attribute); + else + { + attribute->m_prev_attribute = where->m_prev_attribute; + attribute->m_next_attribute = where; + where->m_prev_attribute->m_next_attribute = attribute; + where->m_prev_attribute = attribute; + attribute->m_parent = this; + } + } + + //! Removes first attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_first_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_first_attribute; + if (attribute->m_next_attribute) + { + attribute->m_next_attribute->m_prev_attribute = 0; + } + else + m_last_attribute = 0; + attribute->m_parent = 0; + m_first_attribute = attribute->m_next_attribute; + } + + //! Removes last attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_last_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_last_attribute; + if (attribute->m_prev_attribute) + { + attribute->m_prev_attribute->m_next_attribute = 0; + m_last_attribute = attribute->m_prev_attribute; + } + else + m_first_attribute = 0; + attribute->m_parent = 0; + } + + //! Removes specified attribute from node. + //! \param where Pointer to attribute to be removed. + void remove_attribute(xml_attribute *where) + { + assert(first_attribute() && where->parent() == this); + if (where == m_first_attribute) + remove_first_attribute(); + else if (where == m_last_attribute) + remove_last_attribute(); + else + { + where->m_prev_attribute->m_next_attribute = where->m_next_attribute; + where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; + where->m_parent = 0; + } + } + + //! Removes all attributes of node. + void remove_all_attributes() + { + for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) + attribute->m_parent = 0; + m_first_attribute = 0; + } + + private: + + /////////////////////////////////////////////////////////////////////////// + // Restrictions + + // No copying + xml_node(const xml_node &); + void operator =(const xml_node &); + + /////////////////////////////////////////////////////////////////////////// + // Data members + + // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. + // This is required for maximum performance, as it allows the parser to omit initialization of + // unneded/redundant values. + // + // The rules are as follows: + // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively + // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage + // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage + + node_type m_type; // Type of node; always valid + xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid + xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero + xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid + xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero + xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML document + + //! This class represents root of the DOM hierarchy. + //! It is also an xml_node and a memory_pool through public inheritance. + //! Use parse() function to build a DOM tree from a zero-terminated XML text string. + //! parse() function allocates memory for nodes and attributes by using functions of xml_document, + //! which are inherited from memory_pool. + //! To access root node of the document, use the document itself, as if it was an xml_node. + //! \param Ch Character type to use. + template + class xml_document: public xml_node, public memory_pool + { + + public: + + //! Constructs empty XML document + xml_document() + : xml_node(node_type::node_document) + { + } + + //! Parses zero-terminated XML string according to given flags. + //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. + //! The string must persist for the lifetime of the document. + //! In case of error, rapidxml::parse_error exception will be thrown. + //!

+ //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. + //! Make sure that data is zero-terminated. + //!

+ //! Document can be parsed into multiple times. + //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. + //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. + template + void parse(Ch *text) + { + assert(text); + + // Remove current contents + this->remove_all_nodes(); + this->remove_all_attributes(); + + // Parse BOM, if any + parse_bom(text); + + // Parse children + while (1) + { + // Skip whitespace before node + skip(text); + if (*text == 0) + break; + + // Parse and append new child + if (*text == Ch('<')) + { + ++text; // Skip '<' + if (xml_node *node = parse_node(text)) + this->append_node(node); + } + else + RAPIDXML_PARSE_ERROR("expected <", text); + } + + } + + //! Clears the document by deleting all nodes and clearing the memory pool. + //! All nodes owned by document pool are destroyed. + void clear() + { + this->remove_all_nodes(); + this->remove_all_attributes(); + memory_pool::clear(); + } + + private: + + /////////////////////////////////////////////////////////////////////// + // Internal character utility functions + + // Detect whitespace character + struct whitespace_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; + } + }; + + // Detect node name character + struct node_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; + } + }; + + // Detect attribute name character + struct attribute_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) + struct text_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_no_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_with_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; + } + }; + + // Detect attribute value character + template + struct attribute_value_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Detect attribute value character + template + struct attribute_value_pure_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Insert coded character, using UTF8 or 8-bit ASCII + template + static void insert_coded_character(Ch *&text, unsigned long code) + { + if (Flags & parse_no_utf8) + { + // Insert 8-bit ASCII character + // Todo: possibly verify that code is less than 256 and use replacement char otherwise? + text[0] = static_cast(code); + text += 1; + } + else + { + // Insert UTF8 sequence + if (code < 0x80) // 1 byte sequence + { + text[0] = static_cast(code); + text += 1; + } + else if (code < 0x800) // 2 byte sequence + { + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xC0); + text += 2; + } + else if (code < 0x10000) // 3 byte sequence + { + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xE0); + text += 3; + } + else if (code < 0x110000) // 4 byte sequence + { + text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xF0); + text += 4; + } + else // Invalid, only codes up to 0x10FFFF are allowed in Unicode + { + RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); + } + } + } + + // Skip characters until predicate evaluates to true + template + static void skip(Ch *&text) + { + Ch *tmp = text; + while (StopPred::test(*tmp)) + ++tmp; + text = tmp; + } + + // Skip characters until predicate evaluates to true while doing the following: + // - replacing XML character entity references with proper characters (' & " < > &#...;) + // - condensing whitespace sequences to single space character + template + static Ch *skip_and_expand_character_refs(Ch *&text) + { + // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip + if (Flags & parse_no_entity_translation && + !(Flags & parse_normalize_whitespace) && + !(Flags & parse_trim_whitespace)) + { + skip(text); + return text; + } + + // Use simple skip until first modification is detected + skip(text); + + // Use translation skip + Ch *src = text; + Ch *dest = src; + while (StopPred::test(*src)) + { + // If entity translation is enabled + if (!(Flags & parse_no_entity_translation)) + { + // Test if replacement is needed + if (src[0] == Ch('&')) + { + switch (src[1]) + { + + // & ' + case Ch('a'): + if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) + { + *dest = Ch('&'); + ++dest; + src += 5; + continue; + } + if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) + { + *dest = Ch('\''); + ++dest; + src += 6; + continue; + } + break; + + // " + case Ch('q'): + if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) + { + *dest = Ch('"'); + ++dest; + src += 6; + continue; + } + break; + + // > + case Ch('g'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('>'); + ++dest; + src += 4; + continue; + } + break; + + // < + case Ch('l'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('<'); + ++dest; + src += 4; + continue; + } + break; + + // &#...; - assumes ASCII + case Ch('#'): + if (src[2] == Ch('x')) + { + unsigned long code = 0; + src += 3; // Skip &#x + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 16 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + else + { + unsigned long code = 0; + src += 2; // Skip &# + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 10 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + if (*src == Ch(';')) + ++src; + else + RAPIDXML_PARSE_ERROR("expected ;", src); + continue; + + // Something else + default: + // Ignore, just copy '&' verbatim + break; + + } + } + } + + // If whitespace condensing is enabled + if (Flags & parse_normalize_whitespace) + { + // Test if condensing is needed + if (whitespace_pred::test(*src)) + { + *dest = Ch(' '); ++dest; // Put single space in dest + ++src; // Skip first whitespace char + // Skip remaining whitespace chars + while (whitespace_pred::test(*src)) + ++src; + continue; + } + } + + // No replacement, only copy character + *dest++ = *src++; + + } + + // Return new end + text = src; + return dest; + + } + + /////////////////////////////////////////////////////////////////////// + // Internal parsing functions + + // Parse BOM, if any + template + void parse_bom(Ch *&text) + { + // UTF-8? + if (static_cast(text[0]) == 0xEF && + static_cast(text[1]) == 0xBB && + static_cast(text[2]) == 0xBF) + { + text += 3; // Skup utf-8 bom + } + } + + // Parse XML declaration ( + xml_node *parse_xml_declaration(Ch *&text) + { + // If parsing of declaration is disabled + if (!(Flags & parse_declaration_node)) + { + // Skip until end of declaration + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + + // Create declaration + xml_node *declaration = this->allocate_node(node_type::node_declaration); + + // Skip whitespace before attributes or ?> + skip(text); + + // Parse declaration attributes + parse_node_attributes(text, declaration); + + // Skip ?> + if (text[0] != Ch('?') || text[1] != Ch('>')) + RAPIDXML_PARSE_ERROR("expected ?>", text); + text += 2; + + return declaration; + } + + // Parse XML comment (' + return 0; // Do not produce comment node + } + + // Remember value start + Ch *value = text; + + // Skip until end of comment + while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create comment node + xml_node *comment = this->allocate_node(node_type::node_comment); + comment->value(value, static_cast(text - value)); + + // Place zero terminator after comment value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip '-->' + return comment; + } + + // Parse DOCTYPE + template + xml_node *parse_doctype(Ch *&text) + { + // Remember value start + Ch *value = text; + + // Skip to > + while (*text != Ch('>')) + { + // Determine character type + switch (*text) + { + + // If '[' encountered, scan for matching ending ']' using naive algorithm with depth + // This works for all W3C test files except for 2 most wicked + case Ch('['): + { + ++text; // Skip '[' + int depth = 1; + while (depth > 0) + { + switch (*text) + { + case Ch('['): ++depth; break; + case Ch(']'): --depth; break; + case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + ++text; + } + break; + } + + // Error on end of text + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Other character, skip it + default: + ++text; + + } + } + + // If DOCTYPE nodes enabled + if (Flags & parse_doctype_node) + { + // Create a new doctype node + xml_node *doctype = this->allocate_node(node_type::node_doctype); + doctype->value(value, static_cast(text - value)); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 1; // skip '>' + return doctype; + } + else + { + text += 1; // skip '>' + return 0; + } + + } + + // Parse PI + template + xml_node *parse_pi(Ch *&text) + { + // If creation of PI nodes is enabled + if (Flags & parse_pi_nodes) + { + // Create pi node + xml_node *pi = this->allocate_node(node_type::node_pi); + + // Extract PI target name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected PI target", text); + pi->name(name, static_cast(text - name)); + + // Skip whitespace between pi target and pi + skip(text); + + // Remember start of pi + Ch *value = text; + + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Set pi value (verbatim, no entity expansion or whitespace normalization) + pi->value(value, static_cast(text - value)); + + // Place zero terminator after name and value + if (!(Flags & parse_no_string_terminators)) + { + pi->name()[pi->name_size()] = Ch('\0'); + pi->value()[pi->value_size()] = Ch('\0'); + } + + text += 2; // Skip '?>' + return pi; + } + else + { + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + } + + // Parse and append data + // Return character that ends data. + // This is necessary because this character might have been overwritten by a terminating 0 + template + Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) + { + // Backup to contents start if whitespace trimming is disabled + if (!(Flags & parse_trim_whitespace)) + text = contents_start; + + // Skip until end of data + Ch *value = text, *end; + if (Flags & parse_normalize_whitespace) + end = skip_and_expand_character_refs(text); + else + end = skip_and_expand_character_refs(text); + + // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > + if (Flags & parse_trim_whitespace) + { + if (Flags & parse_normalize_whitespace) + { + // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end + if (*(end - 1) == Ch(' ')) + --end; + } + else + { + // Backup until non-whitespace character is found + while (whitespace_pred::test(*(end - 1))) + --end; + } + } + + // If characters are still left between end and value (this test is only necessary if normalization is enabled) + // Create new data node + if (!(Flags & parse_no_data_nodes)) + { + xml_node *data = this->allocate_node(node_type::node_data); + data->value(value, static_cast(end - value)); + node->append_node(data); + } + + // Add data to parent node if no data exists yet + if (!(Flags & parse_no_element_values)) + if (*node->value() == Ch('\0')) + node->value(value, static_cast(end - value)); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + Ch ch = *text; + *end = Ch('\0'); + return ch; // Return character that ends data; this is required because zero terminator overwritten it + } + + // Return character that ends data + return *text; + } + + // Parse CDATA + template + xml_node *parse_cdata(Ch *&text) + { + // If CDATA is disabled + if (Flags & parse_no_data_nodes) + { + // Skip until end of cdata + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 3; // Skip ]]> + return 0; // Do not produce CDATA node + } + + // Skip until end of cdata + Ch *value = text; + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create new cdata node + xml_node *cdata = this->allocate_node(node_type::node_cdata); + cdata->value(value, static_cast(text - value)); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip ]]> + return cdata; + } + + // Parse element node + template + xml_node *parse_element(Ch *&text) + { + // Create element node + xml_node *element = this->allocate_node(node_type::node_element); + + // Extract element name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected element name", text); + element->name(name, static_cast(text - name)); + + // Skip whitespace between element name and attributes or > + skip(text); + + // Parse attributes, if any + parse_node_attributes(text, element); + + // Determine ending type + if (*text == Ch('>')) + { + ++text; + parse_node_contents(text, element); + } + else if (*text == Ch('/')) + { + ++text; + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; + } + else + RAPIDXML_PARSE_ERROR("expected >", text); + + // Place zero terminator after name + if (!(Flags & parse_no_string_terminators)) + element->name()[element->name_size()] = Ch('\0'); + + // Return parsed element + return element; + } + + // Determine node type, and parse it + template + xml_node *parse_node(Ch *&text) + { + // Parse proper node type + switch (text[0]) + { + + // <... + default: + // Parse and append element node + return parse_element(text); + + // (text); + } + else + { + // Parse PI + return parse_pi(text); + } + + // (text); + } + break; + + // (text); + } + break; + + // (text); + } + + } // switch + + // Attempt to skip other, unrecognized node types starting with ')) + { + if (*text == 0) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + ++text; // Skip '>' + return 0; // No node recognized + + } + } + + // Parse contents of the node - children, data etc. + template + void parse_node_contents(Ch *&text, xml_node *node) + { + // For all children and text + while (1) + { + // Skip whitespace between > and node contents + Ch *contents_start = text; // Store start of node contents before whitespace is skipped + skip(text); + Ch next_char = *text; + + // After data nodes, instead of continuing the loop, control jumps here. + // This is because zero termination inside parse_and_append_data() function + // would wreak havoc with the above code. + // Also, skipping whitespace after data nodes is unnecessary. + after_data_node: + + // Determine what comes next: node closing, child node, data node, or 0? + switch (next_char) + { + + // Node closing or child node + case Ch('<'): + if (text[1] == Ch('/')) + { + // Text has only whitespace characters (clown, 2009/10/14, https://sourceforge.net/p/rapidxml/patches/4/). + if (contents_start != text && !(Flags & parse_trim_whitespace)) + parse_and_append_data(node, contents_start, contents_start); + + // Node closing + text += 2; // Skip '(text); + if (!internal::compare(node->name(), node->name_size(), closing_name, static_cast(text - closing_name), true)) + RAPIDXML_PARSE_ERROR("invalid closing tag name", text); + } + else + { + // No validation, just skip name + skip(text); + } + // Skip remaining whitespace after node name + skip(text); + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; // Skip '>' + return; // Node closed, finished parsing contents + } + else + { + // Child node + ++text; // Skip '<' + if (xml_node *child = parse_node(text)) + node->append_node(child); + + // Skip whitespace between closing-tag of child node + // and the next element (clown, 2009/10/14, https://sourceforge.net/p/rapidxml/patches/4/). + skip(text); + } + break; + + // End of data - error + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Data node + default: + next_char = parse_and_append_data(node, text, contents_start); + contents_start = text; // (clown, 2009/10/16, https://sourceforge.net/p/rapidxml/patches/4/) + goto after_data_node; // Bypass regular processing after data nodes + + } + } + } + + // Parse XML attributes of the node + template + void parse_node_attributes(Ch *&text, xml_node *node) + { + // For all attributes + while (attribute_name_pred::test(*text)) + { + // Extract attribute name + Ch *name = text; + ++text; // Skip first character of attribute name + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected attribute name", name); + + // Create new attribute + xml_attribute *attribute = this->allocate_attribute(); + attribute->name(name, static_cast(text - name)); + node->append_attribute(attribute); + + // Skip whitespace after attribute name + skip(text); + + // Skip = + if (*text != Ch('=')) + RAPIDXML_PARSE_ERROR("expected =", text); + ++text; + + // Add terminating zero after name + if (!(Flags & parse_no_string_terminators)) + attribute->name()[attribute->name_size()] = 0; + + // Skip whitespace after = + skip(text); + + // Skip quote and remember if it was ' or " + Ch quote = *text; + if (quote != Ch('\'') && quote != Ch('"')) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; + + // Extract attribute value and expand char refs in it + Ch *value = text, *end; + const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes + if (quote == Ch('\'')) + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + else + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + + // Set attribute value + attribute->value(value, static_cast(end - value)); + + // Make sure that end quote is present + if (*text != quote) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; // Skip quote + + // Add terminating zero after value + if (!(Flags & parse_no_string_terminators)) + attribute->value()[attribute->value_size()] = 0; + + // Skip whitespace after attribute value + skip(text); + } + } + + }; + + //! \cond internal + namespace internal + { + + // Whitespace (space \n \r \t) + template + const unsigned char lookup_tables::lookup_whitespace[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; + + // Node name (anything but space \n \r \t / > ? \0) + template + const unsigned char lookup_tables::lookup_node_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) (anything but < \0) + template + const unsigned char lookup_tables::lookup_text[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled + // (anything but < \0 &) + template + const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled + // (anything but < \0 & space \n \r \t) + template + const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute name (anything but space \n \r \t / < > = ? ! \0) + template + const unsigned char lookup_tables::lookup_attribute_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote (anything but ' \0) + template + const unsigned char lookup_tables::lookup_attribute_data_1[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote that does not require processing (anything but ' \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote (anything but " \0) + template + const unsigned char lookup_tables::lookup_attribute_data_2[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote that does not require processing (anything but " \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Digits (dec and hex, 255 denotes end of numeric character reference) + template + const unsigned char lookup_tables::lookup_digits[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F + }; + + // Upper case conversion + template + const unsigned char lookup_tables::lookup_upcase[256] = + { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F + }; + } + //! \endcond + +} + +// Undefine internal macros +#undef RAPIDXML_PARSE_ERROR + +// On MSVC, restore warnings state +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif // __RAPIDXML_H__ diff --git a/src/fne/xml/rapidxml_print.h b/src/fne/xml/rapidxml_print.h new file mode 100644 index 00000000..5c270c7a --- /dev/null +++ b/src/fne/xml/rapidxml_print.h @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: MIT +/* + * Digital Voice Modem - Common Library + * MIT Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2006, 2009 Marcin Kalicinski + * Copyright (C) 2019 https://github.com/Fe-Bell/RapidXML + * + */ +/** + * @defgroup xml Extensible Markup Langauge + * @brief Defines and implements XML handling. + * @ingroup common + * + * @file rapidxml_print.h + * @ingroup xml + */ +#if !defined(__RAPIDXML_PRINT_H__) +#define __RAPIDXML_PRINT_H__ + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Copyright (C) 2019 https://github.com/Fe-Bell/RapidXML +// Version 1.17 +// Revision $DateTime: 2023/09/19 23:27:00 $ +//! \file rapidxml_print.h This file contains rapidxml printer implementation + +#include "xml/rapidxml.h" + +// Only include streams if not disabled +#ifndef RAPIDXML_NO_STREAMS + #include + #include +#endif + +namespace rapidxml +{ + + /////////////////////////////////////////////////////////////////////// + // Printing flags + + const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. + + /////////////////////////////////////////////////////////////////////// + // Internal + + //! \cond internal + namespace internal + { + + /////////////////////////////////////////////////////////////////////////// + // Internal character operations + + // Copy characters from given range to given output iterator + template + inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out) + { + while (begin != end) + *out++ = *begin++; + return out; + } + + // Copy characters from given range to given output iterator and expand + // characters into references (< > ' " &) + template + inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out) + { + while (begin != end) + { + if (*begin == noexpand) + { + *out++ = *begin; // No expansion, copy character + } + else + { + switch (*begin) + { + case Ch('<'): + *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('>'): + *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('\''): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';'); + break; + case Ch('"'): + *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('&'): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';'); + break; + default: + *out++ = *begin; // No expansion, copy character + } + } + ++begin; // Step to next character + } + return out; + } + + // Fill given output iterator with repetitions of the same character + template + inline OutIt fill_chars(OutIt out, int n, Ch ch) + { + for (int i = 0; i < n; ++i) + *out++ = ch; + return out; + } + + // Find character + template + inline bool find_char(const Ch *begin, const Ch *end) + { + while (begin != end) + if (*begin++ == ch) + return true; + return false; + } + + /////////////////////////////////////////////////////////////////////////// + // Internal printing operations + + template + inline OutIt print_children(OutIt out, const xml_node *node, int flags, int indent); + + template + inline OutIt print_attributes(OutIt out, const xml_node *node, int flags); + + template + inline OutIt print_data_node(OutIt out, const xml_node *node, int flags, int indent); + + template + inline OutIt print_cdata_node(OutIt out, const xml_node *node, int flags, int indent); + + template + inline OutIt print_element_node(OutIt out, const xml_node *node, int flags, int indent); + + template + inline OutIt print_declaration_node(OutIt out, const xml_node *node, int flags, int indent); + + template + inline OutIt print_comment_node(OutIt out, const xml_node *node, int flags, int indent); + + template + inline OutIt print_doctype_node(OutIt out, const xml_node *node, int flags, int indent); + + template + inline OutIt print_pi_node(OutIt out, const xml_node *node, int flags, int indent); + + // Print node + template + inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print proper node type + switch (node->type()) + { + + // Document + case node_type::node_document: + out = print_children(out, node, flags, indent); + break; + + // Element + case node_type::node_element: + out = print_element_node(out, node, flags, indent); + break; + + // Data + case node_type::node_data: + out = print_data_node(out, node, flags, indent); + break; + + // CDATA + case node_type::node_cdata: + out = print_cdata_node(out, node, flags, indent); + break; + + // Declaration + case node_type::node_declaration: + out = print_declaration_node(out, node, flags, indent); + break; + + // Comment + case node_type::node_comment: + out = print_comment_node(out, node, flags, indent); + break; + + // Doctype + case node_type::node_doctype: + out = print_doctype_node(out, node, flags, indent); + break; + + // Pi + case node_type::node_pi: + out = print_pi_node(out, node, flags, indent); + break; + + // Unknown + default: + assert(0); + break; + } + + // If indenting not disabled, add line break after node + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + + // Return modified iterator + return out; + } + + // Print children of the node + template + inline OutIt print_children(OutIt out, const xml_node *node, int flags, int indent) + { + for (xml_node *child = node->first_node(); child; child = child->next_sibling()) + out = print_node(out, child, flags, indent); + return out; + } + + // Print attributes of the node + template + inline OutIt print_attributes(OutIt out, const xml_node *node, int flags) + { + for (xml_attribute *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + if (attribute->name() && attribute->value()) + { + // Print attribute name + *out = Ch(' '), ++out; + out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); + *out = Ch('='), ++out; + // Print attribute value using appropriate quote type + if (find_char(attribute->value(), attribute->value() + attribute->value_size())) + { + *out = Ch('\''), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); + *out = Ch('\''), ++out; + } + else + { + *out = Ch('"'), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); + *out = Ch('"'), ++out; + } + } + } + return out; + } + + // Print data node + template + inline OutIt print_data_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_type::node_data); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + return out; + } + + // Print data node + template + inline OutIt print_cdata_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_type::node_cdata); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'); ++out; + *out = Ch('!'); ++out; + *out = Ch('['); ++out; + *out = Ch('C'); ++out; + *out = Ch('D'); ++out; + *out = Ch('A'); ++out; + *out = Ch('T'); ++out; + *out = Ch('A'); ++out; + *out = Ch('['); ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch(']'); ++out; + *out = Ch(']'); ++out; + *out = Ch('>'); ++out; + return out; + } + + // Print element node + template + inline OutIt print_element_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_type::node_element); + + // Print element name and attributes, if any + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + out = print_attributes(out, node, flags); + + // If node is childless + if (node->value_size() == 0 && !node->first_node()) + { + // Print childless node tag ending + *out = Ch('/'), ++out; + *out = Ch('>'), ++out; + } + else + { + // Print normal node tag ending + *out = Ch('>'), ++out; + + // Test if node contains a single data node only (and no other nodes) + xml_node *child = node->first_node(); + if (!child) + { + // If node has no children, only print its value without indenting + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + } + else if (child->next_sibling() == 0 && child->type() == node_type::node_data) + { + // If node has a sole data child, only print its value without indenting + out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); + } + else + { + // Print all children with full indenting + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + out = print_children(out, node, flags, indent + 1); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + } + + // Print node end + *out = Ch('<'), ++out; + *out = Ch('/'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch('>'), ++out; + } + return out; + } + + // Print declaration node + template + inline OutIt print_declaration_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print declaration start + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + *out = Ch('x'), ++out; + *out = Ch('m'), ++out; + *out = Ch('l'), ++out; + + // Print attributes + out = print_attributes(out, node, flags); + + // Print declaration end + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + + return out; + } + + // Print comment node + template + inline OutIt print_comment_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_type::node_comment); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + *out = Ch('>'), ++out; + return out; + } + + // Print doctype node + template + inline OutIt print_doctype_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_type::node_doctype); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('D'), ++out; + *out = Ch('O'), ++out; + *out = Ch('C'), ++out; + *out = Ch('T'), ++out; + *out = Ch('Y'), ++out; + *out = Ch('P'), ++out; + *out = Ch('E'), ++out; + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('>'), ++out; + return out; + } + + // Print pi node + template + inline OutIt print_pi_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_type::node_pi); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + return out; + } + + } + //! \endcond + + /////////////////////////////////////////////////////////////////////////// + // Printing + + //! Prints XML to given output iterator. + //! \param out Output iterator to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output iterator pointing to position immediately after last character of printed text. + template + inline OutIt print(OutIt out, const xml_node &node, int flags = 0) + { + return internal::print_node(out, &node, flags, 0); + } + +#ifndef RAPIDXML_NO_STREAMS + + //! Prints XML to given output stream. + //! \param out Output stream to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output stream. + template + inline std::basic_ostream &print(std::basic_ostream &out, const xml_node &node, int flags = 0) + { + print(std::ostream_iterator(out), node, flags); + return out; + } + + //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. + //! \param out Output stream to print to. + //! \param node Node to be printed. + //! \return Output stream. + template + inline std::basic_ostream &operator <<(std::basic_ostream &out, const xml_node &node) + { + return print(out, node); + } + +#endif + +} + +#endif // __RAPIDXML_PRINT_H__ diff --git a/src/fne/xml/rapidxml_utils.h b/src/fne/xml/rapidxml_utils.h new file mode 100644 index 00000000..478608ec --- /dev/null +++ b/src/fne/xml/rapidxml_utils.h @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: MIT +/* + * Digital Voice Modem - Common Library + * MIT Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2006, 2009 Marcin Kalicinski + * Copyright (C) 2019 https://github.com/Fe-Bell/RapidXML + * + */ +/** + * @defgroup xml Extensible Markup Langauge + * @brief Defines and implements XML handling. + * @ingroup common + * + * @file rapidxml_utils.h + * @ingroup xml + */ +#if !defined(__RAPIDXML_UTILS_H__) +#define __RAPIDXML_UTILS_H__ + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Copyright (C) 2019 https://github.com/Fe-Bell/RapidXML +// Version 1.17 +// Revision $DateTime: 2023/09/19 23:27:00 $ +//! \file rapidxml_utils.h This file contains high-level rapidxml utilities that can be useful +//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective. + +#include "xml/rapidxml.h" +#include +#include +#include +#include + +namespace rapidxml +{ + + //! Represents data loaded from a file + template + class file + { + + public: + + //! Loads file into the memory. Data will be automatically destroyed by the destructor. + //! \param filename Filename to load. + file(const char *filename) + { + using namespace std; + + // Open stream + basic_ifstream stream(filename, ios::binary); + if (!stream) + throw runtime_error(string("cannot open file ") + filename); + stream.unsetf(ios::skipws); + + // Determine stream size + stream.seekg(0, ios::end); + size_t size = stream.tellg(); + stream.seekg(0); + + // Load data and add terminating 0 + m_data.resize(size + 1); + stream.read(&m_data.front(), static_cast(size)); + m_data[size] = 0; + } + + //! Loads file into the memory. Data will be automatically destroyed by the destructor + //! \param stream Stream to load from + file(std::basic_istream &stream) + { + using namespace std; + + // Load data and add terminating 0 + stream.unsetf(ios::skipws); + m_data.assign(istreambuf_iterator(stream), istreambuf_iterator()); + if (stream.fail() || stream.bad()) + throw runtime_error("error reading stream"); + m_data.push_back(0); + } + + //! Gets file data. + //! \return Pointer to data of file. + Ch *data() + { + return &m_data.front(); + } + + //! Gets file data. + //! \return Pointer to data of file. + const Ch *data() const + { + return &m_data.front(); + } + + //! Gets file data size. + //! \return Size of file data, in characters. + std::size_t size() const + { + return m_data.size(); + } + + private: + + std::vector m_data; // File data + + }; + + //! Counts children of node. Time complexity is O(n). + //! \return Number of children of node + template + inline std::size_t count_children(xml_node *node) + { + xml_node *child = node->first_node(); + std::size_t count = 0; + while (child) + { + ++count; + child = child->next_sibling(); + } + return count; + } + + //! Counts attributes of node. Time complexity is O(n). + //! \return Number of attributes of node + template + inline std::size_t count_attributes(xml_node *node) + { + xml_attribute *attr = node->first_attribute(); + std::size_t count = 0; + while (attr) + { + ++count; + attr = attr->next_attribute(); + } + return count; + } + +} + +#endif // __RAPIDXML_UTILS_H__ From 676a112313b76f4a91d2805920910431d543b393 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 10 Mar 2025 00:57:34 -0400 Subject: [PATCH 33/34] fix missing check for ENABLE_TCL_SSL compilation directive; properly flag crypto as hard disabled if ENABLE_TCL_SSL isn't set; --- src/fne/CryptoContainer.cpp | 4 +++- src/fne/HostFNE.cpp | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/fne/CryptoContainer.cpp b/src/fne/CryptoContainer.cpp index 2dbdffd8..ba471071 100644 --- a/src/fne/CryptoContainer.cpp +++ b/src/fne/CryptoContainer.cpp @@ -24,7 +24,7 @@ #if defined(ENABLE_TCP_SSL) #include #include -#endif +#endif // ENABLE_TCP_SSL using namespace crypto; @@ -38,6 +38,7 @@ using namespace crypto; // Global Functions // --------------------------------------------------------------------------- +#if defined(ENABLE_TCP_SSL) /** * @brief Calculates the length of a decoded base64 string. * @param b64input String containing the base64 encoded data. @@ -84,6 +85,7 @@ int base64Decode(char* b64message, uint8_t** buffer) return decodeLen; } +#endif // ENABLE_TCP_SSL /** * @brief diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index ad1ccb50..b131e92f 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -356,6 +356,9 @@ bool HostFNE::readParams() yaml::Node cryptoContainer = masterConf["crypto_container"]; bool cryptoContainerEnabled = cryptoContainer["enabled"].as(false); +#if !defined(ENABLE_TCP_SSL) + cryptoContainerEnabled = false; +#endif // ENABLE_TCP_SSL std::string cryptoContainerEKC = cryptoContainer["file"].as(); std::string cryptoContainerPassword = cryptoContainer["password"].as(); uint32_t cryptoContainerReload = cryptoContainer["time"].as(30U); From ded260216a4e4034f0f733953c3067b658f547f6 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 10 Mar 2025 14:41:13 -0400 Subject: [PATCH 34/34] implement the beginnings of KMM frame handling (for future use); --- src/common/CMakeLists.txt | 2 + src/common/dmr/DMRDefines.h | 2 + src/common/nxdn/NXDNDefines.h | 2 + src/common/p25/P25Defines.h | 54 +++++++ src/common/p25/kmm/KMMFrame.cpp | 107 +++++++++++++ src/common/p25/kmm/KMMFrame.h | 140 +++++++++++++++++ src/common/p25/kmm/KMMModifyKey.cpp | 171 +++++++++++++++++++++ src/common/p25/kmm/KMMModifyKey.h | 119 +++++++++++++++ src/common/p25/kmm/KeysetItem.h | 228 ++++++++++++++++++++++++++++ src/host/Host.cpp | 6 +- 10 files changed, 828 insertions(+), 3 deletions(-) create mode 100644 src/common/p25/kmm/KMMFrame.cpp create mode 100644 src/common/p25/kmm/KMMFrame.h create mode 100644 src/common/p25/kmm/KMMModifyKey.cpp create mode 100644 src/common/p25/kmm/KMMModifyKey.h create mode 100644 src/common/p25/kmm/KeysetItem.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 04cd4e5d..6f58ac17 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -28,6 +28,7 @@ file(GLOB common_SRC "src/common/p25/lc/tsbk/*.cpp" "src/common/p25/lc/tsbk/mbt/*.cpp" "src/common/p25/sndcp/*.cpp" + "src/common/p25/kmm/*.cpp" "src/common/p25/lookups/*.cpp" # NXDN module @@ -73,6 +74,7 @@ file(GLOB common_INCLUDE "src/common/p25/lc/tsbk/*.h" "src/common/p25/lc/tsbk/mbt/*.h" "src/common/p25/sndcp/*.h" + "src/common/p25/kmm/*.h" "src/common/p25/lookups/*.h" # NXDN module diff --git a/src/common/dmr/DMRDefines.h b/src/common/dmr/DMRDefines.h index 73a50dcd..86bd049c 100644 --- a/src/common/dmr/DMRDefines.h +++ b/src/common/dmr/DMRDefines.h @@ -22,7 +22,9 @@ #include "common/Defines.h" // Shorthand macro to dmr::defines -- keeps source code that doesn't use "using" concise +#if !defined(DMRDEF) #define DMRDEF dmr::defines +#endif // DMRDEF namespace dmr { namespace defines diff --git a/src/common/nxdn/NXDNDefines.h b/src/common/nxdn/NXDNDefines.h index a40c6af8..077c28e5 100644 --- a/src/common/nxdn/NXDNDefines.h +++ b/src/common/nxdn/NXDNDefines.h @@ -22,7 +22,9 @@ #include "common/Defines.h" // Shorthand macro to nxdn::defines -- keeps source code that doesn't use "using" concise +#if !defined(NXDDEF) #define NXDDEF nxdn::defines +#endif // NXDDEF namespace nxdn { namespace defines diff --git a/src/common/p25/P25Defines.h b/src/common/p25/P25Defines.h index 31bd2ccc..6e7b2505 100644 --- a/src/common/p25/P25Defines.h +++ b/src/common/p25/P25Defines.h @@ -22,7 +22,9 @@ #include "common/Defines.h" // Shorthand macro to p25::defines -- keeps source code that doesn't use "using" concise +#if !defined(P25DEF) #define P25DEF p25::defines +#endif // P25DEF namespace p25 { namespace defines @@ -411,6 +413,58 @@ namespace p25 }; } + /** @brief KMM Message Type */ + namespace KMM_MessageType { + enum : uint8_t { + NULL_CMD = 0x00U, //! Null + + CHANGE_RSI_CMD = 0x03U, //! Change RSI Command + CHANGE_RSI_RSP = 0x04U, //! Change RSI Response + CHANGEOVER_CMD = 0x05U, //! Changeover Command + CHANGEOVER_RSP = 0x06U, //! Changeover Response + + HELLO = 0x0CU, //! Hello + + INVENTORY_CMD = 0x0DU, //! Inventory Command + INVENTORY_RSP = 0x0EU, //! Inventory Response + + MODIFY_KEY_CMD = 0x13U, //! Modify Key Command + + NAK = 0x16U, //! Negative Ack + + ZEROIZE_CMD = 0x21U, //! Zeroize Command + ZEROIZE_RSP = 0x22U, //! Zeroize Response + }; + } + + /** @brief KMM Response Kind */ + namespace KMM_ResponseKind { + enum : uint8_t { + NONE = 0x00U, //! Response Kind 1 (None) + DELAYED = 0x01U, //! Response Kind 2 (Delayed) + IMMEDIATE = 0x02U, //! Response Kind 3 (Immediate) + }; + } + + /** @brief KMM Message Authentication */ + namespace KMM_MAC { + enum : uint8_t { + NO_MAC = 0x00U, //! No Message Authentication + ENH_MAC = 0x02U, //! Enhanced Message Authentication + DES_MAC = 0x03U, //! DES Message Authentication + }; + } + + /** @brief KMM Decryption Instruction - None */ + const uint8_t KMM_DECRYPT_INSTRUCT_NONE = 0x00U; + /** @brief KMM Decryption Instruction - Message Indicator */ + const uint8_t KMM_DECRYPT_INSTRUCT_MI = 0x40U; + + /** @brief KMM Key Format TEK */ + const uint8_t KEY_FORMAT_TEK = 0x80U; + /** @brief KMM Key Format Delete Key */ + const uint8_t KEY_FORMAT_DELETE = 0x20U; + /** @brief SNDCP version 1 */ const uint8_t SNDCP_VERSION_1 = 0x01U; /** @brief 296 byte MTU */ diff --git a/src/common/p25/kmm/KMMFrame.cpp b/src/common/p25/kmm/KMMFrame.cpp new file mode 100644 index 00000000..10cd7f29 --- /dev/null +++ b/src/common/p25/kmm/KMMFrame.cpp @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "p25/P25Defines.h" +#include "p25/kmm/KMMFrame.h" +#include "Log.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::kmm; + +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a copy instance of the KMMFrame class. */ + +KMMFrame::KMMFrame(const KMMFrame& data) : KMMFrame() +{ + copy(data); +} + +/* Initializes a new instance of the KMMFrame class. */ + +KMMFrame::KMMFrame() : + m_messageId(KMM_MessageType::NULL_CMD), + m_messageLength(7U), + m_respKind(KMM_ResponseKind::NONE), + m_complete(true), + m_mfMessageNumber(0U), + m_mfMac(KMM_MAC::NO_MAC) +{ + /* stub */ +} + +/* Finalizes a instance of the KMMFrame class. */ + +KMMFrame::~KMMFrame() = default; + +// --------------------------------------------------------------------------- +// Protected Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to decode a SNDCP header. */ + +bool KMMFrame::decodeHeader(const uint8_t* data, bool outbound) +{ + assert(data != nullptr); + + m_messageId = data[0U]; // Message ID + m_messageLength = __GET_UINT16B(data, 1U); // Message Length + + m_respKind = (data[2U] >> 6U) & 0x03U; // Response Kind + m_mfMessageNumber = (data[2U] >> 4U) & 0x03U; // Message Number + m_mfMac = (data[2U] >> 2U) & 0x03U; // MAC + + bool done = (data[2U] & 0x01U) == 0x01U; // Done Flag + if (!done) + m_complete = true; + else + m_complete = false; + + m_dstLlId = __GET_UINT16(data, 4U); // Destination RSI + m_srcLlId = __GET_UINT16(data, 7U); // Source RSI + + return true; +} + +/* Internal helper to encode a SNDCP header. */ + +void KMMFrame::encodeHeader(uint8_t* data, bool outbound) +{ + assert(data != nullptr); + + data[0U] = m_messageId; // Message ID + __SET_UINT16B(m_messageLength, data, 2U); // Message Length + + data[2U] = ((m_respKind & 0x03U) << 6U) + // Response Kind + ((m_mfMessageNumber & 0x03U) << 4U) + // Message Number + ((m_mfMac & 0x03U) << 2U) + // MAC + ((!m_complete) ? 0x01U : 0x00U); // Done Flag + + __SET_UINT16(m_dstLlId, data, 4U); // Destination RSI + __SET_UINT16(m_srcLlId, data, 7U); // Source RSI +} + +/* Internal helper to copy the the class. */ + +void KMMFrame::copy(const KMMFrame& data) +{ + m_messageId = data.m_messageId; + m_messageLength = data.m_messageLength; + m_respKind = data.m_respKind; + m_complete = data.m_complete; + + m_mfMessageNumber = data.m_mfMessageNumber; + m_mfMac = data.m_mfMac; +} diff --git a/src/common/p25/kmm/KMMFrame.h b/src/common/p25/kmm/KMMFrame.h new file mode 100644 index 00000000..ef9ab0aa --- /dev/null +++ b/src/common/p25/kmm/KMMFrame.h @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @defgroup p25_kmm Key Management Message + * @brief Implementation for the data handling of the TIA-102.AACA Project 25 standard. + * @ingroup p25 + * + * @file KMMFrame.h + * @ingroup p25_kmm + * @file KMMFrame.cpp + * @ingroup p25_kmm + */ +#if !defined(__P25_KMM__KMM_FRAME_H__) +#define __P25_KMM__KMM_FRAME_H__ + +#include "common/Defines.h" +#include "common/Utils.h" + +#include + +namespace p25 +{ + namespace kmm + { + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + + /** + * @addtogroup p25_kmm + * @{ + */ + + const uint32_t KMM_FRAME_LENGTH = 9U; + + /** @} */ + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents a KMM frame packet header. + * @ingroup p25_kmm + */ + class HOST_SW_API KMMFrame { + public: + /** + * @brief Initializes a copy instance of the KMMFrame class. + * @param data Instance of KMMFrame to copy/ + */ + KMMFrame(const KMMFrame& data); + /** + * @brief Initializes a new instance of the KMMFrame class. + */ + KMMFrame(); + /** + * @brief Finalizes a instance of the KMMFrame class. + */ + ~KMMFrame(); + + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + virtual uint32_t length() const { return KMM_FRAME_LENGTH; } + + /** + * @brief Decode a KMM frame. + * @param[in] data Buffer containing KMM frame data to decode. + * @returns bool True, if decoded, otherwise false. + */ + virtual bool decode(const uint8_t* data) = 0; + /** + * @brief Encode a KMM frame. + * @param[out] data Buffer to encode KMM frame data to. + */ + virtual void encode(uint8_t* data) = 0; + + public: + // Common Data + /** + * @brief + */ + __PROTECTED_PROPERTY(uint8_t, messageId, MessageId); + /** + * @brief + */ + __PROTECTED_PROPERTY(uint16_t, messageLength, MessageLength); + + /** + * @brief + */ + __PROTECTED_PROPERTY(uint8_t, respKind, ResponseKind); + + /** + * @brief Destination Logical link ID. + */ + __PROTECTED_PROPERTY(uint32_t, dstLlId, DstLLId); + /** + * @brief Source Logical link ID. + */ + __PROTECTED_PROPERTY(uint32_t, srcLlId, SrcLLId); + + /** + * @brief + */ + __PROTECTED_PROPERTY(bool, complete, Complete); + + protected: + uint8_t m_mfMessageNumber; + uint8_t m_mfMac; + + /** + * @brief Internal helper to decode a KMM header. + * @param data Buffer containing KMM packet data to decode. + * @param outbound Flag indicating whether the packet is inbound or outbound. + * @returns bool True, if decoded, otherwise false. + */ + bool decodeHeader(const uint8_t* data, bool outbound = false); + /** + * @brief Internal helper to encode a KMM header. + * @param data Buffer to encode KMM packet data to. + * @param outbound Flag indicating whether the packet is inbound or outbound. + */ + void encodeHeader(uint8_t* data, bool outbound = false); + + __PROTECTED_COPY(KMMFrame); + }; + } // namespace kmm +} // namespace p25 + +#endif // __P25_KMM__KMM_FRAME_H__ diff --git a/src/common/p25/kmm/KMMModifyKey.cpp b/src/common/p25/kmm/KMMModifyKey.cpp new file mode 100644 index 00000000..4c86e389 --- /dev/null +++ b/src/common/p25/kmm/KMMModifyKey.cpp @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "p25/P25Defines.h" +#include "p25/kmm/KMMModifyKey.h" +#include "Log.h" + +using namespace p25; +using namespace p25::defines; +using namespace p25::kmm; + +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the KMMModifyKey class. */ + +KMMModifyKey::KMMModifyKey() : KMMFrame(), + m_decryptInfoFmt(KMM_DECRYPT_INSTRUCT_NONE), + m_algId(ALGO_UNENCRYPT), + m_kId(0U), + m_keysetItem(), + m_miSet(false), + m_mi(nullptr) +{ + m_mi = new uint8_t[MI_LENGTH_BYTES]; + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); +} + +/* Finalizes a instance of the KMMModifyKey class. */ + +KMMModifyKey::~KMMModifyKey() +{ + if (m_mi != nullptr) { + delete[] m_mi; + m_mi = nullptr; + } +} + +/* Gets the byte length of this KMMFrame. */ + +uint32_t KMMModifyKey::length() const +{ + uint32_t len = KMM_MODIFY_KEY_LENGTH; + if (m_miSet) + len += MI_LENGTH_BYTES; + len += m_keysetItem.length(); + + return len; +} + +/* Decode a KMM modify key. */ + +bool KMMModifyKey::decode(const uint8_t* data) +{ + assert(data != nullptr); + + KMMFrame::decodeHeader(data, false); + + m_decryptInfoFmt = data[10U]; // Decryption Instruction Format + m_algId = data[11U]; // Algorithm ID + m_kId = data[12U]; // Key ID + + uint16_t offset = 0U; + if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + ::memcpy(m_mi, data + 13U, MI_LENGTH_BYTES); + offset += 9U; + } + + m_keysetItem.keysetId(data[13U + offset]); + m_keysetItem.algId(data[14U + offset]); + m_keysetItem.keyLength(data[15U + offset]); + + uint8_t keyCount = data[16U + offset]; + for (uint8_t i = 0U; i < keyCount; i++) { + KeyItem* key = new KeyItem(); + + UInt8Array __keyPayload = std::make_unique(m_keysetItem.keyLength()); + uint8_t* keyPayload = __keyPayload.get(); + + uint8_t keyFormat = data[17U + offset]; + uint8_t keyNameLen = keyFormat & 0x1FU; + + key->keyFormat(keyFormat & 0xE0U); + key->sln(data[18U + offset]); + key->kId(data[19U + offset]); + + ::memcpy(keyPayload, data + (20U + offset), m_keysetItem.keyLength()); + key->setKey(keyPayload, m_keysetItem.keyLength()); + + m_keysetItem.push_back(key); + + offset += 5U + keyNameLen + m_keysetItem.keyLength(); + } + + return true; +} + +/* Encode a KMM modify key. */ + +void KMMModifyKey::encode(uint8_t* data) +{ + assert(data != nullptr); + + KMMFrame::encodeHeader(data, true); + + if (!m_miSet && m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { + m_decryptInfoFmt = KMM_DECRYPT_INSTRUCT_NONE; + } + + data[10U] = m_decryptInfoFmt; // Decryption Instruction Format + data[11U] = m_algId; // Algorithm ID + data[12U] = m_kId; // Key ID + + uint16_t offset = 0U; + if (m_decryptInfoFmt == KMM_DECRYPT_INSTRUCT_MI) { + ::memcpy(data + 13U, m_mi, MI_LENGTH_BYTES); + offset += 9U; + } + + data[13U + offset] = m_keysetItem.keysetId(); + data[14U + offset] = m_keysetItem.algId(); + data[15U + offset] = m_keysetItem.keyLength(); + + uint8_t keyCount = m_keysetItem.keys().size(); + data[16U + offset] = keyCount; + for (auto key : m_keysetItem.keys()) { + uint8_t keyNameLen = key->keyFormat() & 0x1FU; + data[17U + offset] = key->keyFormat(); + data[18U + offset] = key->sln(); + data[19U + offset] = key->kId(); + + UInt8Array __keyPayload = std::make_unique(m_keysetItem.keyLength()); + uint8_t* keyPayload = __keyPayload.get(); + key->getKey(keyPayload); + + ::memcpy(data + (20U + offset), keyPayload, m_keysetItem.keyLength()); + + offset += 5U + keyNameLen + m_keysetItem.keyLength(); + } +} + +// --------------------------------------------------------------------------- +// Protected Class Members +// --------------------------------------------------------------------------- + +/* Internal helper to copy the the class. */ + +void KMMModifyKey::copy(const KMMModifyKey& data) +{ + m_decryptInfoFmt = data.m_decryptInfoFmt; + m_algId = data.m_algId; + m_kId = data.m_kId; + + if (data.m_mi != nullptr) { + ::memset(m_mi, 0x00U, MI_LENGTH_BYTES); + ::memcpy(m_mi, data.m_mi, MI_LENGTH_BYTES); + } + + m_keysetItem = data.m_keysetItem; +} diff --git a/src/common/p25/kmm/KMMModifyKey.h b/src/common/p25/kmm/KMMModifyKey.h new file mode 100644 index 00000000..6e8ae200 --- /dev/null +++ b/src/common/p25/kmm/KMMModifyKey.h @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file KMMModifyKey.h + * @ingroup p25_kmm + * @file KMMModifyKey.cpp + * @ingroup p25_kmm + */ +#if !defined(__P25_KMM__KMM_MODIFY_KEY_H__) +#define __P25_KMM__KMM_MODIFY_KEY_H__ + +#include "common/Defines.h" +#include "common/p25/kmm/KMMFrame.h" +#include "common/p25/kmm/KeysetItem.h" +#include "common/Utils.h" + +#include +#include + +namespace p25 +{ + namespace kmm + { + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + + /** + * @addtogroup p25_kmm + * @{ + */ + + const uint32_t KMM_MODIFY_KEY_LENGTH = KMM_FRAME_LENGTH + 4U; + + /** @} */ + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + class HOST_SW_API KMMModifyKey : public KMMFrame { + public: + /** + * @brief Initializes a new instance of the KMMModifyKey class. + */ + KMMModifyKey(); + /** + * @brief Finalizes a instance of the KMMModifyKey class. + */ + ~KMMModifyKey(); + + /** + * @brief Gets the byte length of this KMMFrame. + * @return uint32_t Length of KMMFrame. + */ + uint32_t length() const override; + + /** + * @brief Decode a KMM modify key packet. + * @param[in] data Buffer containing KMM packet data to decode. + * @returns bool True, if decoded, otherwise false. + */ + bool decode(const uint8_t* data); + /** + * @brief Encode a KMM modify key packet. + * @param[out] data Buffer to encode KMM packet data to. + */ + void encode(uint8_t* data); + + /** @name Encryption data */ + /** + * @brief Sets the encryption message indicator. + * @param[in] mi Buffer containing the 9-byte Message Indicator. + */ + void setMI(const uint8_t* mi); + /** + * @brief Gets the encryption message indicator. + * @param[out] mi Buffer containing the 9-byte Message Indicator. + */ + void getMI(uint8_t* mi) const; + /** @} */ + + public: + /** + * @brief + */ + __PROPERTY(uint8_t, decryptInfoFmt, DecryptInfoFmt); + /** + * @brief Encryption algorithm ID. + */ + __PROPERTY(uint8_t, algId, AlgId); + /** + * @brief Encryption key ID. + */ + __PROPERTY(uint32_t, kId, KId); + + /** + * @brief + */ + __PROPERTY(KeysetItem, keysetItem, KeysetItem); + + __COPY(KMMModifyKey); + + private: + // Encryption data + bool m_miSet; + uint8_t* m_mi; + }; + } // namespace kmm +} // namespace p25 + +#endif // __P25_KMM__KMM_MODIFY_KEY_H__ diff --git a/src/common/p25/kmm/KeysetItem.h b/src/common/p25/kmm/KeysetItem.h new file mode 100644 index 00000000..395ed00d --- /dev/null +++ b/src/common/p25/kmm/KeysetItem.h @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2025 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file KeysetItem.h + * @ingroup p25_kmm + */ +#if !defined(__P25_KMM__KEYSET_ITEM_H__) +#define __P25_KMM__KEYSET_ITEM_H__ + +#include "common/Defines.h" +#include "common/Utils.h" + +#include +#include + +namespace p25 +{ + namespace kmm + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents a key set item within a KMM frame packet. + * @ingroup p25_kmm + */ + class HOST_SW_API KeyItem { + public: + /** + * @brief Initializes a new instance of the KeyItem class. + */ + KeyItem() : + m_keyFormat(0x80U/*P25DEF::KEY_FORMAT_TEK*/), + m_sln(0U), + m_kId(0U), + m_keyLength(0U), + m_keyMaterial(nullptr) + { + /* stub */ + } + /** + * @brief Finalizes a instance of the KeyItem class. + */ + ~KeyItem() + { + if (m_keyMaterial != nullptr) { + delete[] m_keyMaterial; + m_keyMaterial = nullptr; + } + } + + /** + * @brief Equals operator. Copies this KeyItem to another KeyItem. + * @param data Instance of KeyItem to copy. + */ + virtual KeyItem& operator= (const KeyItem& data) + { + if (this != &data) { + m_keyFormat = data.m_keyFormat; + m_sln = data.m_sln; + m_kId = data.m_kId; + + if (data.m_keyLength > 0U) { + if (m_keyMaterial != nullptr) { + delete[] m_keyMaterial; + } + + m_keyMaterial = new uint8_t[data.m_keyLength]; + m_keyLength = data.m_keyLength; + ::memcpy(m_keyMaterial, data.m_keyMaterial, data.m_keyLength); + } + } + + return *this; + } + + /** + * @brief Set the key material. + * @param key + * @param keyLength + */ + void setKey(const uint8_t* key, uint32_t keyLength) + { + assert(key != nullptr); + m_keyLength = keyLength; + ::memcpy(m_keyMaterial, key, keyLength); + } + + /** + * @brief Get the key material. + * @param key + */ + void getKey(uint8_t* key) const + { + assert(key != nullptr); + ::memcpy(key, m_keyMaterial, m_keyLength); + } + + public: + /** + * @brief + */ + __PROPERTY_PLAIN(uint8_t, keyFormat); + /** + * @brief + */ + __PROPERTY_PLAIN(uint16_t, sln); + /** + * @brief + */ + __PROPERTY_PLAIN(uint16_t, kId); + + private: + uint32_t m_keyLength; + uint8_t* m_keyMaterial; + }; + + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents a key set item within a KMM frame packet. + * @ingroup p25_kmm + */ + class HOST_SW_API KeysetItem { + public: + /** + * @brief Initializes a new instance of the KeysetItem class. + */ + KeysetItem() : + m_keysetId(0U), + m_algId(0U), + m_keyLength(0U), + m_keys() + { + /* stub */ + } + /** + * @brief Finalizes a instance of the KeysetItem class. + */ + ~KeysetItem() + { + for (auto key : m_keys) { + delete key; + } + } + + /** + * @brief Equals operator. Copies this KeysetItem to another KeysetItem. + * @param data Instance of KeysetItem to copy. + */ + virtual KeysetItem& operator= (const KeysetItem& data) + { + if (this != &data) { + m_keysetId = data.m_keysetId; + m_algId = data.m_algId; + m_keyLength = data.m_keyLength; + + for (auto key : m_keys) { + delete key; + } + m_keys.clear(); + + for (auto key : data.m_keys) { + KeyItem* copy = key; + m_keys.push_back(copy); + } + } + + return *this; + } + + /** + * @brief Gets the byte length of this keyset item. + * @return uint32_t Length of keyset item. + */ + uint32_t length() const + { + uint32_t len = 4U; + + uint32_t keyItemLength = m_keys.size() * 5U; + uint32_t combinedKeyLength = m_keys.size() * m_keyLength; + len += keyItemLength + combinedKeyLength; + + return len; + } + + /** + * @brief Add a key to the key list. + * @param key + */ + void push_back(KeyItem* key) + { + m_keys.push_back(key); + } + + public: + /** + * @brief + */ + __PROPERTY_PLAIN(uint8_t, keysetId); + /** + * @brief Encryption algorithm ID. + */ + __PROPERTY_PLAIN(uint8_t, algId); + /** + * @brief + */ + __PROPERTY_PLAIN(uint8_t, keyLength); + + /** + * @brief List of keys. + */ + __PROPERTY_PLAIN(std::vector, keys); + }; + } // namespace kmm +} // namespace p25 + +#endif // __P25_KMM__KEYSET_ITEM_H__ diff --git a/src/host/Host.cpp b/src/host/Host.cpp index 09d418f4..8c4a0d6b 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -1519,9 +1519,9 @@ bool Host::rmtPortModemHandler(Modem* modem, uint32_t ms, modem::RESP_TYPE_DVM r if (modem->getTrace()) Utils::dump(1U, "TX Remote Data", buffer, len); - // never send less then 3 bytes - if (len < 3U) - return false; + // never send less then 3 bytes + if (len < 3U) + return false; // send entire modem packet over the remote port m_modemRemotePort->write(buffer, len);