651 lines
22 KiB
TypeScript
651 lines
22 KiB
TypeScript
import * as crypto from 'crypto';
|
|
import type { SnmpConfig, SnmpV3SecurityParams } from './types.js';
|
|
import { SnmpEncoder } from './encoder.js';
|
|
|
|
/**
|
|
* SNMP packet creation utilities
|
|
* Creates SNMP request packets for different SNMP versions
|
|
*/
|
|
export class SnmpPacketCreator {
|
|
/**
|
|
* Create an SNMPv1 GET request
|
|
* @param oid OID to query
|
|
* @param community Community string
|
|
* @param debug Whether to enable debug output
|
|
* @returns Buffer containing the SNMP request
|
|
*/
|
|
public static createSnmpGetRequest(oid: string, community: string, debug: boolean = false): Buffer {
|
|
const oidArray = SnmpEncoder.oidToArray(oid);
|
|
const encodedOid = SnmpEncoder.encodeOID(oidArray);
|
|
|
|
if (debug) {
|
|
console.log('OID array length:', oidArray.length);
|
|
console.log('OID array:', oidArray);
|
|
}
|
|
|
|
// SNMP message structure
|
|
// Sequence
|
|
// Version (Integer)
|
|
// Community (String)
|
|
// PDU (GetRequest)
|
|
// Request ID (Integer)
|
|
// Error Status (Integer)
|
|
// Error Index (Integer)
|
|
// Variable Bindings (Sequence)
|
|
// Variable (Sequence)
|
|
// OID (ObjectIdentifier)
|
|
// Value (Null)
|
|
|
|
// Use the standard method from our test that is known to work
|
|
// Create a fixed request ID (0x00000001) to ensure deterministic behavior
|
|
const requestId = Buffer.from([0x00, 0x00, 0x00, 0x01]);
|
|
|
|
// Encode values
|
|
const versionBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x01]), // ASN.1 Integer, length 1
|
|
Buffer.from([0x00]) // SNMP version 1 (0)
|
|
]);
|
|
|
|
const communityBuf = Buffer.concat([
|
|
Buffer.from([0x04, community.length]), // ASN.1 Octet String, length
|
|
Buffer.from(community) // Community string
|
|
]);
|
|
|
|
const requestIdBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4
|
|
requestId // Fixed Request ID
|
|
]);
|
|
|
|
const errorStatusBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x01]), // ASN.1 Integer, length 1
|
|
Buffer.from([0x00]) // Error Status (0 = no error)
|
|
]);
|
|
|
|
const errorIndexBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x01]), // ASN.1 Integer, length 1
|
|
Buffer.from([0x00]) // Error Index (0)
|
|
]);
|
|
|
|
const oidValueBuf = Buffer.concat([
|
|
Buffer.from([0x30]), // ASN.1 Sequence
|
|
Buffer.from([encodedOid.length + 2]), // Length
|
|
Buffer.from([0x06]), // ASN.1 Object Identifier
|
|
Buffer.from([encodedOid.length]), // Length
|
|
encodedOid, // OID
|
|
Buffer.from([0x05, 0x00]) // Null value
|
|
]);
|
|
|
|
const varBindingsBuf = Buffer.concat([
|
|
Buffer.from([0x30]), // ASN.1 Sequence
|
|
Buffer.from([oidValueBuf.length]), // Length
|
|
oidValueBuf // Variable binding
|
|
]);
|
|
|
|
const pduBuf = Buffer.concat([
|
|
Buffer.from([0xa0]), // ASN.1 Context-specific Constructed 0 (GetRequest)
|
|
Buffer.from([requestIdBuf.length + errorStatusBuf.length + errorIndexBuf.length + varBindingsBuf.length]), // Length
|
|
requestIdBuf, // Request ID
|
|
errorStatusBuf, // Error Status
|
|
errorIndexBuf, // Error Index
|
|
varBindingsBuf // Variable Bindings
|
|
]);
|
|
|
|
const messageBuf = Buffer.concat([
|
|
Buffer.from([0x30]), // ASN.1 Sequence
|
|
Buffer.from([versionBuf.length + communityBuf.length + pduBuf.length]), // Length
|
|
versionBuf, // Version
|
|
communityBuf, // Community
|
|
pduBuf // PDU
|
|
]);
|
|
|
|
if (debug) {
|
|
console.log('SNMP Request buffer:', messageBuf.toString('hex'));
|
|
}
|
|
|
|
return messageBuf;
|
|
}
|
|
|
|
/**
|
|
* Create an SNMPv3 GET request
|
|
* @param oid OID to query
|
|
* @param config SNMP configuration
|
|
* @param engineID Engine ID
|
|
* @param engineBoots Engine boots counter
|
|
* @param engineTime Engine time counter
|
|
* @param requestID Request ID
|
|
* @param debug Whether to enable debug output
|
|
* @returns Buffer containing the SNMP request
|
|
*/
|
|
public static createSnmpV3GetRequest(
|
|
oid: string,
|
|
config: SnmpConfig,
|
|
engineID: Buffer,
|
|
engineBoots: number,
|
|
engineTime: number,
|
|
requestID: number,
|
|
debug: boolean = false
|
|
): Buffer {
|
|
if (debug) {
|
|
console.log('Creating SNMPv3 GET request for OID:', oid);
|
|
console.log('With config:', {
|
|
...config,
|
|
authKey: config.authKey ? '***' : undefined,
|
|
privKey: config.privKey ? '***' : undefined
|
|
});
|
|
}
|
|
|
|
const oidArray = SnmpEncoder.oidToArray(oid);
|
|
const encodedOid = SnmpEncoder.encodeOID(oidArray);
|
|
|
|
if (debug) {
|
|
console.log('Using engine ID:', engineID.toString('hex'));
|
|
console.log('Engine boots:', engineBoots);
|
|
console.log('Engine time:', engineTime);
|
|
console.log('Request ID:', requestID);
|
|
}
|
|
|
|
// Create security parameters
|
|
const securityParams: SnmpV3SecurityParams = {
|
|
msgAuthoritativeEngineID: engineID,
|
|
msgAuthoritativeEngineBoots: engineBoots,
|
|
msgAuthoritativeEngineTime: engineTime,
|
|
msgUserName: config.username || '',
|
|
msgAuthenticationParameters: Buffer.alloc(12, 0), // Will be filled in later for auth
|
|
msgPrivacyParameters: Buffer.alloc(8, 0), // For privacy
|
|
};
|
|
|
|
// Create the PDU (Protocol Data Unit)
|
|
// This is wrapped within the security parameters
|
|
const requestIdBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4
|
|
SnmpEncoder.encodeInteger(requestID) // Request ID
|
|
]);
|
|
|
|
const errorStatusBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x01]), // ASN.1 Integer, length 1
|
|
Buffer.from([0x00]) // Error Status (0 = no error)
|
|
]);
|
|
|
|
const errorIndexBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x01]), // ASN.1 Integer, length 1
|
|
Buffer.from([0x00]) // Error Index (0)
|
|
]);
|
|
|
|
const oidValueBuf = Buffer.concat([
|
|
Buffer.from([0x30]), // ASN.1 Sequence
|
|
Buffer.from([encodedOid.length + 2]), // Length
|
|
Buffer.from([0x06]), // ASN.1 Object Identifier
|
|
Buffer.from([encodedOid.length]), // Length
|
|
encodedOid, // OID
|
|
Buffer.from([0x05, 0x00]) // Null value
|
|
]);
|
|
|
|
const varBindingsBuf = Buffer.concat([
|
|
Buffer.from([0x30]), // ASN.1 Sequence
|
|
Buffer.from([oidValueBuf.length]), // Length
|
|
oidValueBuf // Variable binding
|
|
]);
|
|
|
|
const pduBuf = Buffer.concat([
|
|
Buffer.from([0xa0]), // ASN.1 Context-specific Constructed 0 (GetRequest)
|
|
Buffer.from([requestIdBuf.length + errorStatusBuf.length + errorIndexBuf.length + varBindingsBuf.length]), // Length
|
|
requestIdBuf, // Request ID
|
|
errorStatusBuf, // Error Status
|
|
errorIndexBuf, // Error Index
|
|
varBindingsBuf // Variable Bindings
|
|
]);
|
|
|
|
// Create the security parameters
|
|
const engineIdBuf = Buffer.concat([
|
|
Buffer.from([0x04, securityParams.msgAuthoritativeEngineID.length]), // ASN.1 Octet String
|
|
securityParams.msgAuthoritativeEngineID
|
|
]);
|
|
|
|
const engineBootsBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4
|
|
SnmpEncoder.encodeInteger(securityParams.msgAuthoritativeEngineBoots)
|
|
]);
|
|
|
|
const engineTimeBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4
|
|
SnmpEncoder.encodeInteger(securityParams.msgAuthoritativeEngineTime)
|
|
]);
|
|
|
|
const userNameBuf = Buffer.concat([
|
|
Buffer.from([0x04, securityParams.msgUserName.length]), // ASN.1 Octet String
|
|
Buffer.from(securityParams.msgUserName)
|
|
]);
|
|
|
|
const authParamsBuf = Buffer.concat([
|
|
Buffer.from([0x04, securityParams.msgAuthenticationParameters.length]), // ASN.1 Octet String
|
|
securityParams.msgAuthenticationParameters
|
|
]);
|
|
|
|
const privParamsBuf = Buffer.concat([
|
|
Buffer.from([0x04, securityParams.msgPrivacyParameters.length]), // ASN.1 Octet String
|
|
securityParams.msgPrivacyParameters
|
|
]);
|
|
|
|
// Security parameters sequence
|
|
const securityParamsBuf = Buffer.concat([
|
|
Buffer.from([0x30]), // ASN.1 Sequence
|
|
Buffer.from([engineIdBuf.length + engineBootsBuf.length + engineTimeBuf.length +
|
|
userNameBuf.length + authParamsBuf.length + privParamsBuf.length]), // Length
|
|
engineIdBuf,
|
|
engineBootsBuf,
|
|
engineTimeBuf,
|
|
userNameBuf,
|
|
authParamsBuf,
|
|
privParamsBuf
|
|
]);
|
|
|
|
// Determine security level flags
|
|
let securityFlags = 0;
|
|
if (config.securityLevel === 'authNoPriv' || config.securityLevel === 'authPriv') {
|
|
securityFlags |= 0x01; // Authentication flag
|
|
}
|
|
if (config.securityLevel === 'authPriv') {
|
|
securityFlags |= 0x02; // Privacy flag
|
|
}
|
|
|
|
// Set reportable flag - required for SNMPv3
|
|
securityFlags |= 0x04; // Reportable flag
|
|
|
|
// Create SNMPv3 header
|
|
const msgIdBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4
|
|
SnmpEncoder.encodeInteger(requestID) // Message ID (same as request ID for simplicity)
|
|
]);
|
|
|
|
const msgMaxSizeBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4
|
|
SnmpEncoder.encodeInteger(65507) // Max message size
|
|
]);
|
|
|
|
const msgFlagsBuf = Buffer.concat([
|
|
Buffer.from([0x04, 0x01]), // ASN.1 Octet String, length 1
|
|
Buffer.from([securityFlags])
|
|
]);
|
|
|
|
const msgSecModelBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x01]), // ASN.1 Integer, length 1
|
|
Buffer.from([0x03]) // Security model (3 = USM)
|
|
]);
|
|
|
|
// SNMPv3 header
|
|
const msgHeaderBuf = Buffer.concat([
|
|
Buffer.from([0x30]), // ASN.1 Sequence
|
|
Buffer.from([msgIdBuf.length + msgMaxSizeBuf.length + msgFlagsBuf.length + msgSecModelBuf.length]), // Length
|
|
msgIdBuf,
|
|
msgMaxSizeBuf,
|
|
msgFlagsBuf,
|
|
msgSecModelBuf
|
|
]);
|
|
|
|
// SNMPv3 security parameters
|
|
const msgSecurityBuf = Buffer.concat([
|
|
Buffer.from([0x04]), // ASN.1 Octet String
|
|
Buffer.from([securityParamsBuf.length]), // Length
|
|
securityParamsBuf
|
|
]);
|
|
|
|
// Create scopedPDU
|
|
// In SNMPv3, the PDU is wrapped in a "scoped PDU" structure
|
|
const contextEngineBuf = Buffer.concat([
|
|
Buffer.from([0x04, engineID.length]), // ASN.1 Octet String
|
|
engineID
|
|
]);
|
|
|
|
const contextNameBuf = Buffer.concat([
|
|
Buffer.from([0x04, 0x00]), // ASN.1 Octet String, length 0 (empty context name)
|
|
]);
|
|
|
|
const scopedPduBuf = Buffer.concat([
|
|
Buffer.from([0x30]), // ASN.1 Sequence
|
|
Buffer.from([contextEngineBuf.length + contextNameBuf.length + pduBuf.length]), // Length
|
|
contextEngineBuf,
|
|
contextNameBuf,
|
|
pduBuf
|
|
]);
|
|
|
|
// For authPriv, we need to encrypt the scopedPDU
|
|
let encryptedPdu = scopedPduBuf;
|
|
if (config.securityLevel === 'authPriv' && config.privKey) {
|
|
// In a real implementation, encryption would be applied here
|
|
// For this example, we'll just simulate it
|
|
encryptedPdu = this.simulateEncryption(scopedPduBuf, config);
|
|
}
|
|
|
|
// Final scopedPDU (encrypted or not)
|
|
const finalScopedPduBuf = Buffer.concat([
|
|
Buffer.from([0x04]), // ASN.1 Octet String
|
|
Buffer.from([encryptedPdu.length]), // Length
|
|
encryptedPdu
|
|
]);
|
|
|
|
// Combine everything for the final message
|
|
const versionBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x01]), // ASN.1 Integer, length 1
|
|
Buffer.from([0x03]) // SNMP version 3 (3)
|
|
]);
|
|
|
|
const messageBuf = Buffer.concat([
|
|
Buffer.from([0x30]), // ASN.1 Sequence
|
|
Buffer.from([versionBuf.length + msgHeaderBuf.length + msgSecurityBuf.length + finalScopedPduBuf.length]), // Length
|
|
versionBuf,
|
|
msgHeaderBuf,
|
|
msgSecurityBuf,
|
|
finalScopedPduBuf
|
|
]);
|
|
|
|
// If using authentication, calculate and insert the authentication parameters
|
|
if ((config.securityLevel === 'authNoPriv' || config.securityLevel === 'authPriv') &&
|
|
config.authKey && config.authProtocol) {
|
|
const authenticatedMsg = this.addAuthentication(messageBuf, config, authParamsBuf);
|
|
|
|
if (debug) {
|
|
console.log('Created authenticated SNMPv3 message');
|
|
console.log('Final message length:', authenticatedMsg.length);
|
|
}
|
|
|
|
return authenticatedMsg;
|
|
}
|
|
|
|
if (debug) {
|
|
console.log('Created SNMPv3 message without authentication');
|
|
console.log('Final message length:', messageBuf.length);
|
|
}
|
|
|
|
return messageBuf;
|
|
}
|
|
|
|
/**
|
|
* Simulate encryption for authPriv security level
|
|
* In a real implementation, this would use the specified privacy protocol (DES/AES)
|
|
* @param data Data to encrypt
|
|
* @param config SNMP configuration
|
|
* @returns Encrypted data
|
|
*/
|
|
private static simulateEncryption(data: Buffer, config: SnmpConfig): Buffer {
|
|
// This is a placeholder - in a real implementation, you would:
|
|
// 1. Generate an initialization vector (IV)
|
|
// 2. Use the privacy key derived from the privKey
|
|
// 3. Apply the appropriate encryption algorithm (DES/AES)
|
|
|
|
// For demonstration purposes only
|
|
if (config.privProtocol === 'AES' && config.privKey) {
|
|
try {
|
|
// Create a deterministic IV for demo purposes (not secure for production)
|
|
const iv = Buffer.alloc(16, 0);
|
|
const engineID = Buffer.from([0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
|
|
for (let i = 0; i < 8; i++) {
|
|
iv[i] = engineID[i % engineID.length];
|
|
}
|
|
|
|
// Create a key from the privKey (proper key localization should be used in production)
|
|
const key = crypto.createHash('md5').update(config.privKey).digest();
|
|
|
|
// Create cipher and encrypt
|
|
const cipher = crypto.createCipheriv('aes-128-cfb', key, iv);
|
|
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
|
|
return encrypted;
|
|
} catch (error) {
|
|
console.warn('AES encryption failed, falling back to plaintext:', error);
|
|
return data;
|
|
}
|
|
} else if (config.privProtocol === 'DES' && config.privKey) {
|
|
try {
|
|
// Create a deterministic IV for demo purposes (not secure for production)
|
|
const iv = Buffer.alloc(8, 0);
|
|
const engineID = Buffer.from([0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
|
|
for (let i = 0; i < 8; i++) {
|
|
iv[i] = engineID[i % engineID.length];
|
|
}
|
|
|
|
// Create a key from the privKey (proper key localization should be used in production)
|
|
const key = crypto.createHash('md5').update(config.privKey).digest().slice(0, 8);
|
|
|
|
// Create cipher and encrypt
|
|
const cipher = crypto.createCipheriv('des-cbc', key, iv);
|
|
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
|
|
return encrypted;
|
|
} catch (error) {
|
|
console.warn('DES encryption failed, falling back to plaintext:', error);
|
|
return data;
|
|
}
|
|
}
|
|
|
|
return data; // Return unencrypted data as fallback
|
|
}
|
|
|
|
/**
|
|
* Add authentication to SNMPv3 message
|
|
* @param message Message to authenticate
|
|
* @param config SNMP configuration
|
|
* @param authParamsBuf Authentication parameters buffer
|
|
* @returns Authenticated message
|
|
*/
|
|
private static addAuthentication(message: Buffer, config: SnmpConfig, authParamsBuf: Buffer): Buffer {
|
|
// In a real implementation, this would:
|
|
// 1. Zero out the authentication parameters field
|
|
// 2. Calculate HMAC-MD5 or HMAC-SHA1 over the entire message
|
|
// 3. Insert the HMAC into the authentication parameters field
|
|
|
|
if (!config.authKey) {
|
|
return message;
|
|
}
|
|
|
|
try {
|
|
// Find position of auth parameters in the message
|
|
// This is a more reliable way to find the exact position
|
|
let authParamsPos = -1;
|
|
for (let i = 0; i < message.length - 16; i++) {
|
|
// Look for the auth params pattern: 0x04 0x0C 0x00 0x00...
|
|
if (message[i] === 0x04 && message[i + 1] === 0x0C) {
|
|
// Check if next 12 bytes are all zeros
|
|
let allZeros = true;
|
|
for (let j = 0; j < 12; j++) {
|
|
if (message[i + 2 + j] !== 0) {
|
|
allZeros = false;
|
|
break;
|
|
}
|
|
}
|
|
if (allZeros) {
|
|
authParamsPos = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (authParamsPos === -1) {
|
|
return message;
|
|
}
|
|
|
|
// Create a copy of the message with zeroed auth parameters
|
|
const msgCopy = Buffer.from(message);
|
|
|
|
// Prepare the authentication key according to RFC3414
|
|
// We should use the standard key localization process
|
|
const localizedKey = this.localizeAuthKey(config.authKey,
|
|
Buffer.from([0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06]),
|
|
config.authProtocol);
|
|
|
|
// Calculate HMAC
|
|
let hmac;
|
|
if (config.authProtocol === 'SHA') {
|
|
hmac = crypto.createHmac('sha1', localizedKey).update(msgCopy).digest().slice(0, 12);
|
|
} else {
|
|
// Default to MD5
|
|
hmac = crypto.createHmac('md5', localizedKey).update(msgCopy).digest().slice(0, 12);
|
|
}
|
|
|
|
// Copy HMAC into original message
|
|
hmac.copy(message, authParamsPos + 2);
|
|
|
|
return message;
|
|
} catch (error) {
|
|
console.warn('Authentication failed:', error);
|
|
return message;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Localize authentication key according to RFC3414
|
|
* @param key Authentication key
|
|
* @param engineId Engine ID
|
|
* @param authProtocol Authentication protocol
|
|
* @returns Localized key
|
|
*/
|
|
private static localizeAuthKey(key: string, engineId: Buffer, authProtocol: string = 'MD5'): Buffer {
|
|
try {
|
|
// Convert password to key using hash
|
|
let initialHash;
|
|
if (authProtocol === 'SHA') {
|
|
initialHash = crypto.createHash('sha1');
|
|
} else {
|
|
initialHash = crypto.createHash('md5');
|
|
}
|
|
|
|
// Generate the initial key - repeated hashing of password + padding
|
|
const password = Buffer.from(key);
|
|
let passwordIndex = 0;
|
|
|
|
// Create a buffer of 1MB (1048576 bytes) filled with the password
|
|
const buffer = Buffer.alloc(1048576);
|
|
for (let i = 0; i < 1048576; i++) {
|
|
buffer[i] = password[passwordIndex];
|
|
passwordIndex = (passwordIndex + 1) % password.length;
|
|
}
|
|
|
|
initialHash.update(buffer);
|
|
let initialKey = initialHash.digest();
|
|
|
|
// Localize the key with engine ID
|
|
let localHash;
|
|
if (authProtocol === 'SHA') {
|
|
localHash = crypto.createHash('sha1');
|
|
} else {
|
|
localHash = crypto.createHash('md5');
|
|
}
|
|
|
|
localHash.update(initialKey);
|
|
localHash.update(engineId);
|
|
localHash.update(initialKey);
|
|
|
|
return localHash.digest();
|
|
} catch (error) {
|
|
console.error('Error localizing auth key:', error);
|
|
// Return a fallback key
|
|
return Buffer.from(key);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a discovery message for SNMPv3 engine ID discovery
|
|
* @param config SNMP configuration
|
|
* @param requestID Request ID
|
|
* @returns Discovery message
|
|
*/
|
|
public static createDiscoveryMessage(config: SnmpConfig, requestID: number): Buffer {
|
|
// Basic SNMPv3 header for discovery
|
|
const msgIdBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4
|
|
SnmpEncoder.encodeInteger(requestID)
|
|
]);
|
|
|
|
const msgMaxSizeBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4
|
|
SnmpEncoder.encodeInteger(65507) // Max message size
|
|
]);
|
|
|
|
const msgFlagsBuf = Buffer.concat([
|
|
Buffer.from([0x04, 0x01]), // ASN.1 Octet String, length 1
|
|
Buffer.from([0x00]) // No authentication or privacy
|
|
]);
|
|
|
|
const msgSecModelBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x01]), // ASN.1 Integer, length 1
|
|
Buffer.from([0x03]) // Security model (3 = USM)
|
|
]);
|
|
|
|
// SNMPv3 header
|
|
const msgHeaderBuf = Buffer.concat([
|
|
Buffer.from([0x30]), // ASN.1 Sequence
|
|
Buffer.from([msgIdBuf.length + msgMaxSizeBuf.length + msgFlagsBuf.length + msgSecModelBuf.length]), // Length
|
|
msgIdBuf,
|
|
msgMaxSizeBuf,
|
|
msgFlagsBuf,
|
|
msgSecModelBuf
|
|
]);
|
|
|
|
// Simple security parameters for discovery
|
|
const securityBuf = Buffer.concat([
|
|
Buffer.from([0x04, 0x00]), // Empty octet string
|
|
]);
|
|
|
|
// Simple Get request for discovery
|
|
const requestIdBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x04]), // ASN.1 Integer, length 4
|
|
SnmpEncoder.encodeInteger(requestID + 1)
|
|
]);
|
|
|
|
const errorStatusBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x01]), // ASN.1 Integer, length 1
|
|
Buffer.from([0x00]) // Error Status (0 = no error)
|
|
]);
|
|
|
|
const errorIndexBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x01]), // ASN.1 Integer, length 1
|
|
Buffer.from([0x00]) // Error Index (0)
|
|
]);
|
|
|
|
// Empty varbinds for discovery
|
|
const varBindingsBuf = Buffer.concat([
|
|
Buffer.from([0x30, 0x00]), // Empty sequence
|
|
]);
|
|
|
|
const pduBuf = Buffer.concat([
|
|
Buffer.from([0xa0]), // GetRequest
|
|
Buffer.from([requestIdBuf.length + errorStatusBuf.length + errorIndexBuf.length + varBindingsBuf.length]),
|
|
requestIdBuf,
|
|
errorStatusBuf,
|
|
errorIndexBuf,
|
|
varBindingsBuf
|
|
]);
|
|
|
|
// Context data
|
|
const contextEngineBuf = Buffer.concat([
|
|
Buffer.from([0x04, 0x00]), // Empty octet string
|
|
]);
|
|
|
|
const contextNameBuf = Buffer.concat([
|
|
Buffer.from([0x04, 0x00]), // Empty octet string
|
|
]);
|
|
|
|
const scopedPduBuf = Buffer.concat([
|
|
Buffer.from([0x30]),
|
|
Buffer.from([contextEngineBuf.length + contextNameBuf.length + pduBuf.length]),
|
|
contextEngineBuf,
|
|
contextNameBuf,
|
|
pduBuf
|
|
]);
|
|
|
|
// Version
|
|
const versionBuf = Buffer.concat([
|
|
Buffer.from([0x02, 0x01]), // ASN.1 Integer, length 1
|
|
Buffer.from([0x03]) // SNMP version 3 (3)
|
|
]);
|
|
|
|
// Complete message
|
|
return Buffer.concat([
|
|
Buffer.from([0x30]),
|
|
Buffer.from([versionBuf.length + msgHeaderBuf.length + securityBuf.length + scopedPduBuf.length]),
|
|
versionBuf,
|
|
msgHeaderBuf,
|
|
securityBuf,
|
|
scopedPduBuf
|
|
]);
|
|
}
|
|
} |