net-snmp 3.27.0 → 3.28.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 +19 -2
- package/index.js +24 -11
- package/package.json +10 -1
- package/.claude/skills/git-commit/SKILL.md +0 -54
- package/.claude/skills/prepare-release/SKILL.md +0 -97
- package/.claude/skills/propose-action/SKILL.md +0 -111
- package/.claude/skills/publish-release/SKILL.md +0 -68
- package/.github/FUNDING.yml +0 -1
- package/.travis.yml +0 -13
- package/.vscode/launch.json +0 -249
- package/eslint.config.js +0 -98
- package/example/test.js +0 -70
- package/test/README-test-coverage.md +0 -76
- package/test/TEST-MIB.mib +0 -210
- package/test/cast-set-value.test.js +0 -200
- package/test/crypto.test.js +0 -384
- package/test/dgram-module.test.js +0 -71
- package/test/mib.test.js +0 -139
- package/test/object-types.test.js +0 -269
- package/test/oid-in-subtree.test.js +0 -36
- package/test/receiver-test.sh +0 -83
- package/test/snmp-test.sh +0 -122
- package/test/snmpv3-auth-failures.test.js +0 -331
- package/test/snmpv3-engine-time.test.js +0 -563
- package/test/strict-int-range-checks.test.js +0 -337
- package/test/subagent.test.js +0 -477
- package/test/transport-option.test.js +0 -53
- package/test/varbinds.test.js +0 -53
|
@@ -1,563 +0,0 @@
|
|
|
1
|
-
const assert = require('assert');
|
|
2
|
-
const { performance } = require('perf_hooks');
|
|
3
|
-
const snmp = require('../');
|
|
4
|
-
|
|
5
|
-
const MAX_SIGNED_INT32 = 2147483647;
|
|
6
|
-
const USM_TIME_WINDOW_SECONDS = 150;
|
|
7
|
-
|
|
8
|
-
// A stand-in for a parsed Message, carrying only what the engine time methods
|
|
9
|
-
// read: the USM security parameters and whether the message was authenticated.
|
|
10
|
-
// The real Message.prototype.hasAuthentication returns a truthy number rather
|
|
11
|
-
// than a boolean, so this mirrors that. The end-to-end suite at the bottom of
|
|
12
|
-
// this file exercises the same code paths with real parsed messages.
|
|
13
|
-
const fakeMessage = (engineBoots, engineTime, authenticated) => ({
|
|
14
|
-
msgSecurityParameters: {
|
|
15
|
-
msgAuthoritativeEngineID: Buffer.from('8000B98380ABCDEF12345678', 'hex'),
|
|
16
|
-
msgAuthoritativeEngineBoots: engineBoots,
|
|
17
|
-
msgAuthoritativeEngineTime: engineTime
|
|
18
|
-
},
|
|
19
|
-
hasAuthentication: () => authenticated ? 1 : 0
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
// An SNMPv1 or v2c message has no USM security parameters at all.
|
|
23
|
-
const fakeCommunityMessage = () => ({
|
|
24
|
-
hasAuthentication: () => undefined
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('SNMPv3 authoritative engine time (RFC 3414)', function () {
|
|
28
|
-
|
|
29
|
-
const user = {
|
|
30
|
-
name: 'betty',
|
|
31
|
-
level: snmp.SecurityLevel.authNoPriv,
|
|
32
|
-
authProtocol: snmp.AuthProtocols.sha,
|
|
33
|
-
authKey: 'illhavesomeauth'
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
let session;
|
|
37
|
-
|
|
38
|
-
beforeEach(function () {
|
|
39
|
-
session = snmp.createV3Session('127.0.0.1', user, { port: 16210 });
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
afterEach(function () {
|
|
43
|
-
if (session) {
|
|
44
|
-
session.close();
|
|
45
|
-
session = null;
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// Pretend the current anchor was established `seconds` ago, without waiting.
|
|
50
|
-
const rewindAnchor = (seconds) => {
|
|
51
|
-
session.engineTimeReceivedAt -= seconds * 1000;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
describe('setEngineTime - time window updates (section 3.2 step 7b(1))', function () {
|
|
55
|
-
|
|
56
|
-
it('establishes a notion of time from the first authentic message', function () {
|
|
57
|
-
session.setEngineTime(fakeMessage(3, 5000, true));
|
|
58
|
-
|
|
59
|
-
assert.strictEqual(session.engineTimeBoots, 3);
|
|
60
|
-
assert.strictEqual(session.engineTimeBase, 5000);
|
|
61
|
-
assert.strictEqual(session.latestReceivedEngineTime, 5000);
|
|
62
|
-
assert.notStrictEqual(session.engineTimeReceivedAt, null);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('ignores a message that was not authenticated', function () {
|
|
66
|
-
session.setEngineTime(fakeMessage(3, 5000, false));
|
|
67
|
-
|
|
68
|
-
assert.strictEqual(session.engineTimeBoots, null);
|
|
69
|
-
assert.strictEqual(session.engineTimeBase, null);
|
|
70
|
-
assert.strictEqual(session.latestReceivedEngineTime, null);
|
|
71
|
-
assert.strictEqual(session.engineTimeReceivedAt, null);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('ignores a message with no USM security parameters', function () {
|
|
75
|
-
session.setEngineTime(fakeCommunityMessage());
|
|
76
|
-
|
|
77
|
-
assert.strictEqual(session.engineTimeBoots, null);
|
|
78
|
-
assert.strictEqual(session.engineTimeReceivedAt, null);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('updates when engineBoots has increased', function () {
|
|
82
|
-
session.setEngineTime(fakeMessage(3, 5000, true));
|
|
83
|
-
session.setEngineTime(fakeMessage(4, 20, true));
|
|
84
|
-
|
|
85
|
-
assert.strictEqual(session.engineTimeBoots, 4);
|
|
86
|
-
assert.strictEqual(session.engineTimeBase, 20);
|
|
87
|
-
assert.strictEqual(session.latestReceivedEngineTime, 20);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('ignores a message whose engineBoots has decreased', function () {
|
|
91
|
-
session.setEngineTime(fakeMessage(3, 5000, true));
|
|
92
|
-
session.setEngineTime(fakeMessage(2, 900000, true));
|
|
93
|
-
|
|
94
|
-
assert.strictEqual(session.engineTimeBoots, 3);
|
|
95
|
-
assert.strictEqual(session.engineTimeBase, 5000);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('updates when engineBoots is unchanged and engineTime has advanced', function () {
|
|
99
|
-
session.setEngineTime(fakeMessage(3, 5000, true));
|
|
100
|
-
session.setEngineTime(fakeMessage(3, 5060, true));
|
|
101
|
-
|
|
102
|
-
assert.strictEqual(session.engineTimeBoots, 3);
|
|
103
|
-
assert.strictEqual(session.engineTimeBase, 5060);
|
|
104
|
-
assert.strictEqual(session.latestReceivedEngineTime, 5060);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('ignores a replay of the highest engineTime already received', function () {
|
|
108
|
-
session.setEngineTime(fakeMessage(3, 5000, true));
|
|
109
|
-
const anchoredAt = session.engineTimeReceivedAt;
|
|
110
|
-
|
|
111
|
-
session.setEngineTime(fakeMessage(3, 5000, true));
|
|
112
|
-
|
|
113
|
-
assert.strictEqual(session.engineTimeBase, 5000);
|
|
114
|
-
assert.strictEqual(session.engineTimeReceivedAt, anchoredAt);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('ignores a replay of an engineTime below the highest already received', function () {
|
|
118
|
-
session.setEngineTime(fakeMessage(3, 5000, true));
|
|
119
|
-
session.setEngineTime(fakeMessage(3, 4000, true));
|
|
120
|
-
|
|
121
|
-
assert.strictEqual(session.engineTimeBase, 5000);
|
|
122
|
-
assert.strictEqual(session.latestReceivedEngineTime, 5000);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// Regression test: the comparison in section 3.2 step 7b(1) is against
|
|
126
|
-
// latestReceivedEngineTime, not against the locally advanced notion of
|
|
127
|
-
// snmpEngineTime. Comparing against the advanced notion would mean that
|
|
128
|
-
// a local clock running ahead of the agent could never resynchronise,
|
|
129
|
-
// leaving the session permanently outside the agent's time window.
|
|
130
|
-
it('resynchronises downwards when the local clock has raced ahead of the agent', function () {
|
|
131
|
-
session.setEngineTime(fakeMessage(1, 1000, true));
|
|
132
|
-
rewindAnchor(2000);
|
|
133
|
-
assert.strictEqual(session.getEngineTime().engineTime, 3000);
|
|
134
|
-
|
|
135
|
-
session.setEngineTime(fakeMessage(1, 1100, true));
|
|
136
|
-
|
|
137
|
-
assert.strictEqual(session.engineTimeBase, 1100);
|
|
138
|
-
assert.strictEqual(session.latestReceivedEngineTime, 1100);
|
|
139
|
-
assert.strictEqual(session.getEngineTime().engineTime, 1100);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
describe('getEngineTime - local advancement (section 2.3)', function () {
|
|
144
|
-
|
|
145
|
-
it('has no notion of time before any authentic message', function () {
|
|
146
|
-
assert.strictEqual(session.getEngineTime(), null);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('advances the notion of engineTime with the local clock', function () {
|
|
150
|
-
session.setEngineTime(fakeMessage(2, 1000, true));
|
|
151
|
-
rewindAnchor(75);
|
|
152
|
-
|
|
153
|
-
const notion = session.getEngineTime();
|
|
154
|
-
assert.strictEqual(notion.engineBoots, 2);
|
|
155
|
-
assert.strictEqual(notion.engineTime, 1075);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('does not advance engineBoots while engineTime is in range', function () {
|
|
159
|
-
session.setEngineTime(fakeMessage(2, 1000, true));
|
|
160
|
-
rewindAnchor(100000);
|
|
161
|
-
|
|
162
|
-
assert.strictEqual(session.getEngineTime().engineBoots, 2);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('uses a monotonic clock, so a system clock step cannot move it', function () {
|
|
166
|
-
session.setEngineTime(fakeMessage(2, 1000, true));
|
|
167
|
-
|
|
168
|
-
// engineTimeReceivedAt is a performance.now() reading, which is
|
|
169
|
-
// unrelated to Date.now() and unaffected by system clock steps.
|
|
170
|
-
assert.ok(Math.abs(session.engineTimeReceivedAt - performance.now()) < 1000);
|
|
171
|
-
assert.ok(Math.abs(session.engineTimeReceivedAt - Date.now()) > 1000000);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// RFC 3414 section 2.2.2: when snmpEngineTime reaches its maximum value,
|
|
175
|
-
// snmpEngineBoots is incremented and snmpEngineTime is reset to zero.
|
|
176
|
-
it('rolls engineTime over and increments engineBoots at the 31-bit limit', function () {
|
|
177
|
-
session.setEngineTime(fakeMessage(7, MAX_SIGNED_INT32 - 10, true));
|
|
178
|
-
rewindAnchor(30);
|
|
179
|
-
|
|
180
|
-
const notion = session.getEngineTime();
|
|
181
|
-
assert.strictEqual(notion.engineBoots, 8);
|
|
182
|
-
assert.strictEqual(notion.engineTime, 19);
|
|
183
|
-
assert.ok(notion.engineTime >= 0 && notion.engineTime <= MAX_SIGNED_INT32);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('emits the maximum engineTime rather than rolling over early', function () {
|
|
187
|
-
session.setEngineTime(fakeMessage(7, MAX_SIGNED_INT32 - 10, true));
|
|
188
|
-
rewindAnchor(10);
|
|
189
|
-
|
|
190
|
-
const notion = session.getEngineTime();
|
|
191
|
-
assert.strictEqual(notion.engineBoots, 7);
|
|
192
|
-
assert.strictEqual(notion.engineTime, MAX_SIGNED_INT32);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it('latches engineBoots at its maximum value rather than wrapping', function () {
|
|
196
|
-
session.setEngineTime(fakeMessage(MAX_SIGNED_INT32 - 1, 0, true));
|
|
197
|
-
rewindAnchor((MAX_SIGNED_INT32 + 1) * 5);
|
|
198
|
-
|
|
199
|
-
assert.strictEqual(session.getEngineTime().engineBoots, MAX_SIGNED_INT32);
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
describe('isInTimeWindow - timeliness checks (section 3.2 step 7b(2))', function () {
|
|
204
|
-
|
|
205
|
-
it('accepts a message when no notion of time has been established', function () {
|
|
206
|
-
assert.strictEqual(session.isInTimeWindow(fakeMessage(1, 1000, true)), true);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('accepts an unauthenticated message without checking the window', function () {
|
|
210
|
-
session.setEngineTime(fakeMessage(1, 1000, true));
|
|
211
|
-
|
|
212
|
-
assert.strictEqual(session.isInTimeWindow(fakeMessage(1, 999999, false)), true);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('accepts a message with no USM security parameters', function () {
|
|
216
|
-
assert.strictEqual(session.isInTimeWindow(fakeCommunityMessage()), true);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it('accepts the message that just established the notion of time', function () {
|
|
220
|
-
const message = fakeMessage(1, 1000, true);
|
|
221
|
-
session.setEngineTime(message);
|
|
222
|
-
|
|
223
|
-
assert.strictEqual(session.isInTimeWindow(message), true);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it('accepts a message inside the time window', function () {
|
|
227
|
-
session.setEngineTime(fakeMessage(1, 1000, true));
|
|
228
|
-
|
|
229
|
-
assert.strictEqual(session.isInTimeWindow(fakeMessage(1, 900, true)), true);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('accepts a message exactly at the edge of the time window', function () {
|
|
233
|
-
session.setEngineTime(fakeMessage(1, 1000, true));
|
|
234
|
-
|
|
235
|
-
assert.strictEqual(
|
|
236
|
-
session.isInTimeWindow(fakeMessage(1, 1000 - USM_TIME_WINDOW_SECONDS, true)), true);
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
it('rejects a message more than the time window behind our notion', function () {
|
|
240
|
-
session.setEngineTime(fakeMessage(1, 1000, true));
|
|
241
|
-
|
|
242
|
-
assert.strictEqual(
|
|
243
|
-
session.isInTimeWindow(fakeMessage(1, 1000 - USM_TIME_WINDOW_SECONDS - 1, true)),
|
|
244
|
-
false);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it('rejects a message whose engineBoots disagrees with our notion', function () {
|
|
248
|
-
session.setEngineTime(fakeMessage(5, 1000, true));
|
|
249
|
-
|
|
250
|
-
assert.strictEqual(session.isInTimeWindow(fakeMessage(4, 1000, true)), false);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it('rejects every message once engineBoots has latched at its maximum', function () {
|
|
254
|
-
session.setEngineTime(fakeMessage(MAX_SIGNED_INT32, 1000, true));
|
|
255
|
-
|
|
256
|
-
assert.strictEqual(session.isInTimeWindow(fakeMessage(MAX_SIGNED_INT32, 1000, true)),
|
|
257
|
-
false);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it('measures the window against the advanced notion, not the anchor', function () {
|
|
261
|
-
session.setEngineTime(fakeMessage(1, 1000, true));
|
|
262
|
-
rewindAnchor(200);
|
|
263
|
-
|
|
264
|
-
// Our notion is now 1200, so the original anchor value of 1000 has
|
|
265
|
-
// fallen outside the window, while 1200 is at its centre.
|
|
266
|
-
assert.strictEqual(session.isInTimeWindow(fakeMessage(1, 1000, true)), false);
|
|
267
|
-
assert.strictEqual(session.isInTimeWindow(fakeMessage(1, 1200, true)), true);
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
describe('advanceEngineTime - outgoing requests (section 2.3)', function () {
|
|
272
|
-
|
|
273
|
-
it('does nothing when the session has no cached security parameters', function () {
|
|
274
|
-
session.setEngineTime(fakeMessage(1, 1000, true));
|
|
275
|
-
assert.strictEqual(session.msgSecurityParameters, undefined);
|
|
276
|
-
|
|
277
|
-
session.advanceEngineTime();
|
|
278
|
-
|
|
279
|
-
assert.strictEqual(session.msgSecurityParameters, undefined);
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
it('does nothing when no notion of time has been established', function () {
|
|
283
|
-
session.msgSecurityParameters = {
|
|
284
|
-
msgAuthoritativeEngineBoots: 0,
|
|
285
|
-
msgAuthoritativeEngineTime: 0
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
session.advanceEngineTime();
|
|
289
|
-
|
|
290
|
-
assert.strictEqual(session.msgSecurityParameters.msgAuthoritativeEngineBoots, 0);
|
|
291
|
-
assert.strictEqual(session.msgSecurityParameters.msgAuthoritativeEngineTime, 0);
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
it('writes the advanced notion into the cached security parameters', function () {
|
|
295
|
-
session.msgSecurityParameters = {
|
|
296
|
-
msgAuthoritativeEngineBoots: 1,
|
|
297
|
-
msgAuthoritativeEngineTime: 1000
|
|
298
|
-
};
|
|
299
|
-
session.setEngineTime(fakeMessage(1, 1000, true));
|
|
300
|
-
rewindAnchor(600);
|
|
301
|
-
|
|
302
|
-
session.advanceEngineTime();
|
|
303
|
-
|
|
304
|
-
assert.strictEqual(session.msgSecurityParameters.msgAuthoritativeEngineBoots, 1);
|
|
305
|
-
assert.strictEqual(session.msgSecurityParameters.msgAuthoritativeEngineTime, 1600);
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
it('carries the advanced engineTime on the outgoing request', function () {
|
|
309
|
-
session.msgSecurityParameters = {
|
|
310
|
-
msgAuthoritativeEngineID: Buffer.from('8000B98380ABCDEF12345678', 'hex'),
|
|
311
|
-
msgAuthoritativeEngineBoots: 1,
|
|
312
|
-
msgAuthoritativeEngineTime: 1000
|
|
313
|
-
};
|
|
314
|
-
session.setEngineTime(fakeMessage(1, 1000, true));
|
|
315
|
-
rewindAnchor(420);
|
|
316
|
-
|
|
317
|
-
let sent;
|
|
318
|
-
session.send = (req) => { sent = req; };
|
|
319
|
-
session.get(['1.3.6.1.2.1.1.1.0'], function () {});
|
|
320
|
-
|
|
321
|
-
assert.ok(sent, 'a request should have been handed to send()');
|
|
322
|
-
assert.strictEqual(sent.message.msgSecurityParameters.msgAuthoritativeEngineBoots, 1);
|
|
323
|
-
assert.strictEqual(sent.message.msgSecurityParameters.msgAuthoritativeEngineTime, 1420);
|
|
324
|
-
});
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// These tests drive a real agent over the loopback interface, so the engine
|
|
329
|
-
// time methods are fed genuine parsed and authenticated Messages rather than
|
|
330
|
-
// the stand-in above. They confirm the wiring in Session.onMsg as well as the
|
|
331
|
-
// assumption that Message.hasAuthentication distinguishes the authenticated
|
|
332
|
-
// GetResponse from the unauthenticated discovery Report.
|
|
333
|
-
describe('SNMPv3 authoritative engine time over the wire', function () {
|
|
334
|
-
|
|
335
|
-
const agentPort = 16211;
|
|
336
|
-
const engineID = '8000B98380DEADBEEF00000001';
|
|
337
|
-
const sysDescrOid = '1.3.6.1.2.1.1.1.0';
|
|
338
|
-
|
|
339
|
-
const authUser = {
|
|
340
|
-
name: 'betty',
|
|
341
|
-
level: snmp.SecurityLevel.authNoPriv,
|
|
342
|
-
authProtocol: snmp.AuthProtocols.sha,
|
|
343
|
-
authKey: 'illhavesomeauth'
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
const authPrivUser = {
|
|
347
|
-
name: 'wilma',
|
|
348
|
-
level: snmp.SecurityLevel.authPriv,
|
|
349
|
-
authProtocol: snmp.AuthProtocols.sha,
|
|
350
|
-
authKey: 'illhavesomeauth',
|
|
351
|
-
privProtocol: snmp.PrivProtocols.aes,
|
|
352
|
-
privKey: 'andsomepriv'
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
const noAuthUser = {
|
|
356
|
-
name: 'fred',
|
|
357
|
-
level: snmp.SecurityLevel.noAuthNoPriv
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
let agent;
|
|
361
|
-
let sessions;
|
|
362
|
-
|
|
363
|
-
// The agent echoes the request's engineBoots and engineTime back in its
|
|
364
|
-
// response, so to model an authoritative engine with a clock of its own we
|
|
365
|
-
// overwrite those values on the way out. Clearing the cached buffer makes
|
|
366
|
-
// the message re-serialise and re-authenticate over the new values, so the
|
|
367
|
-
// session receives a properly authenticated message throughout.
|
|
368
|
-
let agentEngineBoots = null;
|
|
369
|
-
let agentEngineTime = null;
|
|
370
|
-
// Values used for the unauthenticated discovery Report only, so that tests
|
|
371
|
-
// can tell which message a session actually synchronised from.
|
|
372
|
-
let agentReportEngineBoots = null;
|
|
373
|
-
let agentReportEngineTime = null;
|
|
374
|
-
|
|
375
|
-
before(function () {
|
|
376
|
-
agent = snmp.createAgent({ port: agentPort, engineID: engineID, disableAuthorization: false },
|
|
377
|
-
function () {});
|
|
378
|
-
const authorizer = agent.getAuthorizer();
|
|
379
|
-
authorizer.addUser(authUser);
|
|
380
|
-
authorizer.addUser(authPrivUser);
|
|
381
|
-
authorizer.addUser(noAuthUser);
|
|
382
|
-
agent.registerProvider({
|
|
383
|
-
name: 'sysDescr',
|
|
384
|
-
type: snmp.MibProviderType.Scalar,
|
|
385
|
-
oid: '1.3.6.1.2.1.1.1',
|
|
386
|
-
scalarType: snmp.ObjectType.OctetString,
|
|
387
|
-
maxAccess: snmp.MaxAccess['read-only']
|
|
388
|
-
});
|
|
389
|
-
agent.getMib().setScalarValue('sysDescr', 'engine time test agent');
|
|
390
|
-
|
|
391
|
-
const listenerSend = agent.listener.send.bind(agent.listener);
|
|
392
|
-
agent.listener.send = function (message, rinfo, socket) {
|
|
393
|
-
const isReport = message.pdu && message.pdu.type === snmp.PduType.Report;
|
|
394
|
-
const boots = isReport && agentReportEngineBoots !== null
|
|
395
|
-
? agentReportEngineBoots
|
|
396
|
-
: agentEngineBoots;
|
|
397
|
-
const time = isReport && agentReportEngineTime !== null
|
|
398
|
-
? agentReportEngineTime
|
|
399
|
-
: agentEngineTime;
|
|
400
|
-
if (time !== null && message.msgSecurityParameters
|
|
401
|
-
&& message.msgSecurityParameters.msgAuthoritativeEngineBoots !== undefined) {
|
|
402
|
-
message.msgSecurityParameters.msgAuthoritativeEngineBoots = boots;
|
|
403
|
-
message.msgSecurityParameters.msgAuthoritativeEngineTime = time;
|
|
404
|
-
message.buffer = null;
|
|
405
|
-
}
|
|
406
|
-
return listenerSend(message, rinfo, socket);
|
|
407
|
-
};
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
after(function (done) {
|
|
411
|
-
agent.close(() => done());
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
beforeEach(function () {
|
|
415
|
-
sessions = [];
|
|
416
|
-
agentEngineBoots = null;
|
|
417
|
-
agentEngineTime = null;
|
|
418
|
-
agentReportEngineBoots = null;
|
|
419
|
-
agentReportEngineTime = null;
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
afterEach(function () {
|
|
423
|
-
sessions.forEach((s) => s.close());
|
|
424
|
-
sessions = [];
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
const createSession = (user) => {
|
|
428
|
-
const session = snmp.createV3Session('127.0.0.1', user,
|
|
429
|
-
{ port: agentPort, timeout: 2000, retries: 0 });
|
|
430
|
-
sessions.push(session);
|
|
431
|
-
return session;
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
const get = (session) => new Promise((resolve, reject) => {
|
|
435
|
-
session.get([sysDescrOid], (error, varbinds) => {
|
|
436
|
-
if (error) {
|
|
437
|
-
reject(error);
|
|
438
|
-
} else if (varbinds[0] instanceof Error) {
|
|
439
|
-
reject(varbinds[0]);
|
|
440
|
-
} else {
|
|
441
|
-
resolve(varbinds);
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
it('synchronises from an authenticated GetResponse, not the discovery Report',
|
|
447
|
-
async function () {
|
|
448
|
-
agentEngineBoots = 12;
|
|
449
|
-
agentEngineTime = 345678;
|
|
450
|
-
// The discovery Report is not authenticated, so these values must be
|
|
451
|
-
// ignored no matter how far ahead of the real engine time they are.
|
|
452
|
-
agentReportEngineBoots = 99;
|
|
453
|
-
agentReportEngineTime = MAX_SIGNED_INT32 - 1;
|
|
454
|
-
|
|
455
|
-
const session = createSession(authUser);
|
|
456
|
-
await get(session);
|
|
457
|
-
|
|
458
|
-
assert.strictEqual(session.engineTimeBoots, 12);
|
|
459
|
-
assert.strictEqual(session.engineTimeBase, 345678);
|
|
460
|
-
assert.strictEqual(session.latestReceivedEngineTime, 345678);
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
it('synchronises over authPriv as well as authNoPriv', async function () {
|
|
464
|
-
agentEngineBoots = 4;
|
|
465
|
-
agentEngineTime = 99000;
|
|
466
|
-
|
|
467
|
-
const session = createSession(authPrivUser);
|
|
468
|
-
await get(session);
|
|
469
|
-
|
|
470
|
-
assert.strictEqual(session.engineTimeBoots, 4);
|
|
471
|
-
assert.strictEqual(session.engineTimeBase, 99000);
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
it('never synchronises for a noAuthNoPriv user, since no message is authentic',
|
|
475
|
-
async function () {
|
|
476
|
-
agentEngineBoots = 4;
|
|
477
|
-
agentEngineTime = 99000;
|
|
478
|
-
|
|
479
|
-
const session = createSession(noAuthUser);
|
|
480
|
-
await get(session);
|
|
481
|
-
|
|
482
|
-
assert.strictEqual(session.engineTimeBoots, null);
|
|
483
|
-
assert.strictEqual(session.engineTimeReceivedAt, null);
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
it('sends an advanced engineTime on a later request', async function () {
|
|
487
|
-
agentEngineBoots = 2;
|
|
488
|
-
agentEngineTime = 1000;
|
|
489
|
-
|
|
490
|
-
const session = createSession(authUser);
|
|
491
|
-
await get(session);
|
|
492
|
-
|
|
493
|
-
// Pretend 500 seconds have passed since the response arrived, and let
|
|
494
|
-
// the agent's clock advance by the same amount.
|
|
495
|
-
session.engineTimeReceivedAt -= 500 * 1000;
|
|
496
|
-
agentEngineTime = 1500;
|
|
497
|
-
|
|
498
|
-
const sent = [];
|
|
499
|
-
const send = session.send.bind(session);
|
|
500
|
-
session.send = function (req, noWait) {
|
|
501
|
-
sent.push(req.message.msgSecurityParameters.msgAuthoritativeEngineTime);
|
|
502
|
-
return send(req, noWait);
|
|
503
|
-
};
|
|
504
|
-
|
|
505
|
-
await get(session);
|
|
506
|
-
|
|
507
|
-
assert.deepStrictEqual(sent, [1500]);
|
|
508
|
-
assert.strictEqual(session.engineTimeBase, 1500);
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
it('tracks the agent across a reboot that resets its engineTime', async function () {
|
|
512
|
-
agentEngineBoots = 2;
|
|
513
|
-
agentEngineTime = 900000;
|
|
514
|
-
|
|
515
|
-
const session = createSession(authUser);
|
|
516
|
-
await get(session);
|
|
517
|
-
assert.strictEqual(session.engineTimeBase, 900000);
|
|
518
|
-
|
|
519
|
-
agentEngineBoots = 3;
|
|
520
|
-
agentEngineTime = 12;
|
|
521
|
-
await get(session);
|
|
522
|
-
|
|
523
|
-
assert.strictEqual(session.engineTimeBoots, 3);
|
|
524
|
-
assert.strictEqual(session.engineTimeBase, 12);
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
it('reports a response that falls outside the time window', async function () {
|
|
528
|
-
agentEngineBoots = 2;
|
|
529
|
-
agentEngineTime = 1000;
|
|
530
|
-
|
|
531
|
-
const session = createSession(authUser);
|
|
532
|
-
await get(session);
|
|
533
|
-
|
|
534
|
-
// A response claiming an engineTime below the highest already received
|
|
535
|
-
// cannot advance our notion of time, and is far enough behind it to be
|
|
536
|
-
// outside the window - RFC 3414 section 3.2 step 7b(2) discards it.
|
|
537
|
-
agentEngineTime = 1000 - 151;
|
|
538
|
-
|
|
539
|
-
await assert.rejects(get(session), (error) => {
|
|
540
|
-
assert.ok(error instanceof snmp.ResponseInvalidError, 'expected ResponseInvalidError');
|
|
541
|
-
assert.strictEqual(error.code, snmp.ResponseInvalidCode.ENotInTimeWindow);
|
|
542
|
-
return true;
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
// Our notion of the agent's time is left untouched by the discard.
|
|
546
|
-
assert.strictEqual(session.engineTimeBoots, 2);
|
|
547
|
-
assert.strictEqual(session.latestReceivedEngineTime, 1000);
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
it('still accepts a response that is only slightly out of order', async function () {
|
|
551
|
-
agentEngineBoots = 2;
|
|
552
|
-
agentEngineTime = 1000;
|
|
553
|
-
|
|
554
|
-
const session = createSession(authUser);
|
|
555
|
-
await get(session);
|
|
556
|
-
|
|
557
|
-
agentEngineTime = 1000 - 20;
|
|
558
|
-
const varbinds = await get(session);
|
|
559
|
-
|
|
560
|
-
assert.strictEqual(varbinds[0].oid, sysDescrOid);
|
|
561
|
-
assert.strictEqual(session.latestReceivedEngineTime, 1000);
|
|
562
|
-
});
|
|
563
|
-
});
|