Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 37 additions & 16 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
; https://docs.platformio.org/page/projectconf.html

[platformio]
default_envs = native
default_envs = native17

[env]
build_unflags =
-fno-exceptions
build_flags =
-fexceptions
-Wall
;-Wextra
-Wno-missing-field-initializers
Expand All @@ -35,46 +38,64 @@ test_build_src = true
platform = native
build_flags =
${env.build_flags}
-std=c++11
-std=gnu++11
-DNATIVE
lib_deps =
${env.lib_deps}
lib_compat_mode = off

[env:native17]
platform = native
build_unflags = -std=gnu++11
build_unflags =
${env.build_unflags}
-std=gnu++11
build_flags =
${env.build_flags}
-std=c++17
-std=gnu++17
-DNATIVE
lib_deps =
${env.lib_deps}
lib_compat_mode = off

[env:native20]
platform = native
build_unflags = -std=gnu++11
build_unflags =
${env.build_unflags}
-std=gnu++11
build_flags =
${env.build_flags}
-std=c++20
-std=gnu++20
-DNATIVE
lib_deps =
${env.lib_deps}
lib_compat_mode = off

[env:mcu]
build_unflags =
${env.build_unflags}
;-std=gnu++11
build_flags =
${env.build_flags}
;-std=gnu++17
lib_deps =
${env.lib_deps}

[env:ttgo-lora32-v21]
platform = espressif32
board = ttgo-lora32-v21
board_build.partitions = no_ota.csv
framework = arduino
monitor_speed = 115200
build_unflags =
${env:mcu.build_unflags}
-std=gnu++11
build_flags =
${env.build_flags}
${env:mcu.build_flags}
-std=gnu++17
-DBOARD_ESP32
-DMSGPACK_USE_BOOST=OFF
lib_deps =
${env.lib_deps}
${env:mcu.lib_deps}

[env:ttgo-t-beam]
platform = espressif32
Expand All @@ -84,11 +105,11 @@ board_build.partitions = no_ota.csv
framework = arduino
monitor_speed = 115200
build_flags =
${env.build_flags}
${env:mcu.build_flags}
-DBOARD_ESP32
-DMSGPACK_USE_BOOST=OFF
lib_deps =
${env.lib_deps}
${env:mcu.lib_deps}

[env:lilygo_tbeam_supreme]
platform = espressif32
Expand All @@ -98,34 +119,34 @@ board_build.partitions = no_ota.csv
framework = arduino
monitor_speed = 115200
build_flags =
${env.build_flags}
${env:mcu.build_flags}
-DBOARD_ESP32
-DMSGPACK_USE_BOOST=OFF
lib_deps =
${env.lib_deps}
${env:mcu.lib_deps}

[env:wiscore_rak4631]
platform = nordicnrf52
board = rak4630
board_build.partitions = no_ota.csv
framework = arduino
monitor_speed = 115200
build_src_filter = ${env.build_src_filter}+<../variants/rak4630>
build_src_filter = ${env:mcu.build_src_filter}+<../variants/rak4630>
build_flags =
${env.build_flags}
${env:mcu.build_flags}
-I variants/rak4630
-fexceptions
-DBOARD_NRF52
-DRNS_USE_ALLOCATOR=1
-DRNS_USE_TLSF=1
-DMSGPACK_USE_BOOST=OFF
lib_deps =
${env.lib_deps}
${env:mcu.lib_deps}
adafruit/Adafruit TinyUSB Library

; Overrides for specific tests (not working!)
[test_env:test_objects]
build_flags =
${env.build_flags}
${env:mcu.build_flags}
;-DTEST_OBJECT_DATA=1
-DTEST_OBJECT_IMPL=1
1 change: 1 addition & 0 deletions src/Bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ MEM("Creating from data-move...");

