net-snmp 3.19.2 → 3.20.1

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 CHANGED
@@ -91,7 +91,7 @@ for each shown in this table:
91
91
  * SNMP proxy forwarder for agent
92
92
  * AgentX subagent
93
93
  * IPv4 and IPv6
94
-
94
+
95
95
  # Standards Compliance
96
96
 
97
97
  This module aims to be fully compliant with the following RFCs:
@@ -258,6 +258,34 @@ Security Model RFC (RFC 3414); 128-bit AES for SNMPv3 was added later in RFC 382
258
258
  localization. Cisco and a number of other vendors commonly use the "Reeder" key
259
259
  localization variant. Other encryption algorithms are not supported.
260
260
 
261
+ ### Compatibility note on DES and recent Node.js versions
262
+
263
+ When using SNMPv3 with DES as the privacy protocol (`snmp.PrivProtocols.des`) on Node.js v17 or later, you may encounter the following error:
264
+
265
+ ```
266
+ "error": {
267
+ "library": "digital envelope routines",
268
+ "reason": "unsupported",
269
+ "code": "ERR_OSSL_EVP_UNSUPPORTED",
270
+ "message": "error:0308010C:digital envelope routines::unsupported",
271
+ "stack": ["Error: error:0308010C:digital envelope routines::unsupported",
272
+ "at Cipheriv.createCipherBase (node:internal/crypto/cipher:121:19)",
273
+ ...
274
+ }
275
+ ```
276
+
277
+ This occurs because newer versions of Node.js have deprecated support for the DES algorithm in OpenSSL for security reasons.
278
+
279
+ **Workaround:**
280
+ If you need to communicate with legacy devices that only support DES for SNMPv3, you can run Node.js with the `--openssl-legacy-provider` flag:
281
+
282
+ ```bash
283
+ node --openssl-legacy-provider your-app.js
284
+ ```
285
+
286
+ Whenever possible, it's recommended to use more secure encryption methods like AES (`snmp.PrivProtocols.aes`) instead of DES.
287
+
288
+
261
289
  ## snmp.AgentXPduType
262
290
 
263
291
  The Agent Extensibility (AgentX) Protocol specifies these PDUs in RFC 2741:
@@ -3473,6 +3501,14 @@ Example programs are included under the module's `example` directory.
3473
3501
 
3474
3502
  * Fix integer and string constraints check on cast
3475
3503
 
3504
+ # Version 3.20.0 - 06/03/2025
3505
+
3506
+ * Fix set value for counter, gauge and unsigned integer types
3507
+
3508
+ # Version 3.20.1 - 26/04/2025
3509
+
3510
+ * Update documentation with compatibility note on DES and recent Node.js versions
3511
+
3476
3512
  # License
3477
3513
 
3478
3514
  Copyright (c) 2020 Mark Abrahams <mark@abrahams.co.nz>
package/index.js CHANGED
@@ -1,20 +1,25 @@
1
1
 
2
2
  // Copyright 2013 Stephen Vickers <stephen.vickers.sv@gmail.com>
3
3
 
4
- var ber = require ("asn1-ber").Ber;
5
- var smartbuffer = require ("smart-buffer");
6
- var dgram = require ("dgram");
7
- var net = require ("net");
8
- var events = require ("events");
9
- var util = require ("util");
10
- var crypto = require ("crypto");
11
- var mibparser = require ("./lib/mib");
4
+ const ber = require ("asn1-ber").Ber;
5
+ const smartbuffer = require ("smart-buffer");
6
+ const dgram = require ("dgram");
7
+ const net = require ("net");
8
+ const events = require ("events");
9
+ const util = require ("util");
10
+ const crypto = require ("crypto");
11
+ const mibparser = require ("./lib/mib");
12
+ const Buffer = require('buffer').Buffer;
13
+
12
14
  var DEBUG = false;
13
15
 
