From 2e85f9325a797977eea9dfea0a925775ddd211d9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Feb 2021 12:49:00 +0100 Subject: Merging upstream version 1.29.0. Signed-off-by: Daniel Baumann --- collectors/node.d.plugin/node_modules/net-snmp.js | 4411 +++++++++++++++------ 1 file changed, 3199 insertions(+), 1212 deletions(-) (limited to 'collectors/node.d.plugin/node_modules') diff --git a/collectors/node.d.plugin/node_modules/net-snmp.js b/collectors/node.d.plugin/node_modules/net-snmp.js index 484597dcb..6b5b754ea 100644 --- a/collectors/node.d.plugin/node_modules/net-snmp.js +++ b/collectors/node.d.plugin/node_modules/net-snmp.js @@ -1,66 +1,77 @@ - // Copyright 2013 Stephen Vickers // SPDX-License-Identifier: MIT -var ber = require ("asn1-ber").Ber; -var dgram = require ("dgram"); -var events = require ("events"); -var util = require ("util"); +var ber = require("asn1-ber").Ber; +var dgram = require("dgram"); +var events = require("events"); +var util = require("util"); +var crypto = require("crypto"); + +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]); + +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); + 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); + 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); ObjectType.Integer32 = ObjectType.Integer; ObjectType.Counter32 = ObjectType.Counter; @@ -68,132 +79,173 @@ ObjectType.Gauge32 = ObjectType.Gauge; ObjectType.Unsigned32 = ObjectType.Gauge32; var PduType = { - 160: "GetRequest", - 161: "GetNextRequest", - 162: "GetResponse", - 163: "SetRequest", - 164: "Trap", - 165: "GetBulkRequest", - 166: "InformRequest", - 167: "TrapV2", - 168: "Report" + 160: "GetRequest", + 161: "GetNextRequest", + 162: "GetResponse", + 163: "SetRequest", + 164: "Trap", + 165: "GetBulkRequest", + 166: "InformRequest", + 167: "TrapV2", + 168: "Report" }; -_expandConstantObject (PduType); +_expandConstantObject(PduType); var TrapType = { - 0: "ColdStart", - 1: "WarmStart", - 2: "LinkDown", - 3: "LinkUp", - 4: "AuthenticationFailure", - 5: "EgpNeighborLoss", - 6: "EnterpriseSpecific" + 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" +}; + +_expandConstantObject(PrivProtocols); + +var MibProviderType = { + "1": "Scalar", + "2": "Table" }; -_expandConstantObject (TrapType); +_expandConstantObject(MibProviderType); var Version1 = 0; var Version2c = 1; +var Version3 = 3; + +var Version = { + "1": Version1, + "2c": Version2c, + "3": Version3 +}; /***************************************************************************** ** Exception class definitions **/ -function ResponseInvalidError (message) { - this.name = "ResponseInvalidError"; - this.message = message; - Error.captureStackTrace(this, ResponseInvalidError); +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(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(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(RequestFailedError, Error); + +function RequestTimedOutError(message) { + this.name = "RequestTimedOutError"; + this.message = message; + Error.captureStackTrace(this, RequestTimedOutError); } -util.inherits (RequestTimedOutError, Error); + +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 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 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 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 ("."); +function oidInSubtree(oidString, nextString) { + var oid = oidString.split("."); + var next = nextString.split("."); - if (oid.length > next.length) - return false; + if (oid.length > next.length) + return false; - for (var i = 0; i < oid.length; i++) { - if (next[i] != oid[i]) - return false; - } + for (var i = 0; i < oid.length; i++) { + if (next[i] != oid[i]) + return false; + } - return true; + return true; } /** @@ -207,1228 +259,3138 @@ function oidInSubtree (oidString, nextString) { ** an error since the integer is too large. **/ -function readInt (buffer) { - return readUint (buffer, true); +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 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); +function readUint64(buffer) { + var value = buffer.readString(ObjectType.Counter64, true); - return value; + return value; } -function readVarbinds (buffer, varbinds) { - buffer.readSequence (); - - while (1) { - buffer.readSequence (); - var oid = buffer.readOID (); - var type = buffer.peek (); - - if (type == null) - break; - - 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) { - 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"); - value = bytes[0] + "." + bytes[1] + "." + bytes[2] + "." + bytes[3]; - } 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"); - } - - varbinds.push ({ - oid: oid, - type: type, - value: 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; + + 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) { + 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"); + value = bytes[0] + "." + bytes[1] + "." + bytes[2] + "." + bytes[3]; + } 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"); + } + + varbinds.push({ + oid: oid, + type: type, + value: value + }); + } } -function writeUint (buffer, type, value) { - var b = new Buffer (4); - b.writeUInt32BE (value, 0); - buffer.writeBuffer (b, type); +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 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; - - if (type == ObjectType.Boolean) { - buffer.writeBoolean (value ? true : false); - } else if (type == ObjectType.Integer) { // also Integer32 - buffer.writeInt (value); - } else if (type == ObjectType.OctetString) { - if (typeof value == "string") - buffer.writeString (value); - else - buffer.writeBuffer (value, ObjectType.OctetString); - } else if (type == ObjectType.Null) { - buffer.writeNull (); - } else if (type == ObjectType.OID) { - buffer.writeOID (value); - } else if (type == ObjectType.IpAddress) { - var bytes = value.split ("."); - if (bytes.length != 4) - throw new RequestInvalidError ("Invalid IP address '" - + value + "'"); - buffer.writeBuffer (new Buffer (bytes), 64); - } else if (type == ObjectType.Counter) { // also Counter32 - writeUint (buffer, ObjectType.Counter, value); - } else if (type == ObjectType.Gauge) { // also Gauge32 & Unsigned32 - writeUint (buffer, ObjectType.Gauge, value); - } else if (type == ObjectType.TimeTicks) { - writeUint (buffer, ObjectType.TimeTicks, value); - } else if (type == ObjectType.Opaque) { - buffer.writeBuffer (value, ObjectType.Opaque); - } else if (type == ObjectType.Counter64) { - writeUint64 (buffer, value); - } else { - throw new RequestInvalidError ("Unknown type '" + type - + "' in request"); - } - } else { - buffer.writeNull (); - } - - buffer.endSequence (); - } - buffer.endSequence (); +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; + + if (type == ObjectType.Boolean) { + buffer.writeBoolean(value ? true : false); + } else if (type == ObjectType.Integer) { // also Integer32 + buffer.writeInt(value); + } else if (type == ObjectType.OctetString) { + if (typeof value == "string") + buffer.writeString(value); + else + buffer.writeBuffer(value, ObjectType.OctetString); + } else if (type == ObjectType.Null) { + buffer.writeNull(); + } else if (type == ObjectType.OID) { + buffer.writeOID(value); + } else if (type == ObjectType.IpAddress) { + var bytes = value.split("."); + if (bytes.length != 4) + throw new RequestInvalidError("Invalid IP address '" + + value + "'"); + buffer.writeBuffer(Buffer.from(bytes), 64); + } else if (type == ObjectType.Counter) { // also Counter32 + writeUint(buffer, ObjectType.Counter, value); + } else if (type == ObjectType.Gauge) { // also Gauge32 & Unsigned32 + writeUint(buffer, ObjectType.Gauge, value); + } else if (type == ObjectType.TimeTicks) { + writeUint(buffer, ObjectType.TimeTicks, value); + } else if (type == ObjectType.Opaque) { + buffer.writeBuffer(value, ObjectType.Opaque); + } else if (type == ObjectType.Counter64) { + writeUint64(buffer, value); + } else if (type == ObjectType.EndOfMibView) { + buffer.writeByte(130); + buffer.writeByte(0); + } else { + throw new RequestInvalidError("Unknown type '" + type + + "' in request"); + } + } else { + buffer.writeNull(); + } + + buffer.endSequence(); + } + buffer.endSequence(); } /***************************************************************************** ** PDU class definitions **/ -var SimplePdu = function (id, varbinds, options) { - this.id = id; - this.varbinds = varbinds; - this.options = options || {}; +var SimplePdu = function () { }; SimplePdu.prototype.toBuffer = function (buffer) { - buffer.startSequence (this.type); + 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); + 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); + writeVarbinds(buffer, this.varbinds); - buffer.endSequence (); + buffer.endSequence(); }; -var GetBulkRequestPdu = function () { - this.type = PduType.GetBulkRequest; - GetBulkRequestPdu.super_.apply (this, arguments); -}; +SimplePdu.prototype.initializeFromVariables = function (id, varbinds, options) { + this.id = id; + this.varbinds = varbinds; + this.options = options || {}; + this.contextName = (options && options.context) ? options.context : ""; +} -util.inherits (GetBulkRequestPdu, SimplePdu); +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); -var GetNextRequestPdu = function () { - this.type = PduType.GetNextRequest; - GetNextRequestPdu.super_.apply (this, arguments); }; -util.inherits (GetNextRequestPdu, SimplePdu); +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 GetResponsePdu = function (buffer) { - this.type = PduType.GetResponse; +var GetBulkRequestPdu = function () { + this.type = PduType.GetBulkRequest; + GetBulkRequestPdu.super_.apply(this, arguments); +}; - buffer.readSequence (this.type); +util.inherits(GetBulkRequestPdu, SimplePdu); - this.id = buffer.readInt (); +GetBulkRequestPdu.createFromBuffer = function (reader) { + var pdu = new GetBulkRequestPdu(); + pdu.initializeFromBuffer(reader); + return pdu; +}; - this.errorStatus = buffer.readInt (); - this.errorIndex = buffer.readInt (); +var GetNextRequestPdu = function () { + this.type = PduType.GetNextRequest; + GetNextRequestPdu.super_.apply(this, arguments); +}; - this.varbinds = []; +util.inherits(GetNextRequestPdu, SimplePdu); - readVarbinds (buffer, this.varbinds); +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); + 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; }; -util.inherits (GetRequestPdu, SimplePdu); +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); + this.type = PduType.InformRequest; + InformRequestPdu.super_.apply(this, arguments); }; -util.inherits (InformRequestPdu, SimplePdu); +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); + 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; }; -util.inherits (SetRequestPdu, SimplePdu); +var TrapPdu = function () { + this.type = PduType.Trap; +}; -var TrapPdu = function (typeOrOid, varbinds, options) { - this.type = PduType.Trap; +TrapPdu.prototype.toBuffer = function (buffer) { + buffer.startSequence(this.type); - this.agentAddr = options.agentAddr || "127.0.0.1"; - this.upTime = options.upTime; + 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)); - if (typeof typeOrOid == "string") { - this.generic = TrapType.EnterpriseSpecific; - this.specific = parseInt (typeOrOid.match (/\.(\d+)$/)[1]); - this.enterprise = typeOrOid.replace (/\.(\d+)$/, ""); - } else { - this.generic = typeOrOid; - this.specific = 0; - this.enterprise = "1.3.6.1.4.1"; - } + writeVarbinds(buffer, this.varbinds); - this.varbinds = varbinds; + buffer.endSequence(); }; -TrapPdu.prototype.toBuffer = function (buffer) { - buffer.startSequence (this.type); +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; +}; - buffer.writeOID (this.enterprise); - buffer.writeBuffer (new Buffer (this.agentAddr.split (".")), - ObjectType.IpAddress); - buffer.writeInt (this.generic); - buffer.writeInt (this.specific); - writeUint (buffer, ObjectType.TimeTicks, - this.upTime || Math.floor (process.uptime () * 100)); +TrapPdu.createFromVariables = function (typeOrOid, varbinds, options) { + var pdu = new TrapPdu(); + pdu.agentAddr = options.agentAddr || "127.0.0.1"; + pdu.upTime = options.upTime; - writeVarbinds (buffer, this.varbinds); + 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"; + } - buffer.endSequence (); + pdu.varbinds = varbinds; + + return pdu; }; var TrapV2Pdu = function () { - this.type = PduType.TrapV2; - TrapV2Pdu.super_.apply (this, arguments); + 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(); + }; -util.inherits (TrapV2Pdu, SimplePdu); +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.INPUT_KEY_LENGTH = 16; +Encryption.DES_KEY_LENGTH = 8; +Encryption.DES_BLOCK_LENGTH = 8; +Encryption.CRYPTO_DES_ALGORITHM = 'des-cbc'; +Encryption.PRIV_PARAMETERS_PLACEHOLDER = Buffer.from('9192939495969798', 'hex'); + +Encryption.encryptPdu = function (scopedPdu, privProtocol, privPassword, authProtocol, engineID) { + var privLocalizedKey; + var encryptionKey; + var preIv; + var salt; + var iv; + var i; + var paddedScopedPduLength; + var paddedScopedPdu; + var encryptedPdu; + var cbcProtocol = Encryption.CRYPTO_DES_ALGORITHM; + + privLocalizedKey = Authentication.passwordToKey(authProtocol, privPassword, engineID); + encryptionKey = Buffer.alloc(Encryption.DES_KEY_LENGTH); + privLocalizedKey.copy(encryptionKey, 0, 0, Encryption.DES_KEY_LENGTH); + preIv = Buffer.alloc(Encryption.DES_BLOCK_LENGTH); + privLocalizedKey.copy(preIv, 0, Encryption.DES_KEY_LENGTH, Encryption.DES_KEY_LENGTH + Encryption.DES_BLOCK_LENGTH); + + salt = Buffer.alloc(Encryption.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(Encryption.DES_BLOCK_LENGTH); + for (i = 0; i < iv.length; i++) { + iv[i] = preIv[i] ^ salt[i]; + } + + if (scopedPdu.length % Encryption.DES_BLOCK_LENGTH == 0) { + paddedScopedPdu = scopedPdu; + } else { + paddedScopedPduLength = Encryption.DES_BLOCK_LENGTH * (Math.floor(scopedPdu.length / Encryption.DES_BLOCK_LENGTH) + 1); + paddedScopedPdu = Buffer.alloc(paddedScopedPduLength); + scopedPdu.copy(paddedScopedPdu, 0, 0, scopedPdu.length); + } + cipher = crypto.createCipheriv(cbcProtocol, encryptionKey, iv); + encryptedPdu = cipher.update(paddedScopedPdu); + encryptedPdu = Buffer.concat([encryptedPdu, cipher.final()]); + debug("Key: " + encryptionKey.toString('hex')); + debug("IV: " + iv.toString('hex')); + debug("Plain: " + paddedScopedPdu.toString('hex')); + debug("Encrypted: " + encryptedPdu.toString('hex')); + + return { + encryptedPdu: encryptedPdu, + msgPrivacyParameters: salt + }; +}; + +Encryption.decryptPdu = function (encryptedPdu, privProtocol, privParameters, privPassword, authProtocol, engineID, forceAutoPaddingDisable) { + var privLocalizedKey; + var decryptionKey; + var preIv; + var salt; + var iv; + var i; + var decryptedPdu; + var cbcProtocol = Encryption.CRYPTO_DES_ALGORITHM; + ; + + privLocalizedKey = Authentication.passwordToKey(authProtocol, privPassword, engineID); + decryptionKey = Buffer.alloc(Encryption.DES_KEY_LENGTH); + privLocalizedKey.copy(decryptionKey, 0, 0, Encryption.DES_KEY_LENGTH); + preIv = Buffer.alloc(Encryption.DES_BLOCK_LENGTH); + privLocalizedKey.copy(preIv, 0, Encryption.DES_KEY_LENGTH, Encryption.DES_KEY_LENGTH + Encryption.DES_BLOCK_LENGTH); + + salt = privParameters; + iv = Buffer.alloc(Encryption.DES_BLOCK_LENGTH); + for (i = 0; i < iv.length; i++) { + iv[i] = preIv[i] ^ salt[i]; + } + + decipher = crypto.createDecipheriv(cbcProtocol, 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(cbcProtocol, decryptionKey, iv); + decipher.setAutoPadding(false); + decryptedPdu = decipher.update(encryptedPdu); + decryptedPdu = Buffer.concat([decryptedPdu, decipher.final()]); + } + debug("Key: " + decryptionKey.toString('hex')); + debug("IV: " + iv.toString('hex')); + debug("Encrypted: " + encryptedPdu.toString('hex')); + debug("Plain: " + decryptedPdu.toString('hex')); + + return decryptedPdu; + +}; + +Encryption.addParametersToMessageBuffer = function (messageBuffer, msgPrivacyParameters) { + privacyParametersOffset = messageBuffer.indexOf(Encryption.PRIV_PARAMETERS_PLACEHOLDER); + msgPrivacyParameters.copy(messageBuffer, privacyParametersOffset, 0, Encryption.DES_IV_LENGTH); +}; /***************************************************************************** - ** Message class definitions + ** Message class definition **/ -var RequestMessage = function (version, community, pdu) { - this.version = version; - this.community = community; - this.pdu = pdu; +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()) { + encryptionResult = Encryption.encryptPdu(scopedPduWriter.buffer, this.user.privProtocol, this.user.privKey, this.user.authProtocol, this.msgSecurityParameters.msgAuthoritativeEngineID); + 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 { + decryptedPdu = Encryption.decryptPdu(this.encryptedPdu, user.privProtocol, + this.msgSecurityParameters.msgPrivacyParameters, user.privKey, user.authProtocol, + this.msgSecurityParameters.msgAuthoritativeEngineID); + 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(this.encryptedPdu, user.privProtocol, + 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.hasAuthentication = function () { + return this.msgGlobalData && this.msgGlobalData.msgFlags && this.msgGlobalData.msgFlags & 1; }; -RequestMessage.prototype.toBuffer = function () { - if (this.buffer) - return this.buffer; +Message.prototype.hasPrivacy = function () { + return this.msgGlobalData && this.msgGlobalData.msgFlags && this.msgGlobalData.msgFlags & 2; +}; + +Message.prototype.isReportable = function () { + return this.msgGlobalData && this.msgGlobalData.msgFlags && this.msgGlobalData.msgFlags & 4; +}; - var writer = new ber.Writer (); +Message.prototype.setReportable = function (flag) { + if (this.msgGlobalData && this.msgGlobalData.msgFlags) { + if (flag) { + this.msgGlobalData.msgFlags = this.msgGlobalData.msgFlags | 4; + } else { + this.msgGlobalData.msgFlags = this.msgGlobalData.msgFlags & (255 - 4); + } + } +}; - writer.startSequence (); +Message.prototype.isAuthenticationDisabled = function () { + return this.disableAuthentication; +}; - writer.writeInt (this.version); - writer.writeString (this.community); +Message.prototype.hasAuthoritativeEngineID = function () { + return this.msgSecurityParameters && this.msgSecurityParameters.msgAuthoritativeEngineID && + this.msgSecurityParameters.msgAuthoritativeEngineID != ""; +}; - this.pdu.toBuffer (writer); +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; +}; - writer.endSequence (); +Message.prototype.createResponseForRequest = function (responsePdu) { + if (this.version == Version3) { + return this.createV3ResponseFromRequest(responsePdu); + } else { + return this.createCommunityResponseFromRequest(responsePdu); + } +}; - this.buffer = writer.buffer; +Message.prototype.createCommunityResponseFromRequest = function (responsePdu) { + return Message.createCommunity(this.version, this.community, responsePdu); +}; - return this.buffer; +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); }; -var ResponseMessage = function (buffer) { - var reader = new ber.Reader (buffer); +Message.createCommunity = function (version, community, pdu) { + var message = new Message(); + + message.version = version; + message.community = community; + message.pdu = pdu; + + return message; +}; - reader.readSequence (); +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); +}; - this.version = reader.readInt (); - this.community = reader.readString (); +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; +}; - var type = reader.peek (); +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); +} - if (type == PduType.GetResponse) { - this.pdu = new GetResponsePdu (reader); - } else { - throw new ResponseInvalidError ("Unknown PDU type '" + type - + "' in response"); - } +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)); + scopedPdu = 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; + 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, community, options) { - this.target = target || "127.0.0.1"; - this.community = community || "public"; - - this.version = (options && options.version) - ? options.version - : Version1; - - this.transport = (options && options.transport) - ? options.transport - : "udp4"; - this.port = (options && options.port ) - ? options.port - : 161; - this.trapPort = (options && options.trapPort ) - ? options.trapPort - : 162; - - this.retries = (options && (options.retries || options.retries == 0)) - ? options.retries - : 1; - this.timeout = (options && options.timeout) - ? options.timeout - : 5000; - - this.sourceAddress = (options && options.sourceAddress ) - ? options.sourceAddress - : undefined; - this.sourcePort = (options && options.sourcePort ) - ? parseInt(options.sourcePort) - : undefined; - - this.idBitsSize = (options && options.idBitsSize) - ? parseInt(options.idBitsSize) - : 32; - - 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); +var Session = function (target, authenticator, options) { + this.target = target || "127.0.0.1"; + + this.version = (options && options.version) + ? options.version + : Version1; + + if (this.version == Version3) { + this.user = authenticator; + } else { + this.community = authenticator || "public"; + } + + this.transport = (options && options.transport) + ? options.transport + : "udp4"; + this.port = (options && options.port) + ? options.port + : 161; + this.trapPort = (options && options.trapPort) + ? options.trapPort + : 162; + + this.retries = (options && (options.retries || options.retries == 0)) + ? options.retries + : 1; + this.timeout = (options && options.timeout) + ? options.timeout + : 5000; + + this.sourceAddress = (options && options.sourceAddress) + ? options.sourceAddress + : undefined; + this.sourcePort = (options && options.sourcePort) + ? parseInt(options.sourcePort) + : undefined; + + this.idBitsSize = (options && options.idBitsSize) + ? parseInt(options.idBitsSize) + : 32; + + this.context = (options && options.context) ? options.context : ""; + + DEBUG = options.debug; + + 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; + 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.id); - req.responseCb (error); - } -}; - -function _generateId (bitSize) { - if (bitSize === 16) { - return Math.floor(Math.random() * 10000) % 65535; - } - return Math.floor(Math.random() * 100000000) % 4294967295; + 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() * 100000000) % 4294967295; } Session.prototype.get = function (oids, responseCb) { - function feedCb (req, message) { - var pdu = message.pdu; - var varbinds = []; - - if (req.message.pdu.varbinds.length != pdu.varbinds.length) { - req.responseCb (new ResponseInvalidError ("Requested OIDs do not " - + "match response OIDs")); - } else { - for (var i = 0; i < req.message.pdu.varbinds.length; i++) { - if (req.message.pdu.varbinds[i].oid != pdu.varbinds[i].oid) { - req.responseCb (new ResponseInvalidError ("OID '" - + req.message.pdu.varbinds[i].oid - + "' in request at positiion '" + i + "' does not " - + "match OID '" + pdu.varbinds[i].oid + "' in response " - + "at position '" + i + "'")); - return; - } else { - varbinds.push (pdu.varbinds[i]); - } - } - - req.responseCb (null, varbinds); - } - } - - var pduVarbinds = []; - - for (var i = 0; i < oids.length; i++) { - var varbind = { - oid: oids[i] - }; - pduVarbinds.push (varbind); - } - - this.simpleGet (GetRequestPdu, feedCb, pduVarbinds, responseCb); - - return this; + function feedCb(req, message) { + var pdu = message.pdu; + var varbinds = []; + + if (req.message.pdu.varbinds.length != pdu.varbinds.length) { + req.responseCb(new ResponseInvalidError("Requested OIDs do not " + + "match response OIDs")); + } else { + for (var i = 0; i < req.message.pdu.varbinds.length; i++) { + if (req.message.pdu.varbinds[i].oid != pdu.varbinds[i].oid) { + req.responseCb(new ResponseInvalidError("OID '" + + req.message.pdu.varbinds[i].oid + + "' in request at positiion '" + i + "' does not " + + "match OID '" + pdu.varbinds[i].oid + "' in response " + + "at position '" + i + "'")); + return; + } else { + varbinds.push(pdu.varbinds[i]); + } + } + + req.responseCb(null, varbinds); + } + } + + var pduVarbinds = []; + + for (var i = 0; i < oids.length; i++) { + var varbind = { + oid: oids[i] + }; + pduVarbinds.push(varbind); + } + + this.simpleGet(GetRequestPdu, feedCb, pduVarbinds, responseCb); + + return this; }; Session.prototype.getBulk = function () { - var oids, nonRepeaters, maxRepetitions, responseCb; - - if (arguments.length >= 4) { - oids = arguments[0]; - nonRepeaters = arguments[1]; - maxRepetitions = arguments[2]; - responseCb = arguments[3]; - } else if (arguments.length >= 3) { - oids = arguments[0]; - nonRepeaters = arguments[1]; - maxRepetitions = 10; - responseCb = arguments[2]; - } else { - oids = arguments[0]; - nonRepeaters = 0; - maxRepetitions = 10; - responseCb = arguments[1]; - } - - function feedCb (req, message) { - var pdu = message.pdu; - var varbinds = []; - var i = 0; - - // first walk through and grab non-repeaters - if (pdu.varbinds.length < nonRepeaters) { - req.responseCb (new ResponseInvalidError ("Varbind count in " - + "response '" + pdu.varbinds.length + "' is less than " - + "non-repeaters '" + nonRepeaters + "' in request")); - } else { - for ( ; i < nonRepeaters; i++) { - if (isVarbindError (pdu.varbinds[i])) { - varbinds.push (pdu.varbinds[i]); - } else if (! oidFollowsOid (req.message.pdu.varbinds[i].oid, - pdu.varbinds[i].oid)) { - req.responseCb (new ResponseInvalidError ("OID '" - + req.message.pdu.varbinds[i].oid + "' in request at " - + "positiion '" + i + "' does not precede " - + "OID '" + pdu.varbinds[i].oid + "' in response " - + "at position '" + i + "'")); - return; - } else { - varbinds.push (pdu.varbinds[i]); - } - } - } - - var repeaters = req.message.pdu.varbinds.length - nonRepeaters; - - // secondly walk through and grab repeaters - if (pdu.varbinds.length % (repeaters)) { - req.responseCb (new ResponseInvalidError ("Varbind count in " - + "response '" + pdu.varbinds.length + "' is not a " - + "multiple of repeaters '" + repeaters - + "' plus non-repeaters '" + nonRepeaters + "' in request")); - } else { - while (i < pdu.varbinds.length) { - for (var j = 0; j < repeaters; j++, i++) { - var reqIndex = nonRepeaters + j; - var respIndex = i; - - if (isVarbindError (pdu.varbinds[respIndex])) { - if (! varbinds[reqIndex]) - varbinds[reqIndex] = []; - varbinds[reqIndex].push (pdu.varbinds[respIndex]); - } else if (! oidFollowsOid ( - req.message.pdu.varbinds[reqIndex].oid, - pdu.varbinds[respIndex].oid)) { - req.responseCb (new ResponseInvalidError ("OID '" - + req.message.pdu.varbinds[reqIndex].oid - + "' in request at positiion '" + (reqIndex) - + "' does not precede OID '" - + pdu.varbinds[respIndex].oid - + "' in response at position '" + (respIndex) + "'")); - return; - } else { - if (! varbinds[reqIndex]) - varbinds[reqIndex] = []; - varbinds[reqIndex].push (pdu.varbinds[respIndex]); - } - } - } - } - - req.responseCb (null, varbinds); - } - - var pduVarbinds = []; - - for (var i = 0; i < oids.length; i++) { - var varbind = { - oid: oids[i] - }; - pduVarbinds.push (varbind); - } - - var options = { - nonRepeaters: nonRepeaters, - maxRepetitions: maxRepetitions - }; - - this.simpleGet (GetBulkRequestPdu, feedCb, pduVarbinds, responseCb, - options); - - return this; + var oids, nonRepeaters, maxRepetitions, responseCb; + + if (arguments.length >= 4) { + oids = arguments[0]; + nonRepeaters = arguments[1]; + maxRepetitions = arguments[2]; + responseCb = arguments[3]; + } else if (arguments.length >= 3) { + oids = arguments[0]; + nonRepeaters = arguments[1]; + maxRepetitions = 10; + responseCb = arguments[2]; + } else { + oids = arguments[0]; + nonRepeaters = 0; + maxRepetitions = 10; + responseCb = arguments[1]; + } + + function feedCb(req, message) { + var pdu = message.pdu; + var varbinds = []; + var i = 0; + + // first walk through and grab non-repeaters + if (pdu.varbinds.length < nonRepeaters) { + req.responseCb(new ResponseInvalidError("Varbind count in " + + "response '" + pdu.varbinds.length + "' is less than " + + "non-repeaters '" + nonRepeaters + "' in request")); + } else { + for (; i < nonRepeaters; i++) { + if (isVarbindError(pdu.varbinds[i])) { + varbinds.push(pdu.varbinds[i]); + } else if (!oidFollowsOid(req.message.pdu.varbinds[i].oid, + pdu.varbinds[i].oid)) { + req.responseCb(new ResponseInvalidError("OID '" + + req.message.pdu.varbinds[i].oid + "' in request at " + + "positiion '" + i + "' does not precede " + + "OID '" + pdu.varbinds[i].oid + "' in response " + + "at position '" + i + "'")); + return; + } else { + varbinds.push(pdu.varbinds[i]); + } + } + } + + var repeaters = req.message.pdu.varbinds.length - nonRepeaters; + + // secondly walk through and grab repeaters + if (pdu.varbinds.length % (repeaters)) { + req.responseCb(new ResponseInvalidError("Varbind count in " + + "response '" + pdu.varbinds.length + "' is not a " + + "multiple of repeaters '" + repeaters + + "' plus non-repeaters '" + nonRepeaters + "' in request")); + } else { + while (i < pdu.varbinds.length) { + for (var j = 0; j < repeaters; j++, i++) { + var reqIndex = nonRepeaters + j; + var respIndex = i; + + if (isVarbindError(pdu.varbinds[respIndex])) { + if (!varbinds[reqIndex]) + varbinds[reqIndex] = []; + varbinds[reqIndex].push(pdu.varbinds[respIndex]); + } else if (!oidFollowsOid( + req.message.pdu.varbinds[reqIndex].oid, + pdu.varbinds[respIndex].oid)) { + req.responseCb(new ResponseInvalidError("OID '" + + req.message.pdu.varbinds[reqIndex].oid + + "' in request at positiion '" + (reqIndex) + + "' does not precede OID '" + + pdu.varbinds[respIndex].oid + + "' in response at position '" + (respIndex) + "'")); + return; + } else { + if (!varbinds[reqIndex]) + varbinds[reqIndex] = []; + varbinds[reqIndex].push(pdu.varbinds[respIndex]); + } + } + } + } + + req.responseCb(null, varbinds); + } + + var pduVarbinds = []; + + for (var i = 0; i < oids.length; i++) { + var varbind = { + oid: oids[i] + }; + pduVarbinds.push(varbind); + } + + var options = { + nonRepeaters: nonRepeaters, + maxRepetitions: maxRepetitions + }; + + this.simpleGet(GetBulkRequestPdu, feedCb, pduVarbinds, responseCb, + options); + + return this; }; Session.prototype.getNext = function (oids, responseCb) { - function feedCb (req, message) { - var pdu = message.pdu; - var varbinds = []; - - if (req.message.pdu.varbinds.length != pdu.varbinds.length) { - req.responseCb (new ResponseInvalidError ("Requested OIDs do not " - + "match response OIDs")); - } else { - for (var i = 0; i < req.message.pdu.varbinds.length; i++) { - if (isVarbindError (pdu.varbinds[i])) { - varbinds.push (pdu.varbinds[i]); - } else if (! oidFollowsOid (req.message.pdu.varbinds[i].oid, - pdu.varbinds[i].oid)) { - req.responseCb (new ResponseInvalidError ("OID '" - + req.message.pdu.varbinds[i].oid + "' in request at " - + "positiion '" + i + "' does not precede " - + "OID '" + pdu.varbinds[i].oid + "' in response " - + "at position '" + i + "'")); - return; - } else { - varbinds.push (pdu.varbinds[i]); - } - } - - req.responseCb (null, varbinds); - } - } - - var pduVarbinds = []; - - for (var i = 0; i < oids.length; i++) { - var varbind = { - oid: oids[i] - }; - pduVarbinds.push (varbind); - } - - this.simpleGet (GetNextRequestPdu, feedCb, pduVarbinds, responseCb); - - return this; + function feedCb(req, message) { + var pdu = message.pdu; + var varbinds = []; + + if (req.message.pdu.varbinds.length != pdu.varbinds.length) { + req.responseCb(new ResponseInvalidError("Requested OIDs do not " + + "match response OIDs")); + } else { + for (var i = 0; i < req.message.pdu.varbinds.length; i++) { + if (isVarbindError(pdu.varbinds[i])) { + varbinds.push(pdu.varbinds[i]); + } else if (!oidFollowsOid(req.message.pdu.varbinds[i].oid, + pdu.varbinds[i].oid)) { + req.responseCb(new ResponseInvalidError("OID '" + + req.message.pdu.varbinds[i].oid + "' in request at " + + "positiion '" + i + "' does not precede " + + "OID '" + pdu.varbinds[i].oid + "' in response " + + "at position '" + i + "'")); + return; + } else { + varbinds.push(pdu.varbinds[i]); + } + } + + req.responseCb(null, varbinds); + } + } + + var pduVarbinds = []; + + for (var i = 0; i < oids.length; i++) { + var varbind = { + oid: oids[i] + }; + pduVarbinds.push(varbind); + } + + this.simpleGet(GetNextRequestPdu, feedCb, pduVarbinds, responseCb); + + return this; }; Session.prototype.inform = function () { - var typeOrOid = arguments[0]; - var varbinds, options = {}, responseCb; - - /** - ** Support the following signatures: - ** - ** typeOrOid, varbinds, options, callback - ** typeOrOid, varbinds, callback - ** typeOrOid, options, callback - ** typeOrOid, callback - **/ - if (arguments.length >= 4) { - varbinds = arguments[1]; - options = arguments[2]; - responseCb = arguments[3]; - } else if (arguments.length >= 3) { - if (arguments[1].constructor != Array) { - varbinds = []; - options = arguments[1]; - responseCb = arguments[2]; - } else { - varbinds = arguments[1]; - responseCb = arguments[2]; - } - } else { - varbinds = []; - responseCb = arguments[1]; - } - - function feedCb (req, message) { - var pdu = message.pdu; - var varbinds = []; - - if (req.message.pdu.varbinds.length != pdu.varbinds.length) { - req.responseCb (new ResponseInvalidError ("Inform OIDs do not " - + "match response OIDs")); - } else { - for (var i = 0; i < req.message.pdu.varbinds.length; i++) { - if (req.message.pdu.varbinds[i].oid != pdu.varbinds[i].oid) { - req.responseCb (new ResponseInvalidError ("OID '" - + req.message.pdu.varbinds[i].oid - + "' in inform at positiion '" + i + "' does not " - + "match OID '" + pdu.varbinds[i].oid + "' in response " - + "at position '" + i + "'")); - return; - } else { - varbinds.push (pdu.varbinds[i]); - } - } - - req.responseCb (null, varbinds); - } - } - - if (typeof typeOrOid != "string") - typeOrOid = "1.3.6.1.6.3.1.1.5." + (typeOrOid + 1); - - var pduVarbinds = [ - { - oid: "1.3.6.1.2.1.1.3.0", - type: ObjectType.TimeTicks, - value: options.upTime || Math.floor (process.uptime () * 100) - }, - { - oid: "1.3.6.1.6.3.1.1.4.1.0", - type: ObjectType.OID, - value: typeOrOid - } - ]; - - for (var i = 0; i < varbinds.length; i++) { - var varbind = { - oid: varbinds[i].oid, - type: varbinds[i].type, - value: varbinds[i].value - }; - pduVarbinds.push (varbind); - } - - options.port = this.trapPort; - - this.simpleGet (InformRequestPdu, feedCb, pduVarbinds, responseCb, options); - - return this; + var typeOrOid = arguments[0]; + var varbinds, options = {}, responseCb; + + /** + ** Support the following signatures: + ** + ** typeOrOid, varbinds, options, callback + ** typeOrOid, varbinds, callback + ** typeOrOid, options, callback + ** typeOrOid, callback + **/ + if (arguments.length >= 4) { + varbinds = arguments[1]; + options = arguments[2]; + responseCb = arguments[3]; + } else if (arguments.length >= 3) { + if (arguments[1].constructor != Array) { + varbinds = []; + options = arguments[1]; + responseCb = arguments[2]; + } else { + varbinds = arguments[1]; + responseCb = arguments[2]; + } + } else { + varbinds = []; + responseCb = arguments[1]; + } + + if (this.version == Version1) { + responseCb(new RequestInvalidError("Inform not allowed for SNMPv1")); + return; + } + + function feedCb(req, message) { + var pdu = message.pdu; + var varbinds = []; + + if (req.message.pdu.varbinds.length != pdu.varbinds.length) { + req.responseCb(new ResponseInvalidError("Inform OIDs do not " + + "match response OIDs")); + } else { + for (var i = 0; i < req.message.pdu.varbinds.length; i++) { + if (req.message.pdu.varbinds[i].oid != pdu.varbinds[i].oid) { + req.responseCb(new ResponseInvalidError("OID '" + + req.message.pdu.varbinds[i].oid + + "' in inform at positiion '" + i + "' does not " + + "match OID '" + pdu.varbinds[i].oid + "' in response " + + "at position '" + i + "'")); + return; + } else { + varbinds.push(pdu.varbinds[i]); + } + } + + req.responseCb(null, varbinds); + } + } + + if (typeof typeOrOid != "string") + typeOrOid = "1.3.6.1.6.3.1.1.5." + (typeOrOid + 1); + + var pduVarbinds = [ + { + oid: "1.3.6.1.2.1.1.3.0", + type: ObjectType.TimeTicks, + value: options.upTime || Math.floor(process.uptime() * 100) + }, + { + oid: "1.3.6.1.6.3.1.1.4.1.0", + type: ObjectType.OID, + value: typeOrOid + } + ]; + + for (var i = 0; i < varbinds.length; i++) { + var varbind = { + oid: varbinds[i].oid, + type: varbinds[i].type, + value: varbinds[i].value + }; + pduVarbinds.push(varbind); + } + + options.port = this.trapPort; + + this.simpleGet(InformRequestPdu, feedCb, pduVarbinds, responseCb, options); + + return this; }; Session.prototype.onClose = function () { - this.cancelRequests (new Error ("Socket forcibly closed")); - this.emit ("close"); + this.cancelRequests(new Error("Socket forcibly closed")); + this.emit("close"); }; Session.prototype.onError = function (error) { - this.emit (error); -}; - -Session.prototype.onMsg = function (buffer, remote) { - try { - var message = new ResponseMessage (buffer); - - var req = this.unregisterRequest (message.pdu.id); - if (! req) - return; - - try { - if (message.version != req.message.version) { - req.responseCb (new ResponseInvalidError ("Version in request '" - + req.message.version + "' does not match version in " - + "response '" + message.version)); - } else if (message.community != req.message.community) { - req.responseCb (new ResponseInvalidError ("Community '" - + req.message.community + "' in request does not match " - + "community '" + message.community + "' in response")); - } else if (message.pdu.type == PduType.GetResponse) { - req.onResponse (req, message); - } else { - req.responseCb (new ResponseInvalidError ("Unknown PDU type '" - + message.pdu.type + "' in response")); - } - } catch (error) { - req.responseCb (error); - } - } catch (error) { - this.emit("error", error); - } + this.emit(error); +}; + +Session.prototype.onMsg = function (buffer) { + try { + var message = Message.createFromBuffer(buffer); + + var req = this.unregisterRequest(message.getReqId()); + if (!req) + return; + + if (!message.processIncomingSecurity(this.user, req.responseCb)) + return; + + try { + if (message.version != req.message.version) { + req.responseCb(new ResponseInvalidError("Version in request '" + + req.message.version + "' does not match version in " + + "response '" + message.version + "'")); + } else if (message.community != req.message.community) { + req.responseCb(new ResponseInvalidError("Community '" + + req.message.community + "' in request does not match " + + "community '" + message.community + "' in response")); + } else if (message.pdu.type == PduType.GetResponse) { + req.onResponse(req, message); + } else if (message.pdu.type == PduType.Report) { + if (!req.originalPdu) { + req.responseCb(new ResponseInvalidError("Unexpected Report PDU")); + return; + } + this.msgSecurityParameters = { + msgAuthoritativeEngineID: message.msgSecurityParameters.msgAuthoritativeEngineID, + msgAuthoritativeEngineBoots: message.msgSecurityParameters.msgAuthoritativeEngineBoots, + msgAuthoritativeEngineTime: message.msgSecurityParameters.msgAuthoritativeEngineTime + }; + req.originalPdu.contextName = this.context; + this.sendV3Req(req.originalPdu, req.feedCb, req.responseCb, req.options, req.port); + } else { + req.responseCb(new ResponseInvalidError("Unknown PDU type '" + + message.pdu.type + "' in response")); + } + } catch (error) { + req.responseCb(error); + } + } catch (error) { + this.emit("error", error); + } }; Session.prototype.onSimpleGetResponse = function (req, message) { - var pdu = message.pdu; - - if (pdu.errorStatus > 0) { - var statusString = ErrorStatus[pdu.errorStatus] - || ErrorStatus.GeneralError; - var statusCode = ErrorStatus[statusString] - || ErrorStatus[ErrorStatus.GeneralError]; - - if (pdu.errorIndex <= 0 || pdu.errorIndex > pdu.varbinds.length) { - req.responseCb (new RequestFailedError (statusString, statusCode)); - } else { - var oid = pdu.varbinds[pdu.errorIndex - 1].oid; - var error = new RequestFailedError (statusString + ": " + oid, - statusCode); - req.responseCb (error); - } - } else { - req.feedCb (req, message); - } + var pdu = message.pdu; + + if (pdu.errorStatus > 0) { + var statusString = ErrorStatus[pdu.errorStatus] + || ErrorStatus.GeneralError; + var statusCode = ErrorStatus[statusString] + || ErrorStatus[ErrorStatus.GeneralError]; + + if (pdu.errorIndex <= 0 || pdu.errorIndex > pdu.varbinds.length) { + req.responseCb(new RequestFailedError(statusString, statusCode)); + } else { + var oid = pdu.varbinds[pdu.errorIndex - 1].oid; + var error = new RequestFailedError(statusString + ": " + oid, + statusCode); + req.responseCb(error); + } + } else { + req.feedCb(req, message); + } }; Session.prototype.registerRequest = function (req) { - if (! this.reqs[req.id]) { - this.reqs[req.id] = req; - if (this.reqCount <= 0) - this.dgram.ref(); - this.reqCount++; - } - var me = this; - req.timer = setTimeout (function () { - if (req.retries-- > 0) { - me.send (req); - } else { - me.unregisterRequest (req.id); - req.responseCb (new RequestTimedOutError ( - "Request timed out")); - } - }, req.timeout); + if (!this.reqs[req.getId()]) { + this.reqs[req.getId()] = req; + if (this.reqCount <= 0) + this.dgram.ref(); + this.reqCount++; + } + var me = this; + req.timer = setTimeout(function () { + if (req.retries-- > 0) { + me.send(req); + } else { + me.unregisterRequest(req.getId()); + req.responseCb(new RequestTimedOutError( + "Request timed out")); + } + }, req.timeout); }; Session.prototype.send = function (req, noWait) { - try { - var me = this; - - var buffer = req.message.toBuffer (); - - this.dgram.send (buffer, 0, buffer.length, req.port, this.target, - function (error, bytes) { - if (error) { - req.responseCb (error); - } else { - if (noWait) { - req.responseCb (null); - } else { - me.registerRequest (req); - } - } - }); - } catch (error) { - req.responseCb (error); - } - - return this; + try { + var me = this; + + var buffer = req.message.toBuffer(); + + this.dgram.send(buffer, 0, buffer.length, req.port, this.target, + function (error, bytes) { + if (error) { + req.responseCb(error); + } else { + if (noWait) { + req.responseCb(null); + } else { + me.registerRequest(req); + } + } + }); + } catch (error) { + req.responseCb(error); + } + + return this; }; Session.prototype.set = function (varbinds, responseCb) { - function feedCb (req, message) { - var pdu = message.pdu; - var varbinds = []; - - if (req.message.pdu.varbinds.length != pdu.varbinds.length) { - req.responseCb (new ResponseInvalidError ("Requested OIDs do not " - + "match response OIDs")); - } else { - for (var i = 0; i < req.message.pdu.varbinds.length; i++) { - if (req.message.pdu.varbinds[i].oid != pdu.varbinds[i].oid) { - req.responseCb (new ResponseInvalidError ("OID '" - + req.message.pdu.varbinds[i].oid - + "' in request at positiion '" + i + "' does not " - + "match OID '" + pdu.varbinds[i].oid + "' in response " - + "at position '" + i + "'")); - return; - } else { - varbinds.push (pdu.varbinds[i]); - } - } - - req.responseCb (null, varbinds); - } - } - - var pduVarbinds = []; - - for (var i = 0; i < varbinds.length; i++) { - var varbind = { - oid: varbinds[i].oid, - type: varbinds[i].type, - value: varbinds[i].value - }; - pduVarbinds.push (varbind); - } - - this.simpleGet (SetRequestPdu, feedCb, pduVarbinds, responseCb); - - return this; + function feedCb(req, message) { + var pdu = message.pdu; + var varbinds = []; + + if (req.message.pdu.varbinds.length != pdu.varbinds.length) { + req.responseCb(new ResponseInvalidError("Requested OIDs do not " + + "match response OIDs")); + } else { + for (var i = 0; i < req.message.pdu.varbinds.length; i++) { + if (req.message.pdu.varbinds[i].oid != pdu.varbinds[i].oid) { + req.responseCb(new ResponseInvalidError("OID '" + + req.message.pdu.varbinds[i].oid + + "' in request at positiion '" + i + "' does not " + + "match OID '" + pdu.varbinds[i].oid + "' in response " + + "at position '" + i + "'")); + return; + } else { + varbinds.push(pdu.varbinds[i]); + } + } + + req.responseCb(null, varbinds); + } + } + + var pduVarbinds = []; + + for (var i = 0; i < varbinds.length; i++) { + var varbind = { + oid: varbinds[i].oid, + type: varbinds[i].type, + value: varbinds[i].value + }; + pduVarbinds.push(varbind); + } + + this.simpleGet(SetRequestPdu, feedCb, pduVarbinds, responseCb); + + return this; }; Session.prototype.simpleGet = function (pduClass, feedCb, varbinds, - responseCb, options) { - var req = {}; - - try { - var id = _generateId (this.idBitsSize); - var pdu = new pduClass (id, varbinds, options); - var message = new RequestMessage (this.version, this.community, pdu); - - req = { - id: id, - message: message, - responseCb: responseCb, - retries: this.retries, - timeout: this.timeout, - onResponse: this.onSimpleGetResponse, - feedCb: feedCb, - port: (options && options.port) ? options.port : this.port - }; - - this.send (req); - } catch (error) { - if (req.responseCb) - req.responseCb (error); - } -}; - -function subtreeCb (req, varbinds) { - var done = 0; - - for (var i = varbinds.length; i > 0; i--) { - if (! oidInSubtree (req.baseOid, varbinds[i - 1].oid)) { - done = 1; - varbinds.pop (); - } - } - - if (varbinds.length > 0) - req.feedCb (varbinds); - - if (done) - return true; + responseCb, options) { + try { + var id = _generateId(this.idBitsSize); + var pdu = SimplePdu.createFromVariables(pduClass, id, varbinds, options); + var message; + var req; + + if (this.version == Version3) { + if (this.msgSecurityParameters) { + this.sendV3Req(pdu, feedCb, responseCb, options, this.port); + } else { + // SNMPv3 discovery + var discoveryPdu = createDiscoveryPdu(this.context); + var discoveryMessage = Message.createDiscoveryV3(discoveryPdu); + var discoveryReq = new Req(this, discoveryMessage, feedCb, responseCb, options); + discoveryReq.originalPdu = pdu; + this.send(discoveryReq); + } + } else { + message = Message.createCommunity(this.version, this.community, pdu); + req = new Req(this, message, feedCb, responseCb, options); + this.send(req); + } + } catch (error) { + if (responseCb) + responseCb(error); + } } -Session.prototype.subtree = function () { - var me = this; - var oid = arguments[0]; - var maxRepetitions, feedCb, doneCb; - - if (arguments.length < 4) { - maxRepetitions = 20; - feedCb = arguments[1]; - doneCb = arguments[2]; - } else { - maxRepetitions = arguments[1]; - feedCb = arguments[2]; - doneCb = arguments[3]; - } - - var req = { - feedCb: feedCb, - doneCb: doneCb, - maxRepetitions: maxRepetitions, - baseOid: oid - }; - - this.walk (oid, maxRepetitions, subtreeCb.bind (me, req), doneCb); - - return this; -}; - -function tableColumnsResponseCb (req, error) { - if (error) { - req.responseCb (error); - } else if (req.error) { - req.responseCb (req.error); - } else { - if (req.columns.length > 0) { - var column = req.columns.pop (); - var me = this; - this.subtree (req.rowOid + column, req.maxRepetitions, - tableColumnsFeedCb.bind (me, req), - tableColumnsResponseCb.bind (me, req)); - } else { - req.responseCb (null, req.table); - } - } +function subtreeCb(req, varbinds) { + var done = 0; + + for (var i = varbinds.length; i > 0; i--) { + if (!oidInSubtree(req.baseOid, varbinds[i - 1].oid)) { + done = 1; + varbinds.pop(); + } + } + + if (varbinds.length > 0) + req.feedCb(varbinds); + + if (done) + return true; +} + +Session.prototype.subtree = function () { + var me = this; + var oid = arguments[0]; + var maxRepetitions, feedCb, doneCb; + + if (arguments.length < 4) { + maxRepetitions = 20; + feedCb = arguments[1]; + doneCb = arguments[2]; + } else { + maxRepetitions = arguments[1]; + feedCb = arguments[2]; + doneCb = arguments[3]; + } + + var req = { + feedCb: feedCb, + doneCb: doneCb, + maxRepetitions: maxRepetitions, + baseOid: oid + }; + + this.walk(oid, maxRepetitions, subtreeCb.bind(me, req), doneCb); + + return this; +}; + +function tableColumnsResponseCb(req, error) { + if (error) { + req.responseCb(error); + } else if (req.error) { + req.responseCb(req.error); + } else { + if (req.columns.length > 0) { + var column = req.columns.pop(); + var me = this; + this.subtree(req.rowOid + column, req.maxRepetitions, + tableColumnsFeedCb.bind(me, req), + tableColumnsResponseCb.bind(me, req)); + } else { + req.responseCb(null, req.table); + } + } } -function tableColumnsFeedCb (req, varbinds) { - for (var i = 0; i < varbinds.length; i++) { - if (isVarbindError (varbinds[i])) { - req.error = new RequestFailedError (varbindError (varbind[i])); - return true; - } - - var oid = varbinds[i].oid.replace (req.rowOid, ""); - if (oid && oid != varbinds[i].oid) { - var match = oid.match (/^(\d+)\.(.+)$/); - if (match && match[1] > 0) { - if (! req.table[match[2]]) - req.table[match[2]] = {}; - req.table[match[2]][match[1]] = varbinds[i].value; - } - } - } +function tableColumnsFeedCb(req, varbinds) { + for (var i = 0; i < varbinds.length; i++) { + if (isVarbindError(varbinds[i])) { + req.error = new RequestFailedError(varbindError(varbind[i])); + return true; + } + + var oid = varbinds[i].oid.replace(req.rowOid, ""); + if (oid && oid != varbinds[i].oid) { + var match = oid.match(/^(\d+)\.(.+)$/); + if (match && match[1] > 0) { + if (!req.table[match[2]]) + req.table[match[2]] = {}; + req.table[match[2]][match[1]] = varbinds[i].value; + } + } + } } Session.prototype.tableColumns = function () { - var me = this; - - var oid = arguments[0]; - var columns = arguments[1]; - var maxRepetitions, responseCb; - - if (arguments.length < 4) { - responseCb = arguments[2]; - maxRepetitions = 20; - } else { - maxRepetitions = arguments[2]; - responseCb = arguments[3]; - } - - var req = { - responseCb: responseCb, - maxRepetitions: maxRepetitions, - baseOid: oid, - rowOid: oid + ".1.", - columns: columns.slice(0), - table: {} - }; - - if (req.columns.length > 0) { - var column = req.columns.pop (); - this.subtree (req.rowOid + column, maxRepetitions, - tableColumnsFeedCb.bind (me, req), - tableColumnsResponseCb.bind (me, req)); - } - - return this; -}; - -function tableResponseCb (req, error) { - if (error) - req.responseCb (error); - else if (req.error) - req.responseCb (req.error); - else - req.responseCb (null, req.table); + var me = this; + + var oid = arguments[0]; + var columns = arguments[1]; + var maxRepetitions, responseCb; + + if (arguments.length < 4) { + responseCb = arguments[2]; + maxRepetitions = 20; + } else { + maxRepetitions = arguments[2]; + responseCb = arguments[3]; + } + + var req = { + responseCb: responseCb, + maxRepetitions: maxRepetitions, + baseOid: oid, + rowOid: oid + ".1.", + columns: columns.slice(0), + table: {} + }; + + if (req.columns.length > 0) { + var column = req.columns.pop(); + this.subtree(req.rowOid + column, maxRepetitions, + tableColumnsFeedCb.bind(me, req), + tableColumnsResponseCb.bind(me, req)); + } + + return this; +}; + +function tableResponseCb(req, error) { + if (error) + req.responseCb(error); + else if (req.error) + req.responseCb(req.error); + else + req.responseCb(null, req.table); } -function tableFeedCb (req, varbinds) { - for (var i = 0; i < varbinds.length; i++) { - if (isVarbindError (varbinds[i])) { - req.error = new RequestFailedError (varbindError (varbind[i])); - return true; - } - - var oid = varbinds[i].oid.replace (req.rowOid, ""); - if (oid && oid != varbinds[i].oid) { - var match = oid.match (/^(\d+)\.(.+)$/); - if (match && match[1] > 0) { - if (! req.table[match[2]]) - req.table[match[2]] = {}; - req.table[match[2]][match[1]] = varbinds[i].value; - } - } - } +function tableFeedCb(req, varbinds) { + for (var i = 0; i < varbinds.length; i++) { + if (isVarbindError(varbinds[i])) { + req.error = new RequestFailedError(varbindError(varbind[i])); + return true; + } + + var oid = varbinds[i].oid.replace(req.rowOid, ""); + if (oid && oid != varbinds[i].oid) { + var match = oid.match(/^(\d+)\.(.+)$/); + if (match && match[1] > 0) { + if (!req.table[match[2]]) + req.table[match[2]] = {}; + req.table[match[2]][match[1]] = varbinds[i].value; + } + } + } } Session.prototype.table = function () { - var me = this; + var me = this; + + var oid = arguments[0]; + var maxRepetitions, responseCb; + + if (arguments.length < 3) { + responseCb = arguments[1]; + maxRepetitions = 20; + } else { + maxRepetitions = arguments[1]; + responseCb = arguments[2]; + } + + var req = { + responseCb: responseCb, + maxRepetitions: maxRepetitions, + baseOid: oid, + rowOid: oid + ".1.", + table: {} + }; + + this.subtree(oid, maxRepetitions, tableFeedCb.bind(me, req), + tableResponseCb.bind(me, req)); + + return this; +}; - var oid = arguments[0]; - var maxRepetitions, responseCb; +Session.prototype.trap = function () { + var req = {}; + + try { + var typeOrOid = arguments[0]; + var varbinds, options = {}, responseCb; + var message; + + /** + ** Support the following signatures: + ** + ** typeOrOid, varbinds, options, callback + ** typeOrOid, varbinds, agentAddr, callback + ** typeOrOid, varbinds, callback + ** typeOrOid, agentAddr, callback + ** typeOrOid, options, callback + ** typeOrOid, callback + **/ + if (arguments.length >= 4) { + varbinds = arguments[1]; + if (typeof arguments[2] == "string") { + options.agentAddr = arguments[2]; + } else if (arguments[2].constructor != Array) { + options = arguments[2]; + } + responseCb = arguments[3]; + } else if (arguments.length >= 3) { + if (typeof arguments[1] == "string") { + varbinds = []; + options.agentAddr = arguments[1]; + } else if (arguments[1].constructor != Array) { + varbinds = []; + options = arguments[1]; + } else { + varbinds = arguments[1]; + agentAddr = null; + } + responseCb = arguments[2]; + } else { + varbinds = []; + responseCb = arguments[1]; + } + + var pdu, pduVarbinds = []; + + for (var i = 0; i < varbinds.length; i++) { + var varbind = { + oid: varbinds[i].oid, + type: varbinds[i].type, + value: varbinds[i].value + }; + pduVarbinds.push(varbind); + } + + var id = _generateId(this.idBitsSize); + + if (this.version == Version2c || this.version == Version3) { + if (typeof typeOrOid != "string") + typeOrOid = "1.3.6.1.6.3.1.1.5." + (typeOrOid + 1); + + pduVarbinds.unshift( + { + oid: "1.3.6.1.2.1.1.3.0", + type: ObjectType.TimeTicks, + value: options.upTime || Math.floor(process.uptime() * 100) + }, + { + oid: "1.3.6.1.6.3.1.1.4.1.0", + type: ObjectType.OID, + value: typeOrOid + } + ); + + pdu = TrapV2Pdu.createFromVariables(id, pduVarbinds, options); + } else { + pdu = TrapPdu.createFromVariables(typeOrOid, pduVarbinds, options); + } + + if (this.version == Version3) { + var msgSecurityParameters = { + msgAuthoritativeEngineID: this.user.engineID, + msgAuthoritativeEngineBoots: 0, + msgAuthoritativeEngineTime: 0 + }; + message = Message.createRequestV3(this.user, msgSecurityParameters, pdu); + } else { + message = Message.createCommunity(this.version, this.community, pdu); + } + + req = { + id: id, + message: message, + responseCb: responseCb, + port: this.trapPort + }; + + this.send(req, true); + } catch (error) { + if (req.responseCb) + req.responseCb(error); + } + + return this; +}; - if (arguments.length < 3) { - responseCb = arguments[1]; - maxRepetitions = 20; - } else { - maxRepetitions = arguments[1]; - responseCb = arguments[2]; - } +Session.prototype.unregisterRequest = function (id) { + var req = this.reqs[id]; + if (req) { + delete this.reqs[id]; + clearTimeout(req.timer); + delete req.timer; + this.reqCount--; + if (this.reqCount <= 0) + this.dgram.unref(); + return req; + } else { + return null; + } +}; - var req = { - responseCb: responseCb, - maxRepetitions: maxRepetitions, - baseOid: oid, - rowOid: oid + ".1.", - table: {} - }; +function walkCb(req, error, varbinds) { + var done = 0; + var oid; + + if (error) { + if (error instanceof RequestFailedError) { + if (error.status != ErrorStatus.NoSuchName) { + req.doneCb(error); + return; + } else { + // signal the version 1 walk code below that it should stop + done = 1; + } + } else { + req.doneCb(error); + return; + } + } + + if (this.version == Version2c || this.version == Version3) { + for (var i = varbinds[0].length; i > 0; i--) { + if (varbinds[0][i - 1].type == ObjectType.EndOfMibView) { + varbinds[0].pop(); + done = 1; + } + } + if (req.feedCb(varbinds[0])) + done = 1; + if (!done) + oid = varbinds[0][varbinds[0].length - 1].oid; + } else { + if (!done) { + if (req.feedCb(varbinds)) { + done = 1; + } else { + oid = varbinds[0].oid; + } + } + } + + if (done) + req.doneCb(null); + else + this.walk(oid, req.maxRepetitions, req.feedCb, req.doneCb, + req.baseOid); +} - this.subtree (oid, maxRepetitions, tableFeedCb.bind (me, req), - tableResponseCb.bind (me, req)); +Session.prototype.walk = function () { + var me = this; + var oid = arguments[0]; + var maxRepetitions, feedCb, doneCb, baseOid; + + if (arguments.length < 4) { + maxRepetitions = 20; + feedCb = arguments[1]; + doneCb = arguments[2]; + } else { + maxRepetitions = arguments[1]; + feedCb = arguments[2]; + doneCb = arguments[3]; + } + + var req = { + maxRepetitions: maxRepetitions, + feedCb: feedCb, + doneCb: doneCb + }; + + if (this.version == Version2c || this.version == Version3) + this.getBulk([oid], 0, maxRepetitions, + walkCb.bind(me, req)); + else + this.getNext([oid], walkCb.bind(me, req)); + + return this; +}; - return this; +Session.prototype.sendV3Req = function (pdu, feedCb, responseCb, options, port) { + var message = Message.createRequestV3(this.user, this.msgSecurityParameters, pdu); + var reqOptions = options || {}; + var req = new Req(this, message, feedCb, responseCb, reqOptions); + req.port = port; + this.send(req); }; -Session.prototype.trap = function () { - var req = {}; - - try { - var typeOrOid = arguments[0]; - var varbinds, options = {}, responseCb; - - /** - ** Support the following signatures: - ** - ** typeOrOid, varbinds, options, callback - ** typeOrOid, varbinds, agentAddr, callback - ** typeOrOid, varbinds, callback - ** typeOrOid, agentAddr, callback - ** typeOrOid, options, callback - ** typeOrOid, callback - **/ - if (arguments.length >= 4) { - varbinds = arguments[1]; - if (typeof arguments[2] == "string") { - options.agentAddr = arguments[2]; - } else if (arguments[2].constructor != Array) { - options = arguments[2]; - } - responseCb = arguments[3]; - } else if (arguments.length >= 3) { - if (typeof arguments[1] == "string") { - varbinds = []; - options.agentAddr = arguments[1]; - } else if (arguments[1].constructor != Array) { - varbinds = []; - options = arguments[1]; - } else { - varbinds = arguments[1]; - agentAddr = null; - } - responseCb = arguments[2]; - } else { - varbinds = []; - responseCb = arguments[1]; - } - - var pdu, pduVarbinds = []; - - for (var i = 0; i < varbinds.length; i++) { - var varbind = { - oid: varbinds[i].oid, - type: varbinds[i].type, - value: varbinds[i].value - }; - pduVarbinds.push (varbind); - } - - var id = _generateId (this.idBitsSize); - - if (this.version == Version2c) { - if (typeof typeOrOid != "string") - typeOrOid = "1.3.6.1.6.3.1.1.5." + (typeOrOid + 1); - - pduVarbinds.unshift ( - { - oid: "1.3.6.1.2.1.1.3.0", - type: ObjectType.TimeTicks, - value: options.upTime || Math.floor (process.uptime () * 100) - }, - { - oid: "1.3.6.1.6.3.1.1.4.1.0", - type: ObjectType.OID, - value: typeOrOid - } - ); - - pdu = new TrapV2Pdu (id, pduVarbinds, options); - } else { - pdu = new TrapPdu (typeOrOid, pduVarbinds, options); - } - - var message = new RequestMessage (this.version, this.community, pdu); - - req = { - id: id, - message: message, - responseCb: responseCb, - port: this.trapPort - }; - - this.send (req, true); - } catch (error) { - if (req.responseCb) - req.responseCb (error); - } - - return this; +var Engine = function (engineID, engineBoots, engineTime) { + if (engineID) { + this.engineID = Buffer.from(engineID, 'hex'); + } else { + this.generateEngineID(); + } + this.engineBoots = 0; + this.engineTime = 10; }; -Session.prototype.unregisterRequest = function (id) { - var req = this.reqs[id]; - if (req) { - delete this.reqs[id]; - clearTimeout (req.timer); - delete req.timer; - this.reqCount--; - if (this.reqCount <= 0) - this.dgram.unref(); - return req; - } else { - return null; - } -}; - -function walkCb (req, error, varbinds) { - var done = 0; - var oid; - - if (error) { - if (error instanceof RequestFailedError) { - if (error.status != ErrorStatus.NoSuchName) { - req.doneCb (error); - return; - } else { - // signal the version 1 walk code below that it should stop - done = 1; - } - } else { - req.doneCb (error); - return; - } - } - - if (this.version == Version2c) { - for (var i = varbinds[0].length; i > 0; i--) { - if (varbinds[0][i - 1].type == ObjectType.EndOfMibView) { - varbinds[0].pop (); - done = 1; - } - } - if (req.feedCb (varbinds[0])) - done = 1; - if (! done) - oid = varbinds[0][varbinds[0].length - 1].oid; - } else { - if (! done) { - if (req.feedCb (varbinds)) { - done = 1; - } else { - oid = varbinds[0].oid; - } - } - } - - if (done) - req.doneCb (null); - else - this.walk (oid, req.maxRepetitions, req.feedCb, req.doneCb, - req.baseOid); +Engine.prototype.generateEngineID = function () { + // generate a 17-byte engine ID in the following format: + // 0x80 + 0x00B983 (enterprise OID) | 0x80 (enterprise-specific format) | 12 bytes of random + this.engineID = Buffer.alloc(17); + this.engineID.fill('8000B98380', 'hex', 0, 5); + this.engineID.fill(crypto.randomBytes(12), 5, 17, 'hex'); +} + +var Listener = function (options, receiver) { + this.receiver = receiver; + this.callback = receiver.onMsg; + this.family = options.transport || 'udp4'; + this.port = options.port || 161; + this.disableAuthorization = options.disableAuthorization || false; +}; + +Listener.prototype.startListening = function (receiver) { + var me = this; + this.dgram = dgram.createSocket(this.family); + this.dgram.bind(this.port); + this.dgram.on("message", me.callback.bind(me.receiver)); +}; + +Listener.prototype.send = function (message, rinfo) { + var me = this; + + var buffer = message.toBuffer(); + + this.dgram.send(buffer, 0, buffer.length, rinfo.port, rinfo.address, + function (error, bytes) { + if (error) { + // me.callback (error); + console.error("Error sending: " + error.message); + } else { + // debug ("Listener sent response message"); + } + }); +}; + +Listener.formatCallbackData = function (pdu, rinfo) { + if (pdu.contextEngineID) { + pdu.contextEngineID = pdu.contextEngineID.toString('hex'); + } + delete pdu.nonRepeaters; + delete pdu.maxRepetitions; + return { + pdu: pdu, + rinfo: rinfo + }; +}; + +Listener.processIncoming = function (buffer, authorizer, callback) { + var message = Message.createFromBuffer(buffer); + var community; + + // Authorization + if (message.version == Version3) { + message.user = authorizer.users.filter(localUser => localUser.name == + message.msgSecurityParameters.msgUserName)[0]; + message.disableAuthentication = authorizer.disableAuthorization; + if (!message.user) { + if (message.msgSecurityParameters.msgUserName != "" && !authorizer.disableAuthorization) { + callback(new RequestFailedError("Local user not found for message with user " + + message.msgSecurityParameters.msgUserName)); + return; + } else if (message.hasAuthentication()) { + callback(new RequestFailedError("Local user not found and message requires authentication with user " + + message.msgSecurityParameters.msgUserName)); + return; + } else { + message.user = { + name: "", + level: SecurityLevel.noAuthNoPriv + }; + } + } + if (!message.processIncomingSecurity(message.user, callback)) { + return; + } + } else { + community = authorizer.communities.filter(localCommunity => localCommunity == message.community)[0]; + if (!community && !authorizer.disableAuthorization) { + callback(new RequestFailedError("Local community not found for message with community " + message.community)); + return; + } + } + + return message; +}; + +var Authorizer = function () { + this.communities = []; + this.users = []; +} + +Authorizer.prototype.addCommunity = function (community) { + if (this.getCommunity(community)) { + return; + } else { + this.communities.push(community); + } +}; + +Authorizer.prototype.getCommunity = function (community) { + return this.communities.filter(localCommunity => localCommunity == community)[0] || null; +}; + +Authorizer.prototype.getCommunities = function () { + return this.communities; +}; + +Authorizer.prototype.deleteCommunity = function (community) { + var index = this.communities.indexOf(community); + if (index > -1) { + this.communities.splice(index, 1); + } +}; + +Authorizer.prototype.addUser = function (user) { + if (this.getUser(user.name)) { + this.deleteUser(user.name); + } + this.users.push(user); +}; + +Authorizer.prototype.getUser = function (userName) { + return this.users.filter(localUser => localUser.name == userName)[0] || null; +}; + +Authorizer.prototype.getUsers = function () { + return this.users; +}; + +Authorizer.prototype.deleteUser = function (userName) { + var index = this.users.findIndex(localUser => localUser.name == userName); + if (index > -1) { + this.users.splice(index, 1); + } +}; + + +/***************************************************************************** + ** Receiver class definition + **/ + +var Receiver = function (options, callback) { + DEBUG = options.debug; + this.listener = new Listener(options, this); + this.authorizer = new Authorizer(); + this.engine = new Engine(options.engineID); + + this.engineBoots = 0; + this.engineTime = 10; + this.disableAuthorization = false; + + this.callback = callback; + this.family = options.transport || 'udp4'; + this.port = options.port || 162; + options.port = this.port; + this.disableAuthorization = options.disableAuthorization || false; + this.context = (options && options.context) ? options.context : ""; + this.listener = new Listener(options, this); +}; + +Receiver.prototype.addCommunity = function (community) { + this.authorizer.addCommunity(community); +}; + +Receiver.prototype.getCommunity = function (community) { + return this.authorizer.getCommunity(community); +}; + +Receiver.prototype.getCommunities = function () { + return this.authorizer.getCommunities(); +}; + +Receiver.prototype.deleteCommunity = function (community) { + this.authorizer.deleteCommunities(community); +}; + +Receiver.prototype.addUser = function (user) { + this.authorizer.addUser(user); +}; + +Receiver.prototype.getUser = function (userName) { + return this.authorizer.getUser(userName); +}; + +Receiver.prototype.getUsers = function () { + return this.authorizer.getUsers(); +}; + +Receiver.prototype.deleteUser = function (userName) { + this.authorizer.deleteUser(userName); +}; + +Receiver.prototype.onMsg = function (buffer, rinfo) { + var message = Listener.processIncoming(buffer, this.authorizer, this.callback); + var reportMessage; + + if (!message) { + return; + } + + // The only GetRequest PDUs supported are those used for SNMPv3 discovery + if (message.pdu.type == PduType.GetRequest) { + if (message.version != Version3) { + this.callback(new RequestInvalidError("Only SNMPv3 discovery GetRequests are supported")); + return; + } else if (message.hasAuthentication()) { + this.callback(new RequestInvalidError("Only discovery (noAuthNoPriv) GetRequests are supported but this message has authentication")); + return; + } else if (!message.isReportable()) { + this.callback(new RequestInvalidError("Only discovery GetRequests are supported and this message does not have the reportable flag set")); + return; + } + var reportMessage = message.createReportResponseMessage(this.engine, this.context); + this.listener.send(reportMessage, rinfo); + return; + } + ; + + // Inform/trap processing + debug(JSON.stringify(message.pdu, null, 2)); + if (message.pdu.type == PduType.Trap || message.pdu.type == PduType.TrapV2) { + this.callback(null, this.formatCallbackData(message.pdu, rinfo)); + } else if (message.pdu.type == PduType.InformRequest) { + message.pdu.type = PduType.GetResponse; + message.buffer = null; + message.setReportable(false); + this.listener.send(message, rinfo); + message.pdu.type = PduType.InformRequest; + this.callback(null, this.formatCallbackData(message.pdu, rinfo)); + } else { + this.callback(new RequestInvalidError("Unexpected PDU type " + message.pdu.type + " (" + PduType[message.pdu.type] + ")")); + } } -Session.prototype.walk = function () { - var me = this; - var oid = arguments[0]; - var maxRepetitions, feedCb, doneCb, baseOid; +Receiver.prototype.formatCallbackData = function (pdu, rinfo) { + if (pdu.contextEngineID) { + pdu.contextEngineID = pdu.contextEngineID.toString('hex'); + } + delete pdu.nonRepeaters; + delete pdu.maxRepetitions; + return { + pdu: pdu, + rinfo: rinfo + }; +}; + +Receiver.prototype.close = function () { + this.listener.close(); +}; + +Receiver.create = function (options, callback) { + var receiver = new Receiver(options, callback); + receiver.listener.startListening(); + return receiver; +}; + +var MibNode = function (address, parent) { + this.address = address; + this.oid = this.address.join('.'); + ; + this.parent = parent; + this.children = {}; +}; + +MibNode.prototype.child = function (index) { + return this.children[index]; +}; + +MibNode.prototype.listChildren = function (lowest) { + var sorted = []; + + lowest = lowest || 0; + + this.children.forEach(function (c, i) { + if (i >= lowest) + sorted.push(i); + }); + + sorted.sort(function (a, b) { + return (a - b); + }); + + return sorted; +}; + +MibNode.prototype.isDescendant = function (address) { + return MibNode.oidIsDescended(this.address, address); +}; + +MibNode.prototype.isAncestor = function (address) { + return MibNode.oidIsDescended(address, this.address); +}; + +MibNode.prototype.getAncestorProvider = function () { + if (this.provider) { + return this; + } else if (!this.parent) { + return null; + } else { + return this.parent.getAncestorProvider(); + } +}; + +MibNode.prototype.getInstanceNodeForTableRow = function () { + var childCount = Object.keys(this.children).length; + if (childCount == 0) { + if (this.value) { + return this; + } else { + return null; + } + } else if (childCount == 1) { + return this.children[0].getInstanceNodeForTableRow(); + } else if (childCount > 1) { + return null; + } +}; + +MibNode.prototype.getInstanceNodeForTableRowIndex = function (index) { + var childCount = Object.keys(this.children).length; + if (childCount == 0) { + if (this.value) { + return this; + } else { + // not found + return null; + } + } else { + if (index.length == 0) { + return this.getInstanceNodeForTableRow(); + } else { + var nextChildIndexPart = index[0]; + if (!nextChildIndexPart) { + return null; + } + remainingIndex = index.slice(1); + return this.children[nextChildIndexPart].getInstanceNodeForTableRowIndex(remainingIndex); + } + } +}; + +MibNode.prototype.getNextInstanceNode = function () { + + node = this; + if (this.value) { + // Need upwards traversal first + node = this; + while (node) { + siblingIndex = node.address.slice(-1)[0]; + node = node.parent; + if (!node) { + // end of MIB + return null; + } else { + childrenAddresses = Object.keys(node.children).sort((a, b) => a - b); + siblingPosition = childrenAddresses.indexOf(siblingIndex.toString()); + if (siblingPosition + 1 < childrenAddresses.length) { + node = node.children[childrenAddresses[siblingPosition + 1]]; + break; + } + } + } + } + // Descent + while (node) { + if (node.value) { + return node; + } + childrenAddresses = Object.keys(node.children).sort((a, b) => a - b); + node = node.children[childrenAddresses[0]]; + if (!node) { + // unexpected + return null; + } + } +}; + +MibNode.prototype.delete = function () { + if (Object.keys(this.children) > 0) { + throw new Error("Cannot delete non-leaf MIB node"); + } + addressLastPart = this.address.slice(-1)[0]; + delete this.parent.children[addressLastPart]; + this.parent = null; +}; + +MibNode.prototype.pruneUpwards = function () { + if (!this.parent) { + return + } + if (Object.keys(this.children).length == 0) { + var lastAddressPart = this.address.splice(-1)[0].toString(); + delete this.parent.children[lastAddressPart]; + this.parent.pruneUpwards(); + this.parent = null; + } +} + +MibNode.prototype.dump = function (options) { + var valueString; + if ((!options.leavesOnly || options.showProviders) && this.provider) { + console.log(this.oid + " [" + MibProviderType[this.provider.type] + ": " + this.provider.name + "]"); + } else if ((!options.leavesOnly) || Object.keys(this.children).length == 0) { + if (this.value) { + valueString = " = "; + valueString += options.showTypes ? ObjectType[this.valueType] + ": " : ""; + valueString += options.showValues ? this.value : ""; + } else { + valueString = ""; + } + console.log(this.oid + valueString); + } + for (node of Object.keys(this.children).sort((a, b) => a - b)) { + this.children[node].dump(options); + } +}; + +MibNode.oidIsDescended = function (oid, ancestor) { + var ancestorAddress = Mib.convertOidToAddress(ancestor); + var address = Mib.convertOidToAddress(oid); + var isAncestor = true; + + if (address.length <= ancestorAddress.length) { + return false; + } - if (arguments.length < 4) { - maxRepetitions = 20; - feedCb = arguments[1]; - doneCb = arguments[2]; - } else { - maxRepetitions = arguments[1]; - feedCb = arguments[2]; - doneCb = arguments[3]; - } + ancestorAddress.forEach(function (o, i) { + if (address[i] !== ancestorAddress[i]) { + isAncestor = false; + } + }); - var req = { - maxRepetitions: maxRepetitions, - feedCb: feedCb, - doneCb: doneCb - }; + return isAncestor; +}; + +var Mib = function () { + this.root = new MibNode([], null); + this.providers = {}; + this.providerNodes = {}; +}; + +Mib.prototype.addNodesForOid = function (oidString) { + var address = Mib.convertOidToAddress(oidString); + return this.addNodesForAddress(address); +}; + +Mib.prototype.addNodesForAddress = function (address) { + var address; + var node; + var i; + + node = this.root; + + for (i = 0; i < address.length; i++) { + if (!node.children.hasOwnProperty(address[i])) { + node.children[address[i]] = new MibNode(address.slice(0, i + 1), node); + } + node = node.children[address[i]]; + } + + return node; +}; + +Mib.prototype.lookup = function (oid) { + var address; + var i; + var node; + + address = Mib.convertOidToAddress(oid); + node = this.root; + for (i = 0; i < address.length; i++) { + if (!node.children.hasOwnProperty(address[i])) { + return null + } + node = node.children[address[i]]; + } + + return node; +}; + +Mib.prototype.getProviderNodeForInstance = function (instanceNode) { + if (instanceNode.provider) { + throw new ReferenceError("Instance node has provider which should never happen"); + } + return instanceNode.getAncestorProvider(); +}; + +Mib.prototype.addProviderToNode = function (provider) { + var node = this.addNodesForOid(provider.oid); + + node.provider = provider; + if (provider.type == MibProviderType.Table) { + if (!provider.index) { + provider.index = [1]; + } + } + this.providerNodes[provider.name] = node; + return node; +}; + +Mib.prototype.registerProvider = function (provider) { + this.providers[provider.name] = provider; +}; + +Mib.prototype.unregisterProvider = function (name) { + var providerNode = this.providerNodes[name]; + if (providerNode) { + providerNodeParent = providerNode.parent; + providerNode.delete(); + providerNodeParent.pruneUpwards(); + delete this.providerNodes[name]; + } + delete this.providers[name]; +}; + +Mib.prototype.getProvider = function (name) { + return this.providers[name]; +}; + +Mib.prototype.getProviders = function () { + return this.providers; +}; + +Mib.prototype.getScalarValue = function (scalarName) { + var providerNode = this.providerNodes[scalarName]; + if (!providerNode || !providerNode.provider || providerNode.provider.type != MibProviderType.Scalar) { + throw new ReferenceError("Failed to get node for registered MIB provider " + scalarName); + } + var instanceAddress = providerNode.address.concat([0]); + if (!this.lookup(instanceAddress)) { + throw new Error("Failed created instance node for registered MIB provider " + scalarName); + } + var instanceNode = this.lookup(instanceAddress); + return instanceNode.value; +}; + +Mib.prototype.setScalarValue = function (scalarName, newValue) { + var providerNode; + var instanceNode; + + if (!this.providers[scalarName]) { + throw new ReferenceError("Provider " + scalarName + " not registered with this MIB"); + } + + providerNode = this.providerNodes[scalarName]; + if (!providerNode) { + providerNode = this.addProviderToNode(this.providers[scalarName]); + } + if (!providerNode || !providerNode.provider || providerNode.provider.type != MibProviderType.Scalar) { + throw new ReferenceError("Could not find MIB node for registered provider " + scalarName); + } + var instanceAddress = providerNode.address.concat([0]); + instanceNode = this.lookup(instanceAddress); + if (!instanceNode) { + this.addNodesForAddress(instanceAddress); + instanceNode = this.lookup(instanceAddress); + instanceNode.valueType = providerNode.provider.scalarType; + } + instanceNode.value = newValue; +}; + +Mib.prototype.getProviderNodeForTable = function (table) { + var providerNode; + var provider; + + providerNode = this.providerNodes[table]; + if (!providerNode) { + throw new ReferenceError("No MIB provider registered for " + table); + } + provider = providerNode.provider; + if (!providerNode) { + throw new ReferenceError("No MIB provider definition for registered provider " + table); + } + if (provider.type != MibProviderType.Table) { + throw new TypeError("Registered MIB provider " + table + + " is not of the correct type (is type " + MibProviderType[provider.type] + ")"); + } + return providerNode; +}; + +Mib.prototype.addTableRow = function (table, row) { + var providerNode; + var provider; + var instance = []; + var instanceAddress; + var instanceNode; + + if (this.providers[table] && !this.providerNodes[table]) { + this.addProviderToNode(this.providers[table]); + } + providerNode = this.getProviderNodeForTable(table); + provider = providerNode.provider; + for (var indexPart of provider.index) { + columnPosition = provider.columns.findIndex(column => column.number == indexPart); + instance.push(row[columnPosition]); + } + for (var i = 0; i < providerNode.provider.columns.length; i++) { + var column = providerNode.provider.columns[i]; + instanceAddress = providerNode.address.concat(column.number).concat(instance); + this.addNodesForAddress(instanceAddress); + instanceNode = this.lookup(instanceAddress); + instanceNode.valueType = column.type; + instanceNode.value = row[i]; + } +}; + +Mib.prototype.getTableColumnDefinitions = function (table) { + var providerNode; + var provider; + + providerNode = this.getProviderNodeForTable(table); + provider = providerNode.provider; + return provider.columns; +}; + +Mib.prototype.getTableColumnCells = function (table, columnNumber) { + providerNode = this.getProviderNodeForTable(table); + columnNode = providerNode.children[columnNumber]; + column = [] + for (var row of Object.keys(columnNode.children)) { + instanceNode = columnNode.children[row].getInstanceNodeForTableRow(); + column.push(instanceNode.value); + } + return column; +}; + +Mib.prototype.getTableRowCells = function (table, rowIndex) { + var providerNode; + var columnNode; + var instanceNode; + var row = []; + + providerNode = this.getProviderNodeForTable(table); + for (var columnNumber of Object.keys(providerNode.children)) { + columnNode = providerNode.children[columnNumber]; + instanceNode = columnNode.getInstanceNodeForTableRowIndex(rowIndex); + row.push(instanceNode.value); + } + return row; +}; + +Mib.prototype.getTableCells = function (table, byRows) { + var providerNode; + var columnNode; + var data = []; + + providerNode = this.getProviderNodeForTable(table); + for (var columnNumber of Object.keys(providerNode.children)) { + columnNode = providerNode.children[columnNumber]; + column = []; + data.push(column); + for (var row of Object.keys(columnNode.children)) { + instanceNode = columnNode.children[row].getInstanceNodeForTableRow(); + column.push(instanceNode.value); + } + } + + if (byRows) { + return Object.keys(data[0]).map(function (c) { + return data.map(function (r) { + return r[c]; + }); + }); + } else { + return data; + } + +}; + +Mib.prototype.getTableSingleCell = function (table, columnNumber, rowIndex) { + var providerNode; + var columnNode; + var instanceNode; + + providerNode = this.getProviderNodeForTable(table); + columnNode = providerNode.children[columnNumber]; + instanceNode = columnNode.getInstanceNodeForTableRowIndex(rowIndex); + return instanceNode.value; +}; + +Mib.prototype.setTableSingleCell = function (table, columnNumber, rowIndex, value) { + var providerNode; + var columnNode; + var instanceNode; + + providerNode = this.getProviderNodeForTable(table); + columnNode = providerNode.children[columnNumber]; + instanceNode = columnNode.getInstanceNodeForTableRowIndex(rowIndex); + instanceNode.value = value; +}; + +Mib.prototype.deleteTableRow = function (table, rowIndex) { + var providerNode; + var columnNode; + var instanceNode; + var row = []; + + providerNode = this.getProviderNodeForTable(table); + for (var columnNumber of Object.keys(providerNode.children)) { + columnNode = providerNode.children[columnNumber]; + instanceNode = columnNode.getInstanceNodeForTableRowIndex(rowIndex); + if (instanceNode) { + instanceParentNode = instanceNode.parent; + instanceNode.delete(); + instanceParentNode.pruneUpwards(); + } else { + throw new ReferenceError("Cannot find row for index " + rowIndex + " at registered provider " + table); + } + } + return row; +}; + +Mib.prototype.dump = function (options) { + if (!options) { + options = {}; + } + var completedOptions = { + leavesOnly: options.leavesOnly || true, + showProviders: options.leavesOnly || true, + showValues: options.leavesOnly || true, + showTypes: options.leavesOnly || true + }; + this.root.dump(completedOptions); +}; + +Mib.convertOidToAddress = function (oid) { + var address; + var oidArray; + var i; + + if (typeof (oid) === 'object' && util.isArray(oid)) { + address = oid; + } else if (typeof (oid) === 'string') { + address = oid.split('.'); + } else { + throw new TypeError('oid (string or array) is required'); + } + + if (address.length < 3) + throw new RangeError('object identifier is too short'); + + oidArray = []; + for (i = 0; i < address.length; i++) { + var n; + + if (address[i] === '') + continue; + + if (address[i] === true || address[i] === false) { + throw new TypeError('object identifier component ' + + address[i] + ' is malformed'); + } + + n = Number(address[i]); + + if (isNaN(n)) { + throw new TypeError('object identifier component ' + + address[i] + ' is malformed'); + } + if (n % 1 !== 0) { + throw new TypeError('object identifier component ' + + address[i] + ' is not an integer'); + } + if (i === 0 && n > 2) { + throw new RangeError('object identifier does not ' + + 'begin with 0, 1, or 2'); + } + if (i === 1 && n > 39) { + throw new RangeError('object identifier second ' + + 'component ' + n + ' exceeds encoding limit of 39'); + } + if (n < 0) { + throw new RangeError('object identifier component ' + + address[i] + ' is negative'); + } + if (n > MAX_INT32) { + throw new RangeError('object identifier component ' + + address[i] + ' is too large'); + } + oidArray.push(n); + } + + return oidArray; + +}; + +var MibRequest = function (requestDefinition) { + this.operation = requestDefinition.operation; + this.address = Mib.convertOidToAddress(requestDefinition.oid); + this.oid = this.address.join('.'); + this.providerNode = requestDefinition.providerNode; + this.instanceNode = requestDefinition.instanceNode; +}; + +MibRequest.prototype.isScalar = function () { + return this.providerNode && this.providerNode.provider && + this.providerNode.provider.type == MibProviderType.Scalar; +}; - if (this.version == Version2c) - this.getBulk ([oid], 0, maxRepetitions, - walkCb.bind (me, req)); - else - this.getNext ([oid], walkCb.bind (me, req)); +MibRequest.prototype.isTabular = function () { + return this.providerNode && this.providerNode.provider && + this.providerNode.provider.type == MibProviderType.Table; +}; + +var Agent = function (options, callback) { + DEBUG = options.debug; + this.listener = new Listener(options, this); + this.engine = new Engine(options.engineID); + this.authorizer = new Authorizer(); + this.mib = new Mib(); + this.callback = callback || function () { + }; + this.context = ""; +}; + +Agent.prototype.getMib = function () { + return this.mib; +}; + +Agent.prototype.getAuthorizer = function () { + return this.authorizer; +}; + +Agent.prototype.registerProvider = function (provider) { + this.mib.registerProvider(provider); +}; - return this; +Agent.prototype.unregisterProvider = function (provider) { + this.mib.unregisterProvider(provider); +}; + +Agent.prototype.getProvider = function (provider) { + return this.mib.getProvider(provider); +}; + +Agent.prototype.getProviders = function () { + return this.mib.getProviders(); +}; + +Agent.prototype.onMsg = function (buffer, rinfo) { + var message = Listener.processIncoming(buffer, this.authorizer, this.callback); + var reportMessage; + var responseMessage; + + if (!message) { + return; + } + + // SNMPv3 discovery + if (message.version == Version3 && message.pdu.type == PduType.GetRequest && + !message.hasAuthoritativeEngineID() && message.isReportable()) { + reportMessage = message.createReportResponseMessage(this.engine, this.context); + this.listener.send(reportMessage, rinfo); + return; + } + + // Request processing + debug(JSON.stringify(message.pdu, null, 2)); + if (message.pdu.type == PduType.GetRequest) { + responseMessage = this.request(message, rinfo); + } else if (message.pdu.type == PduType.SetRequest) { + responseMessage = this.request(message, rinfo); + } else if (message.pdu.type == PduType.GetNextRequest) { + responseMessage = this.getNextRequest(message, rinfo); + } else if (message.pdu.type == PduType.GetBulkRequest) { + responseMessage = this.getBulkRequest(message, rinfo); + } else { + this.callback(new RequestInvalidError("Unexpected PDU type " + + message.pdu.type + " (" + PduType[message.pdu.type] + ")")); + } + +}; + +Agent.prototype.request = function (requestMessage, rinfo) { + var me = this; + var varbindsCompleted = 0; + var requestPdu = requestMessage.pdu; + var varbindsLength = requestPdu.varbinds.length; + var responsePdu = requestPdu.getResponsePduForRequest(); + + for (var i = 0; i < requestPdu.varbinds.length; i++) { + var requestVarbind = requestPdu.varbinds[i]; + var instanceNode = this.mib.lookup(requestVarbind.oid); + var providerNode; + var mibRequest; + var handler; + var responseVarbindType; + + if (!instanceNode) { + mibRequest = new MibRequest({ + operation: requestPdu.type, + oid: requestVarbind.oid + }); + handler = function getNsoHandler(mibRequestForNso) { + mibRequestForNso.done({ + errorStatus: ErrorStatus.NoSuchName, + errorIndex: i + }); + }; + } else { + providerNode = this.mib.getProviderNodeForInstance(instanceNode); + mibRequest = new MibRequest({ + operation: requestPdu.type, + providerNode: providerNode, + instanceNode: instanceNode, + oid: requestVarbind.oid + }); + handler = providerNode.provider.handler; + } + + mibRequest.done = function (error) { + if (error) { + responsePdu.errorStatus = error.errorStatus; + responsePdu.errorIndex = error.errorIndex; + responseVarbind = { + oid: mibRequest.oid, + type: ObjectType.Null, + value: null + }; + } else { + if (requestPdu.type == PduType.SetRequest) { + mibRequest.instanceNode.value = requestVarbind.value; + } + if (requestPdu.type == PduType.GetNextRequest && requestVarbind.type == ObjectType.EndOfMibView) { + responseVarbindType = ObjectType.EndOfMibView; + } else { + responseVarbindType = mibRequest.instanceNode.valueType; + } + responseVarbind = { + oid: mibRequest.oid, + type: responseVarbindType, + value: mibRequest.instanceNode.value + }; + } + me.setSingleVarbind(responsePdu, i, responseVarbind); + if (++varbindsCompleted == varbindsLength) { + me.sendResponse.call(me, rinfo, requestMessage, responsePdu); + } + }; + if (handler) { + handler(mibRequest); + } else { + mibRequest.done(); + } + } + ; +}; + +Agent.prototype.addGetNextVarbind = function (targetVarbinds, startOid) { + var startNode = this.mib.lookup(startOid); + var getNextNode; + + if (!startNode) { + // Off-tree start specified + targetVarbinds.push({ + oid: requestVarbind.oid, + type: ObjectType.Null, + value: null + }); + } else { + getNextNode = startNode.getNextInstanceNode(); + if (!getNextNode) { + // End of MIB + targetVarbinds.push({ + oid: requestVarbind.oid, + type: ObjectType.EndOfMibView, + value: null + }); + } else { + // Normal response + targetVarbinds.push({ + oid: getNextNode.oid, + type: getNextNode.valueType, + value: getNextNode.value + }); + } + } + return getNextNode; +}; + +Agent.prototype.getNextRequest = function (requestMessage, rinfo) { + var requestPdu = requestMessage.pdu; + var varbindsLength = requestPdu.varbinds.length; + var getNextVarbinds = []; + + for (var i = 0; i < varbindsLength; i++) { + this.addGetNextVarbind(getNextVarbinds, requestPdu.varbinds[i].oid); + } + + requestMessage.pdu.varbinds = getNextVarbinds; + this.request(requestMessage, rinfo); +}; + +Agent.prototype.getBulkRequest = function (requestMessage, rinfo) { + var requestPdu = requestMessage.pdu; + var requestVarbinds = requestPdu.varbinds; + var getBulkVarbinds = []; + var startOid = []; + var getNextNode; + + for (var n = 0; n < requestPdu.nonRepeaters; n++) { + this.addGetNextVarbind(getBulkVarbinds, requestVarbinds[n].oid); + } + + for (var v = requestPdu.nonRepeaters; v < requestVarbinds.length; v++) { + startOid.push(requestVarbinds[v].oid); + } + + for (var r = 0; r < requestPdu.maxRepetitions; r++) { + for (var v = requestPdu.nonRepeaters; v < requestVarbinds.length; v++) { + getNextNode = this.addGetNextVarbind(getBulkVarbinds, startOid[v - requestPdu.nonRepeaters]); + if (getNextNode) { + startOid[v - requestPdu.nonRepeaters] = getNextNode.oid; + } + } + } + + requestMessage.pdu.varbinds = getBulkVarbinds; + this.request(requestMessage, rinfo); +}; + +Agent.prototype.setSingleVarbind = function (responsePdu, index, responseVarbind) { + responsePdu.varbinds[index] = responseVarbind; +}; + +Agent.prototype.sendResponse = function (rinfo, requestMessage, responsePdu) { + var responseMessage = requestMessage.createResponseForRequest(responsePdu); + this.listener.send(responseMessage, rinfo); + this.callback(null, Listener.formatCallbackData(responseMessage.pdu, rinfo)); +}; + +Agent.create = function (options, callback) { + var agent = new Agent(options, callback); + agent.listener.startListening(); + return agent; }; /***************************************************************************** @@ -1438,18 +3400,41 @@ Session.prototype.walk = function () { exports.Session = Session; exports.createSession = function (target, community, options) { - return new Session (target, community, options); + if (options.version && !(options.version == Version1 || options.version == Version2c)) { + throw new ResponseInvalidError("SNMP community session requested but version '" + options.version + "' specified in options not valid"); + } else { + return new Session(target, community, options); + } }; +exports.createV3Session = function (target, user, options) { + if (options.version && options.version != Version3) { + throw new ResponseInvalidError("SNMPv3 session requested but version '" + options.version + "' specified in options"); + } else { + options.version = Version3; + } + return new Session(target, user, options); +}; + +exports.createReceiver = Receiver.create; +exports.createAgent = Agent.create; + exports.isVarbindError = isVarbindError; exports.varbindError = varbindError; exports.Version1 = Version1; exports.Version2c = Version2c; +exports.Version3 = Version3; +exports.Version = Version; exports.ErrorStatus = ErrorStatus; exports.TrapType = TrapType; exports.ObjectType = ObjectType; +exports.PduType = PduType; +exports.MibProviderType = MibProviderType; +exports.SecurityLevel = SecurityLevel; +exports.AuthProtocols = AuthProtocols; +exports.PrivProtocols = PrivProtocols; exports.ResponseInvalidError = ResponseInvalidError; exports.RequestInvalidError = RequestInvalidError; @@ -1460,6 +3445,8 @@ exports.RequestTimedOutError = RequestTimedOutError; ** We've added this for testing. **/ exports.ObjectParser = { - readInt: readInt, - readUint: readUint + readInt: readInt, + readUint: readUint }; +exports.Authentication = Authentication; +exports.Encryption = Encryption; -- cgit v1.2.3