inline void assign(const Bytes& bytes) {
#ifdef COW
// shared_ptr copy only — O(1), no heap allocation
_data = bytes.shareData();
_exclusive = false;
#else
Expand Down
30 changes: 28 additions & 2 deletions src/Destination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ Packet Destination::announce(const Bytes& app_data, bool path_response, const In
throw std::invalid_argument("Only IN destination types can be announced");
}

try {
double now = OS::time();
auto it = _object->_path_responses.begin();
while (it != _object->_path_responses.end()) {
Expand Down Expand Up @@ -284,7 +285,15 @@ Packet Destination::announce(const Bytes& app_data, bool path_response, const In
}

// CBA ACCUMULATES
_object->_path_responses.insert({tag, {OS::time(), announce_data}});
try {
_object->_path_responses.insert({tag, {OS::time(), announce_data}});
}
catch (const std::bad_alloc&) {
ERROR("announce: out of memory, path response not stored for " + _object->_hash.toHex());
}
catch (const std::exception& e) {
ERROR(std::string("announce: exception storing path response: ") + e.what());
}
}
//TRACE("Destination::announce: announce_data:" + announce_data.toHex());

Expand All @@ -306,6 +315,15 @@ Packet Destination::announce(const Bytes& app_data, bool path_response, const In
else {
return announce_packet;
}
}
catch (const std::bad_alloc&) {
ERROR("announce: out of memory, announce not sent for " + _object->_hash.toHex());
return {Type::NONE};
}
catch (const std::exception& e) {
ERROR(std::string("announce: exception during announce: ") + e.what());
return {Type::NONE};
}
}

Packet Destination::announce(const Bytes& app_data /*= {}*/, bool path_response /*= false*/) {
Expand Down Expand Up @@ -385,7 +403,15 @@ void Destination::incoming_link_request(const Bytes& data, const Packet& packet)
TRACE("***** Accepting link request");
RNS::Link link = Link::validate_request(*this, data, packet);
if (link) {
_object->_links.insert(link);
try {
_object->_links.insert(link);
}
catch (const std::bad_alloc&) {
ERROR("incoming_link_request: out of memory, link not tracked");
}
catch (const std::exception& e) {
ERROR(std::string("incoming_link_request: exception tracking link: ") + e.what());
}
}
}
}
Expand Down
78 changes: 53 additions & 25 deletions src/Identity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ using namespace RNS::Type::Identity;
using namespace RNS::Cryptography;
using namespace RNS::Utilities;

#ifndef RNS_KNOWN_DESTINATIONS_MAX
#define RNS_KNOWN_DESTINATIONS_MAX 100
#endif

/*static*/ std::map<Bytes, Identity::IdentityEntry> Identity::_known_destinations;
/*static*/ bool Identity::_saving_known_destinations = false;
// CBA
// CBA ACCUMULATES
/*static*/ //uint16_t Identity::_known_destinations_maxsize = 100;
/*static*/ uint16_t Identity::_known_destinations_maxsize = 100;
/*static*/ uint16_t Identity::_known_destinations_maxsize = RNS_KNOWN_DESTINATIONS_MAX;

Identity::Identity(bool create_keys /*= true*/) : _object(new Object()) {
if (create_keys) {
Expand Down Expand Up @@ -196,7 +199,15 @@ Can be used to load previously created and saved identities into Reticulum.
else {
//p _known_destinations[destination_hash] = {OS::time(), packet_hash, public_key, app_data};
// CBA ACCUMULATES
_known_destinations.insert({destination_hash, {OS::time(), packet_hash, public_key, app_data}});
try {
_known_destinations.insert({destination_hash, {OS::time(), packet_hash, public_key, app_data}});
}
catch (const std::bad_alloc&) {
ERROR("remember: out of memory, identity not stored for " + destination_hash.toHex());
}
catch (const std::exception& e) {
ERROR(std::string("remember: exception storing identity: ") + e.what());
}
}
}

Expand Down Expand Up @@ -352,32 +363,49 @@ Recall last heard app_data for a destination hash.
}

