Skip to content
Open
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
270 changes: 144 additions & 126 deletions README.md

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions examples/ESP32_SNMP/ESP32_SNMP.ino
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,22 @@ TimestampCallback* timestampCallbackOID;

char staticString[] = "This value will never change";

/* Versioned sysDescr served on .1.3.6.1.2.1.1.1.0 — the handler keeps the pointer,
so this must be a static buffer, not a local. Lets `snmpget` confirm the exact
library build running on the chip during hardware testing. */
static char sysDescrBuf[64];

// Setup an SNMPTrap for later use
SNMPTrap* settableNumberTrap = new SNMPTrap("public", SNMP_VERSION_2C);
char _changingStringBuf[25];
char* changingString = _changingStringBuf;

void setup(){
Serial.begin(115200);

// Hardware-test banner: confirm the flashed library version in the serial monitor
Serial.printf("SNMP_Agent v%s\n", snmp.getVersion());

WiFi.begin(ssid, password);
// WiFi.begin(ssid);
Serial.println("");
Expand All @@ -81,6 +90,11 @@ void setup(){
stuff[2] = 24;
stuff[3] = 67;

// RFC1213 sysDescr: serves the library version, queryable from the SNMP terminal:
// snmpget -v 2c -c public <IP> .1.3.6.1.2.1.1.1.0
snprintf(sysDescrBuf, sizeof(sysDescrBuf), "ESP32_SNMP demo (SNMP_Agent v%s)", snmp.getVersion());
snmp.addReadOnlyStaticStringHandler(".1.3.6.1.2.1.1.1.0", sysDescrBuf);

// add 'callback' for an OID - pointer to an integer
changingNumberOID = snmp.addIntegerHandler(".1.3.6.1.4.1.5.0", &changingNumber);

Expand Down
13 changes: 12 additions & 1 deletion examples/SNMP_Sensor/SNMP_Sensor.ino
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ const char* oidSysName = ".1.3.6.1.2.1.1.5.0"; // OctetString SysName
const char* oidSysLocation = ".1.3.6.1.2.1.1.6.0"; // OctetString SysLocation
const char* oidSysServices = ".1.3.6.1.2.1.1.7.0"; // Integer sysServices

char sysDescr[] = "SNMP Agent";
/* Versioned sysDescr served on .1.3.6.1.2.1.1.1.0 — filled in setup() with the
running library version so `snmpget` confirms the exact build during hardware
testing. 64 B leaves room for the version string; handler keeps the pointer. */
static char sysDescr[64] = "SNMP Agent";
char sysObjectID[] = "";
uint32_t sysUptime = 0;
char sysContactValue[255];
Expand Down Expand Up @@ -194,6 +197,10 @@ void printFile(const char* filename);
void setup()
{
Serial.begin(115200);

// Hardware-test banner: confirm the flashed library version in the serial monitor
Serial.printf("SNMP_Agent v%s\n", snmp.getVersion());

if (!FS_BEGIN())
{
Serial.println("LittleFS Mount Failed");
Expand All @@ -217,6 +224,10 @@ void setup()
snmp.setUDP(&udp);
snmp.begin();

// Fill sysDescr (.1.3.6.1.2.1.1.1.0) with the version string, queryable from the
// SNMP terminal: snmpget -v 2c -c public <IP> .1.3.6.1.2.1.1.1.0
snprintf(sysDescr, sizeof(sysDescr), "SNMP_Sensor demo (SNMP_Agent v%s)", snmp.getVersion());

addRFC1213MIBHandler(); // RFC1213-MIB (System)
addENTITYMIBHandler(); // ENTITY-MIB
addENTITYSENSORMIBHandler(); // ENTITY-SENSOR-MIB
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=SNMP_Agent
version=3.1.5
version=3.1.23
author=Aidan Cyr <cyraidan@gmail.com>
maintainer=Aidan Cyr <cyraidan@gmail.com>
sentence=SNMP Agent: An fully compliant SNMPv2c Agent for esp32 for acting as an SNMP client device.
Expand Down
39 changes: 19 additions & 20 deletions src/BERDecode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,34 @@
ASNPool::Slot ASNPool::slots[SNMP_POOL_ASN_OBJECTS] = {};
#endif
int ASNPool::usedCount = 0;
int ASNPool::permCount = 0;
int ASNPool::usedCountPeak = 0;

void ASNPool::release(BER_CONTAINER* p){
if(!p) return;
#ifndef SNMP_POOLS_IN_BSS
if(!_poolsReady) { delete p; return; }
#endif
p->~BER_CONTAINER();
/* Double-release guard: without it, a double-destroyed object re-runs its
* destructor AND decrements usedCount twice. The counter then under-reports
* occupancy, rawAlloc() reuses a slot that still holds a live object, and
* live OIDs get corrupted — the root cause of degraded GetBulk responses
* and the "agent goes deaf" incidents (HARDWARE_TEST_REPORT.md §2/§9). */
for(int i = 0; i < SNMP_POOL_ASN_OBJECTS; i++){
if(static_cast<void*>(slots[i].storage) == static_cast<void*>(p)){
if(!slots[i].occupied){
/* Slot already free: second release of the same object.
* Do NOT run the destructor again, do NOT decrement.
* One-shot alarm when DEBUG>0: a caller is destroying twice. */
if(!slots[i].doubleReleaseWarned){
slots[i].doubleReleaseWarned = true;
SNMP_LOGE("ASNPool: DOUBLE RELEASE of slot %d detected (caller destroying an object twice)\n", i);
}
return;
}
p->~BER_CONTAINER();
slots[i].occupied = false;
if(usedCount > 0) usedCount--;
usedCount--;
return;
}
}
Expand Down Expand Up @@ -242,24 +259,6 @@ const char* OIDType::string() {
return _valueStr;
}

void SortableOIDType::generateSortingMap(uint32_t outMap[SNMP_MAX_OID_SUBIDENTIFIERS], int* outLen) const {
int count = 0;

const uint8_t* ptr = this->data;

ptr += 1;
int i = this->dataLen - 1;

while(i > 0 && count < SNMP_MAX_OID_SUBIDENTIFIERS){
long item;
size_t len = decode_ber_longform_integer(ptr, &item, i);
ptr += len; i -= len;
outMap[count++] = (uint32_t)item;
}

*outLen = count;
}

int NullType::fromBuffer(const uint8_t *, size_t){
_length = 0;
return 2;
Expand Down
20 changes: 20 additions & 0 deletions src/BEREncode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,26 @@ bool OIDType::generateInternalData() {
return true;
}

void OIDType::_init_from_cstr(const char* value, size_t len) noexcept {
memcpy(this->_valueStr, value, len);
this->_valueStr[len] = '\0';
this->dataLen = 0;
this->valid = this->generateInternalData();
}

void OIDType::_init_from_cstr_with_data(const char* value, size_t len, const uint8_t* srcData, int srcLen, bool valid_) noexcept {
memcpy(this->_valueStr, value, len);
this->_valueStr[len] = '\0';
if(srcLen > 0 && srcData) {
if(srcLen > (int)sizeof(this->data)) srcLen = (int)sizeof(this->data);
this->dataLen = srcLen;
memcpy(this->data, srcData, (size_t)srcLen);
} else {
this->dataLen = 0;
}
this->valid = valid_;
}

static inline void shift_arr_right(uint8_t* ptr, int num_length_bytes, size_t length){
for(int l = length+num_length_bytes-1; l-num_length_bytes >= 0; l--){
ptr[l] = ptr[l-num_length_bytes];
Expand Down
34 changes: 30 additions & 4 deletions src/SNMPPDUHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,32 @@
#include "include/BER.h"
#include "include/ValueCallbacks.h"

/* Every `asn_new<T>()` returns a RAW POINTER into the static ASNPool
* (placement-new slots). When such a raw pointer is bound to a function
* parameter of type `const std::shared_ptr<BER_CONTAINER>&`, C++
* implicitly constructs a TEMPORARY shared_ptr using the DEFAULT
* `delete T` deleter — which immediately calls `delete` on a pool slot
* address at scope exit → Undefined Behavior. On ESP-01 this corrupted
* pool metadata silently (no exception triggered since the double-free
* happened on a slot not currently in the free-list), causing
* SNMPResponse encode path to fail to build even a single VarBind →
* ZERO UDP TX bytes sent, agent DEAF despite UDP RX confirmed.
*
* FIX: wrap every `asn_new<T>()` passed into a `shared_ptr<T>` context
* with `pool_asn_sp(...)` below. It constructs a shared_ptr whose
* custom deleter calls `asn_delete` instead of `operator delete`. */
namespace {
struct pool_asn_deleter {
void operator()(BER_CONTAINER* p) const noexcept { asn_delete(p); }
};
}
template <typename T>
static inline std::shared_ptr<BER_CONTAINER> pool_asn_sp(T* raw_pool_ptr) noexcept {
static_assert(std::is_base_of<BER_CONTAINER, T>::value,
"pool_asn_sp only accepts BER_CONTAINER-derived pointers");
return std::shared_ptr<BER_CONTAINER>(static_cast<BER_CONTAINER*>(raw_pool_ptr), pool_asn_deleter());
}

template<typename... Args>
static inline bool appendResponseVarBind(VarBind out[], int &outCount, Args&&... args){
if(outCount >= SNMP_MAX_VARBINDS) return false;
Expand All @@ -22,9 +48,9 @@ bool handleGetRequestPDU(ValueCallback* const *callbacks, int callbacksCount, co
SNMP_LOGD("Couldn't find callback\n");
#if 1
if(isGetNextRequest){
appendResponseVarBind(outResponseList, outResponseCount, requestVarBind, asn_new<ImplicitNullType>(ENDOFMIBVIEW));
appendResponseVarBind(outResponseList, outResponseCount, requestVarBind, pool_asn_sp(asn_new<ImplicitNullType>(ENDOFMIBVIEW)));
} else {
appendResponseVarBind(outResponseList, outResponseCount, requestVarBind, asn_new<ImplicitNullType>(NOSUCHOBJECT));
appendResponseVarBind(outResponseList, outResponseCount, requestVarBind, pool_asn_sp(asn_new<ImplicitNullType>(NOSUCHOBJECT)));
}

#else
Expand Down Expand Up @@ -104,7 +130,7 @@ bool handleGetBulkRequestPDU(ValueCallback* const *callbacks, int callbacksCount
const VarBind& requestVarBind = varbindList[i];
ValueCallback* callback = ValueCallback::findCallback(callbacks, callbacksCount, requestVarBind.oid, true);
if(!callback){
appendResponseVarBind(outResponseList, outResponseCount, requestVarBind, asn_new<ImplicitNullType>(ENDOFMIBVIEW));
appendResponseVarBind(outResponseList, outResponseCount, requestVarBind, pool_asn_sp(asn_new<ImplicitNullType>(ENDOFMIBVIEW)));
continue;
}

Expand All @@ -130,7 +156,7 @@ bool handleGetBulkRequestPDU(ValueCallback* const *callbacks, int callbacksCount
SNMP_LOGD("finding next callback for OID: %s\n", oid->string());
ValueCallback* callback = ValueCallback::findCallback(callbacks, callbacksCount, oid, true, foundAt, &foundAt);
if(!callback){
appendResponseVarBind(outResponseList, outResponseCount, oid, asn_new<ImplicitNullType>(ENDOFMIBVIEW));
appendResponseVarBind(outResponseList, outResponseCount, oid, pool_asn_sp(asn_new<ImplicitNullType>(ENDOFMIBVIEW)));
oid = nullptr;
break;
}
Expand Down
Loading
Loading