// 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) { assert.object(options); assert.object(options.log); EventEmitter.call(this); this.buffer = null; this.log = options.log; } util.inherits(Parser, EventEmitter); 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 }); }; ///--- Exports module.exports = Parser;