From c8eb52a57d6285171d5eb98969ac574e0e124e06 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 7 Jun 2024 09:03:37 -0400 Subject: [PATCH] add try {} catch {} around some critical ASIO read calls to prevent application crash (this won't resolve the errors but should prevent application crash); implement support for peer ID list that *always* receives traffic for a TGID regardless of affiliation rules; --- configs/talkgroup_rules.example.yml | 12 ++++++ src/common/lookups/TalkgroupRulesLookup.h | 23 ++++++++++ .../network/rest/http/ClientConnection.h | 42 ++++++++++-------- .../rest/http/SecureClientConnection.h | 40 +++++++++-------- .../rest/http/SecureServerConnection.h | 41 ++++++++++-------- .../network/rest/http/ServerConnection.h | 43 +++++++++++-------- src/fne/network/fne/TagDMRData.cpp | 9 ++++ src/fne/network/fne/TagNXDNData.cpp | 9 ++++ src/fne/network/fne/TagP25Data.cpp | 9 ++++ 9 files changed, 154 insertions(+), 74 deletions(-) diff --git a/configs/talkgroup_rules.example.yml b/configs/talkgroup_rules.example.yml index 8f24b1c8..9cc38c1b 100644 --- a/configs/talkgroup_rules.example.yml +++ b/configs/talkgroup_rules.example.yml @@ -24,6 +24,8 @@ groupVoice: exclusion: [] # List of peer talkgroup rewrites. rewrite: [] + # List of peer IDs that always receive traffic for this talkgroup regardless of affiliation rules. + always: [] # List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access, # sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming). # If this list is empty *all* peers are preferred. (Trunking Only) @@ -55,6 +57,8 @@ groupVoice: exclusion: [] # List of peer talkgroup rewrites. rewrite: [] + # List of peer IDs that always receive traffic for this talkgroup regardless of affiliation rules. + always: [] # List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access, # sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming). # If this list is empty *all* peers are preferred. (Trunking Only) @@ -92,6 +96,8 @@ groupVoice: tgid: 9999 # DMR slot number. slot: 1 + # List of peer IDs that always receive traffic for this talkgroup regardless of affiliation rules. + always: [] # List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access, # sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming). # If this list is empty *all* peers are preferred. (Trunking Only) @@ -121,6 +127,8 @@ groupVoice: exclusion: [] # List of peer talkgroup rewrites. rewrite: [] + # List of peer IDs that always receive traffic for this talkgroup regardless of affiliation rules. + always: [] # List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access, # sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming). # If this list is empty *all* peers are preferred. (Trunking Only) @@ -150,6 +158,8 @@ groupVoice: exclusion: [] # List of peer talkgroup rewrites. rewrite: [] + # List of peer IDs that always receive traffic for this talkgroup regardless of affiliation rules. + always: [] # List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access, # sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming). # If this list is empty *all* peers are preferred. (Trunking Only) @@ -179,6 +189,8 @@ groupVoice: exclusion: [] # List of peer talkgroup rewrites. rewrite: [] + # List of peer IDs that always receive traffic for this talkgroup regardless of affiliation rules. + always: [] # List of site CC peer IDs defining talkgroup access preference (peers listed here will be preferred for access, # sites not listed here will be non-preferred and will cause a AFF_GRP_RSP DENY, typically triggering roaming). # If this list is empty *all* peers are preferred. (Trunking Only) diff --git a/src/common/lookups/TalkgroupRulesLookup.h b/src/common/lookups/TalkgroupRulesLookup.h index 0d3d2027..a22c8556 100644 --- a/src/common/lookups/TalkgroupRulesLookup.h +++ b/src/common/lookups/TalkgroupRulesLookup.h @@ -143,6 +143,7 @@ namespace lookups m_inclusion(), m_exclusion(), m_rewrite(), + m_alwaysSend(), m_preferred(), m_nonPreferred(false) { @@ -181,6 +182,14 @@ namespace lookups } } + yaml::Node& alwaysSendList = node["always"]; + if (alwaysSendList.size() > 0U) { + for (size_t i = 0; i < alwaysSendList.size(); i++) { + uint32_t peerId = alwaysSendList[i].as(0U); + m_alwaysSend.push_back(peerId); + } + } + yaml::Node& preferredList = node["preferred"]; if (preferredList.size() > 0U) { for (size_t i = 0; i < preferredList.size(); i++) { @@ -200,6 +209,7 @@ namespace lookups m_inclusion = data.m_inclusion; m_exclusion = data.m_exclusion; m_rewrite = data.m_rewrite; + m_alwaysSend = data.m_alwaysSend; m_preferred = data.m_preferred; m_nonPreferred = data.m_nonPreferred; } @@ -213,6 +223,8 @@ namespace lookups uint8_t exclusionSize() const { return m_exclusion.size(); } /// Gets the count of rewrites. uint8_t rewriteSize() const { return m_rewrite.size(); } + /// Gets the count of always send. + uint8_t alwaysSendSize() const { return m_alwaysSend.size(); } /// Gets the count of rewrites. uint8_t preferredSize() const { return m_preferred.size(); } @@ -252,6 +264,15 @@ namespace lookups } node["rewrite"] = rewriteList; + yaml::Node alwaysSendList; + if (m_alwaysSend.size() > 0U) { + for (auto alw : m_alwaysSend) { + yaml::Node& newAlw = alwaysSendList.push_back(); + newAlw = __INT_STR(alw); + } + } + node["always"] = alwaysSendList; + yaml::Node preferredList; if (m_preferred.size() > 0U) { for (auto pref : m_preferred) { @@ -275,6 +296,8 @@ namespace lookups __PROPERTY_PLAIN(std::vector, exclusion); /// List of rewrites performed by this rule. __PROPERTY_PLAIN(std::vector, rewrite); + /// List of always send performed by this rule. + __PROPERTY_PLAIN(std::vector, alwaysSend); /// List of peer IDs preferred by this rule. __PROPERTY_PLAIN(std::vector, preferred); diff --git a/src/common/network/rest/http/ClientConnection.h b/src/common/network/rest/http/ClientConnection.h index 0290144a..cdf6da62 100644 --- a/src/common/network/rest/http/ClientConnection.h +++ b/src/common/network/rest/http/ClientConnection.h @@ -7,7 +7,7 @@ * @package DVM / Common Library * @license GPLv2 License (https://opensource.org/licenses/GPL-2.0) * -* Copyright (C) 2023 Bryan Biedenkapp, N2PLL +* Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL * */ #if !defined(__REST_HTTP__CLIENT_CONNECTION_H__) @@ -100,25 +100,29 @@ namespace network HTTPLexer::ResultType result; char* content; - std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); - - std::string contentLength = m_request.headers.find("Content-Length"); - if (contentLength != "") { - size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); - m_request.content = std::string(content, length); - } - - m_request.headers.add("RemoteHost", m_socket.remote_endpoint().address().to_string()); - - if (result == HTTPLexer::GOOD) { - m_requestHandler.handleRequest(m_request, m_reply); - } - else if (result == HTTPLexer::BAD) { - return; - } - else { - read(); + try + { + std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); + + std::string contentLength = m_request.headers.find("Content-Length"); + if (contentLength != "") { + size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); + m_request.content = std::string(content, length); + } + + m_request.headers.add("RemoteHost", m_socket.remote_endpoint().address().to_string()); + + if (result == HTTPLexer::GOOD) { + m_requestHandler.handleRequest(m_request, m_reply); + } + else if (result == HTTPLexer::BAD) { + return; + } + else { + read(); + } } + catch(const std::exception& e) { ::LogError(LOG_REST, "ClientConnection::read(), %s", ec.message().c_str()); } } else if (ec != asio::error::operation_aborted) { if (ec) { diff --git a/src/common/network/rest/http/SecureClientConnection.h b/src/common/network/rest/http/SecureClientConnection.h index 3fb661bc..e8ded3a5 100644 --- a/src/common/network/rest/http/SecureClientConnection.h +++ b/src/common/network/rest/http/SecureClientConnection.h @@ -114,25 +114,29 @@ namespace network HTTPLexer::ResultType result; char* content; - std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); - - std::string contentLength = m_request.headers.find("Content-Length"); - if (contentLength != "") { - size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); - m_request.content = std::string(content, length); - } - - m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string()); - - if (result == HTTPLexer::GOOD) { - m_requestHandler.handleRequest(m_request, m_reply); - } - else if (result == HTTPLexer::BAD) { - return; - } - else { - read(); + try + { + std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); + + std::string contentLength = m_request.headers.find("Content-Length"); + if (contentLength != "") { + size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); + m_request.content = std::string(content, length); + } + + m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string()); + + if (result == HTTPLexer::GOOD) { + m_requestHandler.handleRequest(m_request, m_reply); + } + else if (result == HTTPLexer::BAD) { + return; + } + else { + read(); + } } + catch(const std::exception& e) { ::LogError(LOG_REST, "SecureClientConnection::read(), %s", ec.message().c_str()); } } else if (ec != asio::error::operation_aborted) { if (ec) { diff --git a/src/common/network/rest/http/SecureServerConnection.h b/src/common/network/rest/http/SecureServerConnection.h index 1e9a6946..402739b6 100644 --- a/src/common/network/rest/http/SecureServerConnection.h +++ b/src/common/network/rest/http/SecureServerConnection.h @@ -110,27 +110,32 @@ namespace network HTTPLexer::ResultType result; char* content; - std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); - - std::string contentLength = m_request.headers.find("Content-Length"); - if (contentLength != "") { - size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); - m_request.content = std::string(content, length); - } + // catch exceptions here so we don't blatently crash the system + try + { + std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); + + std::string contentLength = m_request.headers.find("Content-Length"); + if (contentLength != "") { + size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); + m_request.content = std::string(content, length); + } - m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string()); + m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string()); - if (result == HTTPLexer::GOOD) { - m_requestHandler.handleRequest(m_request, m_reply); - write(); - } - else if (result == HTTPLexer::BAD) { - m_reply = HTTPPayload::statusPayload(HTTPPayload::BAD_REQUEST); - write(); - } - else { - read(); + if (result == HTTPLexer::GOOD) { + m_requestHandler.handleRequest(m_request, m_reply); + write(); + } + else if (result == HTTPLexer::BAD) { + m_reply = HTTPPayload::statusPayload(HTTPPayload::BAD_REQUEST); + write(); + } + else { + read(); + } } + catch(const std::exception& e) { ::LogError(LOG_REST, "SecureServerConnection::read(), %s", ec.message().c_str()); } } else if (ec != asio::error::operation_aborted) { if (ec) { diff --git a/src/common/network/rest/http/ServerConnection.h b/src/common/network/rest/http/ServerConnection.h index 4cc6d835..b13b6706 100644 --- a/src/common/network/rest/http/ServerConnection.h +++ b/src/common/network/rest/http/ServerConnection.h @@ -9,7 +9,7 @@ * @license BSL-1.0 License (https://opensource.org/license/bsl1-0-html) * * Copyright (c) 2003-2013 Christopher M. Kohlhoff -* Copyright (C) 2023 Bryan Biedenkapp, N2PLL +* Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL * */ #if !defined(__REST_HTTP__SERVER_CONNECTION_H__) @@ -93,27 +93,32 @@ namespace network HTTPLexer::ResultType result; char* content; - std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); + // catch exceptions here so we don't blatently crash the system + try + { + std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred); - std::string contentLength = m_request.headers.find("Content-Length"); - if (contentLength != "") { - size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); - m_request.content = std::string(content, length); - } + std::string contentLength = m_request.headers.find("Content-Length"); + if (contentLength != "") { + size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); + m_request.content = std::string(content, length); + } - m_request.headers.add("RemoteHost", m_socket.remote_endpoint().address().to_string()); + m_request.headers.add("RemoteHost", m_socket.remote_endpoint().address().to_string()); - if (result == HTTPLexer::GOOD) { - m_requestHandler.handleRequest(m_request, m_reply); - write(); - } - else if (result == HTTPLexer::BAD) { - m_reply = HTTPPayload::statusPayload(HTTPPayload::BAD_REQUEST); - write(); - } - else { - read(); + if (result == HTTPLexer::GOOD) { + m_requestHandler.handleRequest(m_request, m_reply); + write(); + } + else if (result == HTTPLexer::BAD) { + m_reply = HTTPPayload::statusPayload(HTTPPayload::BAD_REQUEST); + write(); + } + else { + read(); + } } + catch(const std::exception& e) { ::LogError(LOG_REST, "ServerConnection::read(), %s", ec.message().c_str()); } } else if (ec != asio::error::operation_aborted) { if (ec) { @@ -151,7 +156,7 @@ namespace network asio::error_code ignored_ec; m_socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); } - catch(const std::exception& e) { ::LogError(LOG_REST, "%s", ec.message().c_str()); } + catch(const std::exception& e) { ::LogError(LOG_REST, "ServerConnection::write(), %s", ec.message().c_str()); } } if (ec != asio::error::operation_aborted) { diff --git a/src/fne/network/fne/TagDMRData.cpp b/src/fne/network/fne/TagDMRData.cpp index 02e2c05e..e02bfbee 100644 --- a/src/fne/network/fne/TagDMRData.cpp +++ b/src/fne/network/fne/TagDMRData.cpp @@ -641,6 +641,15 @@ bool TagDMRData::isPeerPermitted(uint32_t peerId, data::Data& data, uint32_t str } } + // peer always send list takes priority over any following affiliation rules + std::vector alwaysSend = tg.config().alwaysSend(); + if (alwaysSend.size() > 0) { + auto it = std::find(alwaysSend.begin(), alwaysSend.end(), peerId); + if (it != alwaysSend.end()) { + return true; // skip any following checks and always send traffic + } + } + FNEPeerConnection* connection = nullptr; if (peerId > 0 && (m_network->m_peers.find(peerId) != m_network->m_peers.end())) { connection = m_network->m_peers[peerId]; diff --git a/src/fne/network/fne/TagNXDNData.cpp b/src/fne/network/fne/TagNXDNData.cpp index ab0e5056..41a42511 100644 --- a/src/fne/network/fne/TagNXDNData.cpp +++ b/src/fne/network/fne/TagNXDNData.cpp @@ -450,6 +450,15 @@ bool TagNXDNData::isPeerPermitted(uint32_t peerId, lc::RTCH& lc, uint8_t message } } + // peer always send list takes priority over any following affiliation rules + std::vector alwaysSend = tg.config().alwaysSend(); + if (alwaysSend.size() > 0) { + auto it = std::find(alwaysSend.begin(), alwaysSend.end(), peerId); + if (it != alwaysSend.end()) { + return true; // skip any following checks and always send traffic + } + } + FNEPeerConnection* connection = nullptr; if (peerId > 0 && (m_network->m_peers.find(peerId) != m_network->m_peers.end())) { connection = m_network->m_peers[peerId]; diff --git a/src/fne/network/fne/TagP25Data.cpp b/src/fne/network/fne/TagP25Data.cpp index c858df44..a66a0a45 100644 --- a/src/fne/network/fne/TagP25Data.cpp +++ b/src/fne/network/fne/TagP25Data.cpp @@ -941,6 +941,15 @@ bool TagP25Data::isPeerPermitted(uint32_t peerId, lc::LC& control, uint8_t duid, } } + // peer always send list takes priority over any following affiliation rules + std::vector alwaysSend = tg.config().alwaysSend(); + if (alwaysSend.size() > 0) { + auto it = std::find(alwaysSend.begin(), alwaysSend.end(), peerId); + if (it != alwaysSend.end()) { + return true; // skip any following checks and always send traffic + } + } + FNEPeerConnection* connection = nullptr; if (peerId > 0 && (m_network->m_peers.find(peerId) != m_network->m_peers.end())) { connection = m_network->m_peers[peerId];