node-ldapjs/lib/messages/parser.js

231 lines
5.1 KiB
JavaScript

// Copyright 2011 Mark Cavage, Inc. All rights reserved.
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var assert = require('assert-plus');
var asn1 = require('asn1');
var VError = require('verror').VError;
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');
///--- 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 (typeof (options.log) !== 'object')
throw new TypeError('options.log (object) required');
EventEmitter.call(this);
this.buffer = null;
this.log = options.log;
}
util.inherits(Parser, EventEmitter);
module.exports = Parser;
Parser.prototype.write = function (data) {
if (!data || !Buffer.isBuffer(data))
throw new TypeError('data (buffer) required');
var nextMessage = null;
var self = this;
function end() {
if (nextMessage)
return self.write(nextMessage);
return true;
}
self.buffer = (self.buffer ? Buffer.concat([self.buffer, data]) : data);
var ber = new BerReader(self.buffer);
var foundSeq = false;
try {
foundSeq = ber.readSequence();
} catch (e) {
this.emit('error', e);
}
if (!foundSeq || ber.remain < ber.length) {
// ENOTENOUGH
return false;
} else if (ber.remain > ber.length) {
// ETOOMUCH
// This is sort of ugly, but allows us to make miminal copies
nextMessage = self.buffer.slice(ber.offset + ber.length);
ber._size = ber.offset + ber.length;
assert.equal(ber.remain, ber.length);
}
// If we're here, ber holds the message, and nextMessage is temporarily
// pointing at the next sequence of data (if it exists)
self.buffer = null;
var message;
try {
// Bail here if peer isn't speaking protocol at all
message = this.getMessage(ber);
if (!message) {
return end();
}
message.parse(ber);
} catch (e) {
this.emit('error', e, message);
return false;
}
this.emit('message', message);
return end();
};
Parser.prototype.getMessage = function (ber) {
assert.ok(ber);
var self = this;
var messageID = ber.readInt();
var type = ber.readSequence();
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:
this.emit('error',
new Error('Op 0x' + (type ? type.toString(16) : '??') +
' not supported'),
new LDAPResult({
messageID: messageID,
protocolOp: type || Protocol.LDAP_REP_EXTENSION
}));
return false;
}
return new Message({
messageID: messageID,
log: self.log
});
};