# -*- coding: binary -*-


module Rex
module Proto
module DRDA
class Utils

  # Creates a packet with EXCSAT_DDM and an ACCSEC_DDM. This will elicit
  # a response from the target server.
  def self.client_probe(dbname=nil)
    pkt = [
      Rex::Proto::DRDA::Packet::EXCSAT_DDM.new,
      Rex::Proto::DRDA::Packet::ACCSEC_DDM.new(:dbname => dbname)
    ]
    pkt.map {|x| x.to_s}.join
  end

  # Creates a packet with EXCSAT_DDM and an SECCHK_DDM.
  # In order to ever succeed, you do need a successful probe first.
  def self.client_auth(args={})
    dbname = args[:dbname]
    dbuser = args[:dbuser]
    dbpass = args[:dbpass]
    pkt = [
      Rex::Proto::DRDA::Packet::ACCSEC_DDM.new(:format => 0x41),
      Rex::Proto::DRDA::Packet::SECCHK_DDM.new(:dbname => dbname, :dbuser => dbuser, :dbpass => dbpass)
    ]
    pkt.map {|x| x.to_s}.join
  end

  def self.server_packet_info(obj)
    info_hash = {}
    return info_hash unless obj.kind_of? Rex::Proto::DRDA::Packet::SERVER_PACKET
    obj.each do |ddm|
      case ddm.codepoint
      when Constants::EXCSATRD
        info_hash.merge!(_info_excsatrd(ddm))
      when Constants::ACCSECRD
        info_hash.merge!(_info_accsecrd(ddm))
      when Constants::RDBNFNRM
        info_hash.merge!(_info_rdbnfnrm(ddm))
      when Constants::SECCHKRM
        info_hash.merge!(_info_secchkrm(ddm))
      else
        next
      end
    end
    return info_hash
  end

  def self._info_excsatrd(ddm)
    info_hash = {:excsatrd => true}
    ddm.payload.each do |param|
      case param.codepoint
      when Constants::SRVNAM
        info_hash[:instance_name] = Rex::Text.from_ebcdic(param.payload)
      when Constants::SRVCLSNM
        info_hash[:platform] = Rex::Text.from_ebcdic(param.payload)
      when Constants::SRVRLSLV
        info_hash[:version] = Rex::Text.from_ebcdic(param.payload)
      else
        next
      end
    end
    return info_hash
  end

  def self._info_accsecrd(ddm)
    info_hash = {:accsecrd => true}
    ddm.payload.each do |param|
      case param.codepoint
      when Constants::SECMEC
        info_hash[:plaintext_auth] = true if param.payload =~ /\x00\x03/n
      when Constants::SECCHKCD
        info_hash[:security_check_code] = param.payload.unpack("C").first
        # A little spurious? This is always nonzero when there's no SECCHKRM DDM.
        info_hash[:db_login_success] = false unless info_hash[:security_check_code].zero?
      else
        next
      end
    end
    return info_hash
  end

  def self._info_rdbnfnrm(ddm)
    info_hash = {:rdbnfnrm => true}
    info_hash[:database_found] = false
    ddm.payload.each do |param|
      case param.codepoint
      when Constants::RDBNAM
        info_hash[:db_name] = Rex::Text.from_ebcdic(param.payload).unpack("A*").first
      when Constants::SRVDGN
        info_hash[:error_message] = Rex::Text.from_ebcdic(param.payload)
      else
        next
      end
    end
    return info_hash
  end

  def self._info_secchkrm(ddm)
    info_hash = {:secchkrm => true}
    ddm.payload.each do |param|
      case param.codepoint
      when Constants::SRVCOD
        info_hash[:severity_code] = param.payload.unpack("n").first
      when Constants::SECCHKCD
        info_hash[:security_check_code] = param.payload.unpack("C").first
      else
        next
      end
    end
    if info_hash[:severity_code].to_i.zero? and info_hash[:security_check_code].to_i.zero?
      info_hash[:db_login_success] = true
    end
    return info_hash
  end

end

end
end
end
