###
#
# This mixin provides a method to connect to an IPC share on the remote SMB server.
#
# -*- coding: binary -*-

module Msf

  module Exploit::Remote::SMB::Client::Ipc

    include Msf::Exploit::Remote::SMB::Client::Authenticated
    include Msf::Auxiliary::Report

    class SmbIpcError < StandardError; end
    class SmbIpcConnectionError < SmbIpcError; end
    class SmbIpcAuthenticationError < SmbIpcError; end

    module_function

    def connect_ipc
      begin
        if session
          self.simple = session.simple_client
          ipc_tree = simple.client.tree_connect("\\\\#{simple.peerhost}\\IPC$")
        else
          connect
          # smb_login does a tree_connect to the IPC share already.
          ipc_tree = smb_login
        end
      rescue Rex::ConnectionError => e
        raise SmbIpcConnectionError, e.message
      rescue Rex::Proto::SMB::Exceptions::Error, RubySMB::Error::RubySMBError => e
        raise SmbIpcAuthenticationError, "Unable to authenticate ([#{e.class}] #{e})."
      end

      report_dcerpc_service

      ipc_tree
    end

    def report_dcerpc_service
      report_service(
        name: 'dcerpc',
        info: "Module: #{fullname}",
        host: simple.peerhost,
        port: simple.peerport,
        proto: 'tcp',
        resource: { smb: { share: 'IPC$' } },
        parents: {
          name: 'smb',
          host: simple.peerhost,
          port: simple.peerport,
          proto: 'tcp',
          info: "Module: #{fullname}, last negotiated version: SMBv#{simple.client.negotiated_smb_version} (dialect = #{simple.client.dialect})",
          parents: {
            name: 'tcp',
            host: simple.peerhost,
            port: simple.peerport,
            proto: 'tcp'
          }
        }
      )
    end

    def disconnect_ipc(ipc_tree)
      ipc_tree.disconnect! if ipc_tree
    end

  end

end
