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');
|
|
|
|
|
2011-11-07 22:13:53 +00:00
|
|
|
var AbandonRequest = require('./abandon_request');
|
2011-08-04 20:32:01 +00:00
|
|
|
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');
|
2011-09-27 18:49:33 +00:00
|
|
|
var SearchReference = require('./search_reference');
|
2011-08-04 20:32:01 +00:00
|
|
|
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
|
2014-01-18 20:42:21 +00:00
|
|
|
require('buffertools').extend();
|
2011-08-04 20:32:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///--- Globals
|
|
|
|
|
|
|
|
var Ber = asn1.Ber;
|
|
|
|
var BerReader = asn1.BerReader;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///--- API
|
|
|
|
|
|
|
|
function Parser(options) {
|
2012-02-18 08:15:52 +00:00
|
|
|
if (!options || typeof (options) !== 'object')
|
2011-08-04 20:32:01 +00:00
|
|
|
throw new TypeError('options (object) required');
|
2012-02-18 08:54:22 +00:00
|
|
|
if (typeof (options.log) !== 'object')
|
|
|
|
throw new TypeError('options.log (object) required');
|
2011-08-04 20:32:01 +00:00
|
|
|
|
|
|
|
EventEmitter.call(this);
|
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
this.buffer = null;
|
2012-02-18 08:54:22 +00:00
|
|
|
this.log = options.log;
|
2011-08-04 20:32:01 +00:00
|
|
|
}
|
|
|
|
util.inherits(Parser, EventEmitter);
|
|
|
|
module.exports = Parser;
|
|
|
|
|
|
|
|
|
2012-02-18 08:15:52 +00:00
|
|
|
Parser.prototype.write = function (data) {
|
2011-08-04 20:32:01 +00:00
|
|
|
if (!data || !Buffer.isBuffer(data))
|
|
|
|
throw new TypeError('data (buffer) required');
|
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
var nextMessage = null;
|
2011-08-04 20:32:01 +00:00
|
|
|
var self = this;
|
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
function end() {
|
|
|
|
if (nextMessage)
|
|
|
|
return self.write(nextMessage);
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
return true;
|
|
|
|
}
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
self.buffer = (self.buffer ? self.buffer.concat(data) : data);
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
var ber = new BerReader(self.buffer);
|
|
|
|
if (!ber.readSequence())
|
|
|
|
return false;
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
if (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);
|
|
|
|
}
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
// 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;
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
var message = this.getMessage(ber);
|
|
|
|
if (!message)
|
|
|
|
return end();
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
try {
|
|
|
|
message.parse(ber);
|
2011-08-04 20:32:01 +00:00
|
|
|
} catch (e) {
|
2011-11-11 18:08:48 +00:00
|
|
|
this.emit('error', e, message);
|
2011-08-04 20:32:01 +00:00
|
|
|
return false;
|
|
|
|
}
|
2012-07-09 12:23:53 +00:00
|
|
|
this.emit('message', message);
|
2011-08-04 20:32:01 +00:00
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
return end();
|
2011-08-04 20:32:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-02-18 08:15:52 +00:00
|
|
|
Parser.prototype.getMessage = function (ber) {
|
2011-08-04 20:32:01 +00:00
|
|
|
assert.ok(ber);
|
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
var self = this;
|
2011-08-04 20:32:01 +00:00
|
|
|
|
|
|
|
var messageID = ber.readInt();
|
|
|
|
var type = ber.readSequence();
|
|
|
|
|
|
|
|
var Message;
|
|
|
|
switch (type) {
|
|
|
|
|
2011-11-07 22:13:53 +00:00
|
|
|
case Protocol.LDAP_REQ_ABANDON:
|
|
|
|
Message = AbandonRequest;
|
|
|
|
break;
|
|
|
|
|
2011-08-04 20:32:01 +00:00
|
|
|
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;
|
|
|
|
|
2011-09-27 18:49:33 +00:00
|
|
|
case Protocol.LDAP_REP_SEARCH_REF:
|
|
|
|
Message = SearchReference;
|
|
|
|
break;
|
|
|
|
|
2011-08-04 20:32:01 +00:00
|
|
|
case Protocol.LDAP_REP_SEARCH:
|
|
|
|
Message = SearchResponse;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Protocol.LDAP_REQ_UNBIND:
|
|
|
|
Message = UnbindRequest;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2011-11-11 18:08:48 +00:00
|
|
|
this.emit('error',
|
|
|
|
new Error('protocolOp 0x' +
|
|
|
|
(type ? type.toString(16) : '??') +
|
2012-02-18 08:15:52 +00:00
|
|
|
' not supported'),
|
2011-11-11 18:08:48 +00:00
|
|
|
new LDAPResult({
|
|
|
|
messageID: messageID,
|
|
|
|
protocolOp: type || Protocol.LDAP_REP_EXTENSION
|
|
|
|
}));
|
|
|
|
|
2011-08-04 20:32:01 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-11-11 18:08:48 +00:00
|
|
|
|
|
|
|
return new Message({
|
2011-08-04 20:32:01 +00:00
|
|
|
messageID: messageID,
|
2012-02-18 08:54:22 +00:00
|
|
|
log: self.log
|
2011-08-04 20:32:01 +00:00
|
|
|
});
|
|
|
|
};
|