nodejs实现接收Snmp的Trap消息

var assert = require('assert');

var ASN1 = {
	EOC: 0,
	Boolean: 1,
	Integer: 2,
	BitString: 3,
	OctetString: 4,
	Null: 5,
	OID: 6,
	ObjectDescriptor: 7,
	External: 8,
	Real: 9, // float
	Enumeration: 10,
	PDV: 11,
	Utf8String: 12,
	RelativeOID: 13,
	Sequence: 16,
	Set: 17,
	NumericString: 18,
	PrintableString: 19,
	T61String: 20,
	VideotexString: 21,
	IA5String: 22,
	UTCTime: 23,
	GeneralizedTime: 24,
	GraphicString: 25,
	VisibleString: 26,
	GeneralString: 28,
	UniversalString: 29,
	CharacterString: 30,
	BMPString: 31,
	Constructor: 32,
	Context: 128,

	TypeError: function(msg) {
		var e = new Error();
		e.name = 'InvalidAsn1Error';
		e.message = msg || '';
		return e;
	}
};

function Reader(data) {
	if (!data || !Buffer.isBuffer(data))
	throw new TypeError('data must be a node Buffer');

	this._buf = data;
	this._size = data.length;

	// These hold the "current" state
	this._len = 0;
	this._offset = 0;

	var self = this;
	this.__defineGetter__('length', function() { return self._len; });
	this.__defineGetter__('offset', function() { return self._offset; });
	this.__defineGetter__('remain', function() {
	return self._size - self._offset;
	});
	this.__defineGetter__('buffer', function() {
	return self._buf.slice(self._offset);
	});
}


/**
 * Reads a single byte and advances offset; you can pass in `true` to make this
 * a "peek" operation (i.e., get the byte, but don't advance the offset).
 *
 * @param {Boolean} peek true means don't move offset.
 * @return {Number} the next byte, null if not enough data.
 */
Reader.prototype.readByte = function(peek) {
	if (this._size - this._offset < 1)
	return null;

	var b = this._buf[this._offset] & 0xff;

	if (!peek)
	this._offset += 1;

	return b;
};


Reader.prototype.peek = function() {
	return this.readByte(true);
};


/**
 * Reads a (potentially) variable length off the BER buffer.  This call is
 * not really meant to be called directly, as callers have to manipulate
 * the internal buffer afterwards.
 *
 * As a result of this call, you can call `Reader.length`, until the
 * next thing called that does a readLength.
 *
 * @return {Number} the amount of offset to advance the buffer.
 * @throws {InvalidAsn1Error} on bad ASN.1
 */
Reader.prototype.readLength = function(offset) {
	if (offset === undefined)
	offset = this._offset;

	if (offset >= this._size)
	return null;

	var lenB = this._buf[offset++] & 0xff;
	if (lenB === null)
	return null;

	if ((lenB & 0x80) == 0x80) {
	lenB &= 0x7f;

	if (lenB == 0)
	  throw ASN1.TypeError('Indefinite length not supported');

	if (lenB > 4)
	  throw ASN1.TypeError('encoding too long');

	if (this._size - offset < lenB)
	  return null;

	this._len = 0;
	for (var i = 0; i < lenB; i++)
	  this._len = (this._len << 8) + (this._buf[offset++] & 0xff);

	} else {
	// Wasn't a variable length
	this._len = lenB;
	}

	return offset;
};


/**
 * Parses the next sequence in this BER buffer.
 *
 * To get the length of the sequence, call `Reader.length`.
 *
 * @return {Number} the sequence's tag.
 */
Reader.prototype.readSequence = function(tag) {
  var seq = this.peek();
  if (seq === null)
	return null;
  if (tag !== undefined && tag !== seq)
	throw ASN1.TypeError('Expected 0x' + tag.toString(16) + ': got 0x' + seq.toString(16));

  var o = this.readLength(this._offset + 1); // stored in `length`
  if (o === null)
	return null;

  this._offset = o;
  return seq;
};


Reader.prototype.readInt = function() {
  return this.readTag(ASN1.Integer);
};


Reader.prototype.readBoolean = function() {
  return (this.readTag(ASN1.Boolean) === 0 ? false : true);
};


Reader.prototype.readEnumeration = function() {
  return this.readTag(ASN1.Enumeration);
};


Reader.prototype.readString = function(tag, retbuf) {
  if (!tag)
	tag = ASN1.OctetString;

  var b = this.peek();
  if (b === null)
	return null;

  if (b !== tag)
	throw ASN1.TypeError('Expected 0x' + tag.toString(16) + ': got 0x' + b.toString(16));

  var o = this.readLength(this._offset + 1); // stored in `length`

  if (o === null)
	return null;

  if (this.length > this._size - o)
	return null;

  this._offset = o;

  if (this.length === 0)
	return retbuf ? new Buffer(0) : '';

  var str = this._buf.slice(this._offset, this._offset + this.length);
  this._offset += this.length;

  return retbuf ? str : str.toString('utf8');
};

