6226 Zeilen
178 KiB
JavaScript
6226 Zeilen
178 KiB
JavaScript
|
|
// Copyright 2013 Stephen Vickers <stephen.vickers.sv@gmail.com>
|
|
|
|
var ber = require ("asn1-ber").Ber;
|
|
var smartbuffer = require ("smart-buffer");
|
|
var dgram = require ("dgram");
|
|
var net = require ("net");
|
|
var events = require ("events");
|
|
var util = require ("util");
|
|
var crypto = require ("crypto");
|
|
var mibparser = require ("./lib/mib");
|
|
var DEBUG = false;
|
|
|
|
var MIN_SIGNED_INT32 = -2147483648;
|
|
var MAX_SIGNED_INT32 = 2147483647;
|
|
var MIN_UNSIGNED_INT32 = 0;
|
|
var MAX_UNSIGNED_INT32 = 4294967295;
|
|
|
|
function debug (line) {
|
|
if ( DEBUG ) {
|
|
console.debug (line);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
** Constants
|
|
**/
|
|
|
|
|
|
function _expandConstantObject (object) {
|
|
var keys = [];
|
|
for (var key in object)
|
|
keys.push (key);
|
|
for (var i = 0; i < keys.length; i++)
|
|
object[object[keys[i]]] = parseInt (keys[i]);
|
|
}
|
|
|
|
var ErrorStatus = {
|
|
0: "NoError",
|
|
1: "TooBig",
|
|
2: "NoSuchName",
|
|
3: "BadValue",
|
|
4: "ReadOnly",
|
|
5: "GeneralError",
|
|
6: "NoAccess",
|
|
7: "WrongType",
|
|
8: "WrongLength",
|
|
9: "WrongEncoding",
|
|
10: "WrongValue",
|
|
11: "NoCreation",
|
|
12: "InconsistentValue",
|
|
13: "ResourceUnavailable",
|
|
14: "CommitFailed",
|
|
15: "UndoFailed",
|
|
16: "AuthorizationError",
|
|
17: "NotWritable",
|
|
18: "InconsistentName"
|
|
};
|
|
|
|
_expandConstantObject (ErrorStatus);
|
|
|
|
var ObjectType = {
|
|
1: "Boolean",
|
|
2: "Integer",
|
|
3: "BitString",
|
|
4: "OctetString",
|
|
5: "Null",
|
|
6: "OID",
|
|
64: "IpAddress",
|
|
65: "Counter",
|
|
66: "Gauge",
|
|
67: "TimeTicks",
|
|
68: "Opaque",
|
|
70: "Counter64",
|
|
128: "NoSuchObject",
|
|
129: "NoSuchInstance",
|
|
130: "EndOfMibView"
|
|
};
|
|
|
|
_expandConstantObject (ObjectType);
|
|
|
|
// ASN.1
|
|
ObjectType.INTEGER = ObjectType.Integer;
|
|
ObjectType["OCTET STRING"] = ObjectType.OctetString;
|
|
ObjectType["OBJECT IDENTIFIER"] = ObjectType.OID;
|
|
// SNMPv2-SMI
|
|
ObjectType.Integer32 = ObjectType.Integer;
|
|
ObjectType.Counter32 = ObjectType.Counter;
|
|
ObjectType.Gauge32 = ObjectType.Gauge;
|
|
ObjectType.Unsigned32 = ObjectType.Gauge32;
|
|
// SNMPv2-TC
|
|
ObjectType.AutonomousType = ObjectType["OBJECT IDENTIFIER"];
|
|
ObjectType.DateAndTime = ObjectType["OCTET STRING"];
|
|
ObjectType.DisplayString = ObjectType["OCTET STRING"];
|
|
ObjectType.InstancePointer = ObjectType["OBJECT IDENTIFIER"];
|
|
ObjectType.MacAddress = ObjectType["OCTET STRING"];
|
|
ObjectType.PhysAddress = ObjectType["OCTET STRING"];
|
|
ObjectType.RowPointer = ObjectType["OBJECT IDENTIFIER"];
|
|
ObjectType.RowStatus = ObjectType.INTEGER;
|
|
ObjectType.StorageType = ObjectType.INTEGER;
|
|
ObjectType.TestAndIncr = ObjectType.INTEGER;
|
|
ObjectType.TimeStamp = ObjectType.TimeTicks;
|
|
ObjectType.TruthValue = ObjectType.INTEGER;
|
|
ObjectType.TAddress = ObjectType["OCTET STRING"];
|
|
ObjectType.TDomain = ObjectType["OBJECT IDENTIFIER"];
|
|
ObjectType.VariablePointer = ObjectType["OBJECT IDENTIFIER"];
|
|
|
|
var PduType = {
|
|
160: "GetRequest",
|
|
161: "GetNextRequest",
|
|
162: "GetResponse",
|
|
163: "SetRequest",
|
|
164: "Trap",
|
|
165: "GetBulkRequest",
|
|
166: "InformRequest",
|
|
167: "TrapV2",
|
|
168: "Report"
|
|
};
|
|
|
|
_expandConstantObject (PduType);
|
|
|
|
var TrapType = {
|
|
0: "ColdStart",
|
|
1: "WarmStart",
|
|
2: "LinkDown",
|
|
3: "LinkUp",
|
|
4: "AuthenticationFailure",
|
|
5: "EgpNeighborLoss",
|
|
6: "EnterpriseSpecific"
|
|
};
|
|
|
|
_expandConstantObject (TrapType);
|
|
|
|
var SecurityLevel = {
|
|
1: "noAuthNoPriv",
|
|
2: "authNoPriv",
|
|
3: "authPriv"
|
|
};
|
|
|
|
_expandConstantObject (SecurityLevel);
|
|
|
|
var AuthProtocols = {
|
|
"1": "none",
|
|
"2": "md5",
|
|
"3": "sha",
|
|
"4": "sha224",
|
|
"5": "sha256",
|
|
"6": "sha384",
|
|
"7": "sha512"
|
|
};
|
|
|
|
_expandConstantObject (AuthProtocols);
|
|
|
|
var PrivProtocols = {
|
|
"1": "none",
|
|
"2": "des",
|
|
"4": "aes",
|
|
"6": "aes256b",
|
|
"8": "aes256r"
|
|
};
|
|
|
|
_expandConstantObject (PrivProtocols);
|
|
|
|
var UsmStatsBase = "1.3.6.1.6.3.15.1.1";
|
|
|
|
var UsmStats = {
|
|
"1": "Unsupported Security Level",
|
|
"2": "Not In Time Window",
|
|
"3": "Unknown User Name",
|
|
"4": "Unknown Engine ID",
|
|
"5": "Wrong Digest (incorrect password, community or key)",
|
|
"6": "Decryption Error"
|
|
};
|
|
|
|
_expandConstantObject (UsmStats);
|
|
|
|
var MibProviderType = {
|
|
"1": "Scalar",
|
|
"2": "Table"
|
|
};
|
|
|
|
_expandConstantObject (MibProviderType);
|
|
|
|
var Version1 = 0;
|
|
var Version2c = 1;
|
|
var Version3 = 3;
|
|
|
|
var Version = {
|
|
"1": Version1,
|
|
"2c": Version2c,
|
|
"3": Version3
|
|
};
|
|
|
|
var AgentXPduType = {
|
|
1: "Open",
|
|
2: "Close",
|
|
3: "Register",
|
|
4: "Unregister",
|
|
5: "Get",
|
|
6: "GetNext",
|
|
7: "GetBulk",
|
|
8: "TestSet",
|
|
9: "CommitSet",
|
|
10: "UndoSet",
|
|
11: "CleanupSet",
|
|
12: "Notify",
|
|
13: "Ping",
|
|
14: "IndexAllocate",
|
|
15: "IndexDeallocate",
|
|
16: "AddAgentCaps",
|
|
17: "RemoveAgentCaps",
|
|
18: "Response"
|
|
};
|
|
|
|
_expandConstantObject (AgentXPduType);
|
|
|
|
var AccessControlModelType = {
|
|
0: "None",
|
|
1: "Simple"
|
|
};
|
|
|
|
_expandConstantObject (AccessControlModelType);
|
|
|
|
var AccessLevel = {
|
|
0: "None",
|
|
1: "ReadOnly",
|
|
2: "ReadWrite"
|
|
};
|
|
|
|
_expandConstantObject (AccessLevel);
|
|
|
|
// SMIv2 MAX-ACCESS values
|
|
var MaxAccess = {
|
|
0: "not-accessible",
|
|
1: "accessible-for-notify",
|
|
2: "read-only",
|
|
3: "read-write",
|
|
4: "read-create"
|
|
};
|
|
|
|
_expandConstantObject (MaxAccess);
|
|
|
|
// SMIv1 ACCESS value mapping to SMIv2 MAX-ACCESS
|
|
var AccessToMaxAccess = {
|
|
"not-accessible": "not-accessible",
|
|
"read-only": "read-only",
|
|
"read-write": "read-write",
|
|
"write-only": "read-write"
|
|
};
|
|
|
|
var RowStatus = {
|
|
// status values
|
|
1: "active",
|
|
2: "notInService",
|
|
3: "notReady",
|
|
|
|
// actions
|
|
4: "createAndGo",
|
|
5: "createAndWait",
|
|
6: "destroy"
|
|
};
|
|
|
|
_expandConstantObject (RowStatus);
|
|
|
|
var ResponseInvalidCode = {
|
|
1: "EIp4AddressSize",
|
|
2: "EUnknownObjectType",
|
|
3: "EUnknownPduType",
|
|
4: "ECouldNotDecrypt",
|
|
5: "EAuthFailure",
|
|
6: "EReqResOidNoMatch",
|
|
// 7: "ENonRepeaterCountMismatch", // no longer used
|
|
8: "EOutOfOrder",
|
|
9: "EVersionNoMatch",
|
|
10: "ECommunityNoMatch",
|
|
11: "EUnexpectedReport",
|
|
12: "EResponseNotHandled",
|
|
13: "EUnexpectedResponse"
|
|
};
|
|
|
|
_expandConstantObject (ResponseInvalidCode);
|
|
|
|
/*****************************************************************************
|
|
** Exception class definitions
|
|
**/
|
|
|
|
function ResponseInvalidError (message, code, info) {
|
|
this.name = "ResponseInvalidError";
|
|
this.message = message;
|
|
this.code = code;
|
|
this.info = info;
|
|
Error.captureStackTrace(this, ResponseInvalidError);
|
|
}
|
|
util.inherits (ResponseInvalidError, Error);
|
|
|
|
function RequestInvalidError (message) {
|
|
this.name = "RequestInvalidError";
|
|
this.message = message;
|
|
Error.captureStackTrace(this, RequestInvalidError);
|
|
}
|
|
util.inherits (RequestInvalidError, Error);
|
|
|
|
function RequestFailedError (message, status) {
|
|
this.name = "RequestFailedError";
|
|
this.message = message;
|
|
this.status = status;
|
|
Error.captureStackTrace(this, RequestFailedError);
|
|
}
|
|
util.inherits (RequestFailedError, Error);
|
|
|
|
function RequestTimedOutError (message) {
|
|
this.name = "RequestTimedOutError";
|
|
this.message = message;
|
|
Error.captureStackTrace(this, RequestTimedOutError);
|
|
}
|
|
util.inherits (RequestTimedOutError, Error);
|
|
|
|
/*****************************************************************************
|
|
** OID and varbind helper functions
|
|
**/
|
|
|
|
function isVarbindError (varbind) {
|
|
return !!(varbind.type == ObjectType.NoSuchObject
|
|
|| varbind.type == ObjectType.NoSuchInstance
|
|
|| varbind.type == ObjectType.EndOfMibView);
|
|
}
|
|
|
|
function varbindError (varbind) {
|
|
return (ObjectType[varbind.type] || "NotAnError") + ": " + varbind.oid;
|
|
}
|
|
|
|
function oidFollowsOid (oidString, nextString) {
|
|
var oid = {str: oidString, len: oidString.length, idx: 0};
|
|
var next = {str: nextString, len: nextString.length, idx: 0};
|
|
var dotCharCode = ".".charCodeAt (0);
|
|
|
|
function getNumber (item) {
|
|
var n = 0;
|
|
if (item.idx >= item.len)
|
|
return null;
|
|
while (item.idx < item.len) {
|
|
var charCode = item.str.charCodeAt (item.idx++);
|
|
if (charCode == dotCharCode)
|
|
return n;
|
|
n = (n ? (n * 10) : n) + (charCode - 48);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
while (1) {
|
|
var oidNumber = getNumber (oid);
|
|
var nextNumber = getNumber (next);
|
|
|
|
if (oidNumber !== null) {
|
|
if (nextNumber !== null) {
|
|
if (nextNumber > oidNumber) {
|
|
return true;
|
|
} else if (nextNumber < oidNumber) {
|
|
return false;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
function oidInSubtree (oidString, nextString) {
|
|
var oid = oidString.split (".");
|
|
var next = nextString.split (".");
|
|
|
|
if (oid.length > next.length)
|
|
return false;
|
|
|
|
for (var i = 0; i < oid.length; i++) {
|
|
if (next[i] != oid[i])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function readInt32 (buffer) {
|
|
var parsedInt = buffer.readInt ();
|
|
if ( ! Number.isInteger(parsedInt) ) {
|
|
throw new TypeError('Value read as integer ' + parsedInt + ' is not an integer');
|
|
}
|
|
if ( parsedInt < MIN_SIGNED_INT32 || parsedInt > MAX_SIGNED_INT32 ) {
|
|
throw new RangeError('Read integer ' + parsedInt + ' is outside the signed 32-bit range');
|
|
}
|
|
return parsedInt;
|
|
}
|
|
|
|
function readUint32 (buffer) {
|
|
var parsedInt = buffer.readInt ();
|
|
if ( ! Number.isInteger(parsedInt) ) {
|
|
throw new TypeError('Value read as integer ' + parsedInt + ' is not an integer');
|
|
}
|
|
if ( parsedInt < MIN_UNSIGNED_INT32 || parsedInt > MAX_UNSIGNED_INT32 ) {
|
|
throw new RangeError('Read integer ' + parsedInt + ' is outside the unsigned 32-bit range');
|
|
}
|
|
return parsedInt;
|
|
}
|
|
|
|
function readUint64 (buffer) {
|
|
var value = buffer.readString (ObjectType.Counter64, true);
|
|
|
|
return value;
|
|
}
|
|
|
|
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", ResponseInvalidCode.EIp4AddressSize);
|
|
var value = bytes[0] + "." + bytes[1] + "." + bytes[2] + "." + bytes[3];
|
|
return value;
|
|
}
|
|
|
|
function readVarbindValue (buffer, type) {
|
|
var value;
|
|
if (type == ObjectType.Boolean) {
|
|
value = buffer.readBoolean ();
|
|
} else if (type == ObjectType.Integer) {
|
|
value = readInt32 (buffer);
|
|
} else if (type == ObjectType.BitString) {
|
|
value = buffer.readBitString();
|
|
} else if (type == ObjectType.OctetString) {
|
|
value = buffer.readString (null, true);
|
|
} else if (type == ObjectType.Null) {
|
|
buffer.readByte ();
|
|
buffer.readByte ();
|
|
value = null;
|
|
} else if (type == ObjectType.OID) {
|
|
value = buffer.readOID ();
|
|
} else if (type == ObjectType.IpAddress) {
|
|
value = readIpAddress (buffer);
|
|
} else if (type == ObjectType.Counter) {
|
|
value = readUint32 (buffer);
|
|
} else if (type == ObjectType.Gauge) {
|
|
value = readUint32 (buffer);
|
|
} else if (type == ObjectType.TimeTicks) {
|
|
value = readUint32 (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", ResponseInvalidCode.EUnknownObjectType);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function readVarbinds (buffer, varbinds) {
|
|
buffer.readSequence ();
|
|
|
|
while (1) {
|
|
buffer.readSequence ();
|
|
if ( buffer.peek () != ObjectType.OID )
|
|
break;
|
|
var oid = buffer.readOID ();
|
|
var type = buffer.peek ();
|
|
|
|
if (type == null)
|
|
break;
|
|
|
|
var value = readVarbindValue (buffer, type);
|
|
|
|
varbinds.push ({
|
|
oid: oid,
|
|
type: type,
|
|
value: value
|
|
});
|
|
}
|
|
}
|
|
|
|
function writeInt32 (buffer, type, value) {
|
|
if ( ! Number.isInteger(value) ) {
|
|
throw new TypeError('Value to write as integer ' + value + ' is not an integer');
|
|
}
|
|
if ( value < MIN_SIGNED_INT32 || value > MAX_SIGNED_INT32 ) {
|
|
throw new RangeError('Integer to write ' + value + ' is outside the signed 32-bit range');
|
|
}
|
|
buffer.writeInt(value, type);
|
|
}
|
|
|
|
function writeUint32 (buffer, type, value) {
|
|
if ( ! Number.isInteger(value) ) {
|
|
throw new TypeError('Value to write as integer ' + value + ' is not an integer');
|
|
}
|
|
if ( value < MIN_UNSIGNED_INT32 || value > MAX_UNSIGNED_INT32 ) {
|
|
throw new RangeError('Integer to write ' + value + ' is outside the unsigned 32-bit range');
|
|
}
|
|
buffer.writeInt(value, type);
|
|
}
|
|
|
|
function writeUint64 (buffer, value) {
|
|
buffer.writeBuffer (value, ObjectType.Counter64);
|
|
}
|
|
|
|
function writeVarbinds (buffer, varbinds) {
|
|
buffer.startSequence ();
|
|
for (var i = 0; i < varbinds.length; i++) {
|
|
buffer.startSequence ();
|
|
buffer.writeOID (varbinds[i].oid);
|
|
|
|
if (varbinds[i].type && varbinds[i].hasOwnProperty("value")) {
|
|
var type = varbinds[i].type;
|
|
var value = varbinds[i].value;
|
|
|
|
switch ( type ) {
|
|
case ObjectType.Boolean:
|
|
buffer.writeBoolean (value ? true : false);
|
|
break;
|
|
case ObjectType.Integer: // also Integer32
|
|
writeInt32 (buffer, ObjectType.Integer, value);
|
|
break;
|
|
case ObjectType.OctetString:
|
|
if (typeof value == "string")
|
|
buffer.writeString (value);
|
|
else
|
|
buffer.writeBuffer (value, ObjectType.OctetString);
|
|
break;
|
|
case ObjectType.Null:
|
|
buffer.writeNull ();
|
|
break;
|
|
case ObjectType.OID:
|
|
buffer.writeOID (value);
|
|
break;
|
|
case ObjectType.IpAddress:
|
|
var bytes = value.split (".");
|
|
if (bytes.length != 4)
|
|
throw new RequestInvalidError ("Invalid IP address '"
|
|
+ value + "'");
|
|
buffer.writeBuffer (Buffer.from (bytes), 64);
|
|
break;
|
|
case ObjectType.Counter: // also Counter32
|
|
writeUint32 (buffer, ObjectType.Counter, value);
|
|
break;
|
|
case ObjectType.Gauge: // also Gauge32 & Unsigned32
|
|
writeUint32 (buffer, ObjectType.Gauge, value);
|
|
break;
|
|
case ObjectType.TimeTicks:
|
|
writeUint32 (buffer, ObjectType.TimeTicks, value);
|
|
break;
|
|
case ObjectType.Opaque:
|
|
buffer.writeBuffer (value, ObjectType.Opaque);
|
|
break;
|
|
case ObjectType.Counter64:
|
|
writeUint64 (buffer, value);
|
|
break;
|
|
case ObjectType.NoSuchObject:
|
|
case ObjectType.NoSuchInstance:
|
|
case ObjectType.EndOfMibView:
|
|
buffer.writeByte (type);
|
|
buffer.writeByte (0);
|
|
break;
|
|
default:
|
|
throw new RequestInvalidError ("Unknown type '" + type
|
|
+ "' in request");
|
|
}
|
|
} else {
|
|
buffer.writeNull ();
|
|
}
|
|
|
|
buffer.endSequence ();
|
|
}
|
|
buffer.endSequence ();
|
|
}
|
|
|
|
/*****************************************************************************
|
|
** PDU class definitions
|
|
**/
|
|
|
|
var SimplePdu = function () {
|
|
};
|
|
|
|
SimplePdu.prototype.toBuffer = function (buffer) {
|
|
buffer.startSequence (this.type);
|
|
|
|
writeInt32 (buffer, ObjectType.Integer, this.id);
|
|
writeInt32 (buffer, ObjectType.Integer,
|
|
(this.type == PduType.GetBulkRequest)
|
|
? (this.options.nonRepeaters || 0)
|
|
: 0);
|
|
writeInt32 (buffer, ObjectType.Integer,
|
|
(this.type == PduType.GetBulkRequest)
|
|
? (this.options.maxRepetitions || 0)
|
|
: 0);
|
|
|
|
writeVarbinds (buffer, this.varbinds);
|
|
|
|
buffer.endSequence ();
|
|
};
|
|
|
|
SimplePdu.prototype.initializeFromVariables = function (id, varbinds, options) {
|
|
this.id = id;
|
|
this.varbinds = varbinds;
|
|
this.options = options || {};
|
|
this.contextName = (options && options.context) ? options.context : "";
|
|
};
|
|
|
|
SimplePdu.prototype.initializeFromBuffer = function (reader) {
|
|
this.type = reader.peek ();
|
|
reader.readSequence ();
|
|
|
|
this.id = readInt32 (reader);
|
|
this.nonRepeaters = readInt32 (reader);
|
|
this.maxRepetitions = readInt32 (reader);
|
|
|
|
this.varbinds = [];
|
|
readVarbinds (reader, this.varbinds);
|
|
};
|
|
|
|
SimplePdu.prototype.getResponsePduForRequest = function () {
|
|
var responsePdu = GetResponsePdu.createFromVariables(this.id, [], {});
|
|
if ( this.contextEngineID ) {
|
|
responsePdu.contextEngineID = this.contextEngineID;
|
|
responsePdu.contextName = this.contextName;
|
|
}
|
|
return responsePdu;
|
|
};
|
|
|
|
SimplePdu.createFromVariables = function (pduClass, id, varbinds, options) {
|
|
var pdu = new pduClass (id, varbinds, options);
|
|
pdu.id = id;
|
|
pdu.varbinds = varbinds;
|
|
pdu.options = options || {};
|
|
pdu.contextName = (options && options.context) ? options.context : "";
|
|
return pdu;
|
|
};
|
|
|
|
var GetBulkRequestPdu = function () {
|
|
this.type = PduType.GetBulkRequest;
|
|
GetBulkRequestPdu.super_.apply (this, arguments);
|
|
};
|
|
|
|
util.inherits (GetBulkRequestPdu, SimplePdu);
|
|
|
|
GetBulkRequestPdu.createFromBuffer = function (reader) {
|
|
var pdu = new GetBulkRequestPdu ();
|
|
pdu.initializeFromBuffer (reader);
|
|
return pdu;
|
|
};
|
|
|
|
var GetNextRequestPdu = function () {
|
|
this.type = PduType.GetNextRequest;
|
|
GetNextRequestPdu.super_.apply (this, arguments);
|
|
};
|
|
|
|
util.inherits (GetNextRequestPdu, SimplePdu);
|
|
|
|
GetNextRequestPdu.createFromBuffer = function (reader) {
|
|
var pdu = new GetNextRequestPdu ();
|
|
pdu.initializeFromBuffer (reader);
|
|
return pdu;
|
|
};
|
|
|
|
var GetRequestPdu = function () {
|
|
this.type = PduType.GetRequest;
|
|
GetRequestPdu.super_.apply (this, arguments);
|
|
};
|
|
|
|
util.inherits (GetRequestPdu, SimplePdu);
|
|
|
|
GetRequestPdu.createFromBuffer = function (reader) {
|
|
var pdu = new GetRequestPdu();
|
|
pdu.initializeFromBuffer (reader);
|
|
return pdu;
|
|
};
|
|
|
|
GetRequestPdu.createFromVariables = function (id, varbinds, options) {
|
|
var pdu = new GetRequestPdu();
|
|
pdu.initializeFromVariables (id, varbinds, options);
|
|
return pdu;
|
|
};
|
|
|
|
var InformRequestPdu = function () {
|
|
this.type = PduType.InformRequest;
|
|
InformRequestPdu.super_.apply (this, arguments);
|
|
};
|
|
|
|
util.inherits (InformRequestPdu, SimplePdu);
|
|
|
|
InformRequestPdu.createFromBuffer = function (reader) {
|
|
var pdu = new InformRequestPdu();
|
|
pdu.initializeFromBuffer (reader);
|
|
return pdu;
|
|
};
|
|
|
|
var SetRequestPdu = function () {
|
|
this.type = PduType.SetRequest;
|
|
SetRequestPdu.super_.apply (this, arguments);
|
|
};
|
|
|
|
util.inherits (SetRequestPdu, SimplePdu);
|
|
|
|
SetRequestPdu.createFromBuffer = function (reader) {
|
|
var pdu = new SetRequestPdu ();
|
|
pdu.initializeFromBuffer (reader);
|
|
return pdu;
|
|
};
|
|
|
|
var TrapPdu = function () {
|
|
this.type = PduType.Trap;
|
|
};
|
|
|
|
TrapPdu.prototype.toBuffer = function (buffer) {
|
|
buffer.startSequence (this.type);
|
|
|
|
buffer.writeOID (this.enterprise);
|
|
buffer.writeBuffer (Buffer.from (this.agentAddr.split (".")),
|
|
ObjectType.IpAddress);
|
|
writeInt32 (buffer, ObjectType.Integer, this.generic);
|
|
writeInt32 (buffer, ObjectType.Integer, this.specific);
|
|
writeUint32 (buffer, ObjectType.TimeTicks,
|
|
this.upTime || Math.floor (process.uptime () * 100));
|
|
|
|
writeVarbinds (buffer, this.varbinds);
|
|
|
|
buffer.endSequence ();
|
|
};
|
|
|
|
TrapPdu.createFromBuffer = function (reader) {
|
|
var pdu = new TrapPdu();
|
|
reader.readSequence ();
|
|
|
|
pdu.enterprise = reader.readOID ();
|
|
pdu.agentAddr = readIpAddress (reader);
|
|
pdu.generic = readInt32 (reader);
|
|
pdu.specific = readInt32 (reader);
|
|
pdu.upTime = readUint32 (reader);
|
|
|
|
pdu.varbinds = [];
|
|
readVarbinds (reader, pdu.varbinds);
|
|
|
|
return pdu;
|
|
};
|
|
|
|
TrapPdu.createFromVariables = function (typeOrOid, varbinds, options) {
|
|
var pdu = new TrapPdu ();
|
|
pdu.agentAddr = options.agentAddr || "127.0.0.1";
|
|
pdu.upTime = options.upTime;
|
|
|
|
if (typeof typeOrOid == "string") {
|
|
pdu.generic = TrapType.EnterpriseSpecific;
|
|
pdu.specific = parseInt (typeOrOid.match (/\.(\d+)$/)[1]);
|
|
pdu.enterprise = typeOrOid.replace (/\.(\d+)$/, "");
|
|
} else {
|
|
pdu.generic = typeOrOid;
|
|
pdu.specific = 0;
|
|
pdu.enterprise = "1.3.6.1.4.1";
|
|
}
|
|
|
|
pdu.varbinds = varbinds;
|
|
|
|
return pdu;
|
|
};
|
|
|
|
var TrapV2Pdu = function () {
|
|
this.type = PduType.TrapV2;
|
|
TrapV2Pdu.super_.apply (this, arguments);
|
|
};
|
|
|
|
util.inherits (TrapV2Pdu, SimplePdu);
|
|
|
|
TrapV2Pdu.createFromBuffer = function (reader) {
|
|
var pdu = new TrapV2Pdu();
|
|
pdu.initializeFromBuffer (reader);
|
|
return pdu;
|
|
};
|
|
|
|
TrapV2Pdu.createFromVariables = function (id, varbinds, options) {
|
|
var pdu = new TrapV2Pdu();
|
|
pdu.initializeFromVariables (id, varbinds, options);
|
|
return pdu;
|
|
};
|
|
|
|
var SimpleResponsePdu = function() {
|
|
};
|
|
|
|
SimpleResponsePdu.prototype.toBuffer = function (writer) {
|
|
writer.startSequence (this.type);
|
|
|
|
writeInt32 (writer, ObjectType.Integer, this.id);
|
|
writeInt32 (writer, ObjectType.Integer, this.errorStatus || 0);
|
|
writeInt32 (writer, ObjectType.Integer, this.errorIndex || 0);
|
|
writeVarbinds (writer, this.varbinds);
|
|
writer.endSequence ();
|
|
|
|
};
|
|
|
|
SimpleResponsePdu.prototype.initializeFromBuffer = function (reader) {
|
|
reader.readSequence (this.type);
|
|
|
|
this.id = readInt32 (reader);
|
|
this.errorStatus = readInt32 (reader);
|
|
this.errorIndex = readInt32 (reader);
|
|
|
|
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 = new ber.Reader (reader.readString (ber.Sequence | ber.Constructor, true));
|
|
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", ResponseInvalidCode.EUnknownPduType);
|
|
}
|
|
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.algorithms = {};
|
|
|
|
Authentication.algorithms[AuthProtocols.md5] = {
|
|
KEY_LENGTH: 16,
|
|
AUTHENTICATION_CODE_LENGTH: 12,
|
|
CRYPTO_ALGORITHM: 'md5'
|
|
};
|
|
|
|
Authentication.algorithms[AuthProtocols.sha] = {
|
|
KEY_LENGTH: 20,
|
|
AUTHENTICATION_CODE_LENGTH: 12,
|
|
CRYPTO_ALGORITHM: 'sha1'
|
|
};
|
|
|
|
Authentication.algorithms[AuthProtocols.sha224] = {
|
|
KEY_LENGTH: 28,
|
|
AUTHENTICATION_CODE_LENGTH: 16,
|
|
CRYPTO_ALGORITHM: 'sha224'
|
|
};
|
|
|
|
Authentication.algorithms[AuthProtocols.sha256] = {
|
|
KEY_LENGTH: 32,
|
|
AUTHENTICATION_CODE_LENGTH: 24,
|
|
CRYPTO_ALGORITHM: 'sha256'
|
|
};
|
|
|
|
Authentication.algorithms[AuthProtocols.sha384] = {
|
|
KEY_LENGTH: 48,
|
|
AUTHENTICATION_CODE_LENGTH: 32,
|
|
CRYPTO_ALGORITHM: 'sha384'
|
|
};
|
|
|
|
Authentication.algorithms[AuthProtocols.sha512] = {
|
|
KEY_LENGTH: 64,
|
|
AUTHENTICATION_CODE_LENGTH: 48,
|
|
CRYPTO_ALGORITHM: 'sha512'
|
|
};
|
|
|
|
Authentication.authToKeyCache = {};
|
|
|
|
Authentication.computeCacheKey = function (authProtocol, authPasswordString, engineID) {
|
|
var engineIDString = engineID.toString('base64');
|
|
return authProtocol + authPasswordString + engineIDString;
|
|
};
|
|
|
|
// 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;
|
|
var cryptoAlgorithm = Authentication.algorithms[authProtocol].CRYPTO_ALGORITHM;
|
|
|
|
var cacheKey = Authentication.computeCacheKey(authProtocol, authPasswordString, engineID);
|
|
if (Authentication.authToKeyCache[cacheKey] !== undefined) {
|
|
return Authentication.authToKeyCache[cacheKey];
|
|
}
|
|
|
|
buf = Buffer.alloc (Authentication.HMAC_BUFFER_SIZE, authPasswordString);
|
|
|
|
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'));
|
|
|
|
Authentication.authToKeyCache[cacheKey] = finalDigest;
|
|
return finalDigest;
|
|
};
|
|
|
|
Authentication.getParametersLength = function (authProtocol) {
|
|
return Authentication.algorithms[authProtocol].AUTHENTICATION_CODE_LENGTH;
|
|
};
|
|
|
|
Authentication.writeParameters = function (messageBuffer, authProtocol, authPassword, engineID, digestInMessage) {
|
|
var digestToAdd;
|
|
|
|
digestToAdd = Authentication.calculateDigest (messageBuffer, authProtocol, authPassword, engineID);
|
|
digestToAdd.copy (digestInMessage);
|
|
// debug ("Added Auth Parameters: " + digestToAdd.toString('hex'));
|
|
};
|
|
|
|
Authentication.isAuthentic = function (messageBuffer, authProtocol, authPassword, engineID, digestInMessage) {
|
|
var savedDigest;
|
|
var calculatedDigest;
|
|
|
|
if (digestInMessage.length !== Authentication.algorithms[authProtocol].AUTHENTICATION_CODE_LENGTH)
|
|
return false;
|
|
|
|
// save original authenticationParameters field in message
|
|
savedDigest = Buffer.from (digestInMessage);
|
|
|
|
// clear the authenticationParameters field in message
|
|
digestInMessage.fill (0);
|
|
|
|
calculatedDigest = Authentication.calculateDigest (messageBuffer, authProtocol, authPassword, engineID);
|
|
|
|
// replace previously cleared authenticationParameters field in message
|
|
savedDigest.copy (digestInMessage);
|
|
|
|
// debug ("Digest in message: " + digestInMessage.toString('hex'));
|
|
// debug ("Calculated digest: " + calculatedDigest.toString('hex'));
|
|
return calculatedDigest.equals (digestInMessage);
|
|
};
|
|
|
|
Authentication.calculateDigest = function (messageBuffer, authProtocol, authPassword, engineID) {
|
|
var authKey = Authentication.passwordToKey (authProtocol, authPassword, engineID);
|
|
|
|
var cryptoAlgorithm = Authentication.algorithms[authProtocol].CRYPTO_ALGORITHM;
|
|
var hmacAlgorithm = crypto.createHmac (cryptoAlgorithm, authKey);
|
|
hmacAlgorithm.update (messageBuffer);
|
|
var digest = hmacAlgorithm.digest ();
|
|
return digest.subarray (0, Authentication.algorithms[authProtocol].AUTHENTICATION_CODE_LENGTH);
|
|
};
|
|
|
|
var Encryption = {};
|
|
|
|
Encryption.encryptPdu = function (privProtocol, scopedPdu, privPassword, authProtocol, engine) {
|
|
var encryptFunction = Encryption.algorithms[privProtocol].encryptPdu;
|
|
return encryptFunction (scopedPdu, privProtocol, privPassword, authProtocol, engine);
|
|
};
|
|
|
|
Encryption.decryptPdu = function (privProtocol, encryptedPdu, privParameters, privPassword, authProtocol, engine) {
|
|
var decryptFunction = Encryption.algorithms[privProtocol].decryptPdu;
|
|
return decryptFunction (encryptedPdu, privProtocol, privParameters, privPassword, authProtocol, engine);
|
|
};
|
|
|
|
Encryption.debugEncrypt = function (encryptionKey, iv, plainPdu, encryptedPdu) {
|
|
debug ("Key: " + encryptionKey.toString ('hex'));
|
|
debug ("IV: " + iv.toString ('hex'));
|
|
debug ("Plain: " + plainPdu.toString ('hex'));
|
|
debug ("Encrypted: " + encryptedPdu.toString ('hex'));
|
|
};
|
|
|
|
Encryption.debugDecrypt = function (decryptionKey, iv, encryptedPdu, plainPdu) {
|
|
debug ("Key: " + decryptionKey.toString ('hex'));
|
|
debug ("IV: " + iv.toString ('hex'));
|
|
debug ("Encrypted: " + encryptedPdu.toString ('hex'));
|
|
debug ("Plain: " + plainPdu.toString ('hex'));
|
|
};
|
|
|
|
Encryption.generateLocalizedKey = function (algorithm, authProtocol, privPassword, engineID) {
|
|
var privLocalizedKey;
|
|
var encryptionKey;
|
|
|
|
privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engineID);
|
|
encryptionKey = Buffer.alloc (algorithm.KEY_LENGTH);
|
|
privLocalizedKey.copy (encryptionKey, 0, 0, algorithm.KEY_LENGTH);
|
|
|
|
return encryptionKey;
|
|
};
|
|
|
|
Encryption.generateLocalizedKeyBlumenthal = function (algorithm, authProtocol, privPassword, engineID) {
|
|
let authKeyLength;
|
|
let privLocalizedKey;
|
|
let encryptionKey;
|
|
let rounds;
|
|
let hashInput;
|
|
let nextHash;
|
|
let hashAlgorithm;
|
|
|
|
authKeyLength = Authentication.algorithms[authProtocol].KEY_LENGTH;
|
|
rounds = Math.ceil (algorithm.KEY_LENGTH / authKeyLength );
|
|
encryptionKey = Buffer.alloc (algorithm.KEY_LENGTH);
|
|
privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engineID);
|
|
nextHash = privLocalizedKey;
|
|
|
|
for ( let round = 0 ; round < rounds ; round++ ) {
|
|
nextHash.copy (encryptionKey, round * authKeyLength, 0, authKeyLength);
|
|
if ( round < rounds - 1 ) {
|
|
hashAlgorithm = crypto.createHash (Authentication.algorithms[authProtocol].CRYPTO_ALGORITHM);
|
|
hashInput = Buffer.alloc ( (round + 1) * authKeyLength);
|
|
encryptionKey.copy (hashInput, round * authKeyLength, 0, (round + 1) * authKeyLength);
|
|
hashAlgorithm.update (hashInput);
|
|
nextHash = hashAlgorithm.digest ();
|
|
}
|
|
}
|
|
|
|
return encryptionKey;
|
|
};
|
|
|
|
Encryption.generateLocalizedKeyReeder = function (algorithm, authProtocol, privPassword, engineID) {
|
|
let authKeyLength;
|
|
let privLocalizedKey;
|
|
let encryptionKey;
|
|
let rounds;
|
|
let nextPasswordInput;
|
|
|
|
authKeyLength = Authentication.algorithms[authProtocol].KEY_LENGTH;
|
|
rounds = Math.ceil (algorithm.KEY_LENGTH / authKeyLength );
|
|
encryptionKey = Buffer.alloc (algorithm.KEY_LENGTH);
|
|
nextPasswordInput = privPassword;
|
|
|
|
for ( let round = 0 ; round < rounds ; round++ ) {
|
|
privLocalizedKey = Authentication.passwordToKey (authProtocol, nextPasswordInput, engineID);
|
|
privLocalizedKey.copy (encryptionKey, round * authKeyLength, 0, authKeyLength);
|
|
nextPasswordInput = privLocalizedKey;
|
|
}
|
|
|
|
return encryptionKey;
|
|
};
|
|
|
|
Encryption.encryptPduDes = function (scopedPdu, privProtocol, privPassword, authProtocol, engine) {
|
|
var des = Encryption.algorithms[PrivProtocols.des];
|
|
var privLocalizedKey;
|
|
var encryptionKey;
|
|
var preIv;
|
|
var salt;
|
|
var iv;
|
|
var i;
|
|
var paddedScopedPduLength;
|
|
var paddedScopedPdu;
|
|
var encryptedPdu;
|
|
var cipher;
|
|
|
|
encryptionKey = Encryption.generateLocalizedKey (des, authProtocol, privPassword, engine.engineID);
|
|
privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engine.engineID);
|
|
encryptionKey = Buffer.alloc (des.KEY_LENGTH);
|
|
privLocalizedKey.copy (encryptionKey, 0, 0, des.KEY_LENGTH);
|
|
preIv = Buffer.alloc (des.BLOCK_LENGTH);
|
|
privLocalizedKey.copy (preIv, 0, des.KEY_LENGTH, des.KEY_LENGTH + des.BLOCK_LENGTH);
|
|
|
|
salt = Buffer.alloc (des.BLOCK_LENGTH);
|
|
// set local SNMP engine boots part of salt to 1, as we have no persistent engine state
|
|
salt.fill ('00000001', 0, 4, 'hex');
|
|
// set local integer part of salt to random
|
|
salt.fill (crypto.randomBytes (4), 4, 8);
|
|
iv = Buffer.alloc (des.BLOCK_LENGTH);
|
|
for (i = 0; i < iv.length; i++) {
|
|
iv[i] = preIv[i] ^ salt[i];
|
|
}
|
|
|
|
if (scopedPdu.length % des.BLOCK_LENGTH == 0) {
|
|
paddedScopedPdu = scopedPdu;
|
|
} else {
|
|
paddedScopedPduLength = des.BLOCK_LENGTH * (Math.floor (scopedPdu.length / des.BLOCK_LENGTH) + 1);
|
|
paddedScopedPdu = Buffer.alloc (paddedScopedPduLength);
|
|
scopedPdu.copy (paddedScopedPdu, 0, 0, scopedPdu.length);
|
|
}
|
|
cipher = crypto.createCipheriv (des.CRYPTO_ALGORITHM, encryptionKey, iv);
|
|
encryptedPdu = cipher.update (paddedScopedPdu);
|
|
encryptedPdu = Buffer.concat ([encryptedPdu, cipher.final()]);
|
|
// Encryption.debugEncrypt (encryptionKey, iv, paddedScopedPdu, encryptedPdu);
|
|
|
|
return {
|
|
encryptedPdu: encryptedPdu,
|
|
msgPrivacyParameters: salt
|
|
};
|
|
};
|
|
|
|
Encryption.decryptPduDes = function (encryptedPdu, privProtocol, privParameters, privPassword, authProtocol, engine) {
|
|
var des = Encryption.algorithms[PrivProtocols.des];
|
|
var privLocalizedKey;
|
|
var decryptionKey;
|
|
var preIv;
|
|
var salt;
|
|
var iv;
|
|
var i;
|
|
var decryptedPdu;
|
|
var decipher;
|
|
|
|
privLocalizedKey = Authentication.passwordToKey (authProtocol, privPassword, engine.engineID);
|
|
decryptionKey = Buffer.alloc (des.KEY_LENGTH);
|
|
privLocalizedKey.copy (decryptionKey, 0, 0, des.KEY_LENGTH);
|
|
preIv = Buffer.alloc (des.BLOCK_LENGTH);
|
|
privLocalizedKey.copy (preIv, 0, des.KEY_LENGTH, des.KEY_LENGTH + des.BLOCK_LENGTH);
|
|
|
|
salt = privParameters;
|
|
iv = Buffer.alloc (des.BLOCK_LENGTH);
|
|
for (i = 0; i < iv.length; i++) {
|
|
iv[i] = preIv[i] ^ salt[i];
|
|
}
|
|
|
|
decipher = crypto.createDecipheriv (des.CRYPTO_ALGORITHM, decryptionKey, iv);
|
|
decipher.setAutoPadding(false);
|
|
decryptedPdu = decipher.update (encryptedPdu);
|
|
decryptedPdu = Buffer.concat ([decryptedPdu, decipher.final()]);
|
|
// Encryption.debugDecrypt (decryptionKey, iv, encryptedPdu, decryptedPdu);
|
|
|
|
return decryptedPdu;
|
|
};
|
|
|
|
Encryption.generateIvAes = function (aes, engineBoots, engineTime, salt) {
|
|
var iv;
|
|
var engineBootsBuffer;
|
|
var engineTimeBuffer;
|
|
|
|
// iv = engineBoots(4) | engineTime(4) | salt(8)
|
|
iv = Buffer.alloc (aes.BLOCK_LENGTH);
|
|
engineBootsBuffer = Buffer.alloc (4);
|
|
engineBootsBuffer.writeUInt32BE (engineBoots);
|
|
engineTimeBuffer = Buffer.alloc (4);
|
|
engineTimeBuffer.writeUInt32BE (engineTime);
|
|
engineBootsBuffer.copy (iv, 0, 0, 4);
|
|
engineTimeBuffer.copy (iv, 4, 0, 4);
|
|
salt.copy (iv, 8, 0, 8);
|
|
|
|
return iv;
|
|
};
|
|
|
|
Encryption.encryptPduAes = function (scopedPdu, privProtocol, privPassword, authProtocol, engine) {
|
|
var aes = Encryption.algorithms[privProtocol];
|
|
var localizationAlgorithm = aes.localizationAlgorithm;
|
|
var encryptionKey;
|
|
var salt;
|
|
var iv;
|
|
var cipher;
|
|
var encryptedPdu;
|
|
|
|
encryptionKey = localizationAlgorithm (aes, authProtocol, privPassword, engine.engineID);
|
|
salt = Buffer.alloc (8).fill (crypto.randomBytes (8), 0, 8);
|
|
iv = Encryption.generateIvAes (aes, engine.engineBoots, engine.engineTime, salt);
|
|
cipher = crypto.createCipheriv (aes.CRYPTO_ALGORITHM, encryptionKey, iv);
|
|
encryptedPdu = cipher.update (scopedPdu);
|
|
encryptedPdu = Buffer.concat ([encryptedPdu, cipher.final()]);
|
|
// Encryption.debugEncrypt (encryptionKey, iv, scopedPdu, encryptedPdu);
|
|
|
|
return {
|
|
encryptedPdu: encryptedPdu,
|
|
msgPrivacyParameters: salt
|
|
};
|
|
};
|
|
|
|
Encryption.decryptPduAes = function (encryptedPdu, privProtocol, privParameters, privPassword, authProtocol, engine) {
|
|
var aes = Encryption.algorithms[privProtocol];
|
|
var localizationAlgorithm = aes.localizationAlgorithm;
|
|
var decryptionKey;
|
|
var iv;
|
|
var decipher;
|
|
var decryptedPdu;
|
|
|
|
decryptionKey = localizationAlgorithm (aes, authProtocol, privPassword, engine.engineID);
|
|
iv = Encryption.generateIvAes (aes, engine.engineBoots, engine.engineTime, privParameters);
|
|
decipher = crypto.createDecipheriv (aes.CRYPTO_ALGORITHM, decryptionKey, iv);
|
|
decryptedPdu = decipher.update (encryptedPdu);
|
|
decryptedPdu = Buffer.concat ([decryptedPdu, decipher.final()]);
|
|
// Encryption.debugDecrypt (decryptionKey, iv, encryptedPdu, decryptedPdu);
|
|
|
|
return decryptedPdu;
|
|
};
|
|
|
|
Encryption.algorithms = {};
|
|
|
|
Encryption.algorithms[PrivProtocols.des] = {
|
|
CRYPTO_ALGORITHM: 'des-cbc',
|
|
KEY_LENGTH: 8,
|
|
BLOCK_LENGTH: 8,
|
|
encryptPdu: Encryption.encryptPduDes,
|
|
decryptPdu: Encryption.decryptPduDes,
|
|
localizationAlgorithm: Encryption.generateLocalizedKey
|
|
};
|
|
|
|
Encryption.algorithms[PrivProtocols.aes] = {
|
|
CRYPTO_ALGORITHM: 'aes-128-cfb',
|
|
KEY_LENGTH: 16,
|
|
BLOCK_LENGTH: 16,
|
|
encryptPdu: Encryption.encryptPduAes,
|
|
decryptPdu: Encryption.decryptPduAes,
|
|
localizationAlgorithm: Encryption.generateLocalizedKey
|
|
};
|
|
|
|
Encryption.algorithms[PrivProtocols.aes256b] = {
|
|
CRYPTO_ALGORITHM: 'aes-256-cfb',
|
|
KEY_LENGTH: 32,
|
|
BLOCK_LENGTH: 16,
|
|
encryptPdu: Encryption.encryptPduAes,
|
|
decryptPdu: Encryption.decryptPduAes,
|
|
localizationAlgorithm: Encryption.generateLocalizedKeyBlumenthal
|
|
};
|
|
|
|
Encryption.algorithms[PrivProtocols.aes256r] = {
|
|
CRYPTO_ALGORITHM: 'aes-256-cfb',
|
|
KEY_LENGTH: 32,
|
|
BLOCK_LENGTH: 16,
|
|
encryptPdu: Encryption.encryptPduAes,
|
|
decryptPdu: Encryption.decryptPduAes,
|
|
localizationAlgorithm: Encryption.generateLocalizedKeyReeder
|
|
};
|
|
|
|
/*****************************************************************************
|
|
** Message class definition
|
|
**/
|
|
|
|
var Message = function () {
|
|
};
|
|
|
|
Message.prototype.getReqId = function () {
|
|
return this.version == Version3 ? this.msgGlobalData.msgID : this.pdu.id;
|
|
};
|
|
|
|
Message.prototype.toBuffer = function () {
|
|
if ( this.version == Version3 ) {
|
|
return this.toBufferV3();
|
|
} else {
|
|
return this.toBufferCommunity();
|
|
}
|
|
};
|
|
|
|
Message.prototype.toBufferCommunity = function () {
|
|
if (this.buffer)
|
|
return this.buffer;
|
|
|
|
var writer = new ber.Writer ();
|
|
|
|
writer.startSequence ();
|
|
|
|
writeInt32 (writer, ObjectType.Integer, 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;
|
|
|
|
// ScopedPDU
|
|
var scopedPduWriter = new ber.Writer ();
|
|
scopedPduWriter.startSequence ();
|
|
var contextEngineID = this.pdu.contextEngineID ? this.pdu.contextEngineID : this.msgSecurityParameters.msgAuthoritativeEngineID;
|
|
if ( contextEngineID.length == 0 ) {
|
|
scopedPduWriter.writeString ("");
|
|
} else {
|
|
scopedPduWriter.writeBuffer (contextEngineID, ber.OctetString);
|
|
}
|
|
scopedPduWriter.writeString (this.pdu.contextName);
|
|
this.pdu.toBuffer (scopedPduWriter);
|
|
scopedPduWriter.endSequence ();
|
|
|
|
if ( this.hasPrivacy() ) {
|
|
var authoritativeEngine = {
|
|
engineID: this.msgSecurityParameters.msgAuthoritativeEngineID,
|
|
engineBoots: this.msgSecurityParameters.msgAuthoritativeEngineBoots,
|
|
engineTime: this.msgSecurityParameters.msgAuthoritativeEngineTime,
|
|
};
|
|
encryptionResult = Encryption.encryptPdu (this.user.privProtocol, scopedPduWriter.buffer,
|
|
this.user.privKey, this.user.authProtocol, authoritativeEngine);
|
|
}
|
|
|
|
var writer = new ber.Writer ();
|
|
|
|
writer.startSequence ();
|
|
|
|
writeInt32 (writer, ObjectType.Integer, this.version);
|
|
|
|
// HeaderData
|
|
writer.startSequence ();
|
|
writeInt32 (writer, ObjectType.Integer, this.msgGlobalData.msgID);
|
|
writeInt32 (writer, ObjectType.Integer, this.msgGlobalData.msgMaxSize);
|
|
writer.writeByte (ber.OctetString);
|
|
writer.writeByte (1);
|
|
writer.writeByte (this.msgGlobalData.msgFlags);
|
|
writeInt32 (writer, ObjectType.Integer, this.msgGlobalData.msgSecurityModel);
|
|
writer.endSequence ();
|
|
|
|
// msgSecurityParameters
|
|
writer.startSequence (ber.OctetString);
|
|
writer.startSequence ();
|
|
//writer.writeString (this.msgSecurityParameters.msgAuthoritativeEngineID);
|
|
// writing a zero-length buffer fails - should fix asn1-ber for this condition
|
|
if ( this.msgSecurityParameters.msgAuthoritativeEngineID.length == 0 ) {
|
|
writer.writeString ("");
|
|
} else {
|
|
writer.writeBuffer (this.msgSecurityParameters.msgAuthoritativeEngineID, ber.OctetString);
|
|
}
|
|
writeInt32 (writer, ObjectType.Integer, this.msgSecurityParameters.msgAuthoritativeEngineBoots);
|
|
writeInt32 (writer, ObjectType.Integer, this.msgSecurityParameters.msgAuthoritativeEngineTime);
|
|
writer.writeString (this.msgSecurityParameters.msgUserName);
|
|
|
|
var msgAuthenticationParameters = '';
|
|
if ( this.hasAuthentication() ) {
|
|
var authParametersLength = Authentication.getParametersLength (this.user.authProtocol);
|
|
msgAuthenticationParameters = Buffer.alloc (authParametersLength);
|
|
writer.writeBuffer (msgAuthenticationParameters, ber.OctetString);
|
|
} else {
|
|
writer.writeString ("");
|
|
}
|
|
var msgAuthenticationParametersOffset = writer._offset - msgAuthenticationParameters.length;
|
|
|
|
if ( this.hasPrivacy() ) {
|
|
writer.writeBuffer (encryptionResult.msgPrivacyParameters, ber.OctetString);
|
|
} else {
|
|
writer.writeString ("");
|
|
}
|
|
msgAuthenticationParametersOffset -= writer._offset;
|
|
writer.endSequence ();
|
|
writer.endSequence ();
|
|
msgAuthenticationParametersOffset += writer._offset;
|
|
|
|
if ( this.hasPrivacy() ) {
|
|
writer.writeBuffer (encryptionResult.encryptedPdu, ber.OctetString);
|
|
} else {
|
|
writer.writeBuffer (scopedPduWriter.buffer);
|
|
}
|
|
|
|
msgAuthenticationParametersOffset -= writer._offset;
|
|
writer.endSequence ();
|
|
msgAuthenticationParametersOffset += writer._offset;
|
|
|
|
this.buffer = writer.buffer;
|
|
|
|
if ( this.hasAuthentication() ) {
|
|
msgAuthenticationParameters = this.buffer.subarray (msgAuthenticationParametersOffset,
|
|
msgAuthenticationParametersOffset + msgAuthenticationParameters.length);
|
|
Authentication.writeParameters (this.buffer, this.user.authProtocol, this.user.authKey,
|
|
this.msgSecurityParameters.msgAuthoritativeEngineID, msgAuthenticationParameters);
|
|
}
|
|
|
|
return this.buffer;
|
|
};
|
|
|
|
Message.prototype.processIncomingSecurity = function (user, responseCb) {
|
|
if ( this.hasPrivacy() ) {
|
|
if ( ! this.decryptPdu(user, responseCb) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( this.hasAuthentication() && ! this.isAuthenticationDisabled() ) {
|
|
return this.checkAuthentication(user, responseCb);
|
|
} else {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
Message.prototype.decryptPdu = function (user, responseCb) {
|
|
var decryptedPdu;
|
|
var decryptedPduReader;
|
|
try {
|
|
var authoratitiveEngine = {
|
|
engineID: this.msgSecurityParameters.msgAuthoritativeEngineID,
|
|
engineBoots: this.msgSecurityParameters.msgAuthoritativeEngineBoots,
|
|
engineTime: this.msgSecurityParameters.msgAuthoritativeEngineTime
|
|
};
|
|
decryptedPdu = Encryption.decryptPdu(user.privProtocol, this.encryptedPdu,
|
|
this.msgSecurityParameters.msgPrivacyParameters, user.privKey, user.authProtocol,
|
|
authoratitiveEngine);
|
|
decryptedPduReader = new ber.Reader (decryptedPdu);
|
|
this.pdu = readPdu(decryptedPduReader, true);
|
|
return true;
|
|
} catch (error) {
|
|
responseCb (new ResponseInvalidError ("Failed to decrypt PDU: " + error,
|
|
ResponseInvalidCode.ECouldNotDecrypt));
|
|
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 (this.buffer, user.authProtocol, user.authKey,
|
|
this.msgSecurityParameters.msgAuthoritativeEngineID).toString ('hex')
|
|
+ " calculated for message", ResponseInvalidCode.EAuthFailure, { user }));
|
|
return false;
|
|
}
|
|
|
|
};
|
|
|
|
Message.prototype.setMsgFlags = function (bitPosition, flag) {
|
|
if ( this.msgGlobalData && this.msgGlobalData !== undefined && this.msgGlobalData !== null ) {
|
|
if ( flag ) {
|
|
this.msgGlobalData.msgFlags = this.msgGlobalData.msgFlags | ( 2 ** bitPosition );
|
|
} else {
|
|
this.msgGlobalData.msgFlags = this.msgGlobalData.msgFlags & ( 255 - 2 ** bitPosition );
|
|
}
|
|
}
|
|
};
|
|
|
|
Message.prototype.hasAuthentication = function () {
|
|
return this.msgGlobalData && this.msgGlobalData.msgFlags && this.msgGlobalData.msgFlags & 1;
|
|
};
|
|
|
|
Message.prototype.setAuthentication = function (flag) {
|
|
this.setMsgFlags (0, flag);
|
|
};
|
|
|
|
Message.prototype.hasPrivacy = function () {
|
|
return this.msgGlobalData && this.msgGlobalData.msgFlags && this.msgGlobalData.msgFlags & 2;
|
|
};
|
|
|
|
Message.prototype.setPrivacy = function (flag) {
|
|
this.setMsgFlags (1, flag);
|
|
};
|
|
|
|
Message.prototype.isReportable = function () {
|
|
return this.msgGlobalData && this.msgGlobalData.msgFlags && this.msgGlobalData.msgFlags & 4;
|
|
};
|
|
|
|
Message.prototype.setReportable = function (flag) {
|
|
this.setMsgFlags (2, flag);
|
|
};
|
|
|
|
Message.prototype.isAuthenticationDisabled = function () {
|
|
return this.disableAuthentication;
|
|
};
|
|
|
|
Message.prototype.hasAuthoritativeEngineID = function () {
|
|
return this.msgSecurityParameters && this.msgSecurityParameters.msgAuthoritativeEngineID &&
|
|
this.msgSecurityParameters.msgAuthoritativeEngineID != "";
|
|
};
|
|
|
|
Message.prototype.createReportResponseMessage = function (engine, context) {
|
|
var user = {
|
|
name: "",
|
|
level: SecurityLevel.noAuthNoPriv
|
|
};
|
|
var responseSecurityParameters = {
|
|
msgAuthoritativeEngineID: engine.engineID,
|
|
msgAuthoritativeEngineBoots: engine.engineBoots,
|
|
msgAuthoritativeEngineTime: engine.engineTime,
|
|
msgUserName: user.name,
|
|
msgAuthenticationParameters: "",
|
|
msgPrivacyParameters: ""
|
|
};
|
|
var reportPdu = ReportPdu.createFromVariables (this.pdu.id, [], {});
|
|
reportPdu.contextName = context;
|
|
var responseMessage = Message.createRequestV3 (user, responseSecurityParameters, reportPdu);
|
|
responseMessage.msgGlobalData.msgID = this.msgGlobalData.msgID;
|
|
return responseMessage;
|
|
};
|
|
|
|
Message.prototype.createResponseForRequest = function (responsePdu) {
|
|
if ( this.version == Version3 ) {
|
|
return this.createV3ResponseFromRequest(responsePdu);
|
|
} else {
|
|
return this.createCommunityResponseFromRequest(responsePdu);
|
|
}
|
|
};
|
|
|
|
Message.prototype.createCommunityResponseFromRequest = function (responsePdu) {
|
|
return Message.createCommunity(this.version, this.community, responsePdu);
|
|
};
|
|
|
|
Message.prototype.createV3ResponseFromRequest = function (responsePdu) {
|
|
var responseUser = {
|
|
name: this.user.name,
|
|
level: this.user.level,
|
|
authProtocol: this.user.authProtocol,
|
|
authKey: this.user.authKey,
|
|
privProtocol: this.user.privProtocol,
|
|
privKey: this.user.privKey
|
|
};
|
|
var responseSecurityParameters = {
|
|
msgAuthoritativeEngineID: this.msgSecurityParameters.msgAuthoritativeEngineID,
|
|
msgAuthoritativeEngineBoots: this.msgSecurityParameters.msgAuthoritativeEngineBoots,
|
|
msgAuthoritativeEngineTime: this.msgSecurityParameters.msgAuthoritativeEngineTime,
|
|
msgUserName: this.msgSecurityParameters.msgUserName,
|
|
msgAuthenticationParameters: "",
|
|
msgPrivacyParameters: ""
|
|
};
|
|
var responseGlobalData = {
|
|
msgID: this.msgGlobalData.msgID,
|
|
msgMaxSize: 65507,
|
|
msgFlags: this.msgGlobalData.msgFlags & (255 - 4),
|
|
msgSecurityModel: 3
|
|
};
|
|
return Message.createV3 (responseUser, responseGlobalData, responseSecurityParameters, responsePdu);
|
|
};
|
|
|
|
Message.createCommunity = function (version, community, pdu) {
|
|
var message = new Message ();
|
|
|
|
message.version = version;
|
|
message.community = community;
|
|
message.pdu = pdu;
|
|
|
|
return message;
|
|
};
|
|
|
|
Message.createRequestV3 = function (user, msgSecurityParameters, pdu) {
|
|
var authFlag = user.level == SecurityLevel.authNoPriv || user.level == SecurityLevel.authPriv ? 1 : 0;
|
|
var privFlag = user.level == SecurityLevel.authPriv ? 1 : 0;
|
|
var reportableFlag = ( pdu.type == PduType.GetResponse || pdu.type == PduType.TrapV2 ) ? 0 : 1;
|
|
var msgGlobalData = {
|
|
msgID: _generateId(), // random ID
|
|
msgMaxSize: 65507,
|
|
msgFlags: reportableFlag * 4 | privFlag * 2 | authFlag * 1,
|
|
msgSecurityModel: 3
|
|
};
|
|
return Message.createV3 (user, msgGlobalData, msgSecurityParameters, pdu);
|
|
};
|
|
|
|
Message.createV3 = function (user, msgGlobalData, msgSecurityParameters, pdu) {
|
|
var message = new Message ();
|
|
|
|
message.version = 3;
|
|
message.user = user;
|
|
message.msgGlobalData = msgGlobalData;
|
|
message.msgSecurityParameters = {
|
|
msgAuthoritativeEngineID: msgSecurityParameters.msgAuthoritativeEngineID || Buffer.from(""),
|
|
msgAuthoritativeEngineBoots: msgSecurityParameters.msgAuthoritativeEngineBoots || 0,
|
|
msgAuthoritativeEngineTime: msgSecurityParameters.msgAuthoritativeEngineTime || 0,
|
|
msgUserName: user.name || "",
|
|
msgAuthenticationParameters: "",
|
|
msgPrivacyParameters: ""
|
|
};
|
|
message.pdu = pdu;
|
|
|
|
return message;
|
|
};
|
|
|
|
Message.createDiscoveryV3 = function (pdu) {
|
|
var msgSecurityParameters = {
|
|
msgAuthoritativeEngineID: Buffer.from(""),
|
|
msgAuthoritativeEngineBoots: 0,
|
|
msgAuthoritativeEngineTime: 0
|
|
};
|
|
var emptyUser = {
|
|
name: "",
|
|
level: SecurityLevel.noAuthNoPriv
|
|
};
|
|
return Message.createRequestV3 (emptyUser, msgSecurityParameters, pdu);
|
|
};
|
|
|
|
Message.createFromBuffer = function (buffer, user) {
|
|
var reader = new ber.Reader (buffer);
|
|
var message = new Message();
|
|
|
|
reader.readSequence ();
|
|
|
|
message.version = readInt32 (reader);
|
|
|
|
if (message.version != 3) {
|
|
message.community = reader.readString ();
|
|
message.pdu = readPdu(reader, false);
|
|
} else {
|
|
// HeaderData
|
|
message.msgGlobalData = {};
|
|
reader.readSequence ();
|
|
message.msgGlobalData.msgID = readInt32 (reader);
|
|
message.msgGlobalData.msgMaxSize = readInt32 (reader);
|
|
message.msgGlobalData.msgFlags = reader.readString (ber.OctetString, true)[0];
|
|
message.msgGlobalData.msgSecurityModel = readInt32 (reader);
|
|
|
|
// 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 = readInt32 (msgSecurityParametersReader);
|
|
message.msgSecurityParameters.msgAuthoritativeEngineTime = readInt32 (msgSecurityParametersReader);
|
|
message.msgSecurityParameters.msgUserName = msgSecurityParametersReader.readString ();
|
|
message.msgSecurityParameters.msgAuthenticationParameters = msgSecurityParametersReader.readString (ber.OctetString, true);
|
|
message.msgSecurityParameters.msgPrivacyParameters = Buffer.from(msgSecurityParametersReader.readString (ber.OctetString, true));
|
|
|
|
if ( message.hasPrivacy() ) {
|
|
message.encryptedPdu = reader.readString (ber.OctetString, true);
|
|
message.pdu = null;
|
|
} else {
|
|
message.pdu = readPdu(reader, true);
|
|
}
|
|
}
|
|
|
|
message.buffer = buffer;
|
|
|
|
return message;
|
|
};
|
|
|
|
|
|
var Req = function (session, message, feedCb, responseCb, options) {
|
|
|
|
this.message = message;
|
|
this.responseCb = responseCb;
|
|
this.retries = session.retries;
|
|
this.timeout = session.timeout;
|
|
// Add timeout backoff
|
|
this.backoff = session.backoff;
|
|
this.onResponse = session.onSimpleGetResponse;
|
|
this.feedCb = feedCb;
|
|
this.port = (options && options.port) ? options.port : session.port;
|
|
this.context = session.context;
|
|
};
|
|
|
|
Req.prototype.getId = function() {
|
|
return this.message.getReqId ();
|
|
};
|
|
|
|
|
|
/*****************************************************************************
|
|
** Session class definition
|
|
**/
|
|
|
|
var Session = function (target, authenticator, options) {
|
|
this.target = target || "127.0.0.1";
|
|
|
|
options = options || {};
|
|
this.version = options.version
|
|
? options.version
|
|
: Version1;
|
|
|
|
if ( this.version == Version3 ) {
|
|
this.user = authenticator;
|
|
} else {
|
|
this.community = authenticator || "public";
|
|
}
|
|
|
|
this.transport = options.transport
|
|
? options.transport
|
|
: "udp4";
|
|
this.port = options.port
|
|
? options.port
|
|
: 161;
|
|
this.trapPort = options.trapPort
|
|
? options.trapPort
|
|
: 162;
|
|
|
|
this.retries = (options.retries || options.retries == 0)
|
|
? options.retries
|
|
: 1;
|
|
this.timeout = options.timeout
|
|
? options.timeout
|
|
: 5000;
|
|
|
|
this.backoff = options.backoff >= 1.0
|
|
? options.backoff
|
|
: 1.0;
|
|
|
|
this.sourceAddress = options.sourceAddress
|
|
? options.sourceAddress
|
|
: undefined;
|
|
this.sourcePort = options.sourcePort
|
|
? parseInt(options.sourcePort)
|
|
: undefined;
|
|
|
|
this.idBitsSize = options.idBitsSize
|
|
? parseInt(options.idBitsSize)
|
|
: 32;
|
|
|
|
this.context = options.context
|
|
? options.context
|
|
: "";
|
|
|
|
this.backwardsGetNexts = (typeof options.backwardsGetNexts !== 'undefined')
|
|
? options.backwardsGetNexts
|
|
: true;
|
|
|
|
this.reportOidMismatchErrors = (typeof options.reportOidMismatchErrors !== 'undefined')
|
|
? options.reportOidMismatchErrors
|
|
: false;
|
|
|
|
DEBUG = options.debug;
|
|
|
|
this.engine = new Engine (options.engineID);
|
|
this.reqs = {};
|
|
this.reqCount = 0;
|
|
|
|
this.dgram = dgram.createSocket (this.transport);
|
|
this.dgram.unref();
|
|
|
|
var me = this;
|
|
this.dgram.on ("message", me.onMsg.bind (me));
|
|
this.dgram.on ("close", me.onClose.bind (me));
|
|
this.dgram.on ("error", me.onError.bind (me));
|
|
|
|
if (this.sourceAddress || this.sourcePort)
|
|
this.dgram.bind (this.sourcePort, this.sourceAddress);
|
|
};
|
|
|
|
util.inherits (Session, events.EventEmitter);
|
|
|
|
Session.prototype.close = function () {
|
|
this.dgram.close ();
|
|
return this;
|
|
};
|
|
|
|
Session.prototype.cancelRequests = function (error) {
|
|
var id;
|
|
for (id in this.reqs) {
|
|
var req = this.reqs[id];
|
|
this.unregisterRequest (req.getId ());
|
|
req.responseCb (error);
|
|
}
|
|
};
|
|
|
|
function _generateId (bitSize) {
|
|
if (bitSize === 16) {
|
|
return Math.floor(Math.random() * 10000) % 65535;
|
|
}
|
|
return Math.floor(Math.random() * 100000000) % 4294967295;
|
|
}
|
|
|
|
Session.prototype.get = function (oids, responseCb) {
|
|
var reportOidMismatchErrors = this.reportOidMismatchErrors;
|
|
|
|
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", ResponseInvalidCode.EReqResOidNoMatch));
|
|
} else {
|
|
for (var i = 0; i < req.message.pdu.varbinds.length; i++) {
|
|
if ( reportOidMismatchErrors && req.message.pdu.varbinds[i].oid != pdu.varbinds[i].oid ) {
|
|
req.responseCb (new ResponseInvalidError ("OID '"
|
|
+ req.message.pdu.varbinds[i].oid
|
|
+ "' in request at position '" + i + "' does not "
|
|
+ "match OID '" + pdu.varbinds[i].oid + "' in response "
|
|
+ "at position '" + i + "'", ResponseInvalidCode.EReqResOidNoMatch));
|
|
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;
|
|
var reportOidMismatchErrors = this.reportOidMismatchErrors;
|
|
var backwardsGetNexts = this.backwardsGetNexts;
|
|
|
|
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 reqVarbinds = req.message.pdu.varbinds;
|
|
var varbinds = [];
|
|
var i = 0;
|
|
|
|
for ( ; i < reqVarbinds.length && i < pdu.varbinds.length; i++) {
|
|
if (isVarbindError (pdu.varbinds[i])) {
|
|
if ( reportOidMismatchErrors && reqVarbinds[i].oid != pdu.varbinds[i].oid ) {
|
|
req.responseCb (new ResponseInvalidError ("OID '" + reqVarbinds[i].oid
|
|
+ "' in request at position '" + i + "' does not "
|
|
+ "match OID '" + pdu.varbinds[i].oid + "' in response "
|
|
+ "at position '" + i + "'", ResponseInvalidCode.EReqResOidNoMatch));
|
|
return;
|
|
}
|
|
} else {
|
|
if ( ! backwardsGetNexts && ! oidFollowsOid (reqVarbinds[i].oid, pdu.varbinds[i].oid)) {
|
|
req.responseCb (new ResponseInvalidError ("OID '" + reqVarbinds[i].oid
|
|
+ "' in request at positiion '" + i + "' does not "
|
|
+ "precede OID '" + pdu.varbinds[i].oid + "' in response "
|
|
+ "at position '" + i + "'", ResponseInvalidCode.EOutOfOrder));
|
|
return;
|
|
}
|
|
}
|
|
if (i < nonRepeaters)
|
|
varbinds.push (pdu.varbinds[i]);
|
|
else
|
|
varbinds.push ([pdu.varbinds[i]]);
|
|
}
|
|
|
|
var repeaters = reqVarbinds.length - nonRepeaters;
|
|
|
|
for ( ; i < pdu.varbinds.length; i++) {
|
|
var reqIndex = (i - nonRepeaters) % repeaters + nonRepeaters;
|
|
var prevIndex = i - repeaters;
|
|
var prevOid = pdu.varbinds[prevIndex].oid;
|
|
|
|
if (isVarbindError (pdu.varbinds[i])) {
|
|
if ( reportOidMismatchErrors && prevOid != pdu.varbinds[i].oid ) {
|
|
req.responseCb (new ResponseInvalidError ("OID '" + prevOid
|
|
+ "' in response at position '" + prevIndex + "' does not "
|
|
+ "match OID '" + pdu.varbinds[i].oid + "' in response "
|
|
+ "at position '" + i + "'", ResponseInvalidCode.EReqResOidNoMatch));
|
|
return;
|
|
}
|
|
} else {
|
|
if ( ! backwardsGetNexts && ! oidFollowsOid (prevOid, pdu.varbinds[i].oid)) {
|
|
req.responseCb (new ResponseInvalidError ("OID '" + prevOid
|
|
+ "' in response at positiion '" + prevIndex + "' does not "
|
|
+ "precede OID '" + pdu.varbinds[i].oid + "' in response "
|
|
+ "at position '" + i + "'", ResponseInvalidCode.EOutOfOrder));
|
|
return;
|
|
}
|
|
}
|
|
varbinds[reqIndex].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);
|
|
}
|
|
|
|
var options = {
|
|
nonRepeaters: nonRepeaters,
|
|
maxRepetitions: maxRepetitions
|
|
};
|
|
|
|
this.simpleGet (GetBulkRequestPdu, feedCb, pduVarbinds, responseCb,
|
|
options);
|
|
|
|
return this;
|
|
};
|
|
|
|
Session.prototype.getNext = function (oids, responseCb) {
|
|
var backwardsGetNexts = this.backwardsGetNexts;
|
|
|
|
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", ResponseInvalidCode.EReqResOidNoMatch));
|
|
} else {
|
|
for (var i = 0; i < req.message.pdu.varbinds.length; i++) {
|
|
if (isVarbindError (pdu.varbinds[i])) {
|
|
varbinds.push (pdu.varbinds[i]);
|
|
} else if ( ! backwardsGetNexts && ! 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 + "'", ResponseInvalidCode.OutOfOrder));
|
|
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];
|
|
}
|
|
|
|
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", ResponseInvalidCode.EReqResOidNoMatch));
|
|
} 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 + "'", ResponseInvalidCode.EReqResOidNoMatch));
|
|
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) {
|
|
try {
|
|
var message = Message.createFromBuffer (buffer);
|
|
} catch (error) {
|
|
this.emit("error", error);
|
|
return;
|
|
}
|
|
|
|
var req = this.unregisterRequest (message.getReqId ());
|
|
if ( ! req )
|
|
return;
|
|
|
|
if ( ! message.processIncomingSecurity (this.user, req.responseCb) )
|
|
return;
|
|
|
|
if (message.version != req.message.version) {
|
|
req.responseCb (new ResponseInvalidError ("Version in request '"
|
|
+ req.message.version + "' does not match version in "
|
|
+ "response '" + message.version + "'", ResponseInvalidCode.EVersionNoMatch));
|
|
} 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", ResponseInvalidCode.ECommunityNoMatch));
|
|
} else if (message.pdu.type == PduType.Report) {
|
|
this.msgSecurityParameters = {
|
|
msgAuthoritativeEngineID: message.msgSecurityParameters.msgAuthoritativeEngineID,
|
|
msgAuthoritativeEngineBoots: message.msgSecurityParameters.msgAuthoritativeEngineBoots,
|
|
msgAuthoritativeEngineTime: message.msgSecurityParameters.msgAuthoritativeEngineTime
|
|
};
|
|
if ( this.proxy ) {
|
|
this.msgSecurityParameters.msgUserName = this.proxy.user.name;
|
|
this.msgSecurityParameters.msgAuthenticationParameters = "";
|
|
this.msgSecurityParameters.msgPrivacyParameters = "";
|
|
} else {
|
|
if ( ! req.originalPdu || ! req.allowReport ) {
|
|
if (Array.isArray(message.pdu.varbinds) && message.pdu.varbinds[0] && message.pdu.varbinds[0].oid.indexOf(UsmStatsBase) === 0) {
|
|
this.userSecurityModelError (req, message.pdu.varbinds[0].oid);
|
|
return;
|
|
}
|
|
req.responseCb (new ResponseInvalidError ("Unexpected Report PDU", ResponseInvalidCode.EUnexpectedReport) );
|
|
return;
|
|
}
|
|
req.originalPdu.contextName = this.context;
|
|
var timeSyncNeeded = ! message.msgSecurityParameters.msgAuthoritativeEngineBoots && ! message.msgSecurityParameters.msgAuthoritativeEngineTime;
|
|
this.sendV3Req (req.originalPdu, req.feedCb, req.responseCb, req.options, req.port, timeSyncNeeded);
|
|
}
|
|
} else if ( this.proxy ) {
|
|
this.onProxyResponse (req, message);
|
|
} else if (message.pdu.type == PduType.GetResponse) {
|
|
req.onResponse (req, message);
|
|
} else {
|
|
req.responseCb (new ResponseInvalidError ("Unknown PDU type '"
|
|
+ message.pdu.type + "' in response", ResponseInvalidCode.EUnknownPduType));
|
|
}
|
|
};
|
|
|
|
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.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);
|
|
// Apply timeout backoff
|
|
if (req.backoff && req.backoff >= 1)
|
|
req.timeout *= req.backoff;
|
|
};
|
|
|
|
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) {
|
|
var reportOidMismatchErrors = this.reportOidMismatchErrors;
|
|
|
|
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", ResponseInvalidCode.EReqResOidNoMatch));
|
|
} else {
|
|
for (var i = 0; i < req.message.pdu.varbinds.length; i++) {
|
|
if ( reportOidMismatchErrors && req.message.pdu.varbinds[i].oid != pdu.varbinds[i].oid ) {
|
|
req.responseCb (new ResponseInvalidError ("OID '"
|
|
+ req.message.pdu.varbinds[i].oid
|
|
+ "' in request at position '" + i + "' does not "
|
|
+ "match OID '" + pdu.varbinds[i].oid + "' in response "
|
|
+ "at position '" + i + "'", ResponseInvalidCode.EReqResOidNoMatch));
|
|
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 id = _generateId (this.idBitsSize);
|
|
options = Object.assign({}, options, { context: this.context });
|
|
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, true);
|
|
} else {
|
|
this.sendV3Discovery (pdu, feedCb, responseCb, options);
|
|
}
|
|
} else {
|
|
message = Message.createCommunity (this.version, this.community, pdu);
|
|
req = new Req (this, message, feedCb, responseCb, options);
|
|
this.send (req);
|
|
}
|
|
};
|
|
|
|
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 (varbinds[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 (varbinds[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 = {};
|
|
|
|
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];
|
|
options.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.engine.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);
|
|
|
|
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 ( ! varbinds || ! varbinds.length ) {
|
|
req.doneCb(null);
|
|
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);
|
|
}
|
|
|
|
Session.prototype.walk = 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 = {
|
|
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;
|
|
};
|
|
|
|
Session.prototype.sendV3Req = function (pdu, feedCb, responseCb, options, port, allowReport) {
|
|
var message = Message.createRequestV3 (this.user, this.msgSecurityParameters, pdu);
|
|
var reqOptions = options || {};
|
|
var req = new Req (this, message, feedCb, responseCb, reqOptions);
|
|
req.port = port;
|
|
req.originalPdu = pdu;
|
|
req.allowReport = allowReport;
|
|
this.send (req);
|
|
};
|
|
|
|
Session.prototype.sendV3Discovery = function (originalPdu, feedCb, responseCb, options) {
|
|
var discoveryPdu = createDiscoveryPdu(this.context);
|
|
var discoveryMessage = Message.createDiscoveryV3 (discoveryPdu);
|
|
var discoveryReq = new Req (this, discoveryMessage, feedCb, responseCb, options);
|
|
discoveryReq.originalPdu = originalPdu;
|
|
discoveryReq.allowReport = true;
|
|
this.send (discoveryReq);
|
|
};
|
|
|
|
Session.prototype.userSecurityModelError = function (req, oid) {
|
|
var oidSuffix = oid.replace (UsmStatsBase + '.', '').replace (/\.0$/, '');
|
|
var errorType = UsmStats[oidSuffix] || "Unexpected Report PDU";
|
|
req.responseCb (new ResponseInvalidError (errorType, ResponseInvalidCode.EAuthFailure) );
|
|
};
|
|
|
|
Session.prototype.onProxyResponse = function (req, message) {
|
|
if ( message.version != Version3 ) {
|
|
this.callback (new RequestFailedError ("Only SNMP version 3 contexts are supported"));
|
|
return;
|
|
}
|
|
message.pdu.contextName = this.proxy.context;
|
|
message.user = req.proxiedUser;
|
|
message.setAuthentication ( ! (req.proxiedUser.level == SecurityLevel.noAuthNoPriv));
|
|
message.setPrivacy (req.proxiedUser.level == SecurityLevel.authPriv);
|
|
message.msgSecurityParameters = {
|
|
msgAuthoritativeEngineID: req.proxiedEngine.engineID,
|
|
msgAuthoritativeEngineBoots: req.proxiedEngine.engineBoots,
|
|
msgAuthoritativeEngineTime: req.proxiedEngine.engineTime,
|
|
msgUserName: req.proxiedUser.name,
|
|
msgAuthenticationParameters: "",
|
|
msgPrivacyParameters: ""
|
|
};
|
|
message.buffer = null;
|
|
message.pdu.contextEngineID = message.msgSecurityParameters.msgAuthoritativeEngineID;
|
|
message.pdu.contextName = this.proxy.context;
|
|
message.pdu.id = req.proxiedPduId;
|
|
this.proxy.listener.send (message, req.proxiedRinfo);
|
|
};
|
|
|
|
Session.create = function (target, community, options) {
|
|
// Ensure that options may be optional
|
|
var version = (options && options.version) ? options.version : Version1;
|
|
if (version != Version1 && version != Version2c) {
|
|
throw new ResponseInvalidError ("SNMP community session requested but version '" + options.version + "' specified in options not valid",
|
|
ResponseInvalidCode.EVersionNoMatch);
|
|
} else {
|
|
if (!options)
|
|
options = {};
|
|
options.version = version;
|
|
return new Session (target, community, options);
|
|
}
|
|
};
|
|
|
|
Session.createV3 = function (target, user, options) {
|
|
// Ensure that options may be optional
|
|
if ( options && options.version && options.version != Version3 ) {
|
|
throw new ResponseInvalidError ("SNMPv3 session requested but version '" + options.version + "' specified in options",
|
|
ResponseInvalidCode.EVersionNoMatch);
|
|
} else {
|
|
if (!options)
|
|
options = {};
|
|
options.version = Version3;
|
|
}
|
|
return new Session (target, user, options);
|
|
};
|
|
|
|
var Engine = function (engineID, engineBoots, engineTime) {
|
|
if ( engineID ) {
|
|
if ( ! (engineID instanceof Buffer) ) {
|
|
engineID = engineID.replace('0x', '');
|
|
this.engineID = Buffer.from((engineID.toString().length % 2 == 1 ? '0' : '') + engineID.toString(), 'hex');
|
|
} else {
|
|
this.engineID = engineID;
|
|
}
|
|
} else {
|
|
this.generateEngineID ();
|
|
}
|
|
this.engineBoots = 0;
|
|
this.engineTime = 10;
|
|
};
|
|
|
|
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.address = options.address;
|
|
this.disableAuthorization = options.disableAuthorization || false;
|
|
};
|
|
|
|
Listener.prototype.startListening = function () {
|
|
var me = this;
|
|
this.dgram = dgram.createSocket (this.family);
|
|
this.dgram.on ("error", me.receiver.callback);
|
|
this.dgram.bind (this.port, this.address);
|
|
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.user.level == SecurityLevel.authNoPriv || message.user.level == SecurityLevel.authPriv) && ! message.hasAuthentication() ) {
|
|
callback (new RequestFailedError ("Local user " + message.msgSecurityParameters.msgUserName +
|
|
" requires authentication but message does not provide it"));
|
|
return;
|
|
}
|
|
if ( message.user.level == SecurityLevel.authPriv && ! message.hasPrivacy() ) {
|
|
callback (new RequestFailedError ("Local user " + message.msgSecurityParameters.msgUserName +
|
|
" requires privacy but message does not provide it"));
|
|
return;
|
|
}
|
|
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;
|
|
};
|
|
|
|
Listener.prototype.close = function () {
|
|
if ( this.dgram ) {
|
|
this.dgram.close ();
|
|
}
|
|
};
|
|
|
|
var Authorizer = function (options) {
|
|
this.communities = [];
|
|
this.users = [];
|
|
this.disableAuthorization = options.disableAuthorization;
|
|
this.accessControlModelType = options.accessControlModelType || AccessControlModelType.None;
|
|
|
|
if ( this.accessControlModelType == AccessControlModelType.None ) {
|
|
this.accessControlModel = null;
|
|
} else if ( this.accessControlModelType == AccessControlModelType.Simple ) {
|
|
this.accessControlModel = new SimpleAccessControlModel ();
|
|
}
|
|
};
|
|
|
|
Authorizer.prototype.addCommunity = function (community) {
|
|
if ( this.getCommunity (community) ) {
|
|
return;
|
|
} else {
|
|
this.communities.push (community);
|
|
if ( this.accessControlModelType == AccessControlModelType.Simple ) {
|
|
this.accessControlModel.setCommunityAccess (community, AccessLevel.ReadOnly);
|
|
}
|
|
}
|
|
};
|
|
|
|
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);
|
|
if ( this.accessControlModelType == AccessControlModelType.Simple ) {
|
|
this.accessControlModel.setUserAccess (user.name, AccessLevel.ReadOnly);
|
|
}
|
|
};
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
Authorizer.prototype.getAccessControlModelType = function () {
|
|
return this.accessControlModelType;
|
|
};
|
|
|
|
Authorizer.prototype.getAccessControlModel = function () {
|
|
return this.accessControlModel;
|
|
};
|
|
|
|
Authorizer.prototype.isAccessAllowed = function (securityModel, securityName, pduType) {
|
|
if ( this.accessControlModel ) {
|
|
return this.accessControlModel.isAccessAllowed (securityModel, securityName, pduType);
|
|
} else {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
var SimpleAccessControlModel = function () {
|
|
this.communitiesAccess = [];
|
|
this.usersAccess = [];
|
|
};
|
|
|
|
SimpleAccessControlModel.prototype.getCommunityAccess = function (community) {
|
|
return this.communitiesAccess.find (entry => entry.community == community );
|
|
};
|
|
|
|
SimpleAccessControlModel.prototype.getCommunityAccessLevel = function (community) {
|
|
var communityAccessEntry = this.getCommunityAccess (community);
|
|
return communityAccessEntry ? communityAccessEntry.level : AccessLevel.None;
|
|
};
|
|
|
|
SimpleAccessControlModel.prototype.getCommunitiesAccess = function () {
|
|
return this.communitiesAccess;
|
|
};
|
|
|
|
SimpleAccessControlModel.prototype.setCommunityAccess = function (community, accessLevel) {
|
|
let accessEntry = this.getCommunityAccess (community);
|
|
if ( accessEntry ) {
|
|
accessEntry.level = accessLevel;
|
|
} else {
|
|
this.communitiesAccess.push ({
|
|
community: community,
|
|
level: accessLevel
|
|
});
|
|
this.communitiesAccess.sort ((a, b) => (a.community > b.community) ? 1 : -1);
|
|
}
|
|
};
|
|
|
|
SimpleAccessControlModel.prototype.removeCommunityAccess = function (community) {
|
|
this.communitiesAccess.splice ( this.communitiesAccess.findIndex (entry => entry.community == community), 1);
|
|
};
|
|
|
|
SimpleAccessControlModel.prototype.getUserAccess = function (userName) {
|
|
return this.usersAccess.find (entry => entry.userName == userName );
|
|
};
|
|
|
|
SimpleAccessControlModel.prototype.getUserAccessLevel = function (user) {
|
|
var userAccessEntry = this.getUserAccess (user);
|
|
return userAccessEntry ? userAccessEntry.level : AccessLevel.None;
|
|
};
|
|
|
|
SimpleAccessControlModel.prototype.getUsersAccess = function () {
|
|
return this.usersAccess;
|
|
};
|
|
|
|
SimpleAccessControlModel.prototype.setUserAccess = function (userName, accessLevel) {
|
|
let accessEntry = this.getUserAccess (userName);
|
|
if ( accessEntry ) {
|
|
accessEntry.level = accessLevel;
|
|
} else {
|
|
this.usersAccess.push ({
|
|
userName: userName,
|
|
level: accessLevel
|
|
});
|
|
this.usersAccess.sort ((a, b) => (a.userName > b.userName) ? 1 : -1);
|
|
}
|
|
};
|
|
|
|
SimpleAccessControlModel.prototype.removeUserAccess = function (userName) {
|
|
this.usersAccess.splice ( this.usersAccess.findIndex (entry => entry.userName == userName), 1);
|
|
};
|
|
|
|
SimpleAccessControlModel.prototype.isAccessAllowed = function (securityModel, securityName, pduType) {
|
|
var accessLevelConfigured;
|
|
var accessLevelRequired;
|
|
|
|
switch ( securityModel ) {
|
|
case Version1:
|
|
case Version2c:
|
|
accessLevelConfigured = this.getCommunityAccessLevel (securityName);
|
|
break;
|
|
case Version3:
|
|
accessLevelConfigured = this.getUserAccessLevel (securityName);
|
|
break;
|
|
}
|
|
switch ( pduType ) {
|
|
case PduType.SetRequest:
|
|
accessLevelRequired = AccessLevel.ReadWrite;
|
|
break;
|
|
case PduType.GetRequest:
|
|
case PduType.GetNextRequest:
|
|
case PduType.GetBulkRequest:
|
|
accessLevelRequired = AccessLevel.ReadOnly;
|
|
break;
|
|
default:
|
|
accessLevelRequired = AccessLevel.None;
|
|
break;
|
|
}
|
|
switch ( accessLevelRequired ) {
|
|
case AccessLevel.ReadWrite:
|
|
return accessLevelConfigured == AccessLevel.ReadWrite;
|
|
case AccessLevel.ReadOnly:
|
|
return accessLevelConfigured == AccessLevel.ReadWrite || accessLevelConfigured == AccessLevel.ReadOnly;
|
|
case AccessLevel.None:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
};
|
|
|
|
|
|
/*****************************************************************************
|
|
** Receiver class definition
|
|
**/
|
|
|
|
var Receiver = function (options, callback) {
|
|
DEBUG = options.debug;
|
|
this.listener = new Listener (options, this);
|
|
this.authorizer = new Authorizer (options);
|
|
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.includeAuthentication = options.includeAuthentication || false;
|
|
this.context = (options && options.context) ? options.context : "";
|
|
this.listener = new Listener (options, this);
|
|
};
|
|
|
|
Receiver.prototype.getAuthorizer = function () {
|
|
return this.authorizer;
|
|
};
|
|
|
|
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;
|
|
}
|
|
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, 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, rinfo) );
|
|
} else {
|
|
this.callback (new RequestInvalidError ("Unexpected PDU type " + message.pdu.type + " (" + PduType[message.pdu.type] + ")"));
|
|
}
|
|
};
|
|
|
|
Receiver.prototype.formatCallbackData = function (message, rinfo) {
|
|
if ( message.pdu.contextEngineID ) {
|
|
message.pdu.contextEngineID = message.pdu.contextEngineID.toString('hex');
|
|
}
|
|
delete message.pdu.nonRepeaters;
|
|
delete message.pdu.maxRepetitions;
|
|
const formattedData = {
|
|
pdu: message.pdu,
|
|
rinfo: rinfo
|
|
};
|
|
if (this.includeAuthentication) {
|
|
if (message.community) {
|
|
formattedData.pdu.community = message.community;
|
|
} else if (message.user) {
|
|
formattedData.pdu.user = message.user.name;
|
|
}
|
|
}
|
|
|
|
return formattedData;
|
|
};
|
|
|
|
Receiver.prototype.close = function() {
|
|
this.listener.close ();
|
|
};
|
|
|
|
Receiver.create = function (options, callback) {
|
|
var receiver = new Receiver (options, callback);
|
|
receiver.listener.startListening ();
|
|
return receiver;
|
|
};
|
|
|
|
var ModuleStore = function () {
|
|
this.parser = mibparser ();
|
|
};
|
|
|
|
ModuleStore.prototype.getSyntaxTypes = function () {
|
|
var syntaxTypes = {};
|
|
Object.assign (syntaxTypes, ObjectType);
|
|
var entryArray;
|
|
|
|
for ( var mibModule of Object.values (this.parser.Modules) ) {
|
|
entryArray = Object.values (mibModule);
|
|
for ( var mibEntry of entryArray ) {
|
|
if ( mibEntry.MACRO == "TEXTUAL-CONVENTION" ) {
|
|
if ( mibEntry.SYNTAX && ! syntaxTypes[mibEntry.ObjectName] ) {
|
|
if ( typeof mibEntry.SYNTAX == "object" ) {
|
|
syntaxTypes[mibEntry.ObjectName] = syntaxTypes.Integer;
|
|
} else {
|
|
syntaxTypes[mibEntry.ObjectName] = syntaxTypes[mibEntry.SYNTAX];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return syntaxTypes;
|
|
};
|
|
|
|
ModuleStore.prototype.loadFromFile = function (fileName) {
|
|
this.parser.Import (fileName);
|
|
this.parser.Serialize ();
|
|
};
|
|
|
|
ModuleStore.prototype.getModule = function (moduleName) {
|
|
return this.parser.Modules[moduleName];
|
|
};
|
|
|
|
ModuleStore.prototype.getModules = function (includeBase) {
|
|
var modules = {};
|
|
for ( var moduleName of Object.keys(this.parser.Modules) ) {
|
|
if ( includeBase || ModuleStore.BASE_MODULES.indexOf (moduleName) == -1 ) {
|
|
modules[moduleName] = this.parser.Modules[moduleName];
|
|
}
|
|
}
|
|
return modules;
|
|
};
|
|
|
|
ModuleStore.prototype.getModuleNames = function (includeBase) {
|
|
var modules = [];
|
|
for ( var moduleName of Object.keys(this.parser.Modules) ) {
|
|
if ( includeBase || ModuleStore.BASE_MODULES.indexOf (moduleName) == -1 ) {
|
|
modules.push (moduleName);
|
|
}
|
|
}
|
|
return modules;
|
|
};
|
|
|
|
ModuleStore.prototype.getProvidersForModule = function (moduleName) {
|
|
var mibModule = this.parser.Modules[moduleName];
|
|
var scalars = [];
|
|
var tables = [];
|
|
var mibEntry;
|
|
var syntaxTypes;
|
|
var entryArray;
|
|
var currentTableProvider;
|
|
var parentOid;
|
|
var constraintsResults;
|
|
var constraints;
|
|
|
|
if ( ! mibModule ) {
|
|
throw new ReferenceError ("MIB module " + moduleName + " not loaded");
|
|
}
|
|
syntaxTypes = this.getSyntaxTypes ();
|
|
entryArray = Object.values (mibModule);
|
|
for ( var i = 0; i < entryArray.length ; i++ ) {
|
|
mibEntry = entryArray[i];
|
|
var syntax = mibEntry.SYNTAX;
|
|
var access = mibEntry["ACCESS"];
|
|
var maxAccess = (typeof mibEntry["MAX-ACCESS"] != "undefined" ? mibEntry["MAX-ACCESS"] : (access ? AccessToMaxAccess[access] : "not-accessible"));
|
|
var defVal = mibEntry["DEFVAL"];
|
|
|
|
if ( syntax ) {
|
|
constraintsResults = ModuleStore.getConstraintsFromSyntax (syntax);
|
|
syntax = constraintsResults.syntax;
|
|
constraints = constraintsResults.constraints;
|
|
|
|
if ( syntax.startsWith ("SEQUENCE OF") ) {
|
|
// start of table
|
|
currentTableProvider = {
|
|
tableName: mibEntry.ObjectName,
|
|
type: MibProviderType.Table,
|
|
//oid: mibEntry.OID,
|
|
tableColumns: [],
|
|
tableIndex: [1] // default - assume first column is index
|
|
};
|
|
currentTableProvider.maxAccess = MaxAccess[maxAccess];
|
|
|
|
// read table to completion
|
|
while ( currentTableProvider || i >= entryArray.length ) {
|
|
i++;
|
|
mibEntry = entryArray[i];
|
|
if ( ! mibEntry ) {
|
|
tables.push (currentTableProvider);
|
|
currentTableProvider = null;
|
|
i--;
|
|
break;
|
|
}
|
|
syntax = mibEntry.SYNTAX;
|
|
access = mibEntry["ACCESS"];
|
|
maxAccess = (typeof mibEntry["MAX-ACCESS"] != "undefined" ? mibEntry["MAX-ACCESS"] : (access ? AccessToMaxAccess[access] : "not-accessible"));
|
|
defVal = mibEntry["DEFVAL"];
|
|
|
|
constraintsResults = ModuleStore.getConstraintsFromSyntax (syntax);
|
|
syntax = constraintsResults.syntax;
|
|
constraints = constraintsResults.constraints;
|
|
|
|
if ( mibEntry.MACRO == "SEQUENCE" ) {
|
|
// table entry sequence - ignore
|
|
} else if ( ! mibEntry["OBJECT IDENTIFIER"] ) {
|
|
// unexpected
|
|
} else {
|
|
parentOid = mibEntry["OBJECT IDENTIFIER"].split (" ")[0];
|
|
if ( parentOid == currentTableProvider.tableName ) {
|
|
// table entry
|
|
currentTableProvider.name = mibEntry.ObjectName;
|
|
currentTableProvider.oid = mibEntry.OID;
|
|
if ( mibEntry.INDEX ) {
|
|
currentTableProvider.tableIndex = [];
|
|
for ( var indexEntry of mibEntry.INDEX ) {
|
|
indexEntry = indexEntry.trim ();
|
|
if ( indexEntry.includes(" ") ) {
|
|
if ( indexEntry.split(" ")[0] == "IMPLIED" ) {
|
|
currentTableProvider.tableIndex.push ({
|
|
columnName: indexEntry.split(" ")[1],
|
|
implied: true
|
|
});
|
|
} else {
|
|
// unknown condition - guess that last token is name
|
|
currentTableProvider.tableIndex.push ({
|
|
columnName: indexEntry.split(" ").slice(-1)[0],
|
|
});
|
|
}
|
|
} else {
|
|
currentTableProvider.tableIndex.push ({
|
|
columnName: indexEntry
|
|
});
|
|
}
|
|
}
|
|
}
|
|
if ( mibEntry.AUGMENTS ) {
|
|
currentTableProvider.tableAugments = mibEntry.AUGMENTS[0].trim();
|
|
currentTableProvider.tableIndex = null;
|
|
}
|
|
} else if ( parentOid == currentTableProvider.name ) {
|
|
// table column
|
|
var columnDefinition = {
|
|
number: parseInt (mibEntry["OBJECT IDENTIFIER"].split (" ")[1]),
|
|
name: mibEntry.ObjectName,
|
|
type: syntaxTypes[syntax],
|
|
maxAccess: MaxAccess[maxAccess]
|
|
};
|
|
if ( constraints ) {
|
|
columnDefinition.constraints = constraints;
|
|
}
|
|
if (defVal) {
|
|
columnDefinition.defVal = defVal;
|
|
}
|
|
// If this column has syntax RowStatus and
|
|
// the MIB module imports RowStatus from
|
|
// SNMPv2-TC, mark this column as the
|
|
// rowStatus column so we can act on it.
|
|
// (See lib/mibs/SNMPv2-TC.mib#L186.)
|
|
if ( syntax == "RowStatus" &&
|
|
"IMPORTS" in mibModule &&
|
|
Array.isArray(mibModule.IMPORTS["SNMPv2-TC"]) &&
|
|
mibModule.IMPORTS["SNMPv2-TC"].includes("RowStatus") ) {
|
|
|
|
// Mark this column as being rowStatus
|
|
columnDefinition.rowStatus = true;
|
|
}
|
|
currentTableProvider.tableColumns.push (columnDefinition);
|
|
} else {
|
|
// table finished
|
|
tables.push (currentTableProvider);
|
|
// console.log ("Table: " + currentTableProvider.name);
|
|
currentTableProvider = null;
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
} else if ( mibEntry.MACRO == "OBJECT-TYPE" ) {
|
|
// OBJECT-TYPE entries not in a table are scalars
|
|
var scalarDefinition = {
|
|
name: mibEntry.ObjectName,
|
|
type: MibProviderType.Scalar,
|
|
oid: mibEntry.OID,
|
|
scalarType: syntaxTypes[syntax],
|
|
maxAccess: MaxAccess[maxAccess]
|
|
};
|
|
|
|
if (defVal) {
|
|
scalarDefinition.defVal = defVal;
|
|
}
|
|
|
|
if ( constraints ) {
|
|
scalarDefinition.constraints = constraints;
|
|
}
|
|
scalars.push (scalarDefinition);
|
|
// console.log ("Scalar: " + mibEntry.ObjectName);
|
|
}
|
|
}
|
|
}
|
|
return scalars.concat (tables);
|
|
};
|
|
|
|
ModuleStore.prototype.loadBaseModules = function () {
|
|
for ( var mibModule of ModuleStore.BASE_MODULES ) {
|
|
this.parser.Import (__dirname + "/lib/mibs/" + mibModule + ".mib");
|
|
}
|
|
this.parser.Serialize ();
|
|
};
|
|
|
|
ModuleStore.getConstraintsFromSyntax = function (syntax) {
|
|
let constraints;
|
|
|
|
// detect INTEGER ranges, OCTET STRING sizes, and INTEGER enumerations
|
|
if ( typeof syntax == "object" ) {
|
|
let firstSyntaxKey = syntax[Object.keys(syntax)[0]];
|
|
if ( firstSyntaxKey.ranges ) {
|
|
constraints = {
|
|
ranges: firstSyntaxKey.ranges
|
|
};
|
|
syntax = Object.keys(syntax)[0];
|
|
} else if ( firstSyntaxKey.sizes ) {
|
|
constraints = {
|
|
size: firstSyntaxKey.sizes
|
|
};
|
|
syntax = Object.keys(syntax)[0];
|
|
} else {
|
|
constraints = {
|
|
enumeration: syntax.INTEGER
|
|
};
|
|
syntax = "INTEGER";
|
|
}
|
|
} else {
|
|
constraints = null;
|
|
}
|
|
return {
|
|
constraints: constraints,
|
|
syntax: syntax
|
|
};
|
|
};
|
|
|
|
ModuleStore.create = function () {
|
|
var store = new ModuleStore ();
|
|
store.loadBaseModules ();
|
|
return store;
|
|
};
|
|
|
|
ModuleStore.BASE_MODULES = [
|
|
"RFC1155-SMI",
|
|
"RFC1158-MIB",
|
|
"RFC-1212",
|
|
"RFC1213-MIB",
|
|
"SNMPv2-SMI",
|
|
"SNMPv2-CONF",
|
|
"SNMPv2-TC",
|
|
"SNMPv2-MIB"
|
|
];
|
|
|
|
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.findChildImmediatelyBefore = function (index) {
|
|
var sortedChildrenKeys = Object.keys(this.children).sort(function (a, b) {
|
|
return (a - b);
|
|
});
|
|
|
|
if ( sortedChildrenKeys.length === 0 ) {
|
|
return null;
|
|
}
|
|
|
|
for ( var i = 0; i < sortedChildrenKeys.length; i++ ) {
|
|
if ( index < sortedChildrenKeys[i] ) {
|
|
if ( i === 0 ) {
|
|
return null;
|
|
} else {
|
|
return this.children[sortedChildrenKeys[i - 1]];
|
|
}
|
|
}
|
|
}
|
|
return this.children[sortedChildrenKeys[sortedChildrenKeys.length - 1]];
|
|
};
|
|
|
|
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.getTableColumnFromInstanceNode = function () {
|
|
if ( this.parent && this.parent.provider ) {
|
|
return this.address[this.address.length - 1];
|
|
} else if ( ! this.parent ) {
|
|
return null;
|
|
} else {
|
|
return this.parent.getTableColumnFromInstanceNode ();
|
|
}
|
|
};
|
|
|
|
MibNode.prototype.getConstraintsFromProvider = function () {
|
|
var providerNode = this.getAncestorProvider ();
|
|
if ( ! providerNode ) {
|
|
return null;
|
|
}
|
|
var provider = providerNode.provider;
|
|
if ( provider.type == MibProviderType.Scalar ) {
|
|
return provider.constraints;
|
|
} else if ( provider.type == MibProviderType.Table ) {
|
|
var columnNumber = this.getTableColumnFromInstanceNode ();
|
|
if ( ! columnNumber ) {
|
|
return null;
|
|
}
|
|
var columnDefinition = provider.tableColumns.filter (column => column.number == columnNumber)[0];
|
|
return columnDefinition ? columnDefinition.constraints : null;
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
MibNode.prototype.setValue = function (newValue) {
|
|
var len;
|
|
var min;
|
|
var max;
|
|
var range;
|
|
var found = false;
|
|
var constraints = this.getConstraintsFromProvider ();
|
|
if ( ! constraints ) {
|
|
this.value = newValue;
|
|
return true;
|
|
}
|
|
if ( constraints.enumeration ) {
|
|
if ( ! constraints.enumeration[newValue] ) {
|
|
return false;
|
|
}
|
|
} else if ( constraints.ranges ) {
|
|
for ( range of constraints.ranges ) {
|
|
min = "min" in range ? range.min : Number.MIN_SAFE_INTEGER;
|
|
max = "max" in range ? range.max : Number.MAX_SAFE_INTEGER;
|
|
if ( newValue >= min && newValue <= max ) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( ! found ) {
|
|
return false;
|
|
}
|
|
} else if ( constraints.sizes ) {
|
|
// if size is constrained, value must have a length property
|
|
if ( ! ( "length" in newValue ) ) {
|
|
return false;
|
|
}
|
|
len = newValue.length;
|
|
for ( range of constraints.sizes ) {
|
|
min = "min" in range ? range.min : Number.MIN_SAFE_INTEGER;
|
|
max = "max" in range ? range.max : Number.MAX_SAFE_INTEGER;
|
|
if ( len >= min && len <= max ) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( ! found ) {
|
|
return false;
|
|
}
|
|
}
|
|
this.value = newValue;
|
|
return true;
|
|
};
|
|
|
|
MibNode.prototype.getInstanceNodeForTableRow = function () {
|
|
var childCount = Object.keys (this.children).length;
|
|
if ( childCount == 0 ) {
|
|
if ( this.value != null ) {
|
|
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;
|
|
var remainingIndex;
|
|
|
|
if ( childCount == 0 ) {
|
|
if ( this.value != null ) {
|
|
return this;
|
|
} else {
|
|
// not found
|
|
return null;
|
|
}
|
|
} else {
|
|
if ( index.length == 0 ) {
|
|
return this.getInstanceNodeForTableRow();
|
|
} else {
|
|
var nextChildIndexPart = index[0];
|
|
if ( nextChildIndexPart == null ) {
|
|
return null;
|
|
}
|
|
remainingIndex = index.slice(1);
|
|
if ( this.children[nextChildIndexPart] ) {
|
|
return this.children[nextChildIndexPart].getInstanceNodeForTableRowIndex(remainingIndex);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
MibNode.prototype.getInstanceNodesForColumn = function () {
|
|
var columnNode = this;
|
|
var instanceNode = this;
|
|
var instanceNodes = [];
|
|
|
|
while (instanceNode && ( instanceNode == columnNode || columnNode.isAncestor (instanceNode.address) ) ) {
|
|
instanceNode = instanceNode.getNextInstanceNode ();
|
|
if ( instanceNode && columnNode.isAncestor (instanceNode.address) ) {
|
|
instanceNodes.push (instanceNode);
|
|
}
|
|
}
|
|
return instanceNodes;
|
|
};
|
|
|
|
MibNode.prototype.getNextInstanceNode = function () {
|
|
var siblingIndex;
|
|
var childrenAddresses;
|
|
|
|
var node = this;
|
|
if ( this.value != null ) {
|
|
// 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);
|
|
var siblingPosition = childrenAddresses.indexOf(siblingIndex.toString());
|
|
if ( siblingPosition + 1 < childrenAddresses.length ) {
|
|
node = node.children[childrenAddresses[siblingPosition + 1]];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Descent
|
|
while ( node ) {
|
|
if ( node.value != null ) {
|
|
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");
|
|
}
|
|
var 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 != null ) {
|
|
valueString = " = ";
|
|
valueString += options.showTypes ? ObjectType[this.valueType] + ": " : "";
|
|
valueString += options.showValues ? this.value : "";
|
|
} else {
|
|
valueString = "";
|
|
}
|
|
console.log (this.oid + valueString);
|
|
}
|
|
for ( var 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;
|
|
}
|
|
|
|
ancestorAddress.forEach (function (o, i) {
|
|
if (address[i] !== ancestorAddress[i]) {
|
|
isAncestor = false;
|
|
}
|
|
});
|
|
|
|
return isAncestor;
|
|
};
|
|
|
|
var Mib = function () {
|
|
var providersByOid;
|
|
this.root = new MibNode ([], null);
|
|
this.providerNodes = {};
|
|
|
|
// this.providers will be modified throughout this code.
|
|
// Keep this.providersByOid in sync with it
|
|
providersByOid = this.providersByOid = {};
|
|
this.providers = new Proxy({}, {
|
|
set: function (target, key, value) {
|
|
target[key] = value;
|
|
providersByOid[value.oid] = value;
|
|
},
|
|
|
|
deleteProperty: function (target, key) {
|
|
delete providersByOid[target[key].oid];
|
|
delete target[key];
|
|
}
|
|
});
|
|
};
|
|
|
|
Mib.prototype.addNodesForOid = function (oidString) {
|
|
var address = Mib.convertOidToAddress (oidString);
|
|
return this.addNodesForAddress (address);
|
|
};
|
|
|
|
Mib.prototype.addNodesForAddress = function (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;
|
|
|
|
address = Mib.convertOidToAddress (oid);
|
|
return this.lookupAddress(address);
|
|
};
|
|
|
|
Mib.prototype.lookupAddress = function (address) {
|
|
var i;
|
|
var node;
|
|
|
|
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.getTreeNode = function (oid) {
|
|
var address = Mib.convertOidToAddress (oid);
|
|
var node;
|
|
|
|
node = this.lookupAddress (address);
|
|
// OID already on tree
|
|
if ( node ) {
|
|
return node;
|
|
}
|
|
|
|
while ( address.length > 0 ) {
|
|
var last = address.pop ();
|
|
var parent = this.lookupAddress (address);
|
|
if ( parent ) {
|
|
node = parent.findChildImmediatelyBefore (last);
|
|
if ( !node )
|
|
return parent;
|
|
while ( true ) {
|
|
// Find the last descendant
|
|
var childrenAddresses = Object.keys (node.children).sort ( (a, b) => a - b);
|
|
if ( childrenAddresses.length == 0 )
|
|
return node;
|
|
node = node.children[childrenAddresses[childrenAddresses.length - 1]];
|
|
}
|
|
}
|
|
}
|
|
return this.root;
|
|
|
|
};
|
|
|
|
Mib.prototype.getProviderNodeForInstance = function (instanceNode) {
|
|
if ( instanceNode.provider ) {
|
|
// throw new ReferenceError ("Instance node has provider which should never happen");
|
|
return null;
|
|
}
|
|
return instanceNode.getAncestorProvider ();
|
|
};
|
|
|
|
Mib.prototype.addProviderToNode = function (provider) {
|
|
var node = this.addNodesForOid (provider.oid);
|
|
|
|
node.provider = provider;
|
|
if ( provider.type == MibProviderType.Table ) {
|
|
if ( ! provider.tableIndex ) {
|
|
provider.tableIndex = [1];
|
|
}
|
|
}
|
|
this.providerNodes[provider.name] = node;
|
|
return node;
|
|
};
|
|
|
|
Mib.prototype.getColumnFromProvider = function (provider, indexEntry) {
|
|
var column = null;
|
|
if ( indexEntry.columnName ) {
|
|
column = provider.tableColumns.filter (column => column.name == indexEntry.columnName )[0];
|
|
} else if ( indexEntry.columnNumber !== undefined && indexEntry.columnNumber !== null ) {
|
|
column = provider.tableColumns.filter (column => column.number == indexEntry.columnNumber )[0];
|
|
}
|
|
return column;
|
|
};
|
|
|
|
Mib.prototype.populateIndexEntryFromColumn = function (localProvider, indexEntry, i) {
|
|
var column = null;
|
|
var tableProviders;
|
|
if ( ! indexEntry.columnName && ! indexEntry.columnNumber ) {
|
|
throw new Error ("Index entry " + i + ": does not have either a columnName or columnNumber");
|
|
}
|
|
if ( indexEntry.foreign ) {
|
|
// Explicit foreign table is first to search
|
|
column = this.getColumnFromProvider (this.providers[indexEntry.foreign], indexEntry);
|
|
} else {
|
|
// If foreign table isn't given, search the local table next
|
|
column = this.getColumnFromProvider (localProvider, indexEntry);
|
|
if ( ! column ) {
|
|
// as a last resort, try to find the column in a foreign table
|
|
tableProviders = Object.values(this.providers).
|
|
filter ( prov => prov.type == MibProviderType.Table );
|
|
for ( var provider of tableProviders ) {
|
|
column = this.getColumnFromProvider (provider, indexEntry);
|
|
if ( column ) {
|
|
indexEntry.foreign = provider.name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( ! column ) {
|
|
throw new Error ("Could not find column for index entry with column " + indexEntry.columnName);
|
|
}
|
|
if ( indexEntry.columnName && indexEntry.columnName != column.name ) {
|
|
throw new Error ("Index entry " + i + ": Calculated column name " + column.name +
|
|
"does not match supplied column name " + indexEntry.columnName);
|
|
}
|
|
if ( indexEntry.columnNumber && indexEntry.columnNumber != column.number ) {
|
|
throw new Error ("Index entry " + i + ": Calculated column number " + column.number +
|
|
" does not match supplied column number " + indexEntry.columnNumber);
|
|
}
|
|
if ( ! indexEntry.columnName ) {
|
|
indexEntry.columnName = column.name;
|
|
}
|
|
if ( ! indexEntry.columnNumber ) {
|
|
indexEntry.columnNumber = column.number;
|
|
}
|
|
indexEntry.type = column.type;
|
|
|
|
};
|
|
|
|
Mib.prototype.registerProvider = function (provider) {
|
|
this.providers[provider.name] = provider;
|
|
if ( provider.type == MibProviderType.Table ) {
|
|
if ( provider.tableAugments ) {
|
|
if ( provider.tableAugments == provider.name ) {
|
|
throw new Error ("Table " + provider.name + " cannot augment itself");
|
|
}
|
|
var augmentProvider = this.providers[provider.tableAugments];
|
|
if ( ! augmentProvider ) {
|
|
throw new Error ("Cannot find base table " + provider.tableAugments + " to augment");
|
|
}
|
|
provider.tableIndex = JSON.parse(JSON.stringify(augmentProvider.tableIndex));
|
|
provider.tableIndex.map (index => index.foreign = augmentProvider.name);
|
|
} else {
|
|
if ( ! provider.tableIndex ) {
|
|
provider.tableIndex = [1]; // default to first column index
|
|
}
|
|
for ( var i = 0 ; i < provider.tableIndex.length ; i++ ) {
|
|
var indexEntry = provider.tableIndex[i];
|
|
if ( typeof indexEntry == 'number' ) {
|
|
provider.tableIndex[i] = {
|
|
columnNumber: indexEntry
|
|
};
|
|
} else if ( typeof indexEntry == 'string' ) {
|
|
provider.tableIndex[i] = {
|
|
columnName: indexEntry
|
|
};
|
|
}
|
|
indexEntry = provider.tableIndex[i];
|
|
this.populateIndexEntryFromColumn (provider, indexEntry, i);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
Mib.prototype.setScalarDefaultValue = function (name, value) {
|
|
let provider = this.getProvider(name);
|
|
provider.defVal = value;
|
|
};
|
|
|
|
Mib.prototype.setTableRowDefaultValues = function (name, values) {
|
|
let provider = this.getProvider(name);
|
|
let tc = provider.tableColumns;
|
|
|
|
// We must be given an array of exactly the right number of columns
|
|
if (values.length != tc.length) {
|
|
throw new Error(`Incorrect values length: got ${values.length}; expected ${tc.length}`);
|
|
}
|
|
|
|
// Add defVal to each table column.
|
|
tc.forEach((entry, i) => {
|
|
if (typeof values[i] != "undefined") {
|
|
entry.defVal = values[i];
|
|
}
|
|
});
|
|
};
|
|
|
|
Mib.prototype.setScalarRanges = function (name, ranges ) {
|
|
let provider = this.getProvider(name);
|
|
provider.constraints = { ranges };
|
|
};
|
|
|
|
Mib.prototype.setTableColumnRanges = function(name, column, ranges ) {
|
|
let provider = this.getProvider(name);
|
|
let tc = provider.tableColumns;
|
|
tc[column].constraints = { ranges };
|
|
};
|
|
|
|
Mib.prototype.setScalarSizes = function (name, sizes ) {
|
|
let provider = this.getProvider(name);
|
|
provider.constraints = { sizes };
|
|
};
|
|
|
|
Mib.prototype.setTableColumnSizes = function(name, column, sizes ) {
|
|
let provider = this.getProvider(name);
|
|
let tc = provider.tableColumns;
|
|
tc[column].constraints = { sizes };
|
|
};
|
|
|
|
Mib.prototype.registerProviders = function (providers) {
|
|
for ( var provider of providers ) {
|
|
this.registerProvider (provider);
|
|
}
|
|
};
|
|
|
|
Mib.prototype.unregisterProvider = function (name) {
|
|
var providerNode = this.providerNodes[name];
|
|
if ( providerNode ) {
|
|
var 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.dumpProviders = function () {
|
|
var extraInfo;
|
|
for ( var provider of Object.values(this.providers) ) {
|
|
extraInfo = provider.type == MibProviderType.Scalar ? ObjectType[provider.scalarType] : "Columns = " + provider.tableColumns.length;
|
|
console.log(MibProviderType[provider.type] + ": " + provider.name + " (" + provider.oid + "): " + extraInfo);
|
|
}
|
|
};
|
|
|
|
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;
|
|
var provider;
|
|
|
|
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]);
|
|
}
|
|
provider = providerNode.provider;
|
|
if ( ! providerNode || ! provider || 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 = provider.scalarType;
|
|
}
|
|
instanceNode.value = newValue;
|
|
// return instanceNode.setValue (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.getOidAddressFromValue = function (value, indexPart) {
|
|
var oidComponents;
|
|
switch ( indexPart.type ) {
|
|
case ObjectType.OID:
|
|
oidComponents = value.split (".");
|
|
break;
|
|
case ObjectType.OctetString:
|
|
if ( value instanceof Buffer ) {
|
|
// Buffer
|
|
oidComponents = Array.prototype.slice.call (value);
|
|
} else {
|
|
// string
|
|
oidComponents = [...value].map (c => c.charCodeAt());
|
|
}
|
|
break;
|
|
case ObjectType.IpAddress:
|
|
return value.split (".");
|
|
default:
|
|
return [value];
|
|
}
|
|
if ( ! indexPart.implied && ! indexPart.length ) {
|
|
oidComponents.unshift (oidComponents.length);
|
|
}
|
|
return oidComponents;
|
|
};
|
|
|
|
/* What is this empty function here for?
|
|
Mib.prototype.getValueFromOidAddress = function (oid, indexPart) {
|
|
|
|
};
|
|
*/
|
|
|
|
Mib.prototype.getTableRowInstanceFromRow = function (provider, row) {
|
|
var rowIndex = [];
|
|
var foreignColumnParts;
|
|
var localColumnParts;
|
|
var localColumnPosition;
|
|
var oidArrayForValue;
|
|
|
|
// foreign columns are first in row
|
|
foreignColumnParts = provider.tableIndex.filter ( indexPart => indexPart.foreign );
|
|
for ( var i = 0; i < foreignColumnParts.length ; i++ ) {
|
|
//rowIndex.push (row[i]);
|
|
oidArrayForValue = this.getOidAddressFromValue (row[i], foreignColumnParts[i]);
|
|
rowIndex = rowIndex.concat (oidArrayForValue);
|
|
}
|
|
// then local columns
|
|
localColumnParts = provider.tableIndex.filter ( indexPart => ! indexPart.foreign );
|
|
for ( var localColumnPart of localColumnParts ) {
|
|
localColumnPosition = provider.tableColumns.findIndex (column => column.number == localColumnPart.columnNumber);
|
|
oidArrayForValue = this.getOidAddressFromValue (row[foreignColumnParts.length + localColumnPosition], localColumnPart);
|
|
rowIndex = rowIndex.concat (oidArrayForValue);
|
|
}
|
|
return rowIndex;
|
|
};
|
|
|
|
Mib.getRowIndexFromOid = function (oid, index) {
|
|
var addressRemaining = oid.split (".");
|
|
var length = 0;
|
|
var values = [];
|
|
var value;
|
|
for ( var indexPart of index ) {
|
|
switch ( indexPart.type ) {
|
|
case ObjectType.OID:
|
|
if ( indexPart.implied ) {
|
|
length = addressRemaining.length;
|
|
} else {
|
|
length = addressRemaining.shift ();
|
|
}
|
|
value = addressRemaining.splice (0, length);
|
|
values.push (value.join ("."));
|
|
break;
|
|
case ObjectType.IpAddress:
|
|
length = 4;
|
|
value = addressRemaining.splice (0, length);
|
|
values.push (value.join ("."));
|
|
break;
|
|
case ObjectType.OctetString:
|
|
if ( indexPart.implied ) {
|
|
length = addressRemaining.length;
|
|
} else {
|
|
length = addressRemaining.shift ();
|
|
}
|
|
value = addressRemaining.splice (0, length);
|
|
value = value.map (c => String.fromCharCode(c)).join ("");
|
|
values.push (value);
|
|
break;
|
|
default:
|
|
values.push (parseInt (addressRemaining.shift ()) );
|
|
}
|
|
}
|
|
return values;
|
|
};
|
|
|
|
Mib.prototype.getTableRowInstanceFromRowIndex = function (provider, rowIndex) {
|
|
var rowIndexOid = [];
|
|
var indexPart;
|
|
var keyPart;
|
|
for ( var i = 0; i < provider.tableIndex.length ; i++ ) {
|
|
indexPart = provider.tableIndex[i];
|
|
keyPart = rowIndex[i];
|
|
rowIndexOid = rowIndexOid.concat (this.getOidAddressFromValue (keyPart, indexPart));
|
|
}
|
|
return rowIndexOid;
|
|
};
|
|
|
|
Mib.prototype.addTableRow = function (table, row) {
|
|
var providerNode;
|
|
var provider;
|
|
var instance = [];
|
|
var instanceAddress;
|
|
var instanceNode;
|
|
var rowValueOffset;
|
|
|
|
if ( this.providers[table] && ! this.providerNodes[table] ) {
|
|
this.addProviderToNode (this.providers[table]);
|
|
}
|
|
providerNode = this.getProviderNodeForTable (table);
|
|
provider = providerNode.provider;
|
|
rowValueOffset = provider.tableIndex.filter ( indexPart => indexPart.foreign ).length;
|
|
instance = this.getTableRowInstanceFromRow (provider, row);
|
|
for ( var i = 0; i < provider.tableColumns.length ; i++ ) {
|
|
var column = provider.tableColumns[i];
|
|
var isColumnIndex = provider.tableIndex.some ( indexPart => indexPart.columnNumber == column.number );
|
|
// prevent not-accessible and accessible-for-notify index entries from being added as columns in the row
|
|
if ( ! isColumnIndex || ! (column.maxAccess === MaxAccess['not-accessible'] || column.maxAccess === MaxAccess['accessible-for-notify']) ) {
|
|
instanceAddress = providerNode.address.concat (column.number).concat (instance);
|
|
this.addNodesForAddress (instanceAddress);
|
|
instanceNode = this.lookup (instanceAddress);
|
|
instanceNode.valueType = column.type;
|
|
instanceNode.value = row[rowValueOffset + i];
|
|
}
|
|
}
|
|
};
|
|
|
|
Mib.prototype.getTableColumnDefinitions = function (table) {
|
|
var providerNode;
|
|
var provider;
|
|
|
|
providerNode = this.getProviderNodeForTable (table);
|
|
provider = providerNode.provider;
|
|
return provider.tableColumns;
|
|
};
|
|
|
|
Mib.prototype.getTableColumnCells = function (table, columnNumber, includeInstances) {
|
|
var provider = this.providers[table];
|
|
var providerIndex = provider.tableIndex;
|
|
var providerNode = this.getProviderNodeForTable (table);
|
|
var columnNode = providerNode.children[columnNumber];
|
|
if ( ! columnNode ) {
|
|
return null;
|
|
}
|
|
var instanceNodes = columnNode.getInstanceNodesForColumn ();
|
|
var instanceOid;
|
|
var indexValues = [];
|
|
var columnValues = [];
|
|
|
|
for ( var instanceNode of instanceNodes ) {
|
|
instanceOid = Mib.getSubOidFromBaseOid (instanceNode.oid, columnNode.oid);
|
|
indexValues.push (Mib.getRowIndexFromOid (instanceOid, providerIndex));
|
|
columnValues.push (instanceNode.value);
|
|
}
|
|
if ( includeInstances ) {
|
|
return [ indexValues, columnValues ];
|
|
} else {
|
|
return columnValues;
|
|
}
|
|
};
|
|
|
|
Mib.prototype.getTableRowCells = function (table, rowIndex) {
|
|
var provider;
|
|
var providerNode;
|
|
var columnNode;
|
|
var instanceAddress;
|
|
var instanceNode;
|
|
var row = [];
|
|
var rowFound = false;
|
|
|
|
provider = this.providers[table];
|
|
providerNode = this.getProviderNodeForTable (table);
|
|
instanceAddress = this.getTableRowInstanceFromRowIndex (provider, rowIndex);
|
|
for ( var columnNumber of Object.keys (providerNode.children) ) {
|
|
columnNode = providerNode.children[columnNumber];
|
|
if ( columnNode ) {
|
|
instanceNode = columnNode.getInstanceNodeForTableRowIndex (instanceAddress);
|
|
if ( instanceNode ) {
|
|
row.push (instanceNode.value);
|
|
rowFound = true;
|
|
} else {
|
|
row.push (null);
|
|
}
|
|
} else {
|
|
row.push (null);
|
|
}
|
|
}
|
|
if ( rowFound ) {
|
|
return row;
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
Mib.prototype.getTableCells = function (table, byRows, includeInstances) {
|
|
var providerNode;
|
|
var column;
|
|
var data = [];
|
|
|
|
providerNode = this.getProviderNodeForTable (table);
|
|
for ( var columnNumber of Object.keys (providerNode.children) ) {
|
|
column = this.getTableColumnCells (table, columnNumber, includeInstances);
|
|
if ( includeInstances ) {
|
|
data.push (...column);
|
|
includeInstances = false;
|
|
} else {
|
|
data.push (column);
|
|
}
|
|
}
|
|
|
|
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 provider;
|
|
var providerNode;
|
|
var instanceAddress;
|
|
var columnNode;
|
|
var instanceNode;
|
|
|
|
provider = this.providers[table];
|
|
providerNode = this.getProviderNodeForTable (table);
|
|
instanceAddress = this.getTableRowInstanceFromRowIndex (provider, rowIndex);
|
|
columnNode = providerNode.children[columnNumber];
|
|
instanceNode = columnNode.getInstanceNodeForTableRowIndex (instanceAddress);
|
|
return instanceNode.value;
|
|
};
|
|
|
|
Mib.prototype.setTableSingleCell = function (table, columnNumber, rowIndex, value) {
|
|
var provider;
|
|
var providerNode;
|
|
var columnNode;
|
|
var instanceNode;
|
|
var instanceAddress;
|
|
|
|
provider = this.providers[table];
|
|
providerNode = this.getProviderNodeForTable (table);
|
|
instanceAddress = this.getTableRowInstanceFromRowIndex (provider, rowIndex);
|
|
columnNode = providerNode.children[columnNumber];
|
|
instanceNode = columnNode.getInstanceNodeForTableRowIndex (instanceAddress);
|
|
instanceNode.value = value;
|
|
// return instanceNode.setValue (value);
|
|
};
|
|
|
|
Mib.prototype.deleteTableRow = function (table, rowIndex) {
|
|
var provider;
|
|
var providerNode;
|
|
var instanceAddress;
|
|
var columnNode;
|
|
var instanceNode;
|
|
var instanceParentNode;
|
|
|
|
provider = this.providers[table];
|
|
providerNode = this.getProviderNodeForTable (table);
|
|
instanceAddress = this.getTableRowInstanceFromRowIndex (provider, rowIndex);
|
|
for ( var columnNumber of Object.keys (providerNode.children) ) {
|
|
columnNode = providerNode.children[columnNumber];
|
|
instanceNode = columnNode.getInstanceNodeForTableRowIndex (instanceAddress);
|
|
if ( instanceNode ) {
|
|
instanceParentNode = instanceNode.parent;
|
|
instanceNode.delete();
|
|
instanceParentNode.pruneUpwards();
|
|
} else {
|
|
throw new ReferenceError ("Cannot find row for index " + rowIndex + " at registered provider " + table);
|
|
}
|
|
}
|
|
if ( Object.keys (this.providerNodes[table].children).length === 0 ) {
|
|
delete this.providerNodes[table];
|
|
}
|
|
return true;
|
|
};
|
|
|
|
Mib.prototype.dump = function (options) {
|
|
if ( ! options ) {
|
|
options = {};
|
|
}
|
|
var completedOptions = {
|
|
leavesOnly: options.leavesOnly === undefined ? true : options.leavesOnly,
|
|
showProviders: options.showProviders === undefined ? true : options.showProviders,
|
|
showValues: options.showValues === undefined ? true : options.showValues,
|
|
showTypes: options.showTypes === undefined ? true : options.showTypes
|
|
};
|
|
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 < 1)
|
|
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_SIGNED_INT32) {
|
|
throw new RangeError('object identifier component ' +
|
|
address[i] + ' is too large');
|
|
}
|
|
oidArray.push(n);
|
|
}
|
|
|
|
return oidArray;
|
|
|
|
};
|
|
|
|
Mib.getSubOidFromBaseOid = function (oid, base) {
|
|
return oid.substring (base.length + 1);
|
|
};
|
|
|
|
Mib.create = function () {
|
|
return new Mib ();
|
|
};
|
|
|
|
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;
|
|
};
|
|
|
|
MibRequest.prototype.isTabular = function () {
|
|
return this.providerNode && this.providerNode.provider &&
|
|
this.providerNode.provider.type == MibProviderType.Table;
|
|
};
|
|
|
|
var Agent = function (options, callback, mib) {
|
|
DEBUG = options.debug;
|
|
this.listener = new Listener (options, this);
|
|
this.engine = new Engine (options.engineID);
|
|
this.authorizer = new Authorizer (options);
|
|
this.callback = callback || function () {};
|
|
this.mib = mib || new Mib ();
|
|
this.context = "";
|
|
this.forwarder = new Forwarder (this.listener, this.callback);
|
|
};
|
|
|
|
Agent.prototype.getMib = function () {
|
|
return this.mib;
|
|
};
|
|
|
|
Agent.prototype.setMib = function (mib) {
|
|
this.mib = mib;
|
|
};
|
|
|
|
Agent.prototype.getAuthorizer = function () {
|
|
return this.authorizer;
|
|
};
|
|
|
|
Agent.prototype.registerProvider = function (provider) {
|
|
this.mib.registerProvider (provider);
|
|
};
|
|
|
|
Agent.prototype.registerProviders = function (providers) {
|
|
this.mib.registerProviders (providers);
|
|
};
|
|
|
|
Agent.prototype.unregisterProvider = function (name) {
|
|
this.mib.unregisterProvider (name);
|
|
};
|
|
|
|
Agent.prototype.getProvider = function (name) {
|
|
return this.mib.getProvider (name);
|
|
};
|
|
|
|
Agent.prototype.getProviders = function () {
|
|
return this.mib.getProviders ();
|
|
};
|
|
|
|
Agent.prototype.scalarReadCreateHandlerInternal = function (createRequest) {
|
|
let provider = createRequest.provider;
|
|
// If there's a default value specified...
|
|
if ( provider && typeof provider.defVal != "undefined" ) {
|
|
// ... then use it
|
|
return provider.defVal;
|
|
}
|
|
|
|
// We don't have enough information to auto-create the scalar
|
|
return undefined;
|
|
};
|
|
|
|
Agent.prototype.tableRowStatusHandlerInternal = function (createRequest) {
|
|
let provider = createRequest.provider;
|
|
let action = createRequest.action;
|
|
let row = createRequest.row;
|
|
let values = [];
|
|
let missingDefVal = false;
|
|
let rowIndexValues = Array.isArray( row ) ? row.slice(0) : [ row ];
|
|
const tc = provider.tableColumns;
|
|
|
|
tc.forEach(
|
|
(columnInfo) => {
|
|
let entries;
|
|
|
|
// Index columns get successive values from the rowIndexValues array.
|
|
// RowStatus columns get either "active" or "notInService" values.
|
|
// Every other column requires a defVal.
|
|
entries = provider.tableIndex.filter( entry => columnInfo.number === entry.columnNumber );
|
|
if (entries.length > 0 ) {
|
|
// It's an index column. Use the next index value
|
|
values.push(rowIndexValues.shift());
|
|
} else if ( columnInfo.rowStatus ) {
|
|
// It's the RowStatus column. Retain the action value for now; replaced later
|
|
values.push( RowStatus[action] );
|
|
} else if ( "defVal" in columnInfo ) {
|
|
// Neither index nor RowStatus column, so use the default value
|
|
values.push( columnInfo.defVal );
|
|
} else {
|
|
// Default value was required but not found
|
|
console.log("No defVal defined for column:", columnInfo);
|
|
missingDefVal = true;
|
|
values.push( undefined ); // just for debugging; never gets returned
|
|
}
|
|
}
|
|
);
|
|
|
|
// If a default value was missing, we can't auto-create the table row.
|
|
// Otherwise, we're good to go: give 'em the column values.
|
|
return missingDefVal ? undefined : values;
|
|
};
|
|
|
|
Agent.prototype.onMsg = function (buffer, rinfo) {
|
|
var message = Listener.processIncoming (buffer, this.authorizer, this.callback);
|
|
var reportMessage;
|
|
|
|
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.contextName && message.pdu.contextName != "" ) {
|
|
this.onProxyRequest (message, rinfo);
|
|
} else if ( message.pdu.type == PduType.GetRequest ) {
|
|
this.getRequest (message, rinfo);
|
|
} else if ( message.pdu.type == PduType.SetRequest ) {
|
|
this.setRequest (message, rinfo);
|
|
} else if ( message.pdu.type == PduType.GetNextRequest ) {
|
|
this.getNextRequest (message, rinfo);
|
|
} else if ( message.pdu.type == PduType.GetBulkRequest ) {
|
|
this.getBulkRequest (message, rinfo);
|
|
} else {
|
|
this.callback (new RequestInvalidError ("Unexpected PDU type " +
|
|
message.pdu.type + " (" + PduType[message.pdu.type] + ")"));
|
|
}
|
|
};
|
|
|
|
Agent.prototype.castSetValue = function ( type, value ) {
|
|
switch (type) {
|
|
case ObjectType.Boolean:
|
|
return !! value;
|
|
|
|
case ObjectType.Integer:
|
|
if ( typeof value != "number" && typeof value != "string" ) {
|
|
throw new Error("Invalid Integer", value);
|
|
}
|
|
return typeof value == "number" ? value : parseInt(value, 10);
|
|
|
|
case ObjectType.OctetString:
|
|
if ( value instanceof Buffer) {
|
|
return value.toString();
|
|
} else if ( typeof value != "string" ) {
|
|
throw new Error("Invalid OctetString", value);
|
|
} else {
|
|
return value;
|
|
}
|
|
|
|
case ObjectType.OID:
|
|
if ( typeof value != "string" || ! value.match(/[0-9]+\([.][0-9]+\)+/) ) {
|
|
throw new Error("Invalid OID", value);
|
|
}
|
|
return value;
|
|
|
|
case ObjectType.Counter:
|
|
case ObjectType.Counter64:
|
|
// Counters should be initialized to 0 (RFC2578, end of section 7.9)
|
|
// We'll do so.
|
|
return 0;
|
|
|
|
case ObjectType.IpAddress:
|
|
// A 32-bit internet address represented as OCTET STRING of length 4
|
|
if ( typeof value != "string" || value.length != 4 ) {
|
|
throw new Error("Invalid IpAddress", value);
|
|
}
|
|
return value;
|
|
|
|
default :
|
|
// Assume the caller knows what he's doing
|
|
return value;
|
|
}
|
|
};
|
|
|
|
|
|
Agent.prototype.tryCreateInstance = function (varbind, requestType) {
|
|
var row;
|
|
var column;
|
|
var value;
|
|
var subOid;
|
|
var subAddr;
|
|
var address;
|
|
var fullAddress;
|
|
var rowStatusColumn;
|
|
var provider;
|
|
var providersByOid = this.mib.providersByOid;
|
|
var oid = varbind.oid;
|
|
var createRequest;
|
|
|
|
// Look for the provider.
|
|
fullAddress = Mib.convertOidToAddress (oid);
|
|
for ( address = fullAddress.slice(0) ; address.length > 0; address.pop() ) {
|
|
subOid = address.join("."); // create an oid from the current address
|
|
|
|
// Does this oid have a provider?
|
|
provider = providersByOid[subOid];
|
|
if (provider) {
|
|
// Yup. Figure out what to do with it.
|
|
// console.log(`FOUND MATCH TO ${oid}:\n`, providersByOid[subOid]);
|
|
|
|
//
|
|
// Scalar
|
|
//
|
|
if ( provider.type === MibProviderType.Scalar ) {
|
|
|
|
// Does this provider support "read-create"?
|
|
if ( provider.maxAccess != MaxAccess["read-create"] ) {
|
|
// Nope. Nothing we can do to help 'em.
|
|
return undefined;
|
|
}
|
|
|
|
// See if the provider says not to auto-create this scalar
|
|
if ( provider.createHandler === null ) {
|
|
return undefined;
|
|
}
|
|
|
|
// Call the provider-provided handler if available, or the default one if not
|
|
createRequest = {
|
|
provider: provider
|
|
};
|
|
value = ( provider.createHandler || this.scalarReadCreateHandlerInternal ) ( createRequest );
|
|
if ( typeof value == "undefined" ) {
|
|
// Handler said do not create instance
|
|
return undefined;
|
|
}
|
|
|
|
// Ensure the value is of the correct type, and save it
|
|
value = this.castSetValue ( provider.scalarType, value );
|
|
this.mib.setScalarValue ( provider.name, value );
|
|
|
|
// Now there should be an instanceNode available.
|
|
return {
|
|
instanceNode: this.mib.lookup (oid),
|
|
providerType: MibProviderType.Scalar
|
|
};
|
|
}
|
|
|
|
//
|
|
// Table
|
|
//
|
|
|
|
// This is where we would support "read-create" of table
|
|
// columns. RFC2578 section 7.1.12.1, however, implies
|
|
// that rows should be created only via use of the
|
|
// RowStatus column. We'll therefore avoid creating rows
|
|
// based solely on any other column's "read-create"
|
|
// max-access value.
|
|
|
|
//
|
|
// RowStatus setter (actions)
|
|
//
|
|
subOid = Mib.getSubOidFromBaseOid (oid, provider.oid);
|
|
subAddr = subOid.split(".");
|
|
column = parseInt(subAddr.shift(), 10);
|
|
row = Mib.getRowIndexFromOid(subAddr.join("."), provider.tableIndex);
|
|
rowStatusColumn = provider.tableColumns.reduce( (acc, current) => current.rowStatus ? current.number : acc, null );
|
|
|
|
if ( requestType === PduType.SetRequest &&
|
|
typeof rowStatusColumn == "number" &&
|
|
column === rowStatusColumn ) {
|
|
|
|
if ( (varbind.value === RowStatus["createAndGo"] || varbind.value === RowStatus["createAndWait"]) &&
|
|
provider.createHandler !== null ) {
|
|
|
|
// The create handler will return an array
|
|
// containing all table column values for the
|
|
// table row to be added.
|
|
createRequest = {
|
|
provider: provider,
|
|
action: RowStatus[varbind.value],
|
|
row: row
|
|
};
|
|
value = ( provider.createHandler || this.tableRowStatusHandlerInternal )( createRequest );
|
|
if ( typeof value == "undefined") {
|
|
// Handler said do not create instance
|
|
return undefined;
|
|
}
|
|
|
|
if (! Array.isArray( value ) ) {
|
|
throw new Error("createHandler must return an array or undefined; got", value);
|
|
}
|
|
|
|
if ( value.length != provider.tableColumns.length ) {
|
|
throw new Error("createHandler's returned array must contain a value for for each column" );
|
|
}
|
|
|
|
// Map each column's value to the appropriate type
|
|
value = value.map( (v, i) => this.castSetValue ( provider.tableColumns[i].type, v ) );
|
|
|
|
// Add the table row
|
|
this.mib.addTableRow ( provider.name, value );
|
|
|
|
// Now there should be an instanceNode available.
|
|
return {
|
|
instanceNode: this.mib.lookup (oid),
|
|
providerType: MibProviderType.Table,
|
|
action: RowStatus[varbind.value],
|
|
rowIndex: row,
|
|
row: value
|
|
};
|
|
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
// console.log(`NO MATCH TO ${oid}`);
|
|
return undefined;
|
|
};
|
|
|
|
Agent.prototype.isAllowed = function (pduType, provider, instanceNode) {
|
|
var column;
|
|
var maxAccess;
|
|
var columnEntry;
|
|
|
|
if (provider.type === MibProviderType.Scalar) {
|
|
// It's a scalar. We'll use the provider's maxAccess
|
|
maxAccess = provider.maxAccess;
|
|
} else {
|
|
// It's a table column. Use that column's maxAccess.
|
|
column = instanceNode.getTableColumnFromInstanceNode();
|
|
|
|
// In the typical case, we could use (column - 1) to index
|
|
// into tableColumns to get to the correct entry. There is no
|
|
// guarantee, however, that column numbers in the OID are
|
|
// necessarily consecutive; theoretically some could be
|
|
// missing. We'll therefore play it safe and search for the
|
|
// specified column entry.
|
|
|
|
columnEntry = provider.tableColumns.find(entry => entry.number === column);
|
|
maxAccess = columnEntry ? columnEntry.maxAccess || MaxAccess['not-accessible'] : MaxAccess['not-accessible'];
|
|
}
|
|
|
|
switch ( PduType[pduType] ) {
|
|
case "SetRequest":
|
|
// SetRequest requires at least read-write access
|
|
return maxAccess >= MaxAccess["read-write"];
|
|
|
|
case "GetRequest":
|
|
case "GetNextRequest":
|
|
case "GetBulkRequest":
|
|
// GetRequests require at least read-only access
|
|
return maxAccess >= MaxAccess["read-only"];
|
|
|
|
default:
|
|
// Disallow other pdu types
|
|
return false;
|
|
}
|
|
};
|
|
|
|
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 ();
|
|
var mibRequests = [];
|
|
var handlers = [];
|
|
var createResult = [];
|
|
var oldValues = [];
|
|
var securityName = requestMessage.version == Version3 ? requestMessage.user.name : requestMessage.community;
|
|
|
|
for ( let i = 0; i < requestPdu.varbinds.length; i++ ) {
|
|
let instanceNode = this.mib.lookup (requestPdu.varbinds[i].oid);
|
|
let providerNode;
|
|
let rowStatusColumn;
|
|
let getIcsHandler;
|
|
|
|
// If we didn't find an instance node, see if we can
|
|
// automatically create it, either because it has
|
|
// "read-create" MAX-ACCESS, or because it's a RowStatus SET
|
|
// indicating create.
|
|
if ( ! instanceNode ) {
|
|
createResult[i] = this.tryCreateInstance(requestPdu.varbinds[i], requestPdu.type);
|
|
if ( createResult[i] ) {
|
|
instanceNode = createResult[i].instanceNode;
|
|
}
|
|
}
|
|
|
|
// workaround re-write of OIDs less than 4 digits due to asn1-ber length limitation
|
|
if ( requestPdu.varbinds[i].oid.split('.').length < 4 ) {
|
|
requestPdu.varbinds[i].oid = "1.3.6.1";
|
|
}
|
|
|
|
if ( ! instanceNode ) {
|
|
mibRequests[i] = new MibRequest ({
|
|
operation: requestPdu.type,
|
|
oid: requestPdu.varbinds[i].oid
|
|
});
|
|
handlers[i] = function getNsoHandler (mibRequestForNso) {
|
|
mibRequestForNso.done ({
|
|
errorStatus: ErrorStatus.NoError,
|
|
type: ObjectType.NoSuchObject,
|
|
value: null
|
|
});
|
|
};
|
|
} else {
|
|
providerNode = this.mib.getProviderNodeForInstance (instanceNode);
|
|
if ( ! providerNode || instanceNode.value === undefined ) {
|
|
mibRequests[i] = new MibRequest ({
|
|
operation: requestPdu.type,
|
|
oid: requestPdu.varbinds[i].oid
|
|
});
|
|
handlers[i] = function getNsiHandler (mibRequestForNsi) {
|
|
mibRequestForNsi.done ({
|
|
errorStatus: ErrorStatus.NoError,
|
|
type: ObjectType.NoSuchInstance,
|
|
value: null
|
|
});
|
|
};
|
|
} else if ( ! this.isAllowed(requestPdu.type, providerNode.provider, instanceNode ) ) {
|
|
// requested access not allowed (by MAX-ACCESS)
|
|
mibRequests[i] = new MibRequest ({
|
|
operation: requestPdu.type,
|
|
oid: requestPdu.varbinds[i].oid
|
|
});
|
|
handlers[i] = function getRanaHandler (mibRequestForRana) {
|
|
mibRequestForRana.done ({
|
|
errorStatus: ErrorStatus.NoAccess,
|
|
type: ObjectType.Null,
|
|
value: null
|
|
});
|
|
};
|
|
} else if ( this.authorizer.getAccessControlModelType () == AccessControlModelType.Simple &&
|
|
! this.authorizer.getAccessControlModel ().isAccessAllowed (requestMessage.version, securityName, requestMessage.pdu.type) ) {
|
|
// Access control check
|
|
mibRequests[i] = new MibRequest ({
|
|
operation: requestPdu.type,
|
|
oid: requestPdu.varbinds[i].oid
|
|
});
|
|
handlers[i] = function getAccessDeniedHandler (mibRequestForAccessDenied) {
|
|
mibRequestForAccessDenied.done ({
|
|
errorStatus: ErrorStatus.NoAccess,
|
|
type: ObjectType.Null,
|
|
value: null
|
|
});
|
|
};
|
|
} else if ( requestPdu.type === PduType.SetRequest &&
|
|
providerNode.provider.type == MibProviderType.Table &&
|
|
typeof (rowStatusColumn = providerNode.provider.tableColumns.reduce(
|
|
(acc, current) => current.rowStatus ? current.number : acc, null )) == "number" &&
|
|
instanceNode.getTableColumnFromInstanceNode() === rowStatusColumn) {
|
|
|
|
getIcsHandler = function (mibRequestForIcs) {
|
|
mibRequestForIcs.done ({
|
|
errorStatus: ErrorStatus.InconsistentValue,
|
|
type: ObjectType.Null,
|
|
value: null
|
|
});
|
|
};
|
|
|
|
requestPdu.varbinds[i].requestValue = this.castSetValue (requestPdu.varbinds[i].type, requestPdu.varbinds[i].value);
|
|
switch ( requestPdu.varbinds[i].value ) {
|
|
case RowStatus["active"]:
|
|
case RowStatus["notInService"]:
|
|
// Setting either of these states, when the
|
|
// row already exists, is fine
|
|
break;
|
|
|
|
case RowStatus["destroy"]:
|
|
// This case is handled later
|
|
break;
|
|
|
|
case RowStatus["createAndGo"]:
|
|
// Valid if this was a new row creation, but now set to active
|
|
if ( instanceNode.value === RowStatus["createAndGo"] ) {
|
|
requestPdu.varbinds[i].value = RowStatus["active"];
|
|
} else {
|
|
// Row already existed
|
|
mibRequests[i] = new MibRequest ({
|
|
operation: requestPdu.type,
|
|
oid: requestPdu.varbinds[i].oid
|
|
});
|
|
handlers[i] = getIcsHandler;
|
|
}
|
|
break;
|
|
|
|
case RowStatus["createAndWait"]:
|
|
// Valid if this was a new row creation, but now set to notInService
|
|
if ( instanceNode.value === RowStatus["createAndWait"] ) {
|
|
requestPdu.varbinds[i].value = RowStatus["notInService"];
|
|
} else {
|
|
// Row already existed
|
|
mibRequests[i] = new MibRequest ({
|
|
operation: requestPdu.type,
|
|
oid: requestPdu.varbinds[i].oid
|
|
});
|
|
handlers[i] = getIcsHandler;
|
|
}
|
|
break;
|
|
|
|
case RowStatus["notReady"]:
|
|
default:
|
|
// It's not ever legal to set the RowStatus to
|
|
// any value but the six that are defined, and
|
|
// it's not legal to change the state to
|
|
// "notReady".
|
|
//
|
|
// The row already exists, as determined by
|
|
// the fact that we have an instanceNode, so
|
|
// we can not apply a create action to the
|
|
// RowStatus column, as dictated RFC-2579.
|
|
// (See the summary state table on Page 8
|
|
// (inconsistent value)
|
|
mibRequests[i] = new MibRequest ({
|
|
operation: requestPdu.type,
|
|
oid: requestPdu.varbinds[i].oid
|
|
});
|
|
handlers[i] = getIcsHandler;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( requestPdu.type === PduType.SetRequest && ! createResult[i] ) {
|
|
oldValues[i] = instanceNode.value;
|
|
}
|
|
|
|
if ( ! handlers[i] ) {
|
|
mibRequests[i] = new MibRequest ({
|
|
operation: requestPdu.type,
|
|
providerNode: providerNode,
|
|
instanceNode: instanceNode,
|
|
oid: requestPdu.varbinds[i].oid
|
|
});
|
|
|
|
if ( requestPdu.type == PduType.SetRequest ) {
|
|
mibRequests[i].setType = requestPdu.varbinds[i].type;
|
|
mibRequests[i].setValue = requestPdu.varbinds[i].requestValue || requestPdu.varbinds[i].value;
|
|
}
|
|
handlers[i] = providerNode.provider.handler;
|
|
}
|
|
}
|
|
|
|
(function (savedIndex) {
|
|
let responseVarbind;
|
|
mibRequests[savedIndex].done = function (error) {
|
|
let rowIndex = null;
|
|
let row = null;
|
|
let deleted = false;
|
|
let column = -1;
|
|
responseVarbind = {
|
|
oid: mibRequests[savedIndex].oid
|
|
};
|
|
if ( error ) {
|
|
if ( (typeof responsePdu.errorStatus == "undefined" || responsePdu.errorStatus == ErrorStatus.NoError) && error.errorStatus != ErrorStatus.NoError ) {
|
|
responsePdu.errorStatus = error.errorStatus;
|
|
responsePdu.errorIndex = savedIndex + 1;
|
|
}
|
|
responseVarbind.type = error.type || ObjectType.Null;
|
|
responseVarbind.value = error.value || null;
|
|
//responseVarbind.errorStatus: error.errorStatus
|
|
if ( error.errorStatus != ErrorStatus.NoError ) {
|
|
responseVarbind.errorStatus = error.errorStatus;
|
|
}
|
|
} else {
|
|
let provider = providerNode ? providerNode.provider : null;
|
|
let providerName = provider ? provider.name : null;
|
|
let subOid;
|
|
let subAddr;
|
|
if ( providerNode && providerNode.provider && providerNode.provider.type == MibProviderType.Table ) {
|
|
column = instanceNode.getTableColumnFromInstanceNode();
|
|
subOid = Mib.getSubOidFromBaseOid (instanceNode.oid, provider.oid);
|
|
subAddr = subOid.split(".");
|
|
subAddr.shift(); // shift off the column number, leaving the row index values
|
|
rowIndex = Mib.getRowIndexFromOid( subAddr.join("."), provider.tableIndex );
|
|
row = me.mib.getTableRowCells ( providerName, rowIndex );
|
|
}
|
|
if ( requestPdu.type == PduType.SetRequest ) {
|
|
// Is this a RowStatus column with a value of 6 (delete)?
|
|
let rowStatusColumn = provider.type == MibProviderType.Table
|
|
? provider.tableColumns.reduce( (acc, current) => current.rowStatus ? current.number : acc, null )
|
|
: null;
|
|
if ( requestPdu.varbinds[savedIndex].value === RowStatus["destroy"] &&
|
|
typeof rowStatusColumn == "number" &&
|
|
column === rowStatusColumn ) {
|
|
|
|
// Yup. Do the deletion.
|
|
me.mib.deleteTableRow ( providerName, rowIndex );
|
|
deleted = true;
|
|
|
|
// This is going to return the prior state of the RowStatus column,
|
|
// i.e., either "active" or "notInService". That feels wrong, but there
|
|
// is no value we can set it to to indicate just-deleted. One would
|
|
// think we could set it to "notReady", but that is explicitly defined
|
|
// in RFC-2579 as "the conceptual row exists in the agent", which is
|
|
// no longer the case now that we've deleted the row. We're not allowed
|
|
// to ever return "destroy" as a status, so that doesn't give us an
|
|
// option either.
|
|
|
|
} else {
|
|
// No special handling required. Just save the new value.
|
|
let setResult = mibRequests[savedIndex].instanceNode.setValue (me.castSetValue (
|
|
requestPdu.varbinds[savedIndex].type,
|
|
requestPdu.varbinds[savedIndex].value
|
|
));
|
|
if ( ! setResult ) {
|
|
if ( typeof responsePdu.errorStatus == "undefined" || responsePdu.errorStatus == ErrorStatus.NoError ) {
|
|
responsePdu.errorStatus = ErrorStatus.WrongValue;
|
|
responsePdu.errorIndex = savedIndex + 1;
|
|
}
|
|
responseVarbind.errorStatus = ErrorStatus.WrongValue;
|
|
}
|
|
}
|
|
}
|
|
if ( ( requestPdu.type == PduType.GetNextRequest || requestPdu.type == PduType.GetBulkRequest ) &&
|
|
requestPdu.varbinds[savedIndex].type == ObjectType.EndOfMibView ) {
|
|
responseVarbind.type = ObjectType.EndOfMibView;
|
|
} else {
|
|
responseVarbind.type = mibRequests[savedIndex].instanceNode.valueType;
|
|
}
|
|
responseVarbind.value = mibRequests[savedIndex].instanceNode.value;
|
|
}
|
|
if ( providerNode && providerNode.provider && providerNode.provider.name ) {
|
|
responseVarbind.providerName = providerNode.provider.name;
|
|
}
|
|
if ( requestPdu.type == PduType.GetNextRequest || requestPdu.type == PduType.GetNextRequest ) {
|
|
responseVarbind.previousOid = requestPdu.varbinds[savedIndex].previousOid;
|
|
}
|
|
if ( requestPdu.type == PduType.SetRequest ) {
|
|
if ( oldValues[savedIndex] !== undefined ) {
|
|
responseVarbind.oldValue = oldValues[savedIndex];
|
|
}
|
|
responseVarbind.requestType = requestPdu.varbinds[savedIndex].type;
|
|
if ( requestPdu.varbinds[savedIndex].requestValue ) {
|
|
responseVarbind.requestValue = me.castSetValue (requestPdu.varbinds[savedIndex].type, requestPdu.varbinds[savedIndex].requestValue);
|
|
} else {
|
|
responseVarbind.requestValue = me.castSetValue (requestPdu.varbinds[savedIndex].type, requestPdu.varbinds[savedIndex].value);
|
|
}
|
|
}
|
|
if ( createResult[savedIndex] ) {
|
|
responseVarbind.autoCreated = true;
|
|
} else if ( deleted ) {
|
|
responseVarbind.deleted = true;
|
|
}
|
|
if ( providerNode && providerNode.provider.type == MibProviderType.Table ) {
|
|
responseVarbind.column = column;
|
|
responseVarbind.columnPosition = providerNode.provider.tableColumns.findIndex(tc => tc.number == column);
|
|
responseVarbind.rowIndex = rowIndex;
|
|
if ( ! deleted && rowIndex ) {
|
|
row = me.mib.getTableRowCells ( providerNode.provider.name, rowIndex );
|
|
}
|
|
responseVarbind.row = row;
|
|
}
|
|
me.setSingleVarbind (responsePdu, savedIndex, responseVarbind);
|
|
if ( ++varbindsCompleted == varbindsLength) {
|
|
me.sendResponse.call (me, rinfo, requestMessage, responsePdu);
|
|
}
|
|
};
|
|
})(i);
|
|
if ( handlers[i] ) {
|
|
handlers[i] (mibRequests[i]);
|
|
} else {
|
|
mibRequests[i].done ();
|
|
}
|
|
}
|
|
};
|
|
|
|
Agent.prototype.getRequest = function (requestMessage, rinfo) {
|
|
this.request (requestMessage, rinfo);
|
|
};
|
|
|
|
Agent.prototype.setRequest = function (requestMessage, rinfo) {
|
|
this.request (requestMessage, rinfo);
|
|
};
|
|
|
|
Agent.prototype.addGetNextVarbind = function (targetVarbinds, startOid) {
|
|
var startNode;
|
|
var getNextNode;
|
|
|
|
try {
|
|
startNode = this.mib.lookup (startOid);
|
|
} catch ( error ) {
|
|
startOid = '1.3.6.1';
|
|
startNode = this.mib.lookup (startOid);
|
|
}
|
|
|
|
if ( ! startNode ) {
|
|
// Off-tree start specified
|
|
startNode = this.mib.getTreeNode (startOid);
|
|
}
|
|
getNextNode = startNode.getNextInstanceNode();
|
|
if ( ! getNextNode ) {
|
|
// End of MIB
|
|
targetVarbinds.push ({
|
|
previousOid: startOid,
|
|
oid: startOid,
|
|
type: ObjectType.EndOfMibView,
|
|
value: null
|
|
});
|
|
} else {
|
|
// Normal response
|
|
targetVarbinds.push ({
|
|
previousOid: startOid,
|
|
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;
|
|
var endOfMib = false;
|
|
|
|
for (var n = 0 ; n < Math.min (requestPdu.nonRepeaters, requestVarbinds.length) ; n++ ) {
|
|
this.addGetNextVarbind (getBulkVarbinds, requestVarbinds[n].oid);
|
|
}
|
|
|
|
if ( requestPdu.nonRepeaters < requestVarbinds.length ) {
|
|
|
|
for (var v = requestPdu.nonRepeaters ; v < requestVarbinds.length ; v++ ) {
|
|
startOid.push (requestVarbinds[v].oid);
|
|
}
|
|
|
|
while ( getBulkVarbinds.length < requestPdu.maxRepetitions && ! endOfMib ) {
|
|
for (var w = requestPdu.nonRepeaters ; w < requestVarbinds.length ; w++ ) {
|
|
if (getBulkVarbinds.length < requestPdu.maxRepetitions ) {
|
|
getNextNode = this.addGetNextVarbind (getBulkVarbinds, startOid[w - requestPdu.nonRepeaters]);
|
|
if ( getNextNode ) {
|
|
startOid[w - requestPdu.nonRepeaters] = getNextNode.oid;
|
|
if ( getNextNode.type == ObjectType.EndOfMibView ) {
|
|
endOfMib = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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.prototype.onProxyRequest = function (message, rinfo) {
|
|
var contextName = message.pdu.contextName;
|
|
var proxy;
|
|
var proxiedPduId;
|
|
var proxiedUser;
|
|
|
|
if ( message.version != Version3 ) {
|
|
this.callback (new RequestFailedError ("Only SNMP version 3 contexts are supported"));
|
|
return;
|
|
}
|
|
proxy = this.forwarder.getProxy (contextName);
|
|
if ( ! proxy ) {
|
|
this.callback (new RequestFailedError ("No proxy found for message received with context " + contextName));
|
|
return;
|
|
}
|
|
if ( ! proxy.session.msgSecurityParameters ) {
|
|
// Discovery required - but chaining not implemented from here yet
|
|
proxy.session.sendV3Discovery (null, null, this.callback, {});
|
|
} else {
|
|
message.msgSecurityParameters = proxy.session.msgSecurityParameters;
|
|
message.setAuthentication ( ! (proxy.user.level == SecurityLevel.noAuthNoPriv));
|
|
message.setPrivacy (proxy.user.level == SecurityLevel.authPriv);
|
|
proxiedUser = message.user;
|
|
message.user = proxy.user;
|
|
message.buffer = null;
|
|
message.pdu.contextEngineID = proxy.session.msgSecurityParameters.msgAuthoritativeEngineID;
|
|
message.pdu.contextName = "";
|
|
proxiedPduId = message.pdu.id;
|
|
message.pdu.id = _generateId ();
|
|
var req = new Req (proxy.session, message, null, this.callback, {}, true);
|
|
req.port = proxy.port;
|
|
req.proxiedRinfo = rinfo;
|
|
req.proxiedPduId = proxiedPduId;
|
|
req.proxiedUser = proxiedUser;
|
|
req.proxiedEngine = this.engine;
|
|
proxy.session.send (req);
|
|
}
|
|
};
|
|
|
|
Agent.prototype.getForwarder = function () {
|
|
return this.forwarder;
|
|
};
|
|
|
|
Agent.prototype.close = function() {
|
|
this.listener.close ();
|
|
};
|
|
|
|
Agent.create = function (options, callback, mib) {
|
|
var agent = new Agent (options, callback, mib);
|
|
agent.listener.startListening ();
|
|
return agent;
|
|
};
|
|
|
|
var Forwarder = function (listener, callback) {
|
|
this.proxies = {};
|
|
this.listener = listener;
|
|
this.callback = callback;
|
|
};
|
|
|
|
Forwarder.prototype.addProxy = function (proxy) {
|
|
var options = {
|
|
version: Version3,
|
|
port: proxy.port,
|
|
transport: proxy.transport
|
|
};
|
|
proxy.session = Session.createV3 (proxy.target, proxy.user, options);
|
|
proxy.session.proxy = proxy;
|
|
proxy.session.proxy.listener = this.listener;
|
|
this.proxies[proxy.context] = proxy;
|
|
proxy.session.sendV3Discovery (null, null, this.callback);
|
|
};
|
|
|
|
Forwarder.prototype.deleteProxy = function (proxyName) {
|
|
var proxy = this.proxies[proxyName];
|
|
|
|
if ( proxy && proxy.session ) {
|
|
proxy.session.close ();
|
|
}
|
|
delete this.proxies[proxyName];
|
|
};
|
|
|
|
Forwarder.prototype.getProxy = function (proxyName) {
|
|
return this.proxies[proxyName];
|
|
};
|
|
|
|
Forwarder.prototype.getProxies = function () {
|
|
return this.proxies;
|
|
};
|
|
|
|
Forwarder.prototype.dumpProxies = function () {
|
|
var dump = {};
|
|
for ( var proxy of Object.values (this.proxies) ) {
|
|
dump[proxy.context] = {
|
|
context: proxy.context,
|
|
target: proxy.target,
|
|
user: proxy.user,
|
|
port: proxy.port
|
|
};
|
|
}
|
|
console.log (JSON.stringify (dump, null, 2));
|
|
};
|
|
|
|
var AgentXPdu = function () {
|
|
};
|
|
|
|
AgentXPdu.prototype.toBuffer = function () {
|
|
var buffer = new smartbuffer.SmartBuffer();
|
|
this.writeHeader (buffer);
|
|
switch ( this.pduType ) {
|
|
case AgentXPduType.Open:
|
|
buffer.writeUInt32BE (this.timeout);
|
|
AgentXPdu.writeOid (buffer, this.oid);
|
|
AgentXPdu.writeOctetString (buffer, this.descr);
|
|
break;
|
|
case AgentXPduType.Close:
|
|
buffer.writeUInt8 (5); // reasonShutdown == 5
|
|
buffer.writeUInt8 (0); // 3 x reserved bytes
|
|
buffer.writeUInt8 (0);
|
|
buffer.writeUInt8 (0);
|
|
break;
|
|
case AgentXPduType.Register:
|
|
buffer.writeUInt8 (this.timeout);
|
|
buffer.writeUInt8 (this.priority);
|
|
buffer.writeUInt8 (this.rangeSubid);
|
|
buffer.writeUInt8 (0);
|
|
AgentXPdu.writeOid (buffer, this.oid);
|
|
break;
|
|
case AgentXPduType.Unregister:
|
|
buffer.writeUInt8 (0); // reserved
|
|
buffer.writeUInt8 (this.priority);
|
|
buffer.writeUInt8 (this.rangeSubid);
|
|
buffer.writeUInt8 (0); // reserved
|
|
AgentXPdu.writeOid (buffer, this.oid);
|
|
break;
|
|
case AgentXPduType.AddAgentCaps:
|
|
AgentXPdu.writeOid (buffer, this.oid);
|
|
AgentXPdu.writeOctetString (buffer, this.descr);
|
|
break;
|
|
case AgentXPduType.RemoveAgentCaps:
|
|
AgentXPdu.writeOid (buffer, this.oid);
|
|
break;
|
|
case AgentXPduType.Notify:
|
|
AgentXPdu.writeVarbinds (buffer, this.varbinds);
|
|
break;
|
|
case AgentXPduType.Ping:
|
|
break;
|
|
case AgentXPduType.Response:
|
|
buffer.writeUInt32BE (this.sysUpTime);
|
|
buffer.writeUInt16BE (this.error);
|
|
buffer.writeUInt16BE (this.index);
|
|
AgentXPdu.writeVarbinds (buffer, this.varbinds);
|
|
break;
|
|
default:
|
|
// unknown PDU type - should never happen as we control these
|
|
}
|
|
buffer.writeUInt32BE (buffer.length - 20, 16);
|
|
return buffer.toBuffer ();
|
|
};
|
|
|
|
AgentXPdu.prototype.writeHeader = function (buffer) {
|
|
this.flags = this.flags | 0x10; // set NETWORK_BYTE_ORDER
|
|
|
|
buffer.writeUInt8 (1); // h.version = 1
|
|
buffer.writeUInt8 (this.pduType);
|
|
buffer.writeUInt8 (this.flags);
|
|
buffer.writeUInt8 (0); // reserved byte
|
|
buffer.writeUInt32BE (this.sessionID);
|
|
buffer.writeUInt32BE (this.transactionID);
|
|
buffer.writeUInt32BE (this.packetID);
|
|
buffer.writeUInt32BE (0);
|
|
return buffer;
|
|
};
|
|
|
|
AgentXPdu.prototype.readHeader = function (buffer) {
|
|
this.version = buffer.readUInt8 ();
|
|
this.pduType = buffer.readUInt8 ();
|
|
this.flags = buffer.readUInt8 ();
|
|
buffer.readUInt8 (); // reserved byte
|
|
this.sessionID = buffer.readUInt32BE ();
|
|
this.transactionID = buffer.readUInt32BE ();
|
|
this.packetID = buffer.readUInt32BE ();
|
|
this.payloadLength = buffer.readUInt32BE ();
|
|
};
|
|
|
|
AgentXPdu.createFromVariables = function (vars) {
|
|
var pdu = new AgentXPdu ();
|
|
pdu.flags = vars.flags ? vars.flags | 0x10 : 0x10; // set NETWORK_BYTE_ORDER to big endian
|
|
pdu.pduType = vars.pduType || AgentXPduType.Open;
|
|
pdu.sessionID = vars.sessionID || 0;
|
|
pdu.transactionID = vars.transactionID || 0;
|
|
pdu.packetID = vars.packetID || ++AgentXPdu.packetID;
|
|
switch ( pdu.pduType ) {
|
|
case AgentXPduType.Open:
|
|
pdu.timeout = vars.timeout || 0;
|
|
pdu.oid = vars.oid || null;
|
|
pdu.descr = vars.descr || null;
|
|
break;
|
|
case AgentXPduType.Close:
|
|
break;
|
|
case AgentXPduType.Register:
|
|
pdu.timeout = vars.timeout || 0;
|
|
pdu.oid = vars.oid || null;
|
|
pdu.priority = vars.priority || 127;
|
|
pdu.rangeSubid = vars.rangeSubid || 0;
|
|
break;
|
|
case AgentXPduType.Unregister:
|
|
pdu.oid = vars.oid || null;
|
|
pdu.priority = vars.priority || 127;
|
|
pdu.rangeSubid = vars.rangeSubid || 0;
|
|
break;
|
|
case AgentXPduType.AddAgentCaps:
|
|
pdu.oid = vars.oid;
|
|
pdu.descr = vars.descr;
|
|
break;
|
|
case AgentXPduType.RemoveAgentCaps:
|
|
pdu.oid = vars.oid;
|
|
break;
|
|
case AgentXPduType.Notify:
|
|
pdu.varbinds = vars.varbinds;
|
|
break;
|
|
case AgentXPduType.Ping:
|
|
break;
|
|
case AgentXPduType.Response:
|
|
pdu.sysUpTime = vars.sysUpTime || 0;
|
|
pdu.error = vars.error || 0;
|
|
pdu.index = vars.index || 0;
|
|
pdu.varbinds = vars.varbinds || null;
|
|
break;
|
|
default:
|
|
// unsupported PDU type - should never happen as we control these
|
|
throw new RequestInvalidError ("Unknown PDU type '" + pdu.pduType
|
|
+ "' in created PDU");
|
|
|
|
}
|
|
|
|
return pdu;
|
|
};
|
|
|
|
AgentXPdu.createFromBuffer = function (socketBuffer) {
|
|
var pdu = new AgentXPdu ();
|
|
|
|
var buffer = smartbuffer.SmartBuffer.fromBuffer (socketBuffer);
|
|
pdu.readHeader (buffer);
|
|
|
|
switch ( pdu.pduType ) {
|
|
case AgentXPduType.Response:
|
|
pdu.sysUpTime = buffer.readUInt32BE ();
|
|
pdu.error = buffer.readUInt16BE ();
|
|
pdu.index = buffer.readUInt16BE ();
|
|
break;
|
|
case AgentXPduType.Get:
|
|
case AgentXPduType.GetNext:
|
|
pdu.searchRangeList = AgentXPdu.readSearchRangeList (buffer, pdu.payloadLength);
|
|
break;
|
|
case AgentXPduType.GetBulk:
|
|
pdu.nonRepeaters = buffer.readUInt16BE ();
|
|
pdu.maxRepetitions = buffer.readUInt16BE ();
|
|
pdu.searchRangeList = AgentXPdu.readSearchRangeList (buffer, pdu.payloadLength - 4);
|
|
break;
|
|
case AgentXPduType.TestSet:
|
|
pdu.varbinds = AgentXPdu.readVarbinds (buffer, pdu.payloadLength);
|
|
break;
|
|
case AgentXPduType.CommitSet:
|
|
case AgentXPduType.UndoSet:
|
|
case AgentXPduType.CleanupSet:
|
|
break;
|
|
default:
|
|
// Unknown PDU type - shouldn't happen as master agents shouldn't send administrative PDUs
|
|
throw new RequestInvalidError ("Unknown PDU type '" + pdu.pduType
|
|
+ "' in request");
|
|
}
|
|
return pdu;
|
|
};
|
|
|
|
AgentXPdu.writeOid = function (buffer, oid, include = 0) {
|
|
var prefix;
|
|
if ( oid ) {
|
|
var address = oid.split ('.').map ( Number );
|
|
if ( address.length >= 5 && address.slice (0, 4).join('.') == '1.3.6.1' ) {
|
|
prefix = address[4];
|
|
address = address.slice(5);
|
|
} else {
|
|
prefix = 0;
|
|
}
|
|
buffer.writeUInt8 (address.length);
|
|
buffer.writeUInt8 (prefix);
|
|
buffer.writeUInt8 (include);
|
|
buffer.writeUInt8 (0); // reserved
|
|
for ( let addressPart of address ) {
|
|
buffer.writeUInt32BE (addressPart);
|
|
}
|
|
} else {
|
|
buffer.writeUInt32BE (0); // row of zeros for null OID
|
|
}
|
|
};
|
|
|
|
AgentXPdu.writeOctetString = function (buffer, octetString) {
|
|
buffer.writeUInt32BE (octetString.length);
|
|
buffer.writeString (octetString);
|
|
var paddingOctets = ( 4 - octetString.length % 4 ) % 4;
|
|
for ( let i = 0; i < paddingOctets ; i++ ) {
|
|
buffer.writeUInt8 (0);
|
|
}
|
|
};
|
|
|
|
AgentXPdu.writeVarBind = function (buffer, varbind) {
|
|
buffer.writeUInt16BE (varbind.type);
|
|
buffer.writeUInt16BE (0); // reserved
|
|
AgentXPdu.writeOid (buffer, varbind.oid);
|
|
|
|
if (varbind.type && varbind.oid) {
|
|
|
|
switch (varbind.type) {
|
|
case ObjectType.Integer: // also Integer32
|
|
case ObjectType.Counter: // also Counter32
|
|
case ObjectType.Gauge: // also Gauge32 & Unsigned32
|
|
case ObjectType.TimeTicks:
|
|
buffer.writeUInt32BE (varbind.value);
|
|
break;
|
|
case ObjectType.OctetString:
|
|
case ObjectType.Opaque:
|
|
AgentXPdu.writeOctetString (buffer, varbind.value);
|
|
break;
|
|
case ObjectType.OID:
|
|
AgentXPdu.writeOid (buffer, varbind.value);
|
|
break;
|
|
case ObjectType.IpAddress:
|
|
var bytes = varbind.value.split (".");
|
|
if (bytes.length != 4)
|
|
throw new RequestInvalidError ("Invalid IP address '"
|
|
+ varbind.value + "'");
|
|
buffer.writeOctetString (buffer, Buffer.from (bytes));
|
|
break;
|
|
case ObjectType.Counter64:
|
|
buffer.writeUint64 (varbind.value);
|
|
break;
|
|
case ObjectType.Null:
|
|
case ObjectType.EndOfMibView:
|
|
case ObjectType.NoSuchObject:
|
|
case ObjectType.NoSuchInstance:
|
|
break;
|
|
default:
|
|
// Unknown data type - should never happen as the above covers all types in RFC 2741 Section 5.4
|
|
throw new RequestInvalidError ("Unknown type '" + varbind.type
|
|
+ "' in request");
|
|
}
|
|
}
|
|
};
|
|
|
|
AgentXPdu.writeVarbinds = function (buffer, varbinds) {
|
|
if ( varbinds ) {
|
|
for ( var i = 0; i < varbinds.length ; i++ ) {
|
|
var varbind = varbinds[i];
|
|
AgentXPdu.writeVarBind(buffer, varbind);
|
|
}
|
|
}
|
|
};
|
|
|
|
AgentXPdu.readOid = function (buffer) {
|
|
var subidLength = buffer.readUInt8 ();
|
|
var prefix = buffer.readUInt8 ();
|
|
var include = buffer.readUInt8 ();
|
|
buffer.readUInt8 (); // reserved
|
|
|
|
// Null OID check
|
|
if ( subidLength == 0 && prefix == 0 && include == 0) {
|
|
return null;
|
|
}
|
|
var address = [];
|
|
if ( prefix == 0 ) {
|
|
address = [];
|
|
} else {
|
|
address = [1, 3, 6, 1, prefix];
|
|
}
|
|
for ( let i = 0; i < subidLength; i++ ) {
|
|
address.push (buffer.readUInt32BE ());
|
|
}
|
|
var oid = address.join ('.');
|
|
return oid;
|
|
};
|
|
|
|
AgentXPdu.readSearchRange = function (buffer) {
|
|
return {
|
|
start: AgentXPdu.readOid (buffer),
|
|
end: AgentXPdu.readOid (buffer)
|
|
};
|
|
};
|
|
|
|
AgentXPdu.readSearchRangeList = function (buffer, payloadLength) {
|
|
var bytesLeft = payloadLength;
|
|
var bufferPosition = (buffer.readOffset + 1);
|
|
var searchRangeList = [];
|
|
while (bytesLeft > 0) {
|
|
searchRangeList.push (AgentXPdu.readSearchRange (buffer));
|
|
bytesLeft -= (buffer.readOffset + 1) - bufferPosition;
|
|
bufferPosition = buffer.readOffset + 1;
|
|
}
|
|
return searchRangeList;
|
|
};
|
|
|
|
AgentXPdu.readOctetString = function (buffer) {
|
|
var octetStringLength = buffer.readUInt32BE ();
|
|
var paddingOctets = ( 4 - octetStringLength % 4 ) % 4;
|
|
var octetString = buffer.readString (octetStringLength);
|
|
buffer.readString (paddingOctets);
|
|
return octetString;
|
|
};
|
|
|
|
AgentXPdu.readVarbind = function (buffer) {
|
|
var vtype = buffer.readUInt16BE ();
|
|
buffer.readUInt16BE (); // reserved
|
|
var oid = AgentXPdu.readOid (buffer);
|
|
var value;
|
|
|
|
switch (vtype) {
|
|
case ObjectType.Integer:
|
|
case ObjectType.Counter:
|
|
case ObjectType.Gauge:
|
|
case ObjectType.TimeTicks:
|
|
value = buffer.readUInt32BE ();
|
|
break;
|
|
case ObjectType.OctetString:
|
|
case ObjectType.IpAddress:
|
|
case ObjectType.Opaque:
|
|
value = AgentXPdu.readOctetString (buffer);
|
|
break;
|
|
case ObjectType.OID:
|
|
value = AgentXPdu.readOid (buffer);
|
|
break;
|
|
case ObjectType.Counter64:
|
|
value = readUint64 (buffer);
|
|
break;
|
|
case ObjectType.Null:
|
|
case ObjectType.NoSuchObject:
|
|
case ObjectType.NoSuchInstance:
|
|
case ObjectType.EndOfMibView:
|
|
value = null;
|
|
break;
|
|
default:
|
|
// Unknown data type - should never happen as the above covers all types in RFC 2741 Section 5.4
|
|
throw new RequestInvalidError ("Unknown type '" + vtype
|
|
+ "' in varbind");
|
|
}
|
|
|
|
return {
|
|
type: vtype,
|
|
oid: oid,
|
|
value: value
|
|
};
|
|
};
|
|
|
|
AgentXPdu.readVarbinds = function (buffer, payloadLength) {
|
|
var bytesLeft = payloadLength;
|
|
var bufferPosition = (buffer.readOffset + 1);
|
|
var varbindList = [];
|
|
while (bytesLeft > 0) {
|
|
varbindList.push (AgentXPdu.readVarbind (buffer));
|
|
bytesLeft -= (buffer.readOffset + 1) - bufferPosition;
|
|
bufferPosition = buffer.readOffset + 1;
|
|
}
|
|
return varbindList;
|
|
};
|
|
|
|
AgentXPdu.packetID = 1;
|
|
|
|
var Subagent = function (options) {
|
|
DEBUG = options.debug;
|
|
this.mib = new Mib ();
|
|
this.master = options.master || 'localhost';
|
|
this.masterPort = options.masterPort || 705;
|
|
this.timeout = options.timeout || 0;
|
|
this.descr = options.description || "Node net-snmp AgentX sub-agent";
|
|
this.sessionID = 0;
|
|
this.transactionID = 0;
|
|
this.packetID = _generateId();
|
|
this.requestPdus = {};
|
|
this.setTransactions = {};
|
|
};
|
|
|
|
Subagent.prototype.getMib = function () {
|
|
return this.mib;
|
|
};
|
|
|
|
Subagent.prototype.connectSocket = function () {
|
|
var me = this;
|
|
this.socket = new net.Socket ();
|
|
this.socket.connect (this.masterPort, this.master, function () {
|
|
debug ("Connected to '" + me.master + "' on port " + me.masterPort);
|
|
});
|
|
|
|
this.socket.on ("data", me.onMsg.bind (me));
|
|
this.socket.on ("error", function (error) {
|
|
console.error (error);
|
|
});
|
|
};
|
|
|
|
Subagent.prototype.open = function (callback) {
|
|
var pdu = AgentXPdu.createFromVariables ({
|
|
pduType: AgentXPduType.Open,
|
|
timeout: this.timeout,
|
|
oid: this.oid,
|
|
descr: this.descr
|
|
});
|
|
this.sendPdu (pdu, callback);
|
|
};
|
|
|
|
Subagent.prototype.close = function (callback) {
|
|
var pdu = AgentXPdu.createFromVariables ({
|
|
pduType: AgentXPduType.Close,
|
|
sessionID: this.sessionID
|
|
});
|
|
this.sendPdu (pdu, callback);
|
|
};
|
|
|
|
Subagent.prototype.registerProvider = function (provider, callback) {
|
|
var pdu = AgentXPdu.createFromVariables ({
|
|
pduType: AgentXPduType.Register,
|
|
sessionID: this.sessionID,
|
|
rangeSubid: 0,
|
|
timeout: 5,
|
|
priority: 127,
|
|
oid: provider.oid
|
|
});
|
|
this.mib.registerProvider (provider);
|
|
this.sendPdu (pdu, callback);
|
|
};
|
|
|
|
Subagent.prototype.unregisterProvider = function (name, callback) {
|
|
var provider = this.getProvider (name);
|
|
var pdu = AgentXPdu.createFromVariables ({
|
|
pduType: AgentXPduType.Unregister,
|
|
sessionID: this.sessionID,
|
|
rangeSubid: 0,
|
|
priority: 127,
|
|
oid: provider.oid
|
|
});
|
|
this.mib.unregisterProvider (name);
|
|
this.sendPdu (pdu, callback);
|
|
};
|
|
|
|
Subagent.prototype.registerProviders = function (providers, callback) {
|
|
for (var provider of providers) {
|
|
this.registerProvider (provider, callback);
|
|
}
|
|
};
|
|
|
|
Subagent.prototype.getProvider = function (name) {
|
|
return this.mib.getProvider (name);
|
|
};
|
|
|
|
Subagent.prototype.getProviders = function () {
|
|
return this.mib.getProviders ();
|
|
};
|
|
|
|
Subagent.prototype.addAgentCaps = function (oid, descr, callback) {
|
|
var pdu = AgentXPdu.createFromVariables ({
|
|
pduType: AgentXPduType.AddAgentCaps,
|
|
sessionID: this.sessionID,
|
|
oid: oid,
|
|
descr: descr
|
|
});
|
|
this.sendPdu (pdu, callback);
|
|
};
|
|
|
|
Subagent.prototype.removeAgentCaps = function (oid, callback) {
|
|
var pdu = AgentXPdu.createFromVariables ({
|
|
pduType: AgentXPduType.RemoveAgentCaps,
|
|
sessionID: this.sessionID,
|
|
oid: oid
|
|
});
|
|
this.sendPdu (pdu, callback);
|
|
};
|
|
|
|
Subagent.prototype.notify = function (typeOrOid, varbinds, callback) {
|
|
varbinds = 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: Math.floor (process.uptime () * 100)
|
|
},
|
|
{
|
|
oid: "1.3.6.1.6.3.1.1.4.1.0",
|
|
type: ObjectType.OID,
|
|
value: typeOrOid
|
|
}
|
|
];
|
|
|
|
pduVarbinds = pduVarbinds.concat (varbinds);
|
|
|
|
var pdu = AgentXPdu.createFromVariables ({
|
|
pduType: AgentXPduType.Notify,
|
|
sessionID: this.sessionID,
|
|
varbinds: pduVarbinds
|
|
});
|
|
this.sendPdu (pdu, callback);
|
|
};
|
|
|
|
Subagent.prototype.ping = function (callback) {
|
|
var pdu = AgentXPdu.createFromVariables ({
|
|
pduType: AgentXPduType.Ping,
|
|
sessionID: this.sessionID
|
|
});
|
|
this.sendPdu (pdu, callback);
|
|
};
|
|
|
|
Subagent.prototype.sendPdu = function (pdu, callback) {
|
|
debug ("Sending AgentX " + AgentXPduType[pdu.pduType] + " PDU");
|
|
debug (pdu);
|
|
var buffer = pdu.toBuffer ();
|
|
this.socket.write (buffer);
|
|
if ( pdu.pduType != AgentXPduType.Response && ! this.requestPdus[pdu.packetID] ) {
|
|
pdu.callback = callback;
|
|
this.requestPdus[pdu.packetID] = pdu;
|
|
}
|
|
|
|
// Possible timeout / retry mechanism?
|
|
// var me = this;
|
|
// pdu.timer = setTimeout (function () {
|
|
// if (pdu.retries-- > 0) {
|
|
// this.sendPdu (pdu);
|
|
// } else {
|
|
// delete me.requestPdus[pdu.packetID];
|
|
// me.callback (new RequestTimedOutError (
|
|
// "Request timed out"));
|
|
// }
|
|
// }, this.timeout);
|
|
|
|
};
|
|
|
|
Subagent.prototype.onMsg = function (buffer, rinfo) {
|
|
var pdu = AgentXPdu.createFromBuffer (buffer);
|
|
|
|
debug ("Received AgentX " + AgentXPduType[pdu.pduType] + " PDU");
|
|
debug (pdu);
|
|
|
|
switch (pdu.pduType) {
|
|
case AgentXPduType.Response:
|
|
this.response (pdu);
|
|
break;
|
|
case AgentXPduType.Get:
|
|
this.getRequest (pdu);
|
|
break;
|
|
case AgentXPduType.GetNext:
|
|
this.getNextRequest (pdu);
|
|
break;
|
|
case AgentXPduType.GetBulk:
|
|
this.getBulkRequest (pdu);
|
|
break;
|
|
case AgentXPduType.TestSet:
|
|
this.testSet (pdu);
|
|
break;
|
|
case AgentXPduType.CommitSet:
|
|
this.commitSet (pdu);
|
|
break;
|
|
case AgentXPduType.UndoSet:
|
|
this.undoSet (pdu);
|
|
break;
|
|
case AgentXPduType.CleanupSet:
|
|
this.cleanupSet (pdu);
|
|
break;
|
|
default:
|
|
// Unknown PDU type - shouldn't happen as master agents shouldn't send administrative PDUs
|
|
throw new RequestInvalidError ("Unknown PDU type '" + pdu.pduType
|
|
+ "' in request");
|
|
}
|
|
};
|
|
|
|
Subagent.prototype.response = function (pdu) {
|
|
var requestPdu = this.requestPdus[pdu.packetID];
|
|
if (requestPdu) {
|
|
delete this.requestPdus[pdu.packetID];
|
|
// clearTimeout (pdu.timer);
|
|
// delete pdu.timer;
|
|
switch (requestPdu.pduType) {
|
|
case AgentXPduType.Open:
|
|
this.sessionID = pdu.sessionID;
|
|
break;
|
|
case AgentXPduType.Close:
|
|
this.socket.end();
|
|
break;
|
|
case AgentXPduType.Register:
|
|
case AgentXPduType.Unregister:
|
|
case AgentXPduType.AddAgentCaps:
|
|
case AgentXPduType.RemoveAgentCaps:
|
|
case AgentXPduType.Notify:
|
|
case AgentXPduType.Ping:
|
|
break;
|
|
default:
|
|
// Response PDU for request type not handled
|
|
throw new ResponseInvalidError ("Response PDU for type '" + requestPdu.pduType + "' not handled",
|
|
ResponseInvalidCode.EResponseNotHandled);
|
|
}
|
|
if (requestPdu.callback) {
|
|
requestPdu.callback(null, pdu);
|
|
}
|
|
} else {
|
|
// unexpected Response PDU - has no matching request
|
|
throw new ResponseInvalidError ("Unexpected Response PDU with packetID " + pdu.packetID,
|
|
ResponseInvalidCode.EUnexpectedResponse);
|
|
}
|
|
};
|
|
|
|
Subagent.prototype.request = function (pdu, requestVarbinds) {
|
|
var me = this;
|
|
var varbindsCompleted = 0;
|
|
var varbindsLength = requestVarbinds.length;
|
|
var responseVarbinds = [];
|
|
|
|
for ( var i = 0; i < requestVarbinds.length; i++ ) {
|
|
var requestVarbind = requestVarbinds[i];
|
|
var instanceNode = this.mib.lookup (requestVarbind.oid);
|
|
var providerNode;
|
|
var mibRequest;
|
|
var handler;
|
|
var responseVarbindType;
|
|
|
|
if ( ! instanceNode ) {
|
|
mibRequest = new MibRequest ({
|
|
operation: pdu.pduType,
|
|
oid: requestVarbind.oid
|
|
});
|
|
handler = function getNsoHandler (mibRequestForNso) {
|
|
mibRequestForNso.done ({
|
|
errorStatus: ErrorStatus.NoError,
|
|
errorIndex: 0,
|
|
type: ObjectType.NoSuchObject,
|
|
value: null
|
|
});
|
|
};
|
|
} else {
|
|
providerNode = this.mib.getProviderNodeForInstance (instanceNode);
|
|
if ( ! providerNode ) {
|
|
mibRequest = new MibRequest ({
|
|
operation: pdu.pduType,
|
|
oid: requestVarbind.oid
|
|
});
|
|
handler = function getNsiHandler (mibRequestForNsi) {
|
|
mibRequestForNsi.done ({
|
|
errorStatus: ErrorStatus.NoError,
|
|
errorIndex: 0,
|
|
type: ObjectType.NoSuchInstance,
|
|
value: null
|
|
});
|
|
};
|
|
} else {
|
|
mibRequest = new MibRequest ({
|
|
operation: pdu.pduType,
|
|
providerNode: providerNode,
|
|
instanceNode: instanceNode,
|
|
oid: requestVarbind.oid
|
|
});
|
|
if ( pdu.pduType == AgentXPduType.TestSet ) {
|
|
mibRequest.setType = requestVarbind.type;
|
|
mibRequest.setValue = requestVarbind.value;
|
|
}
|
|
handler = providerNode.provider.handler;
|
|
}
|
|
}
|
|
|
|
(function (savedIndex) {
|
|
var responseVarbind;
|
|
mibRequest.done = function (error) {
|
|
if ( error ) {
|
|
responseVarbind = {
|
|
oid: mibRequest.oid,
|
|
type: error.type || ObjectType.Null,
|
|
value: error.value || null
|
|
};
|
|
} else {
|
|
if ( pdu.pduType == AgentXPduType.TestSet ) {
|
|
// more tests?
|
|
} else if ( pdu.pduType == AgentXPduType.CommitSet ) {
|
|
me.setTransactions[pdu.transactionID].originalValue = mibRequest.instanceNode.value;
|
|
mibRequest.instanceNode.value = requestVarbind.value;
|
|
} else if ( pdu.pduType == AgentXPduType.UndoSet ) {
|
|
mibRequest.instanceNode.value = me.setTransactions[pdu.transactionID].originalValue;
|
|
}
|
|
if ( ( pdu.pduType == AgentXPduType.GetNext || pdu.pduType == AgentXPduType.GetBulk ) &&
|
|
requestVarbind.type == ObjectType.EndOfMibView ) {
|
|
responseVarbindType = ObjectType.EndOfMibView;
|
|
} else {
|
|
responseVarbindType = mibRequest.instanceNode.valueType;
|
|
}
|
|
responseVarbind = {
|
|
oid: mibRequest.oid,
|
|
type: responseVarbindType,
|
|
value: mibRequest.instanceNode.value
|
|
};
|
|
}
|
|
responseVarbinds[savedIndex] = responseVarbind;
|
|
if ( ++varbindsCompleted == varbindsLength) {
|
|
if ( pdu.pduType == AgentXPduType.TestSet || pdu.pduType == AgentXPduType.CommitSet
|
|
|| pdu.pduType == AgentXPduType.UndoSet) {
|
|
me.sendSetResponse.call (me, pdu);
|
|
} else {
|
|
me.sendGetResponse.call (me, pdu, responseVarbinds);
|
|
}
|
|
}
|
|
};
|
|
})(i);
|
|
if ( handler ) {
|
|
handler (mibRequest);
|
|
} else {
|
|
mibRequest.done ();
|
|
}
|
|
}
|
|
};
|
|
|
|
Subagent.prototype.addGetNextVarbind = function (targetVarbinds, startOid) {
|
|
var startNode;
|
|
var getNextNode;
|
|
|
|
try {
|
|
startNode = this.mib.lookup (startOid);
|
|
} catch ( error ) {
|
|
startOid = '1.3.6.1';
|
|
startNode = this.mib.lookup (startOid);
|
|
}
|
|
|
|
if ( ! startNode ) {
|
|
// Off-tree start specified
|
|
startNode = this.mib.getTreeNode (startOid);
|
|
}
|
|
getNextNode = startNode.getNextInstanceNode();
|
|
if ( ! getNextNode ) {
|
|
// End of MIB
|
|
targetVarbinds.push ({
|
|
oid: startOid,
|
|
type: ObjectType.EndOfMibView,
|
|
value: null
|
|
});
|
|
} else {
|
|
// Normal response
|
|
targetVarbinds.push ({
|
|
oid: getNextNode.oid,
|
|
type: getNextNode.valueType,
|
|
value: getNextNode.value
|
|
});
|
|
}
|
|
|
|
return getNextNode;
|
|
};
|
|
|
|
Subagent.prototype.getRequest = function (pdu) {
|
|
var requestVarbinds = [];
|
|
|
|
for ( var i = 0; i < pdu.searchRangeList.length; i++ ) {
|
|
requestVarbinds.push ({
|
|
oid: pdu.searchRangeList[i].start,
|
|
value: null,
|
|
type: null
|
|
});
|
|
}
|
|
this.request (pdu, requestVarbinds);
|
|
};
|
|
|
|
Subagent.prototype.getNextRequest = function (pdu) {
|
|
var getNextVarbinds = [];
|
|
|
|
for (var i = 0 ; i < pdu.searchRangeList.length ; i++ ) {
|
|
this.addGetNextVarbind (getNextVarbinds, pdu.searchRangeList[i].start);
|
|
}
|
|
|
|
this.request (pdu, getNextVarbinds);
|
|
};
|
|
|
|
Subagent.prototype.getBulkRequest = function (pdu) {
|
|
var getBulkVarbinds = [];
|
|
var startOid = [];
|
|
var getNextNode;
|
|
var endOfMib = false;
|
|
|
|
for (var n = 0 ; n < pdu.nonRepeaters ; n++ ) {
|
|
this.addGetNextVarbind (getBulkVarbinds, pdu.searchRangeList[n].start);
|
|
}
|
|
|
|
for (var v = pdu.nonRepeaters ; v < pdu.searchRangeList.length ; v++ ) {
|
|
startOid.push (pdu.searchRangeList[v].oid);
|
|
}
|
|
|
|
while ( getBulkVarbinds.length < pdu.maxRepetitions && ! endOfMib ) {
|
|
for (var w = pdu.nonRepeaters ; w < pdu.searchRangeList.length ; w++ ) {
|
|
if (getBulkVarbinds.length < pdu.maxRepetitions ) {
|
|
getNextNode = this.addGetNextVarbind (getBulkVarbinds, startOid[w - pdu.nonRepeaters]);
|
|
if ( getNextNode ) {
|
|
startOid[w - pdu.nonRepeaters] = getNextNode.oid;
|
|
if ( getNextNode.type == ObjectType.EndOfMibView ) {
|
|
endOfMib = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.request (pdu, getBulkVarbinds);
|
|
};
|
|
|
|
Subagent.prototype.sendGetResponse = function (requestPdu, varbinds) {
|
|
var pdu = AgentXPdu.createFromVariables ({
|
|
pduType: AgentXPduType.Response,
|
|
sessionID: requestPdu.sessionID,
|
|
transactionID: requestPdu.transactionID,
|
|
packetID: requestPdu.packetID,
|
|
sysUpTime: 0,
|
|
error: 0,
|
|
index: 0,
|
|
varbinds: varbinds
|
|
});
|
|
this.sendPdu (pdu, null);
|
|
};
|
|
|
|
Subagent.prototype.sendSetResponse = function (setPdu) {
|
|
var responsePdu = AgentXPdu.createFromVariables ({
|
|
pduType: AgentXPduType.Response,
|
|
sessionID: setPdu.sessionID,
|
|
transactionID: setPdu.transactionID,
|
|
packetID: setPdu.packetID,
|
|
sysUpTime: 0,
|
|
error: 0,
|
|
index: 0,
|
|
});
|
|
this.sendPdu (responsePdu, null);
|
|
};
|
|
|
|
Subagent.prototype.testSet = function (setPdu) {
|
|
this.setTransactions[setPdu.transactionID] = setPdu;
|
|
this.request (setPdu, setPdu.varbinds);
|
|
};
|
|
|
|
Subagent.prototype.commitSet = function (setPdu) {
|
|
if ( this.setTransactions[setPdu.transactionID] ) {
|
|
this.request (setPdu, this.setTransactions[setPdu.transactionID].varbinds);
|
|
} else {
|
|
throw new RequestInvalidError ("Unexpected CommitSet PDU with transactionID " + setPdu.transactionID);
|
|
}
|
|
};
|
|
|
|
Subagent.prototype.undoSet = function (setPdu) {
|
|
if ( this.setTransactions[setPdu.transactionID] ) {
|
|
this.request (setPdu, this.setTransactions[setPdu.transactionID].varbinds);
|
|
} else {
|
|
throw new RequestInvalidError ("Unexpected UndoSet PDU with transactionID " + setPdu.transactionID);
|
|
}
|
|
};
|
|
|
|
Subagent.prototype.cleanupSet = function (setPdu) {
|
|
if ( this.setTransactions[setPdu.transactionID] ) {
|
|
delete this.setTransactions[setPdu.transactionID];
|
|
} else {
|
|
throw new RequestInvalidError ("Unexpected CleanupSet PDU with transactionID " + setPdu.transactionID);
|
|
}
|
|
};
|
|
|
|
Subagent.create = function (options) {
|
|
var subagent = new Subagent (options);
|
|
subagent.connectSocket ();
|
|
return subagent;
|
|
};
|
|
|
|
|
|
/*****************************************************************************
|
|
** Exports
|
|
**/
|
|
|
|
exports.Session = Session;
|
|
|
|
exports.createSession = Session.create;
|
|
exports.createV3Session = Session.createV3;
|
|
|
|
exports.createReceiver = Receiver.create;
|
|
exports.createAgent = Agent.create;
|
|
exports.createModuleStore = ModuleStore.create;
|
|
exports.createSubagent = Subagent.create;
|
|
exports.createMib = Mib.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.AgentXPduType = AgentXPduType;
|
|
exports.MibProviderType = MibProviderType;
|
|
exports.SecurityLevel = SecurityLevel;
|
|
exports.AuthProtocols = AuthProtocols;
|
|
exports.PrivProtocols = PrivProtocols;
|
|
exports.AccessControlModelType = AccessControlModelType;
|
|
exports.AccessLevel = AccessLevel;
|
|
exports.MaxAccess = MaxAccess;
|
|
exports.RowStatus = RowStatus;
|
|
|
|
exports.ResponseInvalidCode = ResponseInvalidCode;
|
|
exports.ResponseInvalidError = ResponseInvalidError;
|
|
exports.RequestInvalidError = RequestInvalidError;
|
|
exports.RequestFailedError = RequestFailedError;
|
|
exports.RequestTimedOutError = RequestTimedOutError;
|
|
|
|
/**
|
|
** Added for testing
|
|
**/
|
|
exports.ObjectParser = {
|
|
readInt32: readInt32,
|
|
readUint32: readUint32,
|
|
readVarbindValue: readVarbindValue
|
|
};
|
|
exports.Authentication = Authentication;
|
|
exports.Encryption = Encryption;
|