123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- // Copyright 2011 Mark Cavage, Inc. All rights reserved.
- const EventEmitter = require('events').EventEmitter
- const util = require('util')
- const assert = require('assert-plus')
- const asn1 = require('@ldapjs/asn1')
- const logger = require('../logger')
- const messages = require('@ldapjs/messages')
- const AbandonRequest = messages.AbandonRequest
- const AddRequest = messages.AddRequest
- const AddResponse = messages.AddResponse
- const BindRequest = messages.BindRequest
- const BindResponse = messages.BindResponse
- const CompareRequest = messages.CompareRequest
- const CompareResponse = messages.CompareResponse
- const DeleteRequest = messages.DeleteRequest
- const DeleteResponse = messages.DeleteResponse
- const ExtendedRequest = messages.ExtensionRequest
- const ExtendedResponse = messages.ExtensionResponse
- const ModifyRequest = messages.ModifyRequest
- const ModifyResponse = messages.ModifyResponse
- const ModifyDNRequest = messages.ModifyDnRequest
- const ModifyDNResponse = messages.ModifyDnResponse
- const SearchRequest = messages.SearchRequest
- const SearchEntry = messages.SearchResultEntry
- const SearchReference = messages.SearchResultReference
- const SearchResponse = require('./search_response')
- const UnbindRequest = messages.UnbindRequest
- const LDAPResult = messages.LdapResult
- const Protocol = require('@ldapjs/protocol')
- /// --- Globals
- const BerReader = asn1.BerReader
- /// --- API
- function Parser (options = {}) {
- assert.object(options)
- EventEmitter.call(this)
- this.buffer = null
- this.log = options.log || logger
- }
- util.inherits(Parser, EventEmitter)
- /**
- * The LDAP server/client implementations will receive data from a stream and feed
- * it into this method. This method will collect that data into an internal
- * growing buffer. As that buffer fills with enough data to constitute a valid
- * LDAP message, the data will be parsed, emitted as a message object, and
- * reset the buffer to account for any next message in the stream.
- */
- Parser.prototype.write = function (data) {
- if (!data || !Buffer.isBuffer(data)) { throw new TypeError('data (buffer) required') }
- let nextMessage = null
- const self = this
- function end () {
- if (nextMessage) { return self.write(nextMessage) }
- return true
- }
- self.buffer = self.buffer ? Buffer.concat([self.buffer, data]) : data
- let ber = new BerReader(self.buffer)
- let 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 an odd branch. Basically, it is setting `nextMessage` to
- // a buffer that represents data part of a message subsequent to the one
- // being processed. It then re-creates `ber` as a representation of
- // the message being processed and advances its offset to the value
- // position of the TLV.
- // Set `nextMessage` to the bytes subsequent to the current message's
- // value bytes. That is, slice from the byte immediately following the
- // current message's value bytes until the end of the buffer.
- nextMessage = self.buffer.slice(ber.offset + ber.length)
- const currOffset = ber.offset
- ber = new BerReader(ber.buffer.subarray(0, currOffset + ber.length))
- ber.readSequence()
- 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
- let message
- try {
- if (Object.prototype.toString.call(ber) === '[object BerReader]') {
- // Parse the BER into a JavaScript object representation. The message
- // objects require the full sequence in order to construct the object.
- // At this point, we have already read the sequence tag and length, so
- // we need to rewind the buffer a bit. The `.sequenceToReader` method
- // does this for us.
- message = messages.LdapMessage.parse(ber.sequenceToReader())
- } else {
- // Bail here if peer isn't speaking protocol at all
- message = this.getMessage(ber)
- }
- if (!message) {
- return end()
- }
- // TODO: find a better way to handle logging now that messages and the
- // server are decoupled. ~ jsumners 2023-02-17
- message.log = this.log
- } catch (e) {
- this.emit('error', e, message)
- return false
- }
- this.emit('message', message)
- return end()
- }
- Parser.prototype.getMessage = function (ber) {
- assert.ok(ber)
- const self = this
- const messageId = ber.readInt()
- const type = ber.readSequence()
- let Message
- switch (type) {
- case Protocol.operations.LDAP_REQ_ABANDON:
- Message = AbandonRequest
- break
- case Protocol.operations.LDAP_REQ_ADD:
- Message = AddRequest
- break
- case Protocol.operations.LDAP_RES_ADD:
- Message = AddResponse
- break
- case Protocol.operations.LDAP_REQ_BIND:
- Message = BindRequest
- break
- case Protocol.operations.LDAP_RES_BIND:
- Message = BindResponse
- break
- case Protocol.operations.LDAP_REQ_COMPARE:
- Message = CompareRequest
- break
- case Protocol.operations.LDAP_RES_COMPARE:
- Message = CompareResponse
- break
- case Protocol.operations.LDAP_REQ_DELETE:
- Message = DeleteRequest
- break
- case Protocol.operations.LDAP_RES_DELETE:
- Message = DeleteResponse
- break
- case Protocol.operations.LDAP_REQ_EXTENSION:
- Message = ExtendedRequest
- break
- case Protocol.operations.LDAP_RES_EXTENSION:
- Message = ExtendedResponse
- break
- case Protocol.operations.LDAP_REQ_MODIFY:
- Message = ModifyRequest
- break
- case Protocol.operations.LDAP_RES_MODIFY:
- Message = ModifyResponse
- break
- case Protocol.operations.LDAP_REQ_MODRDN:
- Message = ModifyDNRequest
- break
- case Protocol.operations.LDAP_RES_MODRDN:
- Message = ModifyDNResponse
- break
- case Protocol.operations.LDAP_REQ_SEARCH:
- Message = SearchRequest
- break
- case Protocol.operations.LDAP_RES_SEARCH_ENTRY:
- Message = SearchEntry
- break
- case Protocol.operations.LDAP_RES_SEARCH_REF:
- Message = SearchReference
- break
- case Protocol.operations.LDAP_RES_SEARCH:
- Message = SearchResponse
- break
- case Protocol.operations.LDAP_REQ_UNBIND:
- Message = UnbindRequest
- break
- default:
- this.emit('error',
- new Error('Op 0x' + (type ? type.toString(16) : '??') +
- ' not supported'),
- new LDAPResult({
- messageId,
- protocolOp: type || Protocol.operations.LDAP_RES_EXTENSION
- }))
- return false
- }
- return new Message({
- messageId,
- log: self.log
- })
- }
- /// --- Exports
- module.exports = Parser
|