node-ldapjs/lib/messages/parser.js

258 lines
5.7 KiB
JavaScript

// 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 AbandonRequest = require('./abandon_request');
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 SearchReference = require('./search_reference');
var SearchResponse = require('./search_response');
var UnbindRequest = require('./unbind_request');
var UnbindResponse = require('./unbind_response');
var LDAPResult = require('./result');
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);
}
assert.ok(this._message.parse(data, ber.length));
var message = this._message;
this._reset();
this.emit('message', message);
} catch (e) {
self.emit('error', e, self._message);
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_ABANDON:
Message = AbandonRequest;
break;
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_REF:
Message = SearchReference;
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');
this.emit('error', e, new LDAPResult({
messageID: messageID,
protocolOp: type
}));
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;
};