farhadi / node-smpp

SMPP client and server implementation in node.js
MIT License
419 stars 180 forks source link

Client Not reciving DLR Report #244

Open dotsinspace opened 11 months ago

dotsinspace commented 11 months ago

I am in big trouble..I want to know why my client is only receiving submit_sm not deliver_sm. even though i tried online simulator for testing. it do shows me Deliver_sm ( https://melroselabs.com/tools/smppsender/?host=smscsim.melroselabs.com ) ..But in client dashboard it only shows Accepted ( Submit_sm ) not Delivered ( delivered_sm )

/*
 * IMPORTS
 */
import Debug from 'debug/src/node' // NPM: Debug module for debugging.
import Smpp from './node_modules/smpp' // NPM: Smpp module for handling Smpp connections.
import { Buffer } from 'safer-buffer' // NPM: Buffer module for handling buffers.
import _ from 'underscore' // NPM: Utility module for handling objects.
import { format } from 'fecha' // NPM: Date formatting module.

/*
 * PACKAGES
 */
import Context from 'context'
import Tag from 'tag'

const generateDeliverSmBuffer = (sequenceNumber, serviceType, sourceAddr, destAddr, shortMessage) => {
  // Command ID for deliver_sm
  const commandId = 0x00000005

  // Create a buffer to hold the PDU
  const buffer = Buffer.alloc(100) // Adjust the size as needed

  // Write the PDU fields to the buffer
  buffer.writeUInt32BE(buffer.length, 0) // Command Length
  buffer.writeUInt32BE(commandId, 4) // Command ID
  buffer.writeUInt32BE(0, 8) // Command Status (No Error)
  buffer.writeUInt32BE(sequenceNumber, 12) // Sequence Number

  // Service Type
  buffer.write(serviceType, 16, 'ascii')
  buffer.writeUInt8(0, 16 + serviceType.length) // Null-terminated

  // Source Address
  buffer.write(sourceAddr, 40, 'ascii')
  buffer.writeUInt8(0, 40 + sourceAddr.length) // Null-terminated

  // Destination Address
  buffer.write(destAddr, 70, 'ascii')
  buffer.writeUInt8(0, 70 + destAddr.length) // Null-terminated

  // Short Message
  buffer.write(shortMessage, 100, 'utf8')

  return buffer
}

/*
 * OBJECTS
 */
Smpp.createServer({ 'enable_proxy_protocol_detection': true, 'debug': global.WORKING_ENV.DEVELOPMENT.value !== global.CONFIG_RC.env }, __Session => {
  // Local variable.
  let _functionName

  // Const assignment.
  const _events = ['submit_sm', 'query_sm', 'cancel_sm', 'deliver_sm']

  // Object assignment.
  const _BindTransceiver = async __pdu => {
    // Create Context for incoming request.
    const _SharedContext = await Context({ 'request': __Session })

    console.log(__pdu)

    // Variable assignment.
    _functionName = 'WwwSmppBindTransceiver'

    // Error handling.
    try {
      /*
       * We pause the session to prevent further incoming pdu events,
       * until we authorize the session with some async operation.
       */
      __Session.pause()

      // Debug incoming request.
      _SharedContext.Debug({ 'message': `Bind request received from ${String.ipv6ToIpv4(__Session.remoteAddress)}`, 'status': 'BIND_TRANSCEIVER' }, _functionName)

      // Get smpp details from database.
      const _SmppFindMany = await _SharedContext.DataBase.smpp.findMany({ 'where': { 'ip': { 'equals': 'smscsim.melroselabs.com' } } })

      // If details are empty then report failure.
      if (_SmppFindMany instanceof Error || _.isEmpty(_SmppFindMany)) {
        // Style guide.
        await _SharedContext.Debug({ 'message': `Could not find smpp with given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}`, 'error': _SmppFindMany instanceof Error ? _SmppFindMany : new Error('NO_SMPP_WITH_THIS_IP_FOUND') }, _functionName)

        // Close the session.
        return __Session.send(__pdu.response({ 'command_status': Smpp.ESME_RBINDFAIL }))
      }

      // Style Guide.
      await _SharedContext.Debug({ 'message': `Found ${_SmppFindMany.length} smpp with given username for given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}.` }, _functionName)
      await _SharedContext.Debug({ 'message': `Looking for smpp with correct password for given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)

      /*
       * Loop over all smpp details.
       * and look if any one smpp finds a match.
       */
      for await (const josh of _SmppFindMany) {
        // Respond with success if given username and password matches.
        if (josh.username === __pdu.system_id.toString() && josh.password === __pdu.password.toString()) {
          // Style guide.
          await _SharedContext.Debug({ 'message': `Found smpp with correct password for given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)
          await _SharedContext.Debug({ 'message': `Responding ip: ${String.ipv6ToIpv4(__Session.remoteAddress)} with successful bind.` }, _functionName)

          // Send successful pdu response.
          __Session.send(__pdu.response({ 'command_status': Smpp.ESME_ROK }))

          // Resume the session.
          __Session.resume()

          // Debug incoming request.
          return _SharedContext.Debug({ 'message': `Bind request accepted from ${String.ipv6ToIpv4(__Session.remoteAddress)}`, 'status': 'BIND_TRANSCEIVER' }, _functionName)
        }
      }

      // Style guide.
      await _SharedContext.Debug({ 'message': `Could not find smpp with correct password for given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)

      // Close the session.
      __Session.close()

      // Report failure.
      return __Session.send(__pdu.response({ 'command_status': Smpp.ESME_RBINDFAIL }))
    } catch (error) {
      // Report failure.
      _SharedContext.Debug({ error })

      // End the session.
      return __Session.send(__pdu.response({ 'command_status': Smpp.ESME_RBINDFAIL }))
    }
  }
  const _PushPDU = async (__command, __pdu) => {
    // Create Context for incoming request.
    const _SharedContext = await Context({ 'request': __Session })

    console.log(__command, __pdu)

    // Variable assignment.
    _functionName = 'WwwSmppSubmitSm'

    // Error handling.
    try {
      // Debug incoming request.
      _SharedContext.Debug({ 'message': `Message received from ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)

      // Get client by source and destination.
      const _RouteGet = _SharedContext.Route.get(Tag.Route.ClientByIp('smscsim.melroselabs.com'))

      // If route is empty then report failure.
      if (_.isEmpty(_RouteGet)) {
        // Style guide.
        await _SharedContext.Debug({ 'message': `Could not find client for given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)

        // Report failure.
        return __Session.send(__pdu.response({ 'command_status': Smpp.ESME_RSYSERR }))
      }

      // Style guide.
      await _SharedContext.Debug({ 'message': `Found client for given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)

      // Get pooled client.
      const _RouteGetUse = _RouteGet.use()

      // Style Guide.
      await _SharedContext.Debug({ 'message': `Pooled route from route pool for ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)
      await _SharedContext.Debug({ 'message': `Pushing pdu to client with given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)

      // Push pdu to this route.
      const _RouteGetUsePush = await _RouteGetUse.Push(__command, __pdu)

      // If pushing pdu caught exception then report failure.
      if (_RouteGetUsePush instanceof Error) {
        // Style Guide.
        await _SharedContext.Debug({ 'message': `Failed to push pdu to client with given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}`, 'error': _RouteGetUsePush }, _functionName)

        // Report failure.
        return __Session.send(__pdu.response({ 'command_status': Smpp.ESME_RSYSERR }))
      }

      // Style Guide.
      await _SharedContext.Debug({ 'message': `Successfully pushed pdu to client with given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)

      console.log(_RouteGetUsePush)
      // Respond with submit response.
      __Session.send(__pdu.response({ 'message_state': Smpp.ESME_ROK, 'message_id': _RouteGetUsePush.message_id }))

      // Report success.
      if ('submit_sm' === __command) {
        // Style Guide.
        await _SharedContext.Debug({ 'message': `Responding to client with given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)

        // Object assignment.
        const _DeliverSm = () => __Session.deliver_sm({
          'source_addr': __pdu.destination_addr,
          'destination_addr': __pdu.source_addr,
          'short_message': __pdu.short_message,
          'esm_class': 4,
          'delivery_state': 0,
          'message_id': _RouteGetUsePush.message_id,
          'final_date': new Date(),
          'message_state': 'DELIVERED',
          'receipted_message_id': _RouteGetUsePush.message_id,
          'text': _RouteGetUsePush.message
        })

        // Handle registered delivery accordingly.
        if (1 === __pdu.registered_delivery) {
          // Respond with receipt.
          _DeliverSm()
        } else if (2 === __pdu.registered_delivery) {
          /*
           * Only response with receipt if
           * error has been occurred.
           */
          if (0 !== _RouteGetUsePush.command_status) {
            // Respond with receipt.
            _DeliverSm()
          }
        } else if (3 === __pdu.registered_delivery) {
          // Only respond back if no error has been occurred.
          if (0 === _RouteGetUsePush.command_status) {
            // Respond with receipt.
            _DeliverSm()
          }
        }
      } else if ('query_sm' === __command) {
        // Style Guide.
        await _SharedContext.Debug({ 'message': `Responding to client with given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)

        // Respond with success.
        __Session.send(__pdu.response({
          'message_id': __pdu.message_id,
          'final_date': new Date(),
          'message_state': 'DELIVERED',
          'receipted_message_id': __pdu.message_id,
          'text': _RouteGetUsePush.message
        }))
      } else if ('cancel_sm' === __command) {
        // Style Guide.
        await _SharedContext.Debug({ 'message': `Responding to client with given ip: ${String.ipv6ToIpv4(__Session.remoteAddress)}` }, _functionName)

        // Respond with success.
        __Session.send(__pdu.response({ 'message_state': Smpp.ESME_ROK }))
      }

      // Recycle route.
      return _RouteGet.recycle(_RouteGetUse)
    } catch (error) {
      // Report failure.
      _SharedContext.Debug({ error })

      // Report failure that pdu has not been sent.
      return __Session.send(__pdu.response({ 'command_status': Smpp.ESME_RSYSERR }))
    }
  }

  // Smpp handler for handling server bind.
  __Session.on('bind_transceiver', _BindTransceiver)

  // Loop over events which can be handled by Route.
  _events.forEach(_event => __Session.on(_event, __pdu => _PushPDU(_event, __pdu)))

  // Other common events to listen on.
  __Session.on('error', async error => {
    // Create Context for incoming request.
    const _SharedContext = await Promise.resolve(Context(__Session))

    // Handle errors
    _SharedContext.Debug({ error })

    // Close smpp connection and return success.
    __Session.close()
  })
  __Session.on('unbind', pdu => {
    // Close smpp connection and return success.
    __Session.send(pdu.response())
    __Session.close()
  })
  __Session.on('enquire_link', __pdu => __Session.send(__pdu.response()))
  __Session.on('close', () => __Session.close())
}).listen(global.CONFIG_RC.smppPort, () => Debug('WwwSmpp')(`=> Executing => Running on ${global.CONFIG_RC.smppPort}`))