UNPKG

@vostro/net-snmp

Version:

JavaScript implementation of the Simple Network Management Protocol (SNMP)

1,692 lines (1,414 loc) 148 kB
// Copyright 2013 Stephen Vickers <stephen.vickers.sv@gmail.com> var ber = require ("asn1-ber").Ber; var smartbuffer = require ("smart-buffer"); var dgram = require ("dgram"); var net = require ("net"); var events = require ("events"); var util = require ("util"); var crypto = require ("crypto"); var mibparser = require ("./lib/mib"); var DEBUG = false; var MAX_INT32 = 2147483647; function debug (line) { if ( DEBUG ) { console.debug (line); } } /***************************************************************************** ** Constants **/ function _expandConstantObject (object) { var keys = []; for (var key in object) keys.push (key); for (var i = 0; i < keys.length; i++) object[object[keys[i]]] = parseInt (keys[i]); } var ErrorStatus = { 0: "NoError", 1: "TooBig", 2: "NoSuchName", 3: "BadValue", 4: "ReadOnly", 5: "GeneralError", 6: "NoAccess", 7: "WrongType", 8: "WrongLength", 9: "WrongEncoding", 10: "WrongValue", 11: "NoCreation", 12: "InconsistentValue", 13: "ResourceUnavailable", 14: "CommitFailed", 15: "UndoFailed", 16: "AuthorizationError", 17: "NotWritable", 18: "InconsistentName" }; _expandConstantObject (ErrorStatus); var ObjectType = { 1: "Boolean", 2: "Integer", 4: "OctetString", 5: "Null", 6: "OID", 64: "IpAddress", 65: "Counter", 66: "Gauge", 67: "TimeTicks", 68: "Opaque", 70: "Counter64", 128: "NoSuchObject", 129: "NoSuchInstance", 130: "EndOfMibView" }; _expandConstantObject (ObjectType); // ASN.1 ObjectType.INTEGER = ObjectType.Integer; ObjectType["OCTET STRING"] = ObjectType.OctetString; ObjectType["OBJECT IDENTIFIER"] = ObjectType.OID; // SNMPv2-SMI ObjectType.Integer32 = ObjectType.Integer; ObjectType.Counter32 = ObjectType.Counter; ObjectType.Gauge32 = ObjectType.Gauge; ObjectType.Unsigned32 = ObjectType.Gauge32; // SNMPv2-TC ObjectType.AutonomousType = ObjectType["OBJECT IDENTIFIER"]; ObjectType.DateAndTime = ObjectType["OCTET STRING"]; ObjectType.DisplayString = ObjectType["OCTET STRING"]; ObjectType.InstancePointer = ObjectType["OBJECT IDENTIFIER"]; ObjectType.MacAddress = ObjectType["OCTET STRING"]; ObjectType.PhysAddress = ObjectType["OCTET STRING"]; ObjectType.RowPointer = ObjectType["OBJECT IDENTIFIER"]; ObjectType.RowStatus = ObjectType.INTEGER; ObjectType.StorageType = ObjectType.INTEGER; ObjectType.TestAndIncr = ObjectType.INTEGER; ObjectType.TimeStamp = ObjectType.TimeTicks; ObjectType.TruthValue = ObjectType.INTEGER; ObjectType.TAddress = ObjectType["OCTET STRING"]; ObjectType.TDomain = ObjectType["OBJECT IDENTIFIER"]; ObjectType.VariablePointer = ObjectType["OBJECT IDENTIFIER"]; var PduType = { 160: "GetRequest", 161: "GetNextRequest", 162: "GetResponse", 163: "SetRequest", 164: "Trap", 165: "GetBulkRequest", 166: "InformRequest", 167: "TrapV2", 168: "Report" }; _expandConstantObject (PduType); var TrapType = { 0: "ColdStart", 1: "WarmStart", 2: "LinkDown", 3: "LinkUp", 4: "AuthenticationFailure", 5: "EgpNeighborLoss", 6: "EnterpriseSpecific" }; _expandConstantObject (TrapType); var SecurityLevel = { 1: "noAuthNoPriv", 2: "authNoPriv", 3: "authPriv" }; _expandConstantObject (SecurityLevel); var AuthProtocols = { "1": "none", "2": "md5", "3": "sha" }; _expandConstantObject (AuthProtocols); var PrivProtocols = { "1": "none", "2": "des", "4": "aes" }; _expandConstantObject (PrivProtocols); var UsmStatsBase = "1.3.6.1.6.3.15.1.1"; var UsmStats = { "1": "Unsupported Security Level", "2": "Not In Time Window", "3": "Unknown User Name", "4": "Unknown Engine ID", "5": "Wrong Digest (incorrect password, community or key)", "6": "Decryption Error" }; _expandConstantObject (UsmStats); var MibProviderType = { "1": "Scalar", "2": "Table" }; _expandConstantObject (MibProviderType); var Version1 = 0; var Version2c = 1; var Version3 = 3; var Version = { "1": Version1, "2c": Version2c, "3": Version3 }; var AgentXPduType = { 1: "Open", 2: "Close", 3: "Register", 4: "Unregister", 5: "Get", 6: "GetNext", 7: "GetBulk", 8: "TestSet", 9: "CommitSet", 10: "UndoSet", 11: "CleanupSet", 12: "Notify", 13: "Ping", 14: "IndexAllocate", 15: "IndexDeallocate", 16: "AddAgentCaps", 17: "RemoveAgentCaps", 18: "Response" }; _expandConstantObject (AgentXPduType); /***************************************************************************** ** Exception class definitions **/ function ResponseInvalidError (message) { this.name = "ResponseInvalidError"; this.message = message; Error.captureStackTrace(this, ResponseInvalidError); } util.inherits (ResponseInvalidError, Error); function RequestInvalidError (message) { this.name = "RequestInvalidError"; this.message = message; Error.captureStackTrace(this, RequestInvalidError); } util.inherits (RequestInvalidError, Error); function RequestFailedError (message, status) { this.name = "RequestFailedError"; this.message = message; this.status = status; Error.captureStackTrace(this, RequestFailedError); } util.inherits (RequestFailedError, Error); function RequestTimedOutError (message) { this.name = "RequestTimedOutError"; this.message = message; Error.captureStackTrace(this, RequestTimedOutError); } util.inherits (RequestTimedOutError, Error); /***************************************************************************** ** OID and varbind helper functions **/ function isVarbindError (varbind) { return !!(varbind.type == ObjectType.NoSuchObject || varbind.type == ObjectType.NoSuchInstance || varbind.type == ObjectType.EndOfMibView); } function varbindError (varbind) { return (ObjectType[varbind.type] || "NotAnError") + ": " + varbind.oid; } function oidFollowsOid (oidString, nextString) { var oid = {str: oidString, len: oidString.length, idx: 0}; var next = {str: nextString, len: nextString.length, idx: 0}; var dotCharCode = ".".charCodeAt (0); function getNumber (item) { var n = 0; if (item.idx >= item.len) return null; while (item.idx < item.len) { var charCode = item.str.charCodeAt (item.idx++); if (charCode == dotCharCode) return n; n = (n ? (n * 10) : n) + (charCode - 48); } return n; } while (1) { var oidNumber = getNumber (oid); var nextNumber = getNumber (next); if (oidNumber !== null) { if (nextNumber !== null) { if (nextNumber > oidNumber) { return true; } else if (nextNumber < oidNumber) { return false; } } else { return true; } } else { return true; } } } function oidInSubtree (oidString, nextString) { var oid = oidString.split ("."); var next = nextString.split ("."); if (oid.length > next.length) return false; for (var i = 0; i < oid.length; i++) { if (next[i] != oid[i]) return false; } return true; } /** ** Some SNMP agents produce integers on the wire such as 00 ff ff ff ff. ** The ASN.1 BER parser we use throws an error when parsing this, which we ** believe is correct. So, we decided not to bother the "asn1" developer(s) ** with this, instead opting to work around it here. ** ** If an integer is 5 bytes in length we check if the first byte is 0, and if so ** simply drop it and parse it like it was a 4 byte integer, otherwise throw ** an error since the integer is too large. **/ function readInt (buffer) { return readUint (buffer, true); } function readIpAddress (buffer) { var bytes = buffer.readString (ObjectType.IpAddress, true); if (bytes.length != 4) throw new ResponseInvalidError ("Length '" + bytes.length + "' of IP address '" + bytes.toString ("hex") + "' is not 4"); var value = bytes[0] + "." + bytes[1] + "." + bytes[2] + "." + bytes[3]; return value; } function readUint (buffer, isSigned) { buffer.readByte (); var length = buffer.readByte (); var value = 0; var signedBitSet = false; if (length > 5) { throw new RangeError ("Integer too long '" + length + "'"); } else if (length == 5) { if (buffer.readByte () !== 0) throw new RangeError ("Integer too long '" + length + "'"); length = 4; } for (var i = 0; i < length; i++) { value *= 256; value += buffer.readByte (); if (isSigned && i <= 0) { if ((value & 0x80) == 0x80) signedBitSet = true; } } if (signedBitSet) value -= (1 << (i * 8)); return value; } function readUint64 (buffer) { var value = buffer.readString (ObjectType.Counter64, true); return value; } function readVarbindValue (buffer, type) { var value; if (type == ObjectType.Boolean) { value = buffer.readBoolean (); } else if (type == ObjectType.Integer) { value = readInt (buffer); } else if (type == ObjectType.OctetString) { value = buffer.readString (null, true); } else if (type == ObjectType.Null) { buffer.readByte (); buffer.readByte (); value = null; } else if (type == ObjectType.OID) { value = buffer.readOID (); } else if (type == ObjectType.IpAddress) { value = readIpAddress (buffer); } else if (type == ObjectType.Counter) { value = readUint (buffer); } else if (type == ObjectType.Gauge) { value = readUint (buffer); } else if (type == ObjectType.TimeTicks) { value = readUint (buffer); } else if (type == ObjectType.Opaque) { value = buffer.readString (ObjectType.Opaque, true); } else if (type == ObjectType.Counter64) { value = readUint64 (buffer); } else if (type == ObjectType.NoSuchObject) { buffer.readByte (); buffer.readByte (); value = null; } else if (type == ObjectType.NoSuchInstance) { buffer.readByte (); buffer.readByte (); value = null; } else if (type == ObjectType.EndOfMibView) { buffer.readByte (); buffer.readByte (); value = null; } else { throw new ResponseInvalidError ("Unknown type '" + type + "' in response"); } return value; } function readVarbinds (buffer, varbinds) { buffer.readSequence (); while (1) { buffer.readSequence (); if ( buffer.peek () != ObjectType.OID ) break; var oid = buffer.readOID (); var type = buffer.peek (); if (type == null) break; var value = readVarbindValue (buffer, type); varbinds.push ({ oid: oid, type: type, value: value }); } } function writeUint (buffer, type, value) { var b = Buffer.alloc (4); b.writeUInt32BE (value, 0); buffer.writeBuffer (b, type); } function writeUint64 (buffer, value) { buffer.writeBuffer (value, ObjectType.Counter64); } function writeVarbinds (buffer, varbinds) { buffer.startSequence (); for (var i = 0; i < varbinds.length; i++) { buffer.startSequence (); buffer.writeOID (varbinds[i].oid); if (varbinds[i].type && varbinds[i].hasOwnProperty("value")) { var type = varbinds[i].type; var value = varbinds[i].value; switch ( type ) { case ObjectType.Boolean: buffer.writeBoolean (value ? true : false); break; case ObjectType.Integer: // also Integer32 buffer.writeInt (value); break; case ObjectType.OctetString: if (typeof value == "string") buffer.writeString (value); else buffer.writeBuffer (value, ObjectType.OctetString); break; case ObjectType.Null: buffer.writeNull (); break; case ObjectType.OID: buffer.writeOID (value); break; case ObjectType.IpAddress: var bytes = value.split ("."); if (bytes.length != 4) throw new RequestInvalidError ("Invalid IP address '" + value + "'"); buffer.writeBuffer (Buffer.from (bytes), 64); break; case ObjectType.Counter: // also Counter32 writeUint (buffer, ObjectType.Counter, value); break; case ObjectType.Gauge: // also Gauge32 & Unsigned32 writeUint (buffer, ObjectType.Gauge, value); break; case ObjectType.TimeTicks: writeUint (buffer, ObjectType.TimeTicks, value); break; case ObjectType.Opaque: buffer.writeBuffer (value, ObjectType.Opaque); break; case ObjectType.Counter64: writeUint64 (buffer, value); break; case ObjectType.NoSuchObject: case ObjectType.NoSuchInstance: case ObjectType.EndOfMibView: buffer.writeByte (type); buffer.writeByte (0); break; default: throw new RequestInvalidError ("Unknown type '" + type + "' in request"); } } else { buffer.writeNull (); } buffer.endSequence (); } buffer.endSequence (); } /***************************************************************************** ** PDU class definitions **/ var SimplePdu = function () { }; SimplePdu.prototype.toBuffer = function (buffer) { buffer.startSequence (this.type); buffer.writeInt (this.id); buffer.writeInt ((this.type == PduType.GetBulkRequest) ? (this.options.nonRepeaters || 0) : 0); buffer.writeInt ((this.type == PduType.GetBulkRequest) ? (this.options.maxRepetitions || 0) : 0); writeVarbinds (buffer, this.varbinds); buffer.endSequence (); }; SimplePdu.prototype.initializeFromVariables = function (id, varbinds, options) { this.id = id; this.varbinds = varbinds; this.options = options || {}; this.contextName = (options && options.context) ? options.context : ""; } SimplePdu.prototype.initializeFromBuffer = function (reader) { this.type = reader.peek (); reader.readSequence (); this.id = reader.readInt (); this.nonRepeaters = reader.readInt (); this.maxRepetitions = reader.readInt (); this.varbinds = []; readVarbinds (reader, this.varbinds); }; SimplePdu.prototype.getResponsePduForRequest = function () { var responsePdu = GetResponsePdu.createFromVariables(this.id, [], {}); if ( this.contextEngineID ) { responsePdu.contextEngineID = this.contextEngineID; responsePdu.contextName = this.contextName; } return responsePdu; }; SimplePdu.createFromVariables = function (pduClass, id, varbinds, options) { var pdu = new pduClass (id, varbinds, options); pdu.id = id; pdu.varbinds = varbinds; pdu.options = options || {}; pdu.contextName = (options && options.context) ? options.context : ""; return pdu; }; var GetBulkRequestPdu = function () { this.type = PduType.GetBulkRequest; GetBulkRequestPdu.super_.apply (this, arguments); }; util.inherits (GetBulkRequestPdu, SimplePdu); GetBulkRequestPdu.createFromBuffer = function (reader) { var pdu = new GetBulkRequestPdu (); pdu.initializeFromBuffer (reader); return pdu; }; var GetNextRequestPdu = function () { this.type = PduType.GetNextRequest; GetNextRequestPdu.super_.apply (this, arguments); }; util.inherits (GetNextRequestPdu, SimplePdu); GetNextRequestPdu.createFromBuffer = function (reader) { var pdu = new GetNextRequestPdu (); pdu.initializeFromBuffer (reader); return pdu; }; var GetRequestPdu = function () { this.type = PduType.GetRequest; GetRequestPdu.super_.apply (this, arguments); }; util.inherits (GetRequestPdu, SimplePdu); GetRequestPdu.createFromBuffer = function (reader) { var pdu = new GetRequestPdu(); pdu.initializeFromBuffer (reader); return pdu; }; GetRequestPdu.createFromVariables = function (id, varbinds, options) { var pdu = new GetRequestPdu(); pdu.initializeFromVariables (id, varbinds, options); return pdu; }; var InformRequestPdu = function () { this.type = PduType.InformRequest; InformRequestPdu.super_.apply (this, arguments); }; util.inherits (InformRequestPdu, SimplePdu); InformRequestPdu.createFromBuffer = function (reader) { var pdu = new InformRequestPdu(); pdu.initializeFromBuffer (reader); return pdu; }; var SetRequestPdu = function () { this.type = PduType.SetRequest; SetRequestPdu.super_.apply (this, arguments); }; util.inherits (SetRequestPdu, SimplePdu); SetRequestPdu.createFromBuffer = function (reader) { var pdu = new SetRequestPdu (); pdu.initializeFromBuffer (reader); return pdu; }; var TrapPdu = function () { this.type = PduType.Trap; }; TrapPdu.prototype.toBuffer = function (buffer) { buffer.startSequence (this.type); buffer.writeOID (this.enterprise); buffer.writeBuffer (Buffer.from (this.agentAddr.split (".")), ObjectType.IpAddress); buffer.writeInt (this.generic); buffer.writeInt (this.specific); writeUint (buffer, ObjectType.TimeTicks, this.upTime || Math.floor (process.uptime () * 100)); writeVarbinds (buffer, this.varbinds); buffer.endSequence (); }; TrapPdu.createFromBuffer = function (reader) { var pdu = new TrapPdu(); reader.readSequence (); pdu.enterprise = reader.readOID (); pdu.agentAddr = readIpAddress (reader); pdu.generic = reader.readInt (); pdu.specific = reader.readInt (); pdu.upTime = readUint (reader) pdu.varbinds = []; readVarbinds (reader, pdu.varbinds); return pdu; }; TrapPdu.createFromVariables = function (typeOrOid, varbinds, options) { var pdu = new TrapPdu (); pdu.agentAddr = options.agentAddr || "127.0.0.1"; pdu.upTime = options.upTime; if (typeof typeOrOid == "string") { pdu.generic = TrapType.EnterpriseSpecific; pdu.specific = parseInt (typeOrOid.match (/\.(\d+)$/)[1]); pdu.enterprise = typeOrOid.replace (/\.(\d+)$/, ""); } else { pdu.generic = typeOrOid; pdu.specific = 0; pdu.enterprise = "1.3.6.1.4.1"; } pdu.varbinds = varbinds; return pdu; }; var TrapV2Pdu = function () { this.type = PduType.TrapV2; TrapV2Pdu.super_.apply (this, arguments); }; util.inherits (TrapV2Pdu, SimplePdu); TrapV2Pdu.createFromBuffer = function (reader) { var pdu = new TrapV2Pdu(); pdu.initializeFromBuffer (reader); return pdu; }; TrapV2Pdu.createFromVariables = function (id, varbinds, options) { var pdu = new TrapV2Pdu(); pdu.initializeFromVariables (id, varbinds, options); return pdu; }; var SimpleResponsePdu = function() { }; SimpleResponsePdu.prototype.toBuffer = function (writer) { writer.startSequence (this.type); writer.writeInt (this.id); writer.writeInt (this.errorStatus || 0); writer.writeInt (this.errorIndex || 0); writeVarbinds (writer, this.varbinds); writer.endSequence (); }; SimpleResponsePdu.prototype.initializeFromBuffer = function (reader) { reader.readSequence (this.type); this.id = reader.readInt (); this.errorStatus = reader.readInt (); this.errorIndex = reader.readInt (); this.varbinds = []; readVarbinds (reader, this.varbinds); }; SimpleResponsePdu.prototype.initializeFromVariables = function (id, varbinds, options) { this.id = id; this.varbinds = varbinds; this.options = options || {}; }; var GetResponsePdu = function () { this.type = PduType.GetResponse; GetResponsePdu.super_.apply (this, arguments); }; util.inherits (GetResponsePdu, SimpleResponsePdu); GetResponsePdu.createFromBuffer = function (reader) { var pdu = new GetResponsePdu (); pdu.initializeFromBuffer (reader); return pdu; }; GetResponsePdu.createFromVariables = function (id, varbinds, options) { var pdu = new GetResponsePdu(); pdu.initializeFromVariables (id, varbinds, options); return pdu; }; var ReportPdu = function () { this.type = PduType.Report; ReportPdu.super_.apply (this, arguments); }; util.inherits (ReportPdu, SimpleResponsePdu); ReportPdu.createFromBuffer = function (reader) { var pdu = new ReportPdu (); pdu.initializeFromBuffer (reader); return pdu; }; ReportPdu.createFromVariables = function (id, varbinds, options) { var pdu = new ReportPdu(); pdu.initializeFromVariables (id, varbinds, options); return pdu; }; var readPdu = function (reader, scoped) { var pdu; var contextEngineID; var contextName; if ( scoped ) { reader.readSequence (); contextEngineID = reader.readString (ber.OctetString, true); contextName = reader.readString (); } var type = reader.peek (); if (type == PduType.GetResponse) { pdu = GetResponsePdu.createFromBuffer (reader); } else if (type == PduType.Report ) { pdu = ReportPdu.createFromBuffer (reader); } else if (type == PduType.Trap ) { pdu = TrapPdu.createFromBuffer (reader); } else if (type == PduType.TrapV2 ) { pdu = TrapV2Pdu.createFromBuffer (reader); } else if (type == PduType.InformRequest ) { pdu = InformRequestPdu.createFromBuffer (reader); } else if (type == PduType.GetRequest ) { pdu = GetRequestPdu.createFromBuffer (reader); } else if (type == PduType.SetRequest ) { pdu = SetRequestPdu.createFromBuffer (reader); } else if (type == PduType.GetNextRequest ) { pdu = GetNextRequestPdu.createFromBuffer (reader); } else if (type == PduType.GetBulkRequest ) { pdu = GetBulkRequestPdu.createFromBuffer (reader); } else { throw new ResponseInvalidError ("Unknown PDU type '" + type + "' in response"); } if ( scoped ) { pdu.contextEngineID = contextEngineID; pdu.contextName = contextName; } pdu.scoped = scoped; return pdu; }; var createDiscoveryPdu = function (context) { return GetRequestPdu.createFromVariables(_generateId(), [], {context: context}); }; var Authentication = {}; Authentication.HMAC_BUFFER_SIZE = 1024*1024; Authentication.HMAC_BLOCK_SIZE = 64; Authentication.AUTHENTICATION_CODE_LENGTH = 12; Authentication.AUTH_PARAMETERS_PLACEHOLDER = Buffer.from('8182838485868788898a8b8c', 'hex'); Authentication.algorithms = {}; Authentication.algorithms[AuthProtocols.md5] = { // KEY_LENGTH: 16, CRYPTO_ALGORITHM: 'md5' }; Authentication.algorithms[AuthProtocols.sha] = { // KEY_LENGTH: 20, CRYPTO_ALGORITHM: 'sha1' }; // Adapted from RFC3414 Appendix A.2.1. Password to Key Sample Code for MD5 Authentication.passwordToKey = function (authProtocol, authPasswordString, engineID) { var hashAlgorithm; var firstDigest; var finalDigest; var buf = Buffer.alloc (Authentication.HMAC_BUFFER_SIZE); var bufOffset = 0; var passwordIndex = 0; var count = 0; var password = Buffer.from (authPasswordString); var cryptoAlgorithm = Authentication.algorithms[authProtocol].CRYPTO_ALGORITHM; while (count < Authentication.HMAC_BUFFER_SIZE) { for (var i = 0; i < Authentication.HMAC_BLOCK_SIZE; i++) { buf.writeUInt8(password[passwordIndex++ % password.length], bufOffset++); } count += Authentication.HMAC_BLOCK_SIZE; } hashAlgorithm = crypto.createHash(cryptoAlgorithm); hashAlgorithm.update(buf); firstDigest = hashAlgorithm.digest(); // debug ("First digest: " + firstDigest.toString('hex')); hashAlgorithm = crypto.createHash(cryptoAlgorithm); hashAlgorithm.update(firstDigest); hashAlgorithm.update(engineID); hashAlgorithm.update(firstDigest); finalDigest = hashAlgorithm.digest(); debug ("Localized key: " + finalDigest.toString('hex')); return finalDigest; }; Authentication.addParametersToMessageBuffer = function (messageBuffer, authProtocol, authPassword, engineID) { var authenticationParametersOffset; var digestToAdd; // clear the authenticationParameters field in message authenticationParametersOffset = messageBuffer.indexOf (Authentication.AUTH_PARAMETERS_PLACEHOLDER); messageBuffer.fill (0, authenticationParametersOffset, authenticationParametersOffset + Authentication.AUTHENTICATION_CODE_LENGTH); digestToAdd = Authentication.calculateDigest (messageBuffer, authProtocol, authPassword, engineID); digestToAdd.copy (messageBuffer, authenticationParametersOffset, 0, Authentication.AUTHENTICATION_CODE_LENGTH); debug ("Added Auth Parameters: " + digestToAdd.toString('hex')); }; Authentication.isAuthentic = function (messageBuffer, authProtocol, authPassword, engineID, digestInMessage) { var authenticationParametersOffset; var calculatedDigest; // clear the authenticationParameters field in message authenticationParametersOffset = messageBuffer.indexOf (digestInMessage); messageBuffer.fill (0, authenticationParametersOffset, authenticationParametersOffset + Authentication.AUTHENTICATION_CODE_LENGTH); calculatedDigest = Authentication.calculateDigest (messageBuffer, authProtocol, authPassword, engineID); // replace previously cleared authenticationParameters field in message digestInMessage.copy (messageBuffer, authenticationParametersOffset, 0, Authentication.AUTHENTICATION_CODE_LENGTH); debug ("Digest in message: " + digestInMessage.toString('hex')); debug ("Calculated digest: " + calculatedDigest.toString('hex')); return calculatedDigest.equals (digestInMessage, Authentication.AUTHENTICATION_CODE_LENGTH); }; Authentication.calculateDigest = function (messageBuffer, authProtocol, authPassword, engineID) { var authKey = Authentication.passwordToKey (authProtocol, authPassword, engineID); // Adapted from RFC3147 Section 6.3.1. Processing an Outgoing Message var hashAlgorithm; var kIpad; var kOpad; var firstDigest; var finalDigest; var truncatedDigest; var i; var cryptoAlgorithm = Authentication.algorithms[authProtocol].CRYPTO_ALGORITHM; if (authKey.length > Authentication.HMAC_BLOCK_SIZE) { hashAlgorithm = crypto.createHash (cryptoAlgorithm); hashAlgorithm.update (authKey); authKey = hashAlgorithm.digest (); } // MD(K XOR opad, MD(K XOR ipad, msg)) kIpad = Buffer.alloc (Authentication.HMAC_BLOCK_SIZE); kOpad = Buffer.alloc (Authentication.HMAC_BLOCK_SIZE); for (i = 0; i < authKey.length; i++) { kIpad[i] = authKey[i] ^ 0x36; kOpad[i] = authKey[i] ^ 0x5c; } kIpad.fill (0x36, authKey.length); kOpad.fill (0x5c, authKey.length); // inner MD hashAlgorithm = crypto.createHash (cryptoAlgorithm); hashAlgorithm.update (kIpad); hashAlgorithm.update (messageBuffer); firstDigest = hashAlgorithm.digest (); // outer MD hashAlgorithm = crypto.createHash (cryptoAlgorithm); hashAlgorithm.update (kOpad); hashAlgorithm.update (firstDigest); finalDigest = hashAlgorithm.digest (); truncatedDigest = Buffer.alloc (Authentication.AUTHENTICATION_CODE_LENGTH); finalDigest.copy (truncatedDigest, 0, 0, Authentication.AUTHENTICATION_CODE_LENGTH); return truncatedDigest; }; var Encryption = {}; Encryption.PRIV_PARAMETERS_PLACEHOLDER = Buffer.from ('9192939495969798', 'hex'); Encryption.encryptPdu = function (privProtocol, scopedPdu, privPassword, authProtocol, engine) { var encryptFunction = Encryption.algorithms[privProtocol].encryptPdu; return encryptFunction (scopedPdu, privPassword, authProtocol, engine); }; Encryption.decryptPdu = function (privProtocol, encryptedPdu, privParameters, privPassword, authProtocol, engine, forceAutoPaddingDisable) { var decryptFunction = Encryption.algorithms[privProtocol].decryptPdu; return decryptFunction (encryptedPdu, privParameters, privPassword, authProtocol, engine, forceAutoPaddingDisable); }; Encryption.debugEncrypt = function (encryptionKey, iv, plainPdu, encryptedPdu) { debug ("Key: " + encryptionKey.toString ('hex')); debug ("IV: " + iv.toString ('hex')); debug ("Plain: " + plainPdu.toString ('hex')); debug ("Encrypted: " + encryptedPdu.toString ('hex')); }; Encryption.debugDecrypt = function (decryptionKey, iv, encryptedPdu, plainPdu) { debug ("Key: " + decryptionKey.toString ('hex')); debug ("IV: " + iv.toString ('hex')); debug ("Encrypted: " + encryptedPdu.toString ('hex')); debug ("Plain: " + plainPdu.toString ('hex')); }; Encryption.generateLocalizedKey = function (algorithm, authProtocol, privPassword, engineID) { var privLocalizedKey; var encryptionKey; privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engineID); encryptionKey = Buffer.alloc (algorithm.KEY_LENGTH); privLocalizedKey.copy (encryptionKey, 0, 0, algorithm.KEY_LENGTH); return encryptionKey; }; Encryption.encryptPduDes = function (scopedPdu, privPassword, authProtocol, engine) { var des = Encryption.algorithms[PrivProtocols.des]; var privLocalizedKey; var encryptionKey; var preIv; var salt; var iv; var i; var paddedScopedPduLength; var paddedScopedPdu; var encryptedPdu; encryptionKey = Encryption.generateLocalizedKey (des, authProtocol, privPassword, engine.engineID); privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engine.engineID); encryptionKey = Buffer.alloc (des.KEY_LENGTH); privLocalizedKey.copy (encryptionKey, 0, 0, des.KEY_LENGTH); preIv = Buffer.alloc (des.BLOCK_LENGTH); privLocalizedKey.copy (preIv, 0, des.KEY_LENGTH, des.KEY_LENGTH + des.BLOCK_LENGTH); salt = Buffer.alloc (des.BLOCK_LENGTH); // set local SNMP engine boots part of salt to 1, as we have no persistent engine state salt.fill ('00000001', 0, 4, 'hex'); // set local integer part of salt to random salt.fill (crypto.randomBytes (4), 4, 8); iv = Buffer.alloc (des.BLOCK_LENGTH); for (i = 0; i < iv.length; i++) { iv[i] = preIv[i] ^ salt[i]; } if (scopedPdu.length % des.BLOCK_LENGTH == 0) { paddedScopedPdu = scopedPdu; } else { paddedScopedPduLength = des.BLOCK_LENGTH * (Math.floor (scopedPdu.length / des.BLOCK_LENGTH) + 1); paddedScopedPdu = Buffer.alloc (paddedScopedPduLength); scopedPdu.copy (paddedScopedPdu, 0, 0, scopedPdu.length); } cipher = crypto.createCipheriv (des.CRYPTO_ALGORITHM, encryptionKey, iv); encryptedPdu = cipher.update (paddedScopedPdu); encryptedPdu = Buffer.concat ([encryptedPdu, cipher.final()]); // Encryption.debugEncrypt (encryptionKey, iv, paddedScopedPdu, encryptedPdu); return { encryptedPdu: encryptedPdu, msgPrivacyParameters: salt }; }; Encryption.decryptPduDes = function (encryptedPdu, privParameters, privPassword, authProtocol, engine, forceAutoPaddingDisable) { var des = Encryption.algorithms[PrivProtocols.des]; var privLocalizedKey; var decryptionKey; var preIv; var salt; var iv; var i; var decryptedPdu; privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engine.engineID); decryptionKey = Buffer.alloc (des.KEY_LENGTH); privLocalizedKey.copy (decryptionKey, 0, 0, des.KEY_LENGTH); preIv = Buffer.alloc (des.BLOCK_LENGTH); privLocalizedKey.copy (preIv, 0, des.KEY_LENGTH, des.KEY_LENGTH + des.BLOCK_LENGTH); salt = privParameters; iv = Buffer.alloc (des.BLOCK_LENGTH); for (i = 0; i < iv.length; i++) { iv[i] = preIv[i] ^ salt[i]; } decipher = crypto.createDecipheriv (des.CRYPTO_ALGORITHM, decryptionKey, iv); if ( forceAutoPaddingDisable ) { decipher.setAutoPadding(false); } decryptedPdu = decipher.update (encryptedPdu); // This try-catch is a workaround for a seemingly incorrect error condition // - where sometimes a decrypt error is thrown with decipher.final() // It replaces this line which should have been sufficient: // decryptedPdu = Buffer.concat ([decryptedPdu, decipher.final()]); try { decryptedPdu = Buffer.concat ([decryptedPdu, decipher.final()]); } catch (error) { // debug("Decrypt error: " + error); decipher = crypto.createDecipheriv (des.CRYPTO_ALGORITHM, decryptionKey, iv); decipher.setAutoPadding(false); decryptedPdu = decipher.update (encryptedPdu); decryptedPdu = Buffer.concat ([decryptedPdu, decipher.final()]); } // Encryption.debugDecrypt (decryptionKey, iv, encryptedPdu, decryptedPdu); return decryptedPdu; }; Encryption.generateIvAes = function (aes, engineBoots, engineTime, salt) { var iv; // iv = engineBoots(4) | engineTime(4) | salt(8) iv = Buffer.alloc (aes.BLOCK_LENGTH); engineBootsBuffer = Buffer.alloc (4); engineBootsBuffer.writeUInt32BE (engineBoots); engineTimeBuffer = Buffer.alloc (4); engineTimeBuffer.writeUInt32BE (engineTime); engineBootsBuffer.copy (iv, 0, 0, 4); engineTimeBuffer.copy (iv, 4, 0, 4); salt.copy (iv, 8, 0, 8); return iv; } Encryption.encryptPduAes = function (scopedPdu, privPassword, authProtocol, engine) { var aes = Encryption.algorithms[PrivProtocols.aes]; var encryptionKey; var salt; var iv; var cipher; var encryptedPdu; encryptionKey = Encryption.generateLocalizedKey (aes, authProtocol, privPassword, engine.engineID); salt = Buffer.alloc (8).fill (crypto.randomBytes (8), 0, 8); iv = Encryption.generateIvAes (aes, engine.engineBoots, engine.engineTime, salt); cipher = crypto.createCipheriv (aes.CRYPTO_ALGORITHM, encryptionKey, iv); encryptedPdu = cipher.update (scopedPdu); encryptedPdu = Buffer.concat ([encryptedPdu, cipher.final()]); // Encryption.debugEncrypt (encryptionKey, iv, scopedPdu, encryptedPdu); return { encryptedPdu: encryptedPdu, msgPrivacyParameters: salt }; }; Encryption.decryptPduAes = function (encryptedPdu, privParameters, privPassword, authProtocol, engine) { var aes = Encryption.algorithms[PrivProtocols.aes]; var decryptionKey; var iv; var decipher; var decryptedPdu; decryptionKey = Encryption.generateLocalizedKey (aes, authProtocol, privPassword, engine.engineID); iv = Encryption.generateIvAes (aes, engine.engineBoots, engine.engineTime, privParameters); decipher = crypto.createDecipheriv (aes.CRYPTO_ALGORITHM, decryptionKey, iv); decryptedPdu = decipher.update (encryptedPdu); decryptedPdu = Buffer.concat ([decryptedPdu, decipher.final()]); // Encryption.debugDecrypt (decryptionKey, iv, encryptedPdu, decryptedPdu); return decryptedPdu; }; Encryption.addParametersToMessageBuffer = function (messageBuffer, msgPrivacyParameters) { privacyParametersOffset = messageBuffer.indexOf (Encryption.PRIV_PARAMETERS_PLACEHOLDER); msgPrivacyParameters.copy (messageBuffer, privacyParametersOffset, 0, Encryption.DES_IV_LENGTH); }; Encryption.algorithms = {}; Encryption.algorithms[PrivProtocols.des] = { CRYPTO_ALGORITHM: 'des-cbc', KEY_LENGTH: 8, BLOCK_LENGTH: 8, encryptPdu: Encryption.encryptPduDes, decryptPdu: Encryption.decryptPduDes }; Encryption.algorithms[PrivProtocols.aes] = { CRYPTO_ALGORITHM: 'aes-128-cfb', KEY_LENGTH: 16, BLOCK_LENGTH: 16, encryptPdu: Encryption.encryptPduAes, decryptPdu: Encryption.decryptPduAes }; /***************************************************************************** ** Message class definition **/ var Message = function () { } Message.prototype.getReqId = function () { return this.version == Version3 ? this.msgGlobalData.msgID : this.pdu.id; }; Message.prototype.toBuffer = function () { if ( this.version == Version3 ) { return this.toBufferV3(); } else { return this.toBufferCommunity(); } } Message.prototype.toBufferCommunity = function () { if (this.buffer) return this.buffer; var writer = new ber.Writer (); writer.startSequence (); writer.writeInt (this.version); writer.writeString (this.community); this.pdu.toBuffer (writer); writer.endSequence (); this.buffer = writer.buffer; return this.buffer; }; Message.prototype.toBufferV3 = function () { var encryptionResult; if (this.buffer) return this.buffer; var writer = new ber.Writer (); writer.startSequence (); writer.writeInt (this.version); // HeaderData writer.startSequence (); writer.writeInt (this.msgGlobalData.msgID); writer.writeInt (this.msgGlobalData.msgMaxSize); writer.writeByte (ber.OctetString); writer.writeByte (1); writer.writeByte (this.msgGlobalData.msgFlags); writer.writeInt (this.msgGlobalData.msgSecurityModel); writer.endSequence (); // msgSecurityParameters var msgSecurityParametersWriter = new ber.Writer (); msgSecurityParametersWriter.startSequence (); //msgSecurityParametersWriter.writeString (this.msgSecurityParameters.msgAuthoritativeEngineID); // writing a zero-length buffer fails - should fix asn1-ber for this condition if ( this.msgSecurityParameters.msgAuthoritativeEngineID.length == 0 ) { msgSecurityParametersWriter.writeString (""); } else { msgSecurityParametersWriter.writeBuffer (this.msgSecurityParameters.msgAuthoritativeEngineID, ber.OctetString); } msgSecurityParametersWriter.writeInt (this.msgSecurityParameters.msgAuthoritativeEngineBoots); msgSecurityParametersWriter.writeInt (this.msgSecurityParameters.msgAuthoritativeEngineTime); msgSecurityParametersWriter.writeString (this.msgSecurityParameters.msgUserName); if ( this.hasAuthentication() ) { msgSecurityParametersWriter.writeBuffer (Authentication.AUTH_PARAMETERS_PLACEHOLDER, ber.OctetString); // should never happen where msgFlags has no authentication but authentication parameters still present } else if ( this.msgSecurityParameters.msgAuthenticationParameters.length > 0 ) { msgSecurityParametersWriter.writeBuffer (this.msgSecurityParameters.msgAuthenticationParameters, ber.OctetString); } else { msgSecurityParametersWriter.writeString (""); } if ( this.hasPrivacy() ) { msgSecurityParametersWriter.writeBuffer (Encryption.PRIV_PARAMETERS_PLACEHOLDER, ber.OctetString); // should never happen where msgFlags has no privacy but privacy parameters still present } else if ( this.msgSecurityParameters.msgPrivacyParameters.length > 0 ) { msgSecurityParametersWriter.writeBuffer (this.msgSecurityParameters.msgPrivacyParameters, ber.OctetString); } else { msgSecurityParametersWriter.writeString (""); } msgSecurityParametersWriter.endSequence (); writer.writeBuffer (msgSecurityParametersWriter.buffer, ber.OctetString); // ScopedPDU var scopedPduWriter = new ber.Writer (); scopedPduWriter.startSequence (); var contextEngineID = this.pdu.contextEngineID ? this.pdu.contextEngineID : this.msgSecurityParameters.msgAuthoritativeEngineID; if ( contextEngineID.length == 0 ) { scopedPduWriter.writeString (""); } else { scopedPduWriter.writeBuffer (contextEngineID, ber.OctetString); } scopedPduWriter.writeString (this.pdu.contextName); this.pdu.toBuffer (scopedPduWriter); scopedPduWriter.endSequence (); if ( this.hasPrivacy() ) { var authoritativeEngine = { engineID: this.msgSecurityParameters.msgAuthoritativeEngineID, engineBoots: this.msgSecurityParameters.msgAuthoritativeEngineBoots, engineTime: this.msgSecurityParameters.msgAuthoritativeEngineTime, }; encryptionResult = Encryption.encryptPdu (this.user.privProtocol, scopedPduWriter.buffer, this.user.privKey, this.user.authProtocol, authoritativeEngine); writer.writeBuffer (encryptionResult.encryptedPdu, ber.OctetString); } else { writer.writeBuffer (scopedPduWriter.buffer); } writer.endSequence (); this.buffer = writer.buffer; if ( this.hasPrivacy() ) { Encryption.addParametersToMessageBuffer(this.buffer, encryptionResult.msgPrivacyParameters); } if ( this.hasAuthentication() ) { Authentication.addParametersToMessageBuffer(this.buffer, this.user.authProtocol, this.user.authKey, this.msgSecurityParameters.msgAuthoritativeEngineID); } return this.buffer; }; Message.prototype.processIncomingSecurity = function (user, responseCb) { if ( this.hasPrivacy() ) { if ( ! this.decryptPdu(user, responseCb) ) { return false; } } if ( this.hasAuthentication() && ! this.isAuthenticationDisabled() ) { return this.checkAuthentication(user, responseCb); } else { return true; } }; Message.prototype.decryptPdu = function (user, responseCb) { var decryptedPdu; var decryptedPduReader; try { var authoratitiveEngine = { engineID: this.msgSecurityParameters.msgAuthoritativeEngineID, engineBoots: this.msgSecurityParameters.msgAuthoritativeEngineBoots, engineTime: this.msgSecurityParameters.msgAuthoritativeEngineTime }; decryptedPdu = Encryption.decryptPdu(user.privProtocol, this.encryptedPdu, this.msgSecurityParameters.msgPrivacyParameters, user.privKey, user.authProtocol, authoratitiveEngine); decryptedPduReader = new ber.Reader (decryptedPdu); this.pdu = readPdu(decryptedPduReader, true); return true; // really really occasionally the decrypt truncates a single byte // causing an ASN read failure in readPdu() // in this case, disabling auto padding decrypts the PDU correctly // this try-catch provides the workaround for this condition } catch (possibleTruncationError) { try { decryptedPdu = Encryption.decryptPdu(user.privProtocol, this.encryptedPdu, this.msgSecurityParameters.msgPrivacyParameters, user.privKey, user.authProtocol, this.msgSecurityParameters.msgAuthoritativeEngineID, true); decryptedPduReader = new ber.Reader (decryptedPdu); this.pdu = readPdu(decryptedPduReader, true); return true; } catch (error) { responseCb (new ResponseInvalidError ("Failed to decrypt PDU: " + error)); return false; } } }; Message.prototype.checkAuthentication = function (user, responseCb) { if ( Authentication.isAuthentic(this.buffer, user.authProtocol, user.authKey, this.msgSecurityParameters.msgAuthoritativeEngineID, this.msgSecurityParameters.msgAuthenticationParameters) ) { return true; } else { responseCb (new ResponseInvalidError ("Authentication digest " + this.msgSecurityParameters.msgAuthenticationParameters.toString ('hex') + " received in message does not match digest " + Authentication.calculateDigest (buffer, user.authProtocol, user.authKey, this.msgSecurityParameters.msgAuthoritativeEngineID).toString ('hex') + " calculated for message") ); return false; } }; Message.prototype.setMsgFlags = function (bitPosition, flag) { if ( this.msgGlobalData && this.msgGlobalData !== undefined && this.msgGlobalData !== null ) { if ( flag ) { this.msgGlobalData.msgFlags = this.msgGlobalData.msgFlags | ( 2 ** bitPosition ); } else { this.msgGlobalData.msgFlags = this.msgGlobalData.msgFlags & ( 255 - 2 ** bitPosition ); } } } Message.prototype.hasAuthentication = function () { return this.msgGlobalData && this.msgGlobalData.msgFlags && this.msgGlobalData.msgFlags & 1; }; Message.prototype.setAuthentication = function (flag) { this.setMsgFlags (0, flag); }; Message.prototype.hasPrivacy = function () { return this.msgGlobalData && this.msgGlobalData.msgFlags && this.msgGlobalData.msgFlags & 2; }; Message.prototype.setPrivacy = function (flag) { this.setMsgFlags (1, flag); }; Message.prototype.isReportable = function () { return this.msgGlobalData && this.msgGlobalData.msgFlags && this.msgGlobalData.msgFlags & 4; }; Message.prototype.setReportable = function (flag) { this.setMsgFlags (2, flag); }; Message.prototype.isAuthenticationDisabled = function () { return this.disableAuthentication; }; Message.prototype.hasAuthoritativeEngineID = function () { return this.msgSecurityParameters && this.msgSecurityParameters.msgAuthoritativeEngineID && this.msgSecurityParameters.msgAuthoritativeEngineID != ""; }; Message.prototype.createReportResponseMessage = function (engine, context) { var user = { name: "", level: SecurityLevel.noAuthNoPriv }; var responseSecurityParameters = { msgAuthoritativeEngineID: engine.engineID, msgAuthoritativeEngineBoots: engine.engineBoots, msgAuthoritativeEngineTime: engine.engineTime, msgUserName: user.name, msgAuthenticationParameters: "", msgPrivacyParameters: "" }; var reportPdu = ReportPdu.createFromVariables (this.pdu.id, [], {}); reportPdu.contextName = context; var responseMessage = Message.createRequestV3 (user, responseSecurityParameters, reportPdu); responseMessage.msgGlobalData.msgID = this.msgGlobalData.msgID; return responseMessage; }; Message.prototype.createResponseForRequest = function (responsePdu) { if ( this.version == Version3 ) { return this.createV3ResponseFromRequest(responsePdu); } else { return this.createCommunityResponseFromRequest(responsePdu); } }; Message.prototype.createCommunityResponseFromRequest = function (responsePdu) { return Message.createCommunity(this.version, this.community, responsePdu); }; Message.prototype.createV3ResponseFromRequest = function (responsePdu) { var responseUser = { name: this.user.name, level: this.user.name, authProtocol: this.user.authProtocol, authKey: this.user.authKey, privProtocol: this.user.privProtocol, privKey: this.user.privKey }; var responseSecurityParameters = { msgAuthoritativeEngineID: this.msgSecurityParameters.msgAuthoritativeEngineID, msgAuthoritativeEngineBoots: this.msgSecurityParameters.msgAuthoritativeEngineBoots, msgAuthoritativeEngineTime: this.msgSecurityParameters.msgAuthoritativeEngineTime, msgUserName: this.msgSecurityParameters.msgUserName, msgAuthenticationParameters: "", msgPrivacyParameters: "" }; var responseGlobalData = { msgID: this.msgGlobalData.msgID, msgMaxSize: 65507, msgFlags: this.msgGlobalData.msgFlags & (255 - 4), msgSecurityModel: 3 }; return Message.createV3 (responseUser, responseGlobalData, responseSecurityParameters, responsePdu); }; Message.createCommunity = function (version, community, pdu) { var message = new Message (); message.version = version; message.community = community; message.pdu = pdu; return message; }; Message.createRequestV3 = function (user, msgSecurityParameters, pdu) { var authFlag = user.level == SecurityLevel.authNoPriv || user.level == SecurityLevel.authPriv ? 1 : 0; var privFlag = user.level == SecurityLevel.authPriv ? 1 : 0; var reportableFlag = ( pdu.type == PduType.GetResponse || pdu.type == PduType.TrapV2 ) ? 0 : 1; var msgGlobalData = { msgID: _generateId(), // random ID msgMaxSize: 65507, msgFlags: reportableFlag * 4 | privFlag * 2 | authFlag * 1, msgSecurityModel: 3 }; return Message.createV3 (user, msgGlobalData, msgSecurityParameters, pdu); }; Message.createV3 = function (user, msgGlobalData, msgSecurityParameters, pdu) { var message = new Message (); message.version = 3; message.user = user; message.msgGlobalData = msgGlobalData; message.msgSecurityParameters = { msgAuthoritativeEngineID: msgSecurityParameters.msgAuthoritativeEngineID || Buffer.from(""), msgAuthoritativeEngineBoots: msgSecurityParameters.msgAuthoritativeEngineBoots || 0, msgAuthoritativeEngineTime: msgSecurityParameters.msgAuthoritativeEngineTime || 0, msgUserName: user.name || "", msgAuthenticationParameters: "", msgPrivacyParameters: "" }; message.pdu = pdu; return message; }; Message.createDiscoveryV3 = function (pdu) { var msgSecurityParameters = { msgAuthoritativeEngineID: Buffer.from(""), msgAuthoritativeEngineBoots: 0, msgAuthoritativeEngineTime: 0 }; var emptyUser = { name: "", level: SecurityLevel.noAuthNoPriv }; return Message.createRequestV3 (emptyUser, msgSecurityParameters, pdu); } Message.createFromBuffer = function (buffer, user) { var reader = new ber.Reader (buffer); var message = new Message(); reader.readSequence (); message.version = reader.readInt (); if (message.version != 3) { message.community = reader.readString (); message.pdu = readPdu(reader, false); } else { // HeaderData message.msgGlobalData = {}; reader.readSequence (); message.msgGlobalData.msgID = reader.readInt (); message.msgGlobalData.msgMaxSize = reader.readInt (); message.msgGlobalData.msgFlags = reader.readString (ber.OctetString, true)[0]; message.msgGlobalData.msgSecurityModel = reader.readInt (); // msgSecurityParameters message.msgSecurityParameters = {}; var msgSecurityParametersReader = new ber.Reader (reader.readString (ber.OctetString, true)); msgSecurityParametersReader.readSequence (); message.msgSecurityParameters.msgAuthoritativeEngineID = msgSecurityParametersReader.readString (ber.OctetString, true); message.msgSecurityParameters.msgAuthoritativeEngineBoots = msgSecurityParametersReader.readInt (); message.msgSecurityParameters.msgAuthoritativeEngineTime = msgSecurityParametersReader.readInt (); message.msgSecurityParameters.msgUserName = msgSecurityParametersReader.readString (); message.msgSecurityParameters.msgAuthenticationParameters = Buffer.from(msgSecurityParametersReader.readString (ber.OctetString, true)); message.msgSecurityParameters.msgPrivacyParameters = Buffer.from(msgSecurityParametersReader.readString (ber.OctetString, true)); if ( message.hasPrivacy() ) { message.encryptedPdu = reader.readString (ber.OctetString, true); message.pdu = null; } else { message.pdu = readPdu(reader, true); } } message.buffer = buffer; return message; }; var Req = function (session, message, feedCb, responseCb, options) { this.message = message; this.responseCb = responseCb; this.retries = session.retries; this.timeout = session.timeout; // Add timeout backoff this.backoff = session.backoff; this.onResponse = session.onSimpleGetResponse; this.feedCb = feedCb; this.port = (options && options.port) ? options.port : session.port; this.context = session.context; }; Req.prototype.getId = function() { return this.message.getReqId (); }; /***************************************************************************** ** Session class definition **/ var Session = function (target, authenticator, options) { this.target = target || "127.0.0.1"; options = options || {}; this.version = options.version ? options.version : Version1; if ( this.version == Version3 ) { this.user = authenticator; } else { this.community = authenticator || "public"; } this.transport = options.transport ? options.transport : "udp4"; this.port = options.port ? options.port : 161; this.trapPort = options.trapPort ? options.trapPort : 162; this.retries = (options.retries || options.retries == 0) ? options.retries : 1; this.timeout = options.timeout ? options.timeout : 5000; this.backoff = options.backoff >= 1.0 ? options.backoff : 1.0; this.sourceAddress = options.sourceAddress ? options.sourceAddress : undefined; this.sourcePort = options.sourcePort ? parseInt(options.sourcePort) : undefined; this.idBitsSize = options.idBitsSize ? parseInt(options.idBitsSize) : 32; this.context = options.context ? options.context : ""; this.backwardsGetNexts = (typeof options.backwardsGetNexts !== 'undefined') ? options.backwardsGetNexts : true; DEBUG = options.debug; this.engine = new Engine (); this.reqs = {}; this.reqCount = 0; this.dgram = dgram.createSocket (this.transport); this.dgram.unref(); var me = this; this.dgram.on ("message", me.onMsg.bind (me)); this.dgram.on ("close", me.onClose.bind (me)); this.dgram.on ("error", me.onError.bind (me)); if (this.sourceAddress || this.sourcePort) this.dgram.bind (this.sourcePort, this.sourceAddress); }; util.inherits (Session, events.EventEmitter); Session.prototype.close = function () { this.dgram.close (); return this; }; Session.prototype.cancelRequests = function (error) { var id; for (id in this.reqs) { var req = this.reqs[id]; this.unregisterRequest (req.getId ()); req.responseCb (error); } }; function _generateId (bitSize) { if (bitSize === 16) { return Math.floor(Math.random() * 10000) % 65535; } return Math.floor(Math.random() * 1