module RubySMB
  module Dcerpc
    module Srvsvc

      #https://msdn.microsoft.com/en-us/library/cc247293.aspx

      class NetShareEnumAll < BinData::Record
        attr_reader :opnum

        mandatory_parameter :host

        endian :little

        uint32    :referent_id,  initial_value: 0x00000001
        uint32    :max_count,    initial_value: -> { server_unc.do_num_bytes / 2 }
        uint32    :offset,       initial_value: 0
        uint32    :actual_count, initial_value: -> {max_count}
        stringz16 :server_unc,   pad_front: false, read_length: -> { actual_count * 2 },
                                 initial_value: -> {"\\\\#{host.encode('utf-8')}".encode('utf-16le')}

        string :pad,             length: lambda { pad_length }
        uint32 :level,           initial_value: 1

        uint32 :ctr,             initial_value: 1
        uint32 :ctr_referent_id, initial_value: 0x00000001
        uint32 :ctr_count,       initial_value: 0
        uint32 :pointer_to_array, initial_value: 0

        uint32 :max_buffer,       initial_value: 4294967295

        uint32 :resume_referent_id, initial_value: 0x00000001
        uint32 :resume_handle,    initial_value: 0

        def initialize_instance
          super
          @opnum = NET_SHARE_ENUM_ALL
        end

        def pad_length
          offset = (server_unc.abs_offset + server_unc.to_binary_s.length) % 4
          (4 - offset) % 4
        end

        def self.parse_response(response)

          shares = []

          res = response.dup
          win_error = res.slice!(-4, 4).unpack("V")[0]

          if win_error != 0
            raise RuntimeError, "Invalid DCERPC response: win_error = #{win_error}"
          end

          # Remove unused data
          res.slice!(0, 12) # level, CTR header, Reference ID of CTR
          share_count = res.slice!(0, 4).unpack("V")[0]
          res.slice!(0, 4) # Reference ID of CTR1
          share_max_count = res.slice!(0, 4).unpack("V")[0]

          if share_max_count != share_count
            raise RuntimeError, "Invalid DCERPC response: count != count max (#{share_count}/#{share_max_count})"
          end

          # ReferenceID / Type / ReferenceID of Comment
          types = res.slice!(0, share_count * 12).scan(/.{12}/n).map { |a| a[4, 2].unpack("v")[0] }

          share_count.times do |t|
            length, offset, max_length = res.slice!(0, 12).unpack("VVV")
            if offset != 0
              raise RuntimeError, "Invalid DCERPC response: offset != 0 (#{offset})"
            end

            if length != max_length
              raise RuntimeError, "Invalid DCERPC response: length !=max_length (#{length}/#{max_length})"
            end
            name = res.slice!(0, 2 * length).gsub('\x00', '')
            res.slice!(0, 2) if length % 2 == 1 # pad

            comment_length, comment_offset, comment_max_length = res.slice!(0, 12).unpack("VVV")

            if comment_offset != 0
              raise RuntimeError, "Invalid DCERPC response: comment_offset != 0 (#{comment_offset})"
            end

            if comment_length != comment_max_length
              raise RuntimeError, "Invalid DCERPC response: comment_length != comment_max_length (#{comment_length}/#{comment_max_length})"
            end

            comment = res.slice!(0, 2 * comment_length)

            res.slice!(0, 2) if comment_length % 2 == 1 # pad

            name = name.gsub("\x00", "")
            s_type = ['DISK', 'PRINTER', 'DEVICE', 'IPC', 'SPECIAL', 'TEMPORARY'][types[t]].gsub("\x00", "")
            comment = comment.gsub("\x00", "")

            shares << [name, s_type, comment]
          end

          shares
        end
      end
    end
  end
end
