2011-08-04 20:32:01 +00:00
|
|
|
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
|
|
|
|
|
|
var assert = require('assert');
|
|
|
|
var EventEmitter = require('events').EventEmitter;
|
|
|
|
var util = require('util');
|
|
|
|
|
|
|
|
var asn1 = require('asn1');
|
|
|
|
|
|
|
|
var AddRequest = require('./add_request');
|
|
|
|
var AddResponse = require('./add_response');
|
|
|
|
var BindRequest = require('./bind_request');
|
|
|
|
var BindResponse = require('./bind_response');
|
|
|
|
var CompareRequest = require('./compare_request');
|
|
|
|
var CompareResponse = require('./compare_response');
|
|
|
|
var DeleteRequest = require('./del_request');
|
|
|
|
var DeleteResponse = require('./del_response');
|
|
|
|
var ExtendedRequest = require('./ext_request');
|
|
|
|
var ExtendedResponse = require('./ext_response');
|
|
|
|
var ModifyRequest = require('./modify_request');
|
|
|
|
var ModifyResponse = require('./modify_response');
|
|
|
|
var ModifyDNRequest = require('./moddn_request');
|
|
|
|
var ModifyDNResponse = require('./moddn_response');
|
|
|
|
var SearchRequest = require('./search_request');
|
|
|
|
var SearchEntry = require('./search_entry');
|
|
|
|
var SearchResponse = require('./search_response');
|
|
|
|
var UnbindRequest = require('./unbind_request');
|
|
|
|
var UnbindResponse = require('./unbind_response');
|
2011-09-15 21:49:00 +00:00
|
|
|
|
|
|
|
var LDAPResult = require('./result');
|
2011-08-04 20:32:01 +00:00
|
|
|
var Message = require('./message');
|
|
|
|
|
|
|
|
var Protocol = require('../protocol');
|
|
|
|
|
|
|
|
// Just make sure this adds to the prototype
|
|
|
|
require('buffertools');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///--- Globals
|
|
|
|
|
|
|
|
var Ber = asn1.Ber;
|
|
|
|
var BerReader = asn1.BerReader;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///--- API
|
|
|
|
|
|
|
|
function Parser(options) {
|
|
|
|
if (!options || typeof(options) !== 'object')
|
|
|
|
throw new TypeError('options (object) required');
|
|
|
|
if (!options.log4js || typeof(options.log4js) !== 'object')
|
|
|
|
throw new TypeError('options.log4js (object) required');
|
|
|
|
|
|
|
|
EventEmitter.call(this);
|
|
|
|
|
|
|
|
this._reset();
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
this.log4js = options.log4js;
|
|
|
|
this.log = this.log4js.getLogger('Parser');
|
|
|
|
}
|
|
|
|
util.inherits(Parser, EventEmitter);
|
|
|
|
module.exports = Parser;
|
|
|
|
|
|
|
|
|
|
|
|
Parser.prototype.write = function(data) {
|
|
|
|
if (!data || !Buffer.isBuffer(data))
|
|
|
|
throw new TypeError('data (buffer) required');
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
if (this._buffer)
|
|
|
|
data = this._buffer.concat(data);
|
|
|
|
|
|
|
|
if (this.log.isTraceEnabled())
|
|
|
|
this.log.trace('Processing buffer (concat\'d): ' + util.inspect(data));
|
|
|
|
|
|
|
|
// If there's more than one message in this buffer
|
|
|
|
var extra;
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (this._message === null) {
|
|
|
|
var ber = new BerReader(data);
|
|
|
|
if (!this._newMessage(ber))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
data = data.slice(ber.offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data.length > this._messageLength) {
|
|
|
|
extra = data.slice(ber.length);
|
|
|
|
data = data.slice(0, ber.length);
|
|
|
|
}
|
|
|
|
|
2011-09-15 21:49:00 +00:00
|
|
|
assert.ok(this._message.parse(data, ber.length));
|
2011-08-04 20:32:01 +00:00
|
|
|
|
|
|
|
var message = this._message;
|
|
|
|
this._reset();
|
|
|
|
this.emit('message', message);
|
|
|
|
|
|
|
|
} catch (e) {
|
2011-09-15 21:49:00 +00:00
|
|
|
self.emit('error', e, self._message);
|
2011-08-04 20:32:01 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Another message is already there
|
|
|
|
if (extra) {
|
|
|
|
if (this.log.isTraceEnabled())
|
|
|
|
this.log.trace('parsing extra bytes: ' + util.inspect(extra));
|
|
|
|
|
|
|
|
return this.write(extra);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Parser.prototype._newMessage = function(ber) {
|
|
|
|
assert.ok(ber);
|
|
|
|
|
|
|
|
if (this._messageLength === null) {
|
|
|
|
if (ber.readSequence() === null) { // not enough data for the length?
|
|
|
|
this._buffer = ber.buffer;
|
|
|
|
if (this.log.isTraceEnabled())
|
|
|
|
this.log.trace('Not enough data for the message header');
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
this._messageLength = ber.length;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ber.remain < this._messageLength) {
|
|
|
|
if (this.log.isTraceEnabled())
|
|
|
|
this.log.trace('Not enough data for the message');
|
|
|
|
|
|
|
|
this._buffer = ber.buffer;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var messageID = ber.readInt();
|
|
|
|
var type = ber.readSequence();
|
|
|
|
|
|
|
|
if (this.log.isTraceEnabled())
|
|
|
|
this.log.trace('message id=%d, type=0x%s', messageID, type.toString(16));
|
|
|
|
|
|
|
|
var Message;
|
|
|
|
switch (type) {
|
|
|
|
|
|
|
|
case Protocol.LDAP_REQ_ADD:
|
|
|
|
Message = AddRequest;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REP_ADD:
|
|
|
|
Message = AddResponse;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REQ_BIND:
|
|
|
|
Message = BindRequest;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REP_BIND:
|
|
|
|
Message = BindResponse;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REQ_COMPARE:
|
|
|
|
Message = CompareRequest;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REP_COMPARE:
|
|
|
|
Message = CompareResponse;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REQ_DELETE:
|
|
|
|
Message = DeleteRequest;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REP_DELETE:
|
|
|
|
Message = DeleteResponse;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REQ_EXTENSION:
|
|
|
|
Message = ExtendedRequest;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REP_EXTENSION:
|
|
|
|
Message = ExtendedResponse;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REQ_MODIFY:
|
|
|
|
Message = ModifyRequest;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REP_MODIFY:
|
|
|
|
Message = ModifyResponse;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REQ_MODRDN:
|
|
|
|
Message = ModifyDNRequest;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REP_MODRDN:
|
|
|
|
Message = ModifyDNResponse;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REQ_SEARCH:
|
|
|
|
Message = SearchRequest;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REP_SEARCH_ENTRY:
|
|
|
|
Message = SearchEntry;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REP_SEARCH:
|
|
|
|
Message = SearchResponse;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REQ_UNBIND:
|
|
|
|
Message = UnbindRequest;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
var e = new Error('protocolOp 0x' + type.toString(16) + ' not supported');
|
2011-09-15 21:49:00 +00:00
|
|
|
this.emit('error', e, new LDAPResult({
|
2011-09-26 20:47:33 +00:00
|
|
|
messageID: messageID,
|
|
|
|
protocolOp: type
|
2011-09-15 21:49:00 +00:00
|
|
|
}));
|
2011-08-04 20:32:01 +00:00
|
|
|
this._reset();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
assert.ok(Message);
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
this._message = new Message({
|
|
|
|
messageID: messageID,
|
|
|
|
log4js: self.log4js
|
|
|
|
});
|
|
|
|
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Parser.prototype._reset = function() {
|
|
|
|
this._message = null;
|
|
|
|
this._messageLength = null;
|
|
|
|
this._buffer = null;
|
|
|
|
};
|