diff options
Diffstat (limited to 'node.d/node_modules/net-snmp.js')
-rw-r--r-- | node.d/node_modules/net-snmp.js | 1466 |
1 files changed, 1466 insertions, 0 deletions
diff --git a/node.d/node_modules/net-snmp.js b/node.d/node_modules/net-snmp.js new file mode 100644 index 000000000..6fbd4e721 --- /dev/null +++ b/node.d/node_modules/net-snmp.js @@ -0,0 +1,1466 @@ + +// Copyright 2013 Stephen Vickers <stephen.vickers.sv@gmail.com> + +var ber = require ("asn1").Ber; +var dgram = require ("dgram"); +var events = require ("events"); +var util = require ("util"); + +/***************************************************************************** + ** Constants + **/ + +function _expandConstantObject (object) { + var keys = []; + for (key in object) + keys.push (key); + for (var i = 0; i < keys.length; i++) + object[object[keys[i]]] = parseInt (keys[i]); +} + +var ErrorStatus = { + 0: "NoError", + 1: "TooBig", + 2: "NoSuchName", + 3: "BadValue", + 4: "ReadOnly", + 5: "GeneralError", + 6: "NoAccess", + 7: "WrongType", + 8: "WrongLength", + 9: "WrongEncoding", + 10: "WrongValue", + 11: "NoCreation", + 12: "InconsistentValue", + 13: "ResourceUnavailable", + 14: "CommitFailed", + 15: "UndoFailed", + 16: "AuthorizationError", + 17: "NotWritable", + 18: "InconsistentName" +}; + +_expandConstantObject (ErrorStatus); + +var ObjectType = { + 1: "Boolean", + 2: "Integer", + 4: "OctetString", + 5: "Null", + 6: "OID", + 64: "IpAddress", + 65: "Counter", + 66: "Gauge", + 67: "TimeTicks", + 68: "Opaque", + 70: "Counter64", + 128: "NoSuchObject", + 129: "NoSuchInstance", + 130: "EndOfMibView" +}; + +_expandConstantObject (ObjectType); + +ObjectType.Integer32 = ObjectType.Integer; +ObjectType.Counter32 = ObjectType.Counter; +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" +}; + +_expandConstantObject (PduType); + +var TrapType = { + 0: "ColdStart", + 1: "WarmStart", + 2: "LinkDown", + 3: "LinkUp", + 4: "AuthenticationFailure", + 5: "EgpNeighborLoss", + 6: "EnterpriseSpecific" +}; + +_expandConstantObject (TrapType); + +var Version1 = 0; +var Version2c = 1; + +/***************************************************************************** + ** Exception class definitions + **/ + +function ResponseInvalidError (message) { + this.name = "ResponseInvalidError"; + this.message = message; + Error.captureStackTrace(this, ResponseInvalidError); +} +util.inherits (ResponseInvalidError, Error); + +function RequestInvalidError (message) { + this.name = "RequestInvalidError"; + this.message = message; + Error.captureStackTrace(this, RequestInvalidError); +} +util.inherits (RequestInvalidError, Error); + +function RequestFailedError (message, status) { + this.name = "RequestFailedError"; + this.message = message; + this.status = status; + Error.captureStackTrace(this, RequestFailedError); +} +util.inherits (RequestFailedError, Error); + +function RequestTimedOutError (message) { + this.name = "RequestTimedOutError"; + this.message = message; + Error.captureStackTrace(this, RequestTimedOutError); +} +util.inherits (RequestTimedOutError, Error); + +/***************************************************************************** + ** OID and varbind helper functions + **/ + +function isVarbindError (varbind) { + if (varbind.type == ObjectType.NoSuchObject + || varbind.type == ObjectType.NoSuchInstance + || varbind.type == ObjectType.EndOfMibView) + return true; + else + return false; +} + +function varbindError (varbind) { + return (ObjectType[varbind.type] || "NotAnError") + ": " + varbind.oid; +} + +function oidFollowsOid (oidString, nextString) { + var oid = {str: oidString, len: oidString.length, idx: 0}; + var next = {str: nextString, len: nextString.length, idx: 0}; + var dotCharCode = ".".charCodeAt (0); + + function getNumber (item) { + var n = 0; + if (item.idx >= item.len) + return null; + while (item.idx < item.len) { + var charCode = item.str.charCodeAt (item.idx++); + if (charCode == dotCharCode) + return n; + n = (n ? (n * 10) : n) + (charCode - 48); + } + return n; + } + + while (1) { + var oidNumber = getNumber (oid); + var nextNumber = getNumber (next); + + if (oidNumber !== null) { + if (nextNumber !== null) { + if (nextNumber > oidNumber) { + return true; + } else if (nextNumber < oidNumber) { + return false; + } + } else { + return true; + } + } else { + return true; + } + } +} + +function oidInSubtree (oidString, nextString) { + var oid = oidString.split ("."); + var next = nextString.split ("."); + + if (oid.length > next.length) + return false; + + for (var i = 0; i < oid.length; i++) { + if (next[i] != oid[i]) + return false; + } + + return true; +} + +/** + ** Some SNMP agents produce integers on the wire such as 00 ff ff ff ff. + ** The ASN.1 BER parser we use throws an error when parsing this, which we + ** believe is correct. So, we decided not to bother the "asn1" developer(s) + ** with this, instead opting to work around it here. + ** + ** If an integer is 5 bytes in length we check if the first byte is 0, and if so + ** simply drop it and parse it like it was a 4 byte integer, otherwise throw + ** an error since the integer is too large. + **/ + +function readInt (buffer) { + return readUint (buffer, true); +} + +function readUint (buffer, isSigned) { + buffer.readByte (); + var length = buffer.readByte (); + + 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; + } + + value = 0, signedBitSet = false; + + 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); + + if (value.length > 8) + throw new RequestInvalidError ("64 bit unsigned integer too long '" + + value.length + "'") + + 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 writeUint (buffer, type, value) { + var b = new Buffer (4); + b.writeUInt32BE (value, 0); + buffer.writeBuffer (b, type); +} + +function writeUint64 (buffer, value) { + if (value.length > 8) + throw new RequestInvalidError ("64 bit unsigned integer too long '" + + value.length + "'") + 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 (); +} + +/***************************************************************************** + ** PDU class definitions + **/ + +var SimplePdu = function (id, varbinds, options) { + this.id = id; + this.varbinds = varbinds; + this.options = options || {}; +}; + +SimplePdu.prototype.toBuffer = function (buffer) { + buffer.startSequence (this.type); + + buffer.writeInt (this.id); + buffer.writeInt ((this.type == PduType.GetBulkRequest) + ? (this.options.nonRepeaters || 0) + : 0); + buffer.writeInt ((this.type == PduType.GetBulkRequest) + ? (this.options.maxRepetitions || 0) + : 0); + + writeVarbinds (buffer, this.varbinds); + + buffer.endSequence (); +}; + +var GetBulkRequestPdu = function () { + this.type = PduType.GetBulkRequest; + GetBulkRequestPdu.super_.apply (this, arguments); +}; + +util.inherits (GetBulkRequestPdu, SimplePdu); + +var GetNextRequestPdu = function () { + this.type = PduType.GetNextRequest; + GetNextRequestPdu.super_.apply (this, arguments); +}; + +util.inherits (GetNextRequestPdu, SimplePdu); + +var GetResponsePdu = function (buffer) { + this.type = PduType.GetResponse; + + buffer.readSequence (this.type); + + this.id = buffer.readInt (); + + this.errorStatus = buffer.readInt (); + this.errorIndex = buffer.readInt (); + + this.varbinds = []; + + readVarbinds (buffer, this.varbinds); +}; + +var GetRequestPdu = function () { + this.type = PduType.GetRequest; + GetRequestPdu.super_.apply (this, arguments); +}; + +util.inherits (GetRequestPdu, SimplePdu); + +var InformRequestPdu = function () { + this.type = PduType.InformRequest; + InformRequestPdu.super_.apply (this, arguments); +}; + +util.inherits (InformRequestPdu, SimplePdu); + +var SetRequestPdu = function () { + this.type = PduType.SetRequest; + SetRequestPdu.super_.apply (this, arguments); +}; + +util.inherits (SetRequestPdu, SimplePdu); + +var TrapPdu = function (typeOrOid, varbinds, options) { + this.type = PduType.Trap; + + this.agentAddr = options.agentAddr || "127.0.0.1"; + this.upTime = options.upTime; + + 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"; + } + + this.varbinds = varbinds; +}; + +TrapPdu.prototype.toBuffer = function (buffer) { + buffer.startSequence (this.type); + + 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)); + + writeVarbinds (buffer, this.varbinds); + + buffer.endSequence (); +}; + +var TrapV2Pdu = function () { + this.type = PduType.TrapV2; + TrapV2Pdu.super_.apply (this, arguments); +}; + +util.inherits (TrapV2Pdu, SimplePdu); + +/***************************************************************************** + ** Message class definitions + **/ + +var RequestMessage = function (version, community, pdu) { + this.version = version; + this.community = community; + this.pdu = pdu; +}; + +RequestMessage.prototype.toBuffer = 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; +}; + +var ResponseMessage = function (buffer) { + var reader = new ber.Reader (buffer); + + reader.readSequence (); + + this.version = reader.readInt (); + this.community = reader.readString (); + + var type = reader.peek (); + + if (type == PduType.GetResponse) { + this.pdu = new GetResponsePdu (reader); + } else { + throw new ResponseInvalidError ("Unknown PDU type '" + type + + "' in response"); + } +} + +/***************************************************************************** + ** 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.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) + req.dgram.bind (this.sourcePort, this.sourceAddress); +}; + +util.inherits (Session, events.EventEmitter); + +Session.prototype.close = function () { + this.dgram.close (); + return this; +} + +Session.prototype.cancelRequests = function (error) { + for (id in this.reqs) { + var req = this.reqs[id]; + this.unregisterRequest (req.id); + req.responseCb (error); + } +} + +function _generateId () { + return Math.floor (Math.random () + Math.random () * 10000000) +} + +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; +}; + +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; +}; + +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; +}; + +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; +}; + +Session.prototype.onClose = function () { + 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); + } +}; + +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); + } +}; + +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); +}; + +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; +}; + +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; +}; + +Session.prototype.simpleGet = function (pduClass, feedCb, varbinds, + responseCb, options) { + var req = {} + + try { + var id = _generateId (); + 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; +} + +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; + } + } + } +} + +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); +} + +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 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; +} + +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 (); + + 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; +}; + +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); +} + +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.getBulk ([oid], 0, maxRepetitions, + walkCb.bind (me, req)); + else + this.getNext ([oid], walkCb.bind (me, req)); + + return this; +} + +/***************************************************************************** + ** Exports + **/ + +exports.Session = Session; + +exports.createSession = function (target, community, version, options) { + return new Session (target, community, version, options); +}; + +exports.isVarbindError = isVarbindError; +exports.varbindError = varbindError; + +exports.Version1 = Version1; +exports.Version2c = Version2c; + +exports.ErrorStatus = ErrorStatus; +exports.TrapType = TrapType; +exports.ObjectType = ObjectType; + +exports.ResponseInvalidError = ResponseInvalidError; +exports.RequestInvalidError = RequestInvalidError; +exports.RequestFailedError = RequestFailedError; +exports.RequestTimedOutError = RequestTimedOutError; + +/** + ** We've added this for testing. + **/ +exports.ObjectParser = { + readInt: readInt, + readUint: readUint +}; |