net-snmp 3.23.0 → 3.25.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 CHANGED
@@ -1947,6 +1947,8 @@ var myScalarProvider = {
1947
1947
  // e.g. can update the MIB data before responding to the request here
1948
1948
  mibRequest.done ();
1949
1949
  }
1950
+ // Note: handler is optional for scalar providers - if omitted,
1951
+ // mibRequest.done() is called automatically
1950
1952
  };
1951
1953
  var mib = agent.getMib ();
1952
1954
  mib.registerProvider (myScalarProvider);
@@ -2157,6 +2159,8 @@ objects`, below, for details.
2157
2159
  * `handler` *(optional)* - an optional callback function, which is called before the request to the
2158
2160
  MIB is made. This could update the MIB value(s) handled by this provider. If not given,
2159
2161
  the values are simply returned from (or set in) the MIB without any other processing.
2162
+ If no `handler` is provided, the system automatically calls `mibRequest.done()` to complete
2163
+ the request, making both scalar and table providers fully functional without explicit handlers.
2160
2164
  The callback function takes a `MibRequest` instance, which has a `done()` function. This
2161
2165
  must be called when finished processing the request. To signal an error, give a single error object
2162
2166
  in the form of `{errorStatus: <status>}`, where `<status>` is a value from ErrorStatus e.g.
@@ -2704,6 +2708,79 @@ to both register the provider on its internal `Mib` object, *and* send a Registe
2704
2708
  agent for the provider's MIB region. The latter step is skipped if registering the provider directly
2705
2709
  on the MIB object.
2706
2710
 
2711
+ ## Simplified Provider Example
2712
+
2713
+ AgentX subagents provide automatic request handling for both scalar and table providers, making them simple to implement:
2714
+
2715
+ ```js
2716
+ // Both scalars and tables work with automatic request handling (no explicit handlers needed)
2717
+ var stringProvider = {
2718
+ name: "scalarString",
2719
+ type: snmp.MibProviderType.Scalar,
2720
+ oid: "1.3.6.1.4.1.8072.9999.9999.1",
2721
+ scalarType: snmp.ObjectType.OctetString,
2722
+ maxAccess: snmp.MaxAccess["read-write"]
2723
+ // No handler required - automatic fallback will call mibRequest.done()
2724
+ };
2725
+
2726
+ var intProvider = {
2727
+ name: "scalarInt",
2728
+ type: snmp.MibProviderType.Scalar,
2729
+ oid: "1.3.6.1.4.1.8072.9999.9999.3",
2730
+ scalarType: snmp.ObjectType.Integer,
2731
+ maxAccess: snmp.MaxAccess["read-write"]
2732
+ // No handler required - automatic fallback will call mibRequest.done()
2733
+ };
2734
+
2735
+ // Table providers also work without explicit handlers
2736
+ var tableProvider = {
2737
+ name: "smallIfTable",
2738
+ type: snmp.MibProviderType.Table,
2739
+ oid: "1.3.6.1.4.1.8072.9999.9999.2",
2740
+ maxAccess: snmp.MaxAccess['not-accessible'],
2741
+ tableColumns: [
2742
+ {
2743
+ number: 1,
2744
+ name: "ifIndex",
2745
+ type: snmp.ObjectType.Integer,
2746
+ maxAccess: snmp.MaxAccess['read-only']
2747
+ },
2748
+ {
2749
+ number: 2,
2750
+ name: "ifDescr",
2751
+ type: snmp.ObjectType.OctetString,
2752
+ maxAccess: snmp.MaxAccess['read-write']
2753
+ }
2754
+ ],
2755
+ tableIndex: [
2756
+ {
2757
+ columnName: "ifIndex"
2758
+ }
2759
+ ]
2760
+ // No handler required - automatic fallback works for tables too!
2761
+ };
2762
+
2763
+ // Register providers and set values
2764
+ subagent.registerProvider(stringProvider, function(error, data) {
2765
+ if (!error) {
2766
+ subagent.getMib().setScalarValue("scalarString", "Hello World!");
2767
+ }
2768
+ });
2769
+
2770
+ subagent.registerProvider(intProvider, function(error, data) {
2771
+ if (!error) {
2772
+ subagent.getMib().setScalarValue("scalarInt", 42);
2773
+ }
2774
+ });
2775
+
2776
+ subagent.registerProvider(tableProvider, function(error, data) {
2777
+ if (!error) {
2778
+ subagent.getMib().addTableRow("smallIfTable", [1, "lo"]);
2779
+ subagent.getMib().addTableRow("smallIfTable", [2, "eth0"]);
2780
+ }
2781
+ });
2782
+ ```
2783
+
2707
2784
  ## snmp.createSubagent (options)
2708
2785
 
2709
2786
  The `createSubagent ()` function instantiates and returns an instance of the `Subagent`
@@ -2764,6 +2841,11 @@ subsequent `Response` PDU from the master to the `Register` PDU. This is not to
2764
2841
  with the `handler` optional callback on the provider definition, which is invoked for any
2765
2842
  "request processing" PDU received by the subagent for MIB objects in the registered MIB region.
2766
2843
 
2844
+ **Note for AgentX subagents**: Neither scalar nor table providers require explicit `handler` functions.
2845
+ If no `handler` is provided, the subagent automatically calls `mibRequest.done()` to complete the request,
2846
+ allowing both scalar and table values to be served directly from the MIB without additional processing.
2847
+ This greatly simplifies provider definitions - you only need to specify the structure and `maxAccess` properties.
2848
+
2767
2849
  ## subagent.unregisterProvider (name, callback)
2768
2850
 
2769
2851
  Unregisters a previously registered MIB region by the supplied name of the provider. Sends
@@ -2814,6 +2896,48 @@ Sends a "ping" to the master agent using a `Ping` PDU, to confirm that the maste
2814
2896
  responsive. The supplied `callback` is called on reception of the subsequent
2815
2897
  `Response` PDU from the master to the `Ping` PDU.
2816
2898
 
2899
+ ## subagent.setBulkSetHandler (callback)
2900
+
2901
+ Sets a bulk set handler for the subagent that will be called for both **TestSet** and **CommitSet**
2902
+ phases of SNMP SET operations. This provides atomic validation capabilities, allowing you to validate
2903
+ all varbinds as a complete set before committing any changes.
2904
+
2905
+ The handler callback function receives two arguments:
2906
+ * `testSet` - A boolean indicating the phase: `true` for TestSet phase, `false` for CommitSet phase
2907
+ * `varbinds` - An array of all varbinds in the SET request
2908
+
2909
+ During the TestSet phase (`testSet = true`), you should validate all the requested changes but not
2910
+ apply them. During the CommitSet phase (`testSet = false`), you should apply the previously validated changes.
2911
+
2912
+ Example usage:
2913
+
2914
+ ```js
2915
+ subagent.setBulkSetHandler(function(testSet, varbinds) {
2916
+ if (testSet) {
2917
+ // TestSet phase - validate all varbinds atomically
2918
+ console.log("Validating SET operation with", varbinds.length, "varbinds");
2919
+ for (let varbind of varbinds) {
2920
+ // Perform validation logic here
2921
+ if (!isValidValue(varbind.oid, varbind.value)) {
2922
+ throw new Error("Invalid value for " + varbind.oid);
2923
+ }
2924
+ }
2925
+ console.log("All varbinds validated successfully");
2926
+ } else {
2927
+ // CommitSet phase - apply the changes
2928
+ console.log("Committing SET operation");
2929
+ for (let varbind of varbinds) {
2930
+ // Apply the validated changes here
2931
+ applyValue(varbind.oid, varbind.value);
2932
+ }
2933
+ console.log("All changes committed");
2934
+ }
2935
+ });
2936
+ ```
2937
+
2938
+ This approach ensures atomic SET operations where either all varbinds in a request succeed or all fail,
2939
+ maintaining data consistency across your MIB implementation.
2940
+
2817
2941
  ## subagent.on ("close", callback)
2818
2942
 
2819
2943
  The `close` event is emitted by the subagent when its underlying TCP socket is closed.
@@ -3565,6 +3689,14 @@ Example programs are included under the module's `example` directory.
3565
3689
 
3566
3690
  * Add support for custom dgram module
3567
3691
 
3692
+ # Version 3.24.0 - 28/08/2025
3693
+
3694
+ * Improve USM error handling compliance with RFC 3414
3695
+
3696
+ # Version 3.25.0 - 28/08/2025
3697
+
3698
+ * Add separate AgentX subagent TestSet and CommitSet phases
3699
+
3568
3700
  # License
3569
3701
 
3570
3702
  Copyright (c) 2020 Mark Abrahams <mark@abrahams.co.nz>
package/eslint.config.js CHANGED
@@ -23,10 +23,13 @@ module.exports = [
23
23
  setTimeout: 'readonly',
24
24
  clearInterval: 'readonly',
25
25
  clearTimeout: 'readonly',
26
+ setImmediate: 'readonly',
27
+ clearImmediate: 'readonly',
26
28
 
27
29
  // Mocha globals
28
30
  describe: 'readonly',
29
31
  it: 'readonly',
32
+ xit: 'readonly',
30
33
  before: 'readonly',
31
34
  after: 'readonly',
32
35
  beforeEach: 'readonly',
@@ -15,33 +15,39 @@ var stringProvider = {
15
15
  name: "scalarString",
16
16
  type: snmp.MibProviderType.Scalar,
17
17
  oid: "1.3.6.1.4.1.8072.9999.9999.1",
18
- scalarType: snmp.ObjectType.OctetString
18
+ scalarType: snmp.ObjectType.OctetString,
19
+ maxAccess: snmp.MaxAccess["read-write"]
19
20
  };
20
21
  var intProvider = {
21
22
  name: "scalarInt",
22
23
  type: snmp.MibProviderType.Scalar,
23
24
  oid: "1.3.6.1.4.1.8072.9999.9999.3",
24
- scalarType: snmp.ObjectType.Integer
25
+ scalarType: snmp.ObjectType.Integer,
26
+ maxAccess: snmp.MaxAccess["read-write"]
25
27
  };
26
28
  var tableProvider = {
27
29
  name: "smallIfTable",
28
30
  type: snmp.MibProviderType.Table,
29
31
  oid: "1.3.6.1.4.1.8072.9999.9999.2",
32
+ maxAccess: snmp.MaxAccess['not-accessible'],
30
33
  tableColumns: [
31
34
  {
32
35
  number: 1,
33
36
  name: "ifIndex",
34
- type: snmp.ObjectType.Integer
37
+ type: snmp.ObjectType.Integer,
38
+ maxAccess: snmp.MaxAccess['read-only']
35
39
  },
36
40
  {
37
41
  number: 2,
38
42
  name: "ifDescr",
39
- type: snmp.ObjectType.OctetString
43
+ type: snmp.ObjectType.OctetString,
44
+ maxAccess: snmp.MaxAccess['read-write']
40
45
  },
41
46
  {
42
47
  number: 3,
43
48
  name: "ifType",
44
- type: snmp.ObjectType.Integer
49
+ type: snmp.ObjectType.Integer,
50
+ maxAccess: snmp.MaxAccess['read-only']
45
51
  }
46
52
  ],
47
53
  tableIndex: [
@@ -49,10 +55,7 @@ var tableProvider = {
49
55
  columnName: "ifIndex"
50
56
  }
51
57
  ],
52
- handler: function (mibRequest) {
53
- // e.g. can update the table before responding to the request here
54
- mibRequest.done ();
55
- }
58
+
56
59
  };
57
60
 
58
61
  agent.open(function (error, data) {
@@ -66,9 +69,41 @@ agent.open(function (error, data) {
66
69
  agent.registerProvider (tableProvider, null);
67
70
  agent.getMib ().addTableRow ("smallIfTable", [1, "lo", 24]);
68
71
  agent.getMib ().addTableRow ("smallIfTable", [2, "eth0", 6]);
72
+
73
+ console.log("AgentX subagent ready and providers registered successfully.");
74
+
69
75
  agent.on("close", function() {
70
76
  console.log ("Subagent socket closed");
71
77
  });
78
+
79
+ // Handle graceful shutdown on Ctrl-C
80
+ process.on('SIGINT', function() {
81
+ console.log('\nReceived SIGINT. Cleaning up...');
82
+
83
+ // Unregister all providers
84
+ try {
85
+ agent.unregisterProvider("scalarString", function(err, data) {
86
+ if (err) console.error("Error unregistering scalarString:", err);
87
+ });
88
+ agent.unregisterProvider("scalarInt", function(err, data) {
89
+ if (err) console.error("Error unregistering scalarInt:", err);
90
+ });
91
+ agent.unregisterProvider("smallIfTable", function(err, data) {
92
+ if (err) console.error("Error unregistering smallIfTable:", err);
93
+ });
94
+ } catch (e) {
95
+ console.error("Error during unregistration:", e.message);
96
+ }
97
+
98
+ // Close the agent after a brief delay to allow unregistrations to complete
99
+ setTimeout(function() {
100
+ agent.close(function(err) {
101
+ if (err) console.error("Error closing agent:", err);
102
+ console.log("Agent closed gracefully");
103
+ process.exit(0);
104
+ });
105
+ }, 100);
106
+ });
72
107
  }
73
108
  });
74
109