Reader.prototype.readOID = function(tag) {
  if (!tag)
	tag = ASN1.OID;

  var b = this.readString(tag, true);
  if (b === null)
	return null;

  var values = [];
  var value = 0;

  for (var i = 0; i < b.length; i++) {
	var byte = b[i] & 0xff;

	value <<= 7;
	value += byte & 0x7f;
	if ((byte & 0x80) == 0) {
	  values.push(value);
	  value = 0;
	}
  }

  value = values.shift();
  values.unshift(value % 40);
  values.unshift((value / 40) >> 0);

  return values.join('.');
};


Reader.prototype.readTag = function(tag) {
  assert.ok(tag !== undefined);

  var b = this.peek();

  if (b === null)
	return null;

  if (b !== tag)
	throw ASN1.TypeError('Expected 0x' + tag.toString(16) + ': got 0x' + b.toString(16));

  var o = this.readLength(this._offset + 1); // stored in `length`
  if (o === null)
	return null;

  if (this.length > 4)
	throw ASN1.TypeError('Integer too long: ' + this.length);

  if (this.length > this._size - o)
	return null;
  this._offset = o;

  var fb = this._buf[this._offset];
  var value = 0;

  for (var i = 0; i < this.length; i++) {
	value <<= 8;
	value |= (this._buf[this._offset++] & 0xff);
  }

  if ((fb & 0x80) == 0x80 && i !== 4)
	value -= (1 << (i * 8));

  return value >> 0;
};


var dgram = require("dgram");
function parseTrapPacket(buffer){
	var pkt = {};
	var reader = new Reader(buffer);
	reader.readSequence();
	pkt.version = reader.readInt();//02 01 00
	pkt.community = reader.readString();//04 06 70 75 62 6c 69 63
	pkt.type = reader.readSequence();//a4
	pkt.enterprise = reader.readOID()//0x06, 0x0c, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0x91, 0x28, 0x02, 0x02, 0x47, 0x64
	var bytes = reader.readString(64, true);//0x40, 0x04, 0xc0, 0xa8, 0x17, 0x0a,
	pkt.agentAddr = bytes[0] + "." + bytes[1] + "." + bytes[2] + "." + bytes[3];
	pkt.specific = reader.readInt();// 0x02, 0x01, 0x06,
	pkt.generic = reader.readInt();//0x02, 0x01, 0x0a
	pkt.upTime = reader.readTag(67);//
	pkt.varbinds = readVarbinds(reader);
	return pkt;
};
function readVarbinds (reader) {
	var vbs = [];
	reader.readSequence ();
	while (1) {
		reader.readSequence();
		var oid = reader.readOID (), type = reader.peek (), value = '';
		if (type == null) break;

		switch(type){
			case 1:
				value = reader.readBoolean();
			break;
			case 2:
			case 65:
			case 66:
			case 67:
				value = reader.readTag(2);
			break;
			case 4:
				value = reader.readString (null);
			break;
			case 5:
			case 128:
			case 129:
			case 130:
				reader.readByte ();
				reader.readByte ();
				value = null;
			break;
			case 6:
				value = reader.readOID();
			break;
			case 64:
				var bytes = reader.readString(64, true);
				value = bytes.length == 4 ? bytes[0] + '.' + bytes[1] + '.' + bytes[2] + '.' + bytes[3] : '';
			break;
			case 68:
			case 70:
				value = reader.readString(type, true);
			break;
		}
		vbs.push ({
			oid: oid,
			type: type,
			value: value
		});
	}
	return vbs;
}
function Receiver(port, onTrap, onError, onStart){
	this.port = port;
	this.socket = null;
	this.isRunning = false;
	this.onTrap = onTrap;
	this.onError = onError;
	this.onStart = onStart;
};
Receiver.prototype.start = function(){
	var self = this;
	if(self.isRunning) return;
	var socket = self.socket =  dgram.createSocket('udp4');
	socket.on('error', function(err){
		socket.close();
		self.isRunning = false;
		if(self.onError){
			self.onError(err);
		}
	});
	socket.on('message', function(msg, remote){
		if(self.onTrap){
			var pkt = parseTrapPacket(msg);
			self.onTrap(remote, pkt);
		}
	});
	socket.on('listening', function(){
		self.isRunning = true;
		if(self.onStart){
			self.onStart(self.port);
		}
	});
	socket.bind(self.port);
};
Receiver.prototype.stop = function(){
	var self = this;
	if(self.isRunning){
		if(self.socket){
			self.socket.close();
			self.isRunning = false;
		}
	}
};

var trap = new Receiver(162, function(remote, pkt){
	console.log(JSON.stringify(remote), JSON.stringify(pkt));
}, '', function(port){
	console.log('begin listen on ' + port);
});
trap.start();

module.exports.TrapReceiver = Receiver;

  

posted @ 2015-08-27 14:28  风的线条昵称已被使用  阅读(1880)  评论(0编辑  收藏  举报