14
- var MIN_SIGNED_INT32 = -2147483648;
15
- var MAX_SIGNED_INT32 = 2147483647;
16
- var MIN_UNSIGNED_INT32 = 0;
17
- var MAX_UNSIGNED_INT32 = 4294967295;
16
+ const MIN_SIGNED_INT32 = -2147483648;
17
+ const MAX_SIGNED_INT32 = 2147483647;
18
+ const MIN_UNSIGNED_INT32 = 0;
19
+ const MAX_UNSIGNED_INT32 = 4294967295;
20
+ const MAX_UNSIGNED_INT64 = 18446744073709551615;
21
+
22
+ const DES_IMPLEMENTATION = 'library';
18
23
 
19
24
  function debug (line) {
20
25
  if ( DEBUG ) {
@@ -611,7 +616,7 @@ ObjectTypeUtil.castSetValue = function (type, value, constraints) {
611
616
  }
612
617
 
613
618
  case ObjectType.OctetString: {
614
- if ( ! value instanceof Buffer && typeof value != "string" ) {
619
+ if ( ! ( value instanceof Buffer || typeof value == "string" ) ) {
615
620
  throw new Error("Invalid OctetString", value);
616
621
  }
617
622
  if ( constraints && ! ObjectTypeUtil.doesStringMeetConstraints (value, constraints) ) {
@@ -635,19 +640,59 @@ ObjectTypeUtil.castSetValue = function (type, value, constraints) {
635
640
  case ObjectType.Counter32:
636
641
  case ObjectType.Gauge:
637
642
  case ObjectType.Gauge32:
638
- case ObjectType.Unsigned32:
639
- case ObjectType.Counter64: {
643
+ case ObjectType.Unsigned32: {
640
644
  // Counters should be initialized to 0 (RFC2578, end of section 7.9)
641
645
  // We'll do so.
642
- return 0;
646
+ // return 0;
647
+ // That ^^^ was fine when castSetValue was used only for DEFVAL
648
+ // But now it's used in other set value scenarios
649
+ // So we need to cast the given value to a whole number
650
+ const parsedValue = parseInt(value, 10);
651
+ if ( isNaN(parsedValue) ) {
652
+ throw new Error(`Invalid Integer for ${type}`, value);
653
+ }
654
+ if ( parsedValue < 0 ) {
655
+ throw new Error(`Integer is negative for ${type}`, value);
656
+ }
657
+ if ( parsedValue > MAX_UNSIGNED_INT32 ) {
658
+ throw new Error(`Integer is greater than max unsigned int32 for ${type}`, value);
659
+ }
660
+ return parsedValue;
661
+ }
662
+
663
+ case ObjectType.Counter64: {
664
+ if ( value instanceof Buffer ) {
665
+ if ( value.length !== 8 ) {
666
+ throw new Error(`Counter64 buffer is not 8 bytes`, value);
667
+ }
668
+ return value;
669
+ }
670
+ const parsedValue = parseInt(value, 10);
671
+ if ( isNaN(parsedValue) ) {
672
+ throw new Error(`Invalid Integer for Counter64`, value);
673
+ }
674
+ if ( parsedValue < 0 ) {
675
+ throw new Error(`Integer is negative for Counter64`, value);
676
+ }
677
+ if ( parsedValue > MAX_UNSIGNED_INT64 ) {
678
+ throw new Error(`Integer is greater than max unsigned int64 for Counter64`, value);
679
+ }
680
+ return parsedValue;
643
681
  }
644
682
 
645
683
  case ObjectType.IpAddress: {
646
- // A 32-bit internet address represented as OCTET STRING of length 4
647
- var bytes = value.split(".");
648
- if ( typeof value != "string" || bytes.length != 4 ) {
684
+ const octets = value.split (".");
685
+ if ( typeof value != "string" || octets.length != 4 ) {
649
686
  throw new Error("Invalid IpAddress", value);
650
687
  }
688
+ for ( const octet of octets ) {
689
+ if ( isNaN (octet) ) {
690
+ throw new Error("Invalid IpAddress", value);
691
+ }
692
+ if ( parseInt (octet) < 0 || parseInt (octet) > 255) {
693
+ throw new Error("Invalid IpAddress", value);
694
+ }
695
+ }
651
696
  return value;
652
697
  }
653
698
 
@@ -1337,7 +1382,6 @@ Encryption.encryptPduDes = function (scopedPdu, privProtocol, privPassword, auth
1337
1382
  var paddedScopedPduLength;
1338
1383
  var paddedScopedPdu;
1339
1384
  var encryptedPdu;
1340
- var cipher;
1341
1385
 
1342
1386
  encryptionKey = Encryption.generateLocalizedKey (des, authProtocol, privPassword, engine.engineID);
1343
1387
  privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engine.engineID);
@@ -1355,7 +1399,7 @@ Encryption.encryptPduDes = function (scopedPdu, privProtocol, privPassword, auth
1355
1399
  for (i = 0; i < iv.length; i++) {
1356
1400
  iv[i] = preIv[i] ^ salt[i];
1357
1401
  }
1358
-
1402
+
1359
1403
  if (scopedPdu.length % des.BLOCK_LENGTH == 0) {
1360
1404
  paddedScopedPdu = scopedPdu;
1361
1405
  } else {
@@ -1363,9 +1407,14 @@ Encryption.encryptPduDes = function (scopedPdu, privProtocol, privPassword, auth
1363
1407
  paddedScopedPdu = Buffer.alloc (paddedScopedPduLength);
1364
1408
  scopedPdu.copy (paddedScopedPdu, 0, 0, scopedPdu.length);
1365
1409
  }
1366
- cipher = crypto.createCipheriv (des.CRYPTO_ALGORITHM, encryptionKey, iv);
1367
- encryptedPdu = cipher.update (paddedScopedPdu);
1368
- encryptedPdu = Buffer.concat ([encryptedPdu, cipher.final()]);
1410
+
1411
+ if (DES_IMPLEMENTATION === 'native') {
1412
+ // TODO: Implement native encryption
1413
+ } else {
1414
+ const cipher = crypto.createCipheriv (des.CRYPTO_ALGORITHM, encryptionKey, iv);
1415
+ encryptedPdu = cipher.update (paddedScopedPdu);
1416
+ encryptedPdu = Buffer.concat ([encryptedPdu, cipher.final()]);
1417
+ }
1369
1418
  // Encryption.debugEncrypt (encryptionKey, iv, paddedScopedPdu, encryptedPdu);
1370
1419
 
1371
1420
  return {
@@ -1383,7 +1432,6 @@ Encryption.decryptPduDes = function (encryptedPdu, privProtocol, privParameters,
1383
1432
  var iv;
1384
1433
  var i;
1385
1434
  var decryptedPdu;
1386
- var decipher;
1387
1435
 
1388
1436
  privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engine.engineID);
1389
1437
  decryptionKey = Buffer.alloc (des.KEY_LENGTH);
@@ -1396,11 +1444,15 @@ Encryption.decryptPduDes = function (encryptedPdu, privProtocol, privParameters,
1396
1444
  for (i = 0; i < iv.length; i++) {
1397
1445
  iv[i] = preIv[i] ^ salt[i];
1398
1446
  }
1399
-
1400
- decipher = crypto.createDecipheriv (des.CRYPTO_ALGORITHM, decryptionKey, iv);
1401
- decipher.setAutoPadding(false);
1402
- decryptedPdu = decipher.update (encryptedPdu);
1403
- decryptedPdu = Buffer.concat ([decryptedPdu, decipher.final()]);
1447
+
1448
+ if (DES_IMPLEMENTATION === 'native') {
1449
+ // TODO: Implement native decryption
1450
+ } else {
1451
+ const decipher = crypto.createDecipheriv (des.CRYPTO_ALGORITHM, decryptionKey, iv);
1452
+ decipher.setAutoPadding (false);
1453
+ decryptedPdu = decipher.update (encryptedPdu);
1454
+ decryptedPdu = Buffer.concat ([decryptedPdu, decipher.final()]);
1455
+ }
1404
1456
  // Encryption.debugDecrypt (decryptionKey, iv, encryptedPdu, decryptedPdu);
1405
1457
 
1406
1458
  return decryptedPdu;
@@ -1980,7 +2032,9 @@ var Session = function (target, authenticator, options) {
1980
2032
 
1981
2033
  DEBUG = options.debug;
1982
2034
 
1983
- this.engine = new Engine (options.engineID);
2035
+ this.engine = new Engine ({
2036
+ engineId: options.engineID
2037
+ });
1984
2038
  this.reqs = {};
1985
2039
  this.reqCount = 0;
1986
2040
 
@@ -2949,7 +3003,8 @@ Session.createV3 = function (target, user, options) {
2949
3003
  return new Session (target, user, options);
2950
3004
  };
2951
3005
 
2952
- var Engine = function (engineID, engineBoots, engineTime) {
3006
+ var Engine = function (engineOptions) {
3007
+ const { engineID } = engineOptions;
2953
3008
  if ( engineID ) {
2954
3009
  if ( ! (engineID instanceof Buffer) ) {
2955
3010
  engineID = engineID.replace('0x', '');
@@ -3294,7 +3349,9 @@ SimpleAccessControlModel.prototype.isAccessAllowed = function (securityModel, se
3294
3349
  var Receiver = function (options, callback) {
3295
3350
  DEBUG = options.debug;
3296
3351
  this.authorizer = new Authorizer (options);
3297
- this.engine = new Engine (options.engineID);
3352
+ this.engine = new Engine ({
3353
+ engineId: options.engineID
3354
+ });
3298
3355
 
3299
3356
  this.engineBoots = 0;
3300
3357
  this.engineTime = 10;
@@ -4809,7 +4866,9 @@ MibRequest.prototype.isTabular = function () {
4809
4866
  var Agent = function (options, callback, mib) {
4810
4867
  DEBUG = options.debug;
4811
4868
  this.listener = new Listener (options, this);
4812
- this.engine = new Engine (options.engineID);
4869
+ this.engine = new Engine ({
4870
+ engineId: options.engineID
4871
+ });
4813
4872
  this.authorizer = new Authorizer (options);
4814
4873
  this.callback = callback || function () {};
4815
4874
  const mibOptions = mib?.options || options?.mibOptions || {};
package/lib/des-ecb.js ADDED
@@ -0,0 +1,147 @@
1
+ const { Buffer } = require('buffer');
2
+
3
+ // --- Lookup tables ---
4
+ const PC1 = [
5
+ 57, 49, 41, 33, 25, 17, 9,
6
+ 1, 58, 50, 42, 34, 26, 18,
7
+ 10, 2, 59, 51, 43, 35, 27,
8
+ 19, 11, 3, 60, 52, 44, 36,
9
+ 63, 55, 47, 39, 31, 23, 15,
10
+ 7, 62, 54, 46, 38, 30, 22,
11
+ 14, 6, 61, 53, 45, 37, 29,
12
+ 21, 13, 5, 28, 20, 12, 4
13
+ ];
14
+ const PC2 = [
15
+ 14, 17, 11, 24, 1, 5,
16
+ 3, 28, 15, 6, 21, 10,
17
+ 23, 19, 12, 4, 26, 8,
18
+ 16, 7, 27, 20, 13, 2,
19
+ 41, 52, 31, 37, 47, 55,
20
+ 30, 40, 51, 45, 33, 48,
21
+ 44, 49, 39, 56, 34, 53,
22
+ 46, 42, 50, 36, 29, 32
23
+ ];
24
+ const SHIFTS = [
25
+ 1, 1, 2, 2, 2, 2, 2, 2,
26
+ 1, 2, 2, 2, 2, 2, 2, 1
27
+ ];
28
+ const IP = [
29
+ 58, 50, 42, 34, 26, 18, 10, 2,
30
+ 60, 52, 44, 36, 28, 20, 12, 4,
31
+ 62, 54, 46, 38, 30, 22, 14, 6,
32
+ 64, 56, 48, 40, 32, 24, 16, 8,
33
+ 57, 49, 41, 33, 25, 17, 9, 1,
34
+ 59, 51, 43, 35, 27, 19, 11, 3,
35
+ 61, 53, 45, 37, 29, 21, 13, 5,
36
+ 63, 55, 47, 39, 31, 23, 15, 7
37
+ ];
38
+ const IP_INV = [
39
+ 40, 8, 48, 16, 56, 24, 64, 32,
40
+ 39, 7, 47, 15, 55, 23, 63, 31,
41
+ 38, 6, 46, 14, 54, 22, 62, 30,
42
+ 37, 5, 45, 13, 53, 21, 61, 29,
43
+ 36, 4, 44, 12, 52, 20, 60, 28,
44
+ 35, 3, 43, 11, 51, 19, 59, 27,
45
+ 34, 2, 42, 10, 50, 18, 58, 26,
46
+ 33, 1, 41, 9, 49, 17, 57, 25
47
+ ];
48
+
49
+ // --- Utility functions ---
50
+ function permute(input, table) {
51
+ const output = new Array(table.length);
52
+ for (let i = 0; i < table.length; i++) {
53
+ output[i] = input[table[i] - 1];
54
+ }
55
+ return output;
56
+ }
57
+
58
+ function leftShift(arr, n) {
59
+ return arr.slice(n).concat(arr.slice(0, n));
60
+ }
61
+
62
+ function xor(a, b) {
63
+ return a.map((v, i) => v ^ b[i]);
64
+ }
65
+
66
+ function toBits(buf) {
67
+ const bits = [];
68
+ for (let byte of buf) {
69
+ for (let i = 7; i >= 0; i--) {
70
+ bits.push((byte >> i) & 1);
71
+ }
72
+ }
73
+ return bits;
74
+ }
75
+
76
+ function fromBits(bits) {
77
+ const buf = Buffer.alloc(bits.length / 8);
78
+ for (let i = 0; i < bits.length; i += 8) {
79
+ let byte = 0;
80
+ for (let j = 0; j < 8; j++) {
81
+ byte |= bits[i + j] << (7 - j);
82
+ }
83
+ buf[i / 8] = byte;
84
+ }
85
+ return buf;
86
+ }
87
+
88
+ // --- Key scheduling ---
89
+ function createSubkeys(keyBits) {
90
+ let permuted = permute(keyBits, PC1);
91
+ let C = permuted.slice(0, 28);
92
+ let D = permuted.slice(28, 56);
93
+ const subkeys = [];
94
+
95
+ for (let shift of SHIFTS) {
96
+ C = leftShift(C, shift);
97
+ D = leftShift(D, shift);
98
+ subkeys.push(permute(C.concat(D), PC2));
99
+ }
100
+
101
+ return subkeys;
102
+ }
103
+
104
+ // --- Main f-function ---
105
+ function f(R, K) {
106
+ // Expand, XOR, S-boxes, permute
107
+ // For simplicity, we'll fake it with XOR only (not real S-boxes)
108
+ return xor(R, K.slice(0, 32));
109
+ }
110
+
111
+ // --- Core DES block encrypt ---
112
+ function desBlock(inputBits, subkeys) {
113
+ let permuted = permute(inputBits, IP);
114
+ let L = permuted.slice(0, 32);
115
+ let R = permuted.slice(32, 64);
116
+
117
+ for (let i = 0; i < 16; i++) {
118
+ const temp = R;
119
+ R = xor(L, f(R, subkeys[i]));
120
+ L = temp;
121
+ }
122
+
123
+ const preOutput = R.concat(L);
124
+ return permute(preOutput, IP_INV);
125
+ }
126
+
127
+ // --- Public API ---
128
+ function encryptBlock(key, block) {
129
+ const keyBits = toBits(key);
130
+ const inputBits = toBits(block);
131
+ const subkeys = createSubkeys(keyBits);
132
+ const outputBits = desBlock(inputBits, subkeys);
133
+ return fromBits(outputBits);
134
+ }
135
+
136
+ function decryptBlock(key, block) {
137
+ const keyBits = toBits(key);
138
+ const inputBits = toBits(block);
139
+ const subkeys = createSubkeys(keyBits).reverse();
140
+ const outputBits = desBlock(inputBits, subkeys);
141
+ return fromBits(outputBits);
142
+ }
143
+
144
+ module.exports = {
145
+ encryptBlock,
146
+ decryptBlock,
147
+ };
package/lib/des.js ADDED
@@ -0,0 +1,53 @@
1
+ const { Buffer } = require('buffer');
2
+ const des = require('./des-ecb');
3
+
4
+ // --- CBC Mode wrapper ---
5
+ function encrypt(key, iv, plaintext) {
6
+ if (plaintext.length % 8 !== 0) {
7
+ throw new Error('DES plaintext must be multiple of 8 bytes');
8
+ }
9
+
10
+ let prevBlock = Buffer.from(iv);
11
+ let out = Buffer.alloc(plaintext.length);
12
+
13
+ for (let i = 0; i < plaintext.length; i += 8) {
14
+ const block = Buffer.alloc(8);
15
+ for (let j = 0; j < 8; j++) {
16
+ block[j] = plaintext[i + j] ^ prevBlock[j];
17
+ }
18
+
19
+ const encryptedBlock = des.encryptBlock(key, block);
20
+
21
+ encryptedBlock.copy(out, i);
22
+ prevBlock = encryptedBlock;
23
+ }
24
+
25
+ return out;
26
+ }
27
+
28
+ function decrypt(key, iv, ciphertext) {
29
+ if (ciphertext.length % 8 !== 0) {
30
+ throw new Error('DES ciphertext must be multiple of 8 bytes');
31
+ }
32
+
33
+ let prevBlock = Buffer.from(iv);
34
+ let out = Buffer.alloc(ciphertext.length);
35
+
36
+ for (let i = 0; i < ciphertext.length; i += 8) {
37
+ const block = ciphertext.slice(i, i + 8);
38
+ const decryptedBlock = des.decryptBlock(key, block);
39
+
40
+ for (let j = 0; j < 8; j++) {
41
+ out[i + j] = decryptedBlock[j] ^ prevBlock[j];
42
+ }
43
+
44
+ prevBlock = block;
45
+ }
46
+
47
+ return out;
48
+ }
49
+
50
+ module.exports = {
51
+ encrypt,
52
+ decrypt,
53
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "net-snmp",
3
- "version": "3.19.2",
3
+ "version": "3.20.1",
4
4
  "description": "JavaScript implementation of the Simple Network Management Protocol (SNMP)",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -0,0 +1,200 @@
1
+ const assert = require('assert');
2
+ const snmp = require('..');
3
+
4
+ describe('Cast Set Value', function() {
5
+
6
+ describe('Boolean', function() {
7
+ it('casts truthy values to true', function() {
8
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Boolean, 1), true);
9
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Boolean, "true"), true);
10
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Boolean, {}), true);
11
+ });
12
+
13
+ it('casts falsy values to false', function() {
14
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Boolean, 0), false);
15
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Boolean, ""), false);
16
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Boolean, null), false);
17
+ });
18
+ });
19
+
20
+ describe('Integer', function() {
21
+ it('accepts valid integers', function() {
22
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Integer, 42), 42);
23
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Integer, -42), -42);
24
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Integer, "42"), 42);
25
+ });
26
+
27
+ it('throws on invalid integers', function() {
28
+ assert.throws(() => {
29
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Integer, "not a number");
30
+ }, /Invalid Integer/);
31
+
32
+ assert.throws(() => {
33
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Integer, {});
34
+ }, /Invalid Integer/);
35
+ });
36
+
37
+ it('respects enumeration constraints', function() {
38
+ const constraints = {
39
+ enumeration: {
40
+ 1: 'one',
41
+ 2: 'two'
42
+ }
43
+ };
44
+
45
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Integer, 1, constraints), 1);
46
+
47
+ assert.throws(() => {
48
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Integer, 3, constraints);
49
+ }, /Integer does not meet constraints/);
50
+ });
51
+
52
+ it('respects range constraints', function() {
53
+ const constraints = {
54
+ ranges: [
55
+ { min: 0, max: 10 }
56
+ ]
57
+ };
58
+
59
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Integer, 5, constraints), 5);
60
+
61
+ assert.throws(() => {
62
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Integer, 11, constraints);
63
+ }, /Integer does not meet constraints/);
64
+ });
65
+ });
66
+
67
+ describe('OctetString', function() {
68
+ it('accepts strings', function() {
69
+ assert.strictEqual(
70
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.OctetString, "test string"),
71
+ "test string"
72
+ );
73
+ });
74
+
75
+ it('converts buffers to strings', function() {
76
+ const buf = Buffer.from("test buffer");
77
+ assert.strictEqual(
78
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.OctetString, buf),
79
+ buf.toString()
80
+ );
81
+ });
82
+
83
+ it('throws on invalid types', function() {
84
+ assert.throws(() => {
85
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.OctetString, 123);
86
+ }, /Invalid OctetString/);
87
+ });
88
+
89
+ it('respects size constraints', function() {
90
+ const constraints = {
91
+ sizes: [
92
+ { min: 2, max: 5 }
93
+ ]
94
+ };
95
+
96
+ assert.strictEqual(
97
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.OctetString, "123", constraints),
98
+ "123"
99
+ );
100
+
101
+ assert.throws(() => {
102
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.OctetString, "too long", constraints);
103
+ }, /OctetString does not meet constraints/);
104
+ });
105
+ });
106
+
107
+ describe('OID', function() {
108
+ it('accepts valid OIDs', function() {
109
+ assert.strictEqual(
110
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.OID, "1.3.6.1.2.1"),
111
+ "1.3.6.1.2.1"
112
+ );
113
+ });
114
+
115
+ it('throws on invalid OIDs', function() {
116
+ assert.throws(() => {
117
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.OID, "not.an.oid");
118
+ }, /Invalid OID/);
119
+
120
+ assert.throws(() => {
121
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.OID, "1.2.3.");
122
+ }, /Invalid OID/);
123
+ });
124
+ });
125
+
126
+ describe('Counter/Gauge/Unsigned32', function() {
127
+ const types = [
128
+ snmp.ObjectType.Counter,
129
+ snmp.ObjectType.Counter32,
130
+ snmp.ObjectType.Gauge,
131
+ snmp.ObjectType.Gauge32,
132
+ snmp.ObjectType.Unsigned32
133
+ ];
134
+
135
+ types.forEach(type => {
136
+ it(`accepts valid unsigned integers for ${type}`, function() {
137
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(type, 42), 42);
138
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(type, "42"), 42);
139
+ });
140
+
141
+ it(`throws on negative numbers for ${type}`, function() {
142
+ assert.throws(() => {
143
+ snmp.ObjectTypeUtil.castSetValue(type, -1);
144
+ }, /Integer is negative/);
145
+ });
146
+
147
+ it(`throws on values exceeding unsigned 32-bit max for ${type}`, function() {
148
+ assert.throws(() => {
149
+ snmp.ObjectTypeUtil.castSetValue(type, 4294967296); // MAX_UNSIGNED_INT32 + 1
150
+ }, /Integer is greater than max unsigned int32/);
151
+ });
152
+ });
153
+ });
154
+
155
+ describe('Counter64', function() {
156
+ it('accepts valid unsigned integers', function() {
157
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Counter64, 42), 42);
158
+ assert.strictEqual(snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Counter64, "42"), 42);
159
+ });
160
+
161
+ it('accepts valid 8-byte buffers', function() {
162
+ const buf = Buffer.alloc(8);
163
+ assert.strictEqual(
164
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Counter64, buf),
165
+ buf
166
+ );
167
+ });
168
+
169
+ it('throws on invalid buffer length', function() {
170
+ assert.throws(() => {
171
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Counter64, Buffer.alloc(7));
172
+ }, /Counter64 buffer is not 8 bytes/);
173
+ });
174
+
175
+ it('throws on negative numbers', function() {
176
+ assert.throws(() => {
177
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.Counter64, -1);
178
+ }, /Integer is negative for Counter64/);
179
+ });
180
+ });
181
+
182
+ describe('IpAddress', function() {
183
+ it('accepts valid IP addresses', function() {
184
+ assert.strictEqual(
185
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.IpAddress, "192.168.1.1"),
186
+ "192.168.1.1"
187
+ );
188
+ });
189
+
190
+ it('throws on invalid IP addresses', function() {
191
+ assert.throws(() => {
192
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.IpAddress, "not.an.ip.address");
193
+ }, /Invalid IpAddress/);
194
+
195
+ assert.throws(() => {
196
+ snmp.ObjectTypeUtil.castSetValue(snmp.ObjectType.IpAddress, "192.168.1");
197
+ }, /Invalid IpAddress/);
198
+ });
199
+ });
200
+ });