# -*- coding:binary -*-
require 'spec_helper'

require 'msf/core'
require 'msf/core/exploit/smb/server/share'
require 'rex/proto/smb/constants'

RSpec.describe Msf::Exploit::Remote::SMB::Server::Share do

  include_context "Msf::StringIO"

  subject(:mod) do
    mod = Msf::Exploit.new
    mod.extend described_class
    mod.send(:initialize)

    mod
  end

  let(:valid_query_path_standard_info_params) do
    "\xed\x03\x00\x00\x00\x00\x5c\x00\x74\x00\x65\x00\x73\x00\x74\x00" +
    "\x2e\x00\x65\x00\x78\x00\x65\x00\x00\x00"
  end
  let(:query_path_standard_info_res_length) { 83 }

  let(:valid_query_path_basic_info_params) do
    "\xec\x03\x00\x00\x00\x00\x5c\x00\x74\x00\x65\x00\x73\x00\x74\x00" +
    "\x2e\x00\x65\x00\x78\x00\x65\x00\x00\x00"
  end
  let(:query_path_basic_info_res_length) { 101 }

  let(:non_existent_query_path_basic_info_params) do
    "\xec\x03\x00\x00\x00\x00\x5c\x00\x74\x00\x65\x00\x73\x00\x74\x00" +
    "\x2e\x00\x65\x00\x78\x00\x61\x00\x00\x00"
  end
  let(:not_found_res_length) { 39 }

  before(:example) do
    msf_io.string = ''
    mod.instance_variable_set('@state', {
      msf_io => {
        :multiplex_id => 0x41424344,
        :process_id   => 0x45464748,
        :file_id      => 0xdead,
        :dir_id       => 0xbeef
      }
    })
    mod.lo = 0
    mod.hi = 0
    mod.share = 'test'
    mod.file_name = 'test.exe'
    mod.file_contents = 'metasploit'
  end

  describe "#smb_cmd_trans2_query_path_information" do

    context "when valid SMB_QUERY_PATH_STANDARD_INFO parameters" do
      it "returns the number of bytes answered" do
        expect(mod.smb_cmd_trans2_query_path_information(msf_io, valid_query_path_standard_info_params)).to eq(query_path_standard_info_res_length)
      end

      it "send SMB_QUERY_PATH_STANDARD_INFO response with the file size" do
        mod.smb_cmd_trans2_query_path_information(msf_io, valid_query_path_standard_info_params)
        res = msf_io.read

        trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct
        trans2_res.from_s(res)
        param_count = trans2_res['Payload'].v['ParamCount']
        data_count = trans2_res['Payload'].v['DataCount']

        data  = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count]
        smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_STANDARD_INFO_HDR.make_struct
        smb_data.from_s(data)

        expect(smb_data.v['EndOfFile']).to eq(mod.file_contents.length)
      end
    end

    context "when valid SMB_QUERY_PATH_BASIC_INFO parameters" do
      it "returns the number of bytes answered" do
        expect(mod.smb_cmd_trans2_query_path_information(msf_io, valid_query_path_basic_info_params)).to eq(query_path_basic_info_res_length)
      end

      it "send SMB_QUERY_PATH_BASIC_INFO response with the file attributes" do
        mod.smb_cmd_trans2_query_path_information(msf_io, valid_query_path_basic_info_params)
        res = msf_io.read

        trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct
        trans2_res.from_s(res)
        param_count = trans2_res['Payload'].v['ParamCount']
        data_count = trans2_res['Payload'].v['DataCount']

        data  = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count]
        smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_BASIC_INFO_HDR.make_struct
        smb_data.from_s(data)

        expect(smb_data.v['ExtFileAttributes']).to eq(0x80)
      end
    end

    context "when non existent file SMB_QUERY_PATH_BASIC_INFO parameters" do
      it "returns the number of bytes answered" do
        expect(mod.smb_cmd_trans2_query_path_information(msf_io, non_existent_query_path_basic_info_params)).to eq(not_found_res_length)
      end

      it "send TRANS2 response with error" do
        mod.smb_cmd_trans2_query_path_information(msf_io, non_existent_query_path_basic_info_params)
        res = msf_io.read

        trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct
        trans2_res.from_s(res)

        expect(trans2_res['Payload']['SMB'].v['ErrorClass']).to eq(Rex::Proto::SMB::Constants::SMB_STATUS_OBJECT_NAME_NOT_FOUND)
      end
    end
  end

end


