net-snmp 3.28.0 → 3.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -1
- package/index.js +98 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -157,6 +157,18 @@ constants are defined in this object:
|
|
|
157
157
|
* `NotWritable`
|
|
158
158
|
* `InconsistentName`
|
|
159
159
|
|
|
160
|
+
Only the first six of these - `NoError` through `GeneralError` - are defined by
|
|
161
|
+
SNMPv1. The agent produces error statuses using the full SNMPv2 set regardless
|
|
162
|
+
of the version of the request, then translates them when responding to an
|
|
163
|
+
SNMPv1 request, using the mapping given in RFC 2089 section 2.1 (repeated in
|
|
164
|
+
RFC 3584 section 4.4). So an SNMPv1 manager sees `NoSuchName` where an SNMPv2c
|
|
165
|
+
or SNMPv3 manager would see `NoAccess`, `NoCreation`, `NotWritable`,
|
|
166
|
+
`InconsistentName` or `AuthorizationError`; `BadValue` in place of `WrongType`,
|
|
167
|
+
`WrongLength`, `WrongEncoding`, `WrongValue` or `InconsistentValue`; and
|
|
168
|
+
`GeneralError` in place of `ResourceUnavailable`, `CommitFailed` or
|
|
169
|
+
`UndoFailed`. `ReadOnly` is listed for completeness, since SNMPv1 defines it,
|
|
170
|
+
but no conformant agent generates it - see RFC 1908 section 3.1.2.
|
|
171
|
+
|
|
160
172
|
## snmp.ObjectType
|
|
161
173
|
|
|
162
174
|
This object contains constants used to specify syntax for varbind objects,
|
|
@@ -2171,7 +2183,12 @@ level provided by this provider. The allowable values are the
|
|
|
2171
2183
|
numeric values from the MaxAccess export. If a `maxAccess` value is
|
|
2172
2184
|
specified, a `get` request to the agent will return a `noAccess`
|
|
2173
2185
|
error if `maxAccess` is not at least "read-only" (2). `maxAccess`
|
|
2174
|
-
must be at least "read-write" (3) for a `set` request to succeed
|
|
2186
|
+
must be at least "read-write" (3) for a `set` request to succeed;
|
|
2187
|
+
a `set` below that level returns a `notWritable` error, which is the
|
|
2188
|
+
code RFC 3416 section 4.2.5 reserves for a variable that exists but
|
|
2189
|
+
cannot be modified whatever value is supplied. Note that `noAccess`
|
|
2190
|
+
there means something narrower - a variable denied because it is not
|
|
2191
|
+
in the requester's MIB view.
|
|
2175
2192
|
* `defVal` *(optional)* - the default value to assign for scalar
|
|
2176
2193
|
objects automatically created, when `maxAccess` is set to
|
|
2177
2194
|
"read-create" (4). Note that table columns can specify such `defVal`
|
|
@@ -3779,6 +3796,18 @@ Example programs are included under the module's `example` directory.
|
|
|
3779
3796
|
|
|
3780
3797
|
* Replace `.npmignore` with a `files` allowlist in `package.json`, so the published package ships only the module, its libraries, examples and reference files
|
|
3781
3798
|
|
|
3799
|
+
# Version 3.28.1 - 12/09/2026
|
|
3800
|
+
|
|
3801
|
+
* Reject table instance OIDs whose row index encoding is malformed, instead of silently decoding them to a different row - a SetRequest naming a non-implied `OctetString` or OID index that claimed a different length than it supplied, or that carried unexpected trailing components, would create a row at the decoded index while answering `NoSuchInstance` for the OID that was requested
|
|
3802
|
+
|
|
3803
|
+
* Decode index parts declared with a fixed `length` without consuming a leading length component, matching the way they are encoded - an index value of `AB` in such a column previously read back as `B`
|
|
3804
|
+
|
|
3805
|
+
# Version 3.29.0 - 13/09/2026
|
|
3806
|
+
|
|
3807
|
+
* Answer a SetRequest for an object whose `maxAccess` is below "read-write" with a `notWritable` error instead of `noAccess`, for both the agent and the AgentX subagent - RFC 3416 section 4.2.5 reserves `noAccess` for a variable denied because it is not in the requester's MIB view, and gives `notWritable` for one that exists but cannot be modified whatever value is supplied
|
|
3808
|
+
|
|
3809
|
+
* Translate error statuses to their SNMPv1 equivalents when the agent responds to a version 1 request, using the mapping in RFC 2089 section 2.1 - the agent produces statuses from the SNMPv2 set whatever version it is answering, and previously sent codes such as `noAccess` and `notWritable` to SNMPv1 managers unchanged, even though SNMPv1 defines no such values. A SetRequest for a read-only object now returns `noSuchName` to an SNMPv1 manager
|
|
3810
|
+
|
|
3782
3811
|
# License
|
|
3783
3812
|
|
|
3784
3813
|
Copyright (c) 2020 Mark Abrahams <mark@abrahams.co.nz>
|
package/index.js
CHANGED
|
@@ -71,6 +71,27 @@ var ErrorStatus = {
|
|
|
71
71
|
|
|
72
72
|
_expandConstantObject (ErrorStatus);
|
|
73
73
|
|
|
74
|
+
// SNMPv2 introduced error-status values that carry no meaning for an SNMPv1
|
|
75
|
+
// manager, whose ErrorStatus ASN.1 stops at genErr(5). A multi-lingual agent
|
|
76
|
+
// must therefore translate before responding to a version 1 request. Table
|
|
77
|
+
// from RFC 2089 section 2.1, repeated in RFC 3584 section 4.4. Values absent
|
|
78
|
+
// from this table - noError, tooBig, noSuchName, badValue, readOnly and
|
|
79
|
+
// genErr - are already valid in SNMPv1 and pass through untouched.
|
|
80
|
+
var ErrorStatusV2ToV1 = {};
|
|
81
|
+
ErrorStatusV2ToV1[ErrorStatus.NoAccess] = ErrorStatus.NoSuchName;
|
|
82
|
+
ErrorStatusV2ToV1[ErrorStatus.WrongType] = ErrorStatus.BadValue;
|
|
83
|
+
ErrorStatusV2ToV1[ErrorStatus.WrongLength] = ErrorStatus.BadValue;
|
|
84
|
+
ErrorStatusV2ToV1[ErrorStatus.WrongEncoding] = ErrorStatus.BadValue;
|
|
85
|
+
ErrorStatusV2ToV1[ErrorStatus.WrongValue] = ErrorStatus.BadValue;
|
|
86
|
+
ErrorStatusV2ToV1[ErrorStatus.NoCreation] = ErrorStatus.NoSuchName;
|
|
87
|
+
ErrorStatusV2ToV1[ErrorStatus.InconsistentValue] = ErrorStatus.BadValue;
|
|
88
|
+
ErrorStatusV2ToV1[ErrorStatus.ResourceUnavailable] = ErrorStatus.GeneralError;
|
|
89
|
+
ErrorStatusV2ToV1[ErrorStatus.CommitFailed] = ErrorStatus.GeneralError;
|
|
90
|
+
ErrorStatusV2ToV1[ErrorStatus.UndoFailed] = ErrorStatus.GeneralError;
|
|
91
|
+
ErrorStatusV2ToV1[ErrorStatus.AuthorizationError] = ErrorStatus.NoSuchName;
|
|
92
|
+
ErrorStatusV2ToV1[ErrorStatus.NotWritable] = ErrorStatus.NoSuchName;
|
|
93
|
+
ErrorStatusV2ToV1[ErrorStatus.InconsistentName] = ErrorStatus.NoSuchName;
|
|
94
|
+
|
|
74
95
|
var ObjectType = {
|
|
75
96
|
1: "Boolean",
|
|
76
97
|
2: "Integer",
|
|
@@ -4707,41 +4728,73 @@ Mib.prototype.getTableRowInstanceFromRow = function (provider, row) {
|
|
|
4707
4728
|
return rowIndex;
|
|
4708
4729
|
};
|
|
4709
4730
|
|
|
4731
|
+
// Splices exactly `length` components off the front of an index address,
|
|
4732
|
+
// rejecting encodings that claim more components than they supply.
|
|
4733
|
+
Mib.spliceIndexValue = function (addressRemaining, length, oid) {
|
|
4734
|
+
var value = addressRemaining.splice (0, length);
|
|
4735
|
+
if ( value.length !== length ) {
|
|
4736
|
+
throw new RangeError ("Index encoding in \"" + oid + "\" claims " + length +
|
|
4737
|
+
" components but supplies " + value.length);
|
|
4738
|
+
}
|
|
4739
|
+
return value;
|
|
4740
|
+
};
|
|
4741
|
+
|
|
4742
|
+
// Determines how many OID components an index part occupies, consuming a
|
|
4743
|
+
// leading length component for variable-length parts that are neither
|
|
4744
|
+
// implied nor of a fixed length.
|
|
4745
|
+
Mib.getIndexPartLength = function (indexPart, addressRemaining, oid) {
|
|
4746
|
+
var length;
|
|
4747
|
+
if ( indexPart.implied ) {
|
|
4748
|
+
return addressRemaining.length;
|
|
4749
|
+
}
|
|
4750
|
+
if ( indexPart.length ) {
|
|
4751
|
+
return indexPart.length;
|
|
4752
|
+
}
|
|
4753
|
+
length = Number (addressRemaining.shift ());
|
|
4754
|
+
if ( ! Number.isInteger (length) || length < 0 ) {
|
|
4755
|
+
throw new RangeError ("Index encoding in \"" + oid + "\" has a missing or invalid length component");
|
|
4756
|
+
}
|
|
4757
|
+
return length;
|
|
4758
|
+
};
|
|
4759
|
+
|
|
4710
4760
|
Mib.getRowIndexFromOid = function (oid, index) {
|
|
4711
|
-
var addressRemaining = oid.split (".");
|
|
4761
|
+
var addressRemaining = oid === "" ? [] : oid.split (".");
|
|
4712
4762
|
var length = 0;
|
|
4713
4763
|
var values = [];
|
|
4714
4764
|
var value;
|
|
4715
4765
|
for ( var indexPart of index ) {
|
|
4716
4766
|
switch ( indexPart.type ) {
|
|
4717
4767
|
case ObjectType.OID:
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
} else {
|
|
4721
|
-
length = addressRemaining.shift ();
|
|
4722
|
-
}
|
|
4723
|
-
value = addressRemaining.splice (0, length);
|
|
4768
|
+
length = Mib.getIndexPartLength (indexPart, addressRemaining, oid);
|
|
4769
|
+
value = Mib.spliceIndexValue (addressRemaining, length, oid);
|
|
4724
4770
|
values.push (value.join ("."));
|
|
4725
4771
|
break;
|
|
4726
4772
|
case ObjectType.IpAddress:
|
|
4727
4773
|
length = 4;
|
|
4728
|
-
value =
|
|
4774
|
+
value = Mib.spliceIndexValue (addressRemaining, length, oid);
|
|
4729
4775
|
values.push (value.join ("."));
|
|
4730
4776
|
break;
|
|
4731
4777
|
case ObjectType.OctetString:
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
} else {
|
|
4735
|
-
length = addressRemaining.shift ();
|
|
4736
|
-
}
|
|
4737
|
-
value = addressRemaining.splice (0, length);
|
|
4778
|
+
length = Mib.getIndexPartLength (indexPart, addressRemaining, oid);
|
|
4779
|
+
value = Mib.spliceIndexValue (addressRemaining, length, oid);
|
|
4738
4780
|
value = value.map (c => String.fromCharCode(c)).join ("");
|
|
4739
4781
|
values.push (value);
|
|
4740
4782
|
break;
|
|
4741
4783
|
default:
|
|
4742
|
-
|
|
4784
|
+
if ( addressRemaining.length === 0 ) {
|
|
4785
|
+
throw new RangeError ("Index encoding in \"" + oid + "\" is missing a component");
|
|
4786
|
+
}
|
|
4787
|
+
value = parseInt (addressRemaining.shift ());
|
|
4788
|
+
if ( Number.isNaN (value) ) {
|
|
4789
|
+
throw new RangeError ("Index encoding in \"" + oid + "\" has a non-numeric component");
|
|
4790
|
+
}
|
|
4791
|
+
values.push (value);
|
|
4743
4792
|
}
|
|
4744
4793
|
}
|
|
4794
|
+
if ( addressRemaining.length > 0 ) {
|
|
4795
|
+
throw new RangeError ("Index encoding in \"" + oid + "\" has " + addressRemaining.length +
|
|
4796
|
+
" unexpected trailing component(s)");
|
|
4797
|
+
}
|
|
4745
4798
|
return values;
|
|
4746
4799
|
};
|
|
4747
4800
|
|
|
@@ -5297,7 +5350,15 @@ Agent.prototype.tryCreateInstance = function (varbind, requestType) {
|
|
|
5297
5350
|
subOid = Mib.getSubOidFromBaseOid (oid, provider.oid);
|
|
5298
5351
|
subAddr = subOid.split(".");
|
|
5299
5352
|
column = parseInt(subAddr.shift(), 10);
|
|
5300
|
-
|
|
5353
|
+
try {
|
|
5354
|
+
row = Mib.getRowIndexFromOid(subAddr.join("."), provider.tableIndex);
|
|
5355
|
+
} catch (error) {
|
|
5356
|
+
// The OID is not a valid index encoding for this table, so it names no
|
|
5357
|
+
// row and there is nothing to create. Leave the varbind to be answered
|
|
5358
|
+
// as NoSuchInstance.
|
|
5359
|
+
debug (error);
|
|
5360
|
+
return undefined;
|
|
5361
|
+
}
|
|
5301
5362
|
rowStatusColumn = provider.tableColumns.reduce( (acc, current) => current.rowStatus ? current.number : acc, null );
|
|
5302
5363
|
|
|
5303
5364
|
if ( requestType === PduType.SetRequest &&
|
|
@@ -5468,14 +5529,19 @@ Agent.prototype.request = function (socket, requestMessage, rinfo) {
|
|
|
5468
5529
|
});
|
|
5469
5530
|
};
|
|
5470
5531
|
} else if ( ! this.isAllowed(requestPdu.type, providerNode.provider, instanceNode ) ) {
|
|
5471
|
-
//
|
|
5532
|
+
// Requested access not allowed (by MAX-ACCESS). RFC 3416 section
|
|
5533
|
+
// 4.2.5 reserves noAccess for a variable denied because it is not
|
|
5534
|
+
// in the appropriate MIB view (rule 1); a variable that exists but
|
|
5535
|
+
// can not be modified no matter what value is supplied is
|
|
5536
|
+
// notWritable (rule 2). MAX-ACCESS below read-write is the latter.
|
|
5537
|
+
const accessErrorStatus = isSetRequest ? ErrorStatus.NotWritable : ErrorStatus.NoAccess;
|
|
5472
5538
|
mibRequests[i] = new MibRequest ({
|
|
5473
5539
|
operation: requestPdu.type,
|
|
5474
5540
|
oid: requestPdu.varbinds[i].oid
|
|
5475
5541
|
});
|
|
5476
5542
|
mibRequests[i].handler = function getRanaHandler (mibRequestForRana) {
|
|
5477
5543
|
mibRequestForRana.done ({
|
|
5478
|
-
errorStatus:
|
|
5544
|
+
errorStatus: accessErrorStatus,
|
|
5479
5545
|
type: ObjectType.Null,
|
|
5480
5546
|
value: null
|
|
5481
5547
|
});
|
|
@@ -5880,6 +5946,16 @@ Agent.prototype.setSingleVarbind = function (responsePdu, index, responseVarbind
|
|
|
5880
5946
|
};
|
|
5881
5947
|
|
|
5882
5948
|
Agent.prototype.sendResponse = function (socket, rinfo, requestMessage, responsePdu) {
|
|
5949
|
+
// The agent is multi-lingual: it answers each request in the version that
|
|
5950
|
+
// request arrived with. Error statuses are produced internally using the
|
|
5951
|
+
// SNMPv2 set, so a response to a version 1 request must be translated down
|
|
5952
|
+
// to something an SNMPv1 manager understands - RFC 2089 section 2.1.
|
|
5953
|
+
if ( requestMessage.version == Version1 ) {
|
|
5954
|
+
const version1ErrorStatus = ErrorStatusV2ToV1[responsePdu.errorStatus];
|
|
5955
|
+
if ( version1ErrorStatus !== undefined ) {
|
|
5956
|
+
responsePdu.errorStatus = version1ErrorStatus;
|
|
5957
|
+
}
|
|
5958
|
+
}
|
|
5883
5959
|
var responseMessage = requestMessage.createResponseForRequest (responsePdu);
|
|
5884
5960
|
this.listener.send (responseMessage, rinfo, socket);
|
|
5885
5961
|
this.callback (null, Listener.formatCallbackData (responseMessage.pdu, rinfo) );
|
|
@@ -6709,8 +6785,11 @@ Subagent.prototype.request = function (pdu, requestVarbinds) {
|
|
|
6709
6785
|
});
|
|
6710
6786
|
mibRequests[i].handler = providerNode.provider.handler;
|
|
6711
6787
|
if ( ! me.isAllowed(pdu.pduType, mibRequests[i].providerNode?.provider, mibRequests[i].instanceNode) ) {
|
|
6788
|
+
// A set below read-write MAX-ACCESS is notWritable, not
|
|
6789
|
+
// noAccess - RFC 3416 section 4.2.5 rule 2, and RFC 2741
|
|
6790
|
+
// section 7.2.1.4 for the AgentX TestSet phase.
|
|
6712
6791
|
mibRequests[i].error = {
|
|
6713
|
-
errorStatus: ErrorStatus.NoAccess,
|
|
6792
|
+
errorStatus: isSetRequest ? ErrorStatus.NotWritable : ErrorStatus.NoAccess,
|
|
6714
6793
|
errorIndex: i + 1,
|
|
6715
6794
|
type: ObjectType.Null,
|
|
6716
6795
|
value: null
|