parser.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
  2. const EventEmitter = require('events').EventEmitter
  3. const util = require('util')
  4. const assert = require('assert-plus')
  5. const asn1 = require('@ldapjs/asn1')
  6. const logger = require('../logger')
  7. const messages = require('@ldapjs/messages')
  8. const AbandonRequest = messages.AbandonRequest
  9. const AddRequest = messages.AddRequest
  10. const AddResponse = messages.AddResponse
  11. const BindRequest = messages.BindRequest
  12. const BindResponse = messages.BindResponse
  13. const CompareRequest = messages.CompareRequest
  14. const CompareResponse = messages.CompareResponse
  15. const DeleteRequest = messages.DeleteRequest
  16. const DeleteResponse = messages.DeleteResponse
  17. const ExtendedRequest = messages.ExtensionRequest
  18. const ExtendedResponse = messages.ExtensionResponse
  19. const ModifyRequest = messages.ModifyRequest
  20. const ModifyResponse = messages.ModifyResponse
  21. const ModifyDNRequest = messages.ModifyDnRequest
  22. const ModifyDNResponse = messages.ModifyDnResponse
  23. const SearchRequest = messages.SearchRequest
  24. const SearchEntry = messages.SearchResultEntry
  25. const SearchReference = messages.SearchResultReference
  26. const SearchResponse = require('./search_response')
  27. const UnbindRequest = messages.UnbindRequest
  28. const LDAPResult = messages.LdapResult
  29. const Protocol = require('@ldapjs/protocol')
  30. /// --- Globals
  31. const BerReader = asn1.BerReader
  32. /// --- API
  33. function Parser (options = {}) {
  34. assert.object(options)
  35. EventEmitter.call(this)
  36. this.buffer = null
  37. this.log = options.log || logger
  38. }
  39. util.inherits(Parser, EventEmitter)
  40. /**
  41. * The LDAP server/client implementations will receive data from a stream and feed
  42. * it into this method. This method will collect that data into an internal
  43. * growing buffer. As that buffer fills with enough data to constitute a valid
  44. * LDAP message, the data will be parsed, emitted as a message object, and
  45. * reset the buffer to account for any next message in the stream.
  46. */
  47. Parser.prototype.write = function (data) {
  48. if (!data || !Buffer.isBuffer(data)) { throw new TypeError('data (buffer) required') }
  49. let nextMessage = null
  50. const self = this
  51. function end () {
  52. if (nextMessage) { return self.write(nextMessage) }
  53. return true
  54. }
  55. self.buffer = self.buffer ? Buffer.concat([self.buffer, data]) : data
  56. let ber = new BerReader(self.buffer)
  57. let foundSeq = false
  58. try {
  59. foundSeq = ber.readSequence()
  60. } catch (e) {
  61. this.emit('error', e)
  62. }
  63. if (!foundSeq || ber.remain < ber.length) {
  64. // ENOTENOUGH
  65. return false
  66. } else if (ber.remain > ber.length) {
  67. // ETOOMUCH
  68. // This is an odd branch. Basically, it is setting `nextMessage` to
  69. // a buffer that represents data part of a message subsequent to the one
  70. // being processed. It then re-creates `ber` as a representation of
  71. // the message being processed and advances its offset to the value
  72. // position of the TLV.
  73. // Set `nextMessage` to the bytes subsequent to the current message's
  74. // value bytes. That is, slice from the byte immediately following the
  75. // current message's value bytes until the end of the buffer.
  76. nextMessage = self.buffer.slice(ber.offset + ber.length)
  77. const currOffset = ber.offset
  78. ber = new BerReader(ber.buffer.subarray(0, currOffset + ber.length))
  79. ber.readSequence()
  80. assert.equal(ber.remain, ber.length)
  81. }
  82. // If we're here, ber holds the message, and nextMessage is temporarily
  83. // pointing at the next sequence of data (if it exists)
  84. self.buffer = null
  85. let message
  86. try {
  87. if (Object.prototype.toString.call(ber) === '[object BerReader]') {
  88. // Parse the BER into a JavaScript object representation. The message
  89. // objects require the full sequence in order to construct the object.
  90. // At this point, we have already read the sequence tag and length, so
  91. // we need to rewind the buffer a bit. The `.sequenceToReader` method
  92. // does this for us.
  93. message = messages.LdapMessage.parse(ber.sequenceToReader())
  94. } else {
  95. // Bail here if peer isn't speaking protocol at all
  96. message = this.getMessage(ber)
  97. }
  98. if (!message) {
  99. return end()
  100. }
  101. // TODO: find a better way to handle logging now that messages and the
  102. // server are decoupled. ~ jsumners 2023-02-17
  103. message.log = this.log
  104. } catch (e) {
  105. this.emit('error', e, message)
  106. return false
  107. }
  108. this.emit('message', message)
  109. return end()
  110. }
  111. Parser.prototype.getMessage = function (ber) {
  112. assert.ok(ber)
  113. const self = this
  114. const messageId = ber.readInt()
  115. const type = ber.readSequence()
  116. let Message
  117. switch (type) {
  118. case Protocol.operations.LDAP_REQ_ABANDON:
  119. Message = AbandonRequest
  120. break
  121. case Protocol.operations.LDAP_REQ_ADD:
  122. Message = AddRequest
  123. break
  124. case Protocol.operations.LDAP_RES_ADD:
  125. Message = AddResponse
  126. break
  127. case Protocol.operations.LDAP_REQ_BIND:
  128. Message = BindRequest
  129. break
  130. case Protocol.operations.LDAP_RES_BIND:
  131. Message = BindResponse
  132. break
  133. case Protocol.operations.LDAP_REQ_COMPARE:
  134. Message = CompareRequest
  135. break
  136. case Protocol.operations.LDAP_RES_COMPARE:
  137. Message = CompareResponse
  138. break
  139. case Protocol.operations.LDAP_REQ_DELETE:
  140. Message = DeleteRequest
  141. break
  142. case Protocol.operations.LDAP_RES_DELETE:
  143. Message = DeleteResponse
  144. break
  145. case Protocol.operations.LDAP_REQ_EXTENSION:
  146. Message = ExtendedRequest
  147. break
  148. case Protocol.operations.LDAP_RES_EXTENSION:
  149. Message = ExtendedResponse
  150. break
  151. case Protocol.operations.LDAP_REQ_MODIFY:
  152. Message = ModifyRequest
  153. break
  154. case Protocol.operations.LDAP_RES_MODIFY:
  155. Message = ModifyResponse
  156. break
  157. case Protocol.operations.LDAP_REQ_MODRDN:
  158. Message = ModifyDNRequest
  159. break
  160. case Protocol.operations.LDAP_RES_MODRDN:
  161. Message = ModifyDNResponse
  162. break
  163. case Protocol.operations.LDAP_REQ_SEARCH:
  164. Message = SearchRequest
  165. break
  166. case Protocol.operations.LDAP_RES_SEARCH_ENTRY:
  167. Message = SearchEntry
  168. break
  169. case Protocol.operations.LDAP_RES_SEARCH_REF:
  170. Message = SearchReference
  171. break
  172. case Protocol.operations.LDAP_RES_SEARCH:
  173. Message = SearchResponse
  174. break
  175. case Protocol.operations.LDAP_REQ_UNBIND:
  176. Message = UnbindRequest
  177. break
  178. default:
  179. this.emit('error',
  180. new Error('Op 0x' + (type ? type.toString(16) : '??') +
  181. ' not supported'),
  182. new LDAPResult({
  183. messageId,
  184. protocolOp: type || Protocol.operations.LDAP_RES_EXTENSION
  185. }))
  186. return false
  187. }
  188. return new Message({
  189. messageId,
  190. log: self.log
  191. })
  192. }
  193. /// --- Exports
  194. module.exports = Parser