/*static*/ void Identity::cull_known_destinations() {
TRACE("Transport::cull_path_table()");
TRACE("Identity::cull_known_destinations()");
if (_known_destinations.size() > _known_destinations_maxsize) {
// prune by age
uint16_t count = 0;
std::vector<std::pair<Bytes, IdentityEntry>> sorted_pairs;
// Copy key/value pairs from map into vector
std::for_each(_known_destinations.begin(), _known_destinations.end(), [&](const std::pair<const Bytes, IdentityEntry>& ref) {
sorted_pairs.push_back(ref);
});
// Sort vector using specified comparator
std::sort(sorted_pairs.begin(), sorted_pairs.end(), [](const std::pair<Bytes, IdentityEntry> &left, const std::pair<Bytes, IdentityEntry> &right) {
return left.second._timestamp < right.second._timestamp;
});
// Iterate vector of sorted values
for (auto& [destination_hash, identity_entry] : sorted_pairs) {
TRACE("Transport::cull_path_table: Removing destination " + destination_hash.toHex() + " from known destinations");
// Remove destination from known destinations
if (_known_destinations.erase(destination_hash) < 1) {
WARNING("Failed to remove destination " + destination_hash.toHex() + " from known destinations");
try {
// Build lightweight (timestamp, key) index to avoid copying full IdentityEntry
// objects — prevents OOM on heap-constrained devices when the table is full.
std::vector<std::pair<double, Bytes>> sorted_keys;
sorted_keys.reserve(_known_destinations.size());
for (const auto& [key, entry] : _known_destinations) {
sorted_keys.emplace_back(entry._timestamp, key);
}
++count;
if (_known_destinations.size() <= _known_destinations_maxsize) {
break;
// Sort ascending by timestamp (oldest first)
std::sort(sorted_keys.begin(), sorted_keys.end());

uint16_t count = 0;
for (const auto& [timestamp, destination_hash] : sorted_keys) {
TRACE("Identity::cull_known_destinations: Removing destination " + destination_hash.toHex() + " from known destinations");
if (_known_destinations.erase(destination_hash) < 1) {
WARNING("Failed to remove destination " + destination_hash.toHex() + " from known destinations");
}
++count;
if (_known_destinations.size() <= _known_destinations_maxsize) {
break;
}
}
DEBUG("Removed " + std::to_string(count) + " path(s) from known destinations");
}
catch (const std::bad_alloc& e) {
ERROR("cull_known_destinations: out of memory building sort index, falling back to single erase");
// Fallback: std::min_element does no heap allocation — erase one oldest entry
auto oldest = std::min_element(
_known_destinations.begin(), _known_destinations.end(),
[](const std::pair<const Bytes, IdentityEntry>& a,
const std::pair<const Bytes, IdentityEntry>& b) {
return a.second._timestamp < b.second._timestamp;
}
);
if (oldest != _known_destinations.end()) {
_known_destinations.erase(oldest);
}
}
DEBUG("Removed " + std::to_string(count) + " path(s) from known destinations");
catch (const std::exception& e) {
ERROR(std::string("cull_known_destinations: exception: ") + e.what());
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/Identity.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ namespace RNS {
inline const Cryptography::Ed25519PrivateKey::Ptr sig_prv() const { assert(_object); return _object->_sig_prv; }
inline const Cryptography::X25519PublicKey::Ptr pub() const { assert(_object); return _object->_pub; }
inline const Cryptography::Ed25519PublicKey::Ptr sig_pub() const { assert(_object); return _object->_sig_pub; }
inline static uint16_t known_destinations_maxsize() { return _known_destinations_maxsize; }
inline static void known_destinations_maxsize(uint16_t known_destinations_maxsize) { _known_destinations_maxsize = known_destinations_maxsize; }

inline std::string toString() const { if (!_object) return ""; return "{Identity:" + _object->_hash.toHex() + "}"; }

Expand Down
1 change: 1 addition & 0 deletions src/Link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ void Link::teardown_packet(const Packet& packet) {
}
}
catch (std::exception& e) {
ERRORF("Error while decrypting teardown packet from %s. The contained exception was: %s", toString().c_str(), e.what());
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/Packet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,10 @@ bool Packet::unpack() {
_object->_packed = false;
update_hash();
}
catch (const std::bad_alloc&) {
ERROR("Packet::unpack: out of memory unpacking packet");
return false;
}
catch (std::exception& e) {
ERROR(std::string("Received malformed packet, dropping it. The contained exception was: ") + e.what());
return false;
Expand Down
Loading