class Msf::Analyze

  def initialize(framework)
    @framework = framework
  end

  def host(eval_host)
    suggested_modules = {}

    mrefs, _mports, _mservs = Msf::Modules::Metadata::Cache.instance.all_remote_exploit_maps

    unless eval_host.vulns
      return {}
    end

    vuln_refs = []
    eval_host.vulns.each do |vuln|
      next if vuln.service.nil?
      vuln_refs.push(*vuln.refs)
    end

    # finds all modules that have references matching those found on host vulns with service data
    found_modules = mrefs.values_at(*(vuln_refs.map { |x| x.name.upcase } & mrefs.keys)).map { |x| x.values }.flatten.uniq
    found_modules.each do |fnd_mod|
      # next if exploit_filter_by_service(fnd_mod, vuln.service)
      next unless exploit_matches_host_os(fnd_mod, eval_host)
    end

    suggested_modules[:modules] = found_modules

    suggested_modules
  end


  private

  # Tests for various service conditions by comparing the module's full_name (which
  # is basically a pathname) to the intended target service record. The service.info
  # column is tested against a regex in most/all cases and "false" is returned in the
  # event of a match between an incompatible module and service fingerprint.
  def exploit_filter_by_service(mod, serv)

    # Filter out Unix vs Windows exploits for SMB services
    return true if (mod.full_name =~ /\/samba/ and serv.info.to_s =~ /windows/i)
    return true if (mod.full_name =~ /\/windows/ and serv.info.to_s =~ /samba|unix|vxworks|qnx|netware/i)
    return true if (mod.full_name =~ /\/netware/ and serv.info.to_s =~ /samba|unix|vxworks|qnx/i)

    # Filter out IIS exploits for non-Microsoft services
    return true if (mod.full_name =~ /\/iis\/|\/isapi\// and (serv.info.to_s !~ /microsoft|asp/i))

    # Filter out Apache exploits for non-Apache services
    return true if (mod.full_name =~ /\/apache/ and serv.info.to_s !~ /apache|ibm/i)

    false
  end

  # Determines if an exploit (mod, an instantiated module) is suitable for the host (host)
  # defined operating system. Returns true if the host.os isn't defined, if the module's target
  # OS isn't defined, if the module's OS is "unix" and the host's OS is not "windows," or
  # if the module's target is "php." Or, of course, in the event the host.os actually matches.
  # This is a fail-open gate; if there's a doubt, assume the module will work on this target.
  def exploit_matches_host_os(mod, host)
    hos = host.os_name
    return true if hos.nil? || hos.empty?

    set = mod.platform.split(',').map{ |x| x.downcase }
    return true if set.empty?

    # Special cases
    return true if set.include?("unix") and hos !~ /windows/i

    if set.include?("unix")
      # Skip archaic old HPUX bugs if we have a solid match against another OS
      return false if set.include?("hpux") and mod.refname.index("hpux") and hos =~ /linux|irix|solaris|aix|bsd/i
      # Skip AIX bugs if we have a solid match against another OS
      return false if set.include?("aix") and mod.refname.index("aix") and hos =~ /linux|irix|solaris|hpux|bsd/i
      # Skip IRIX bugs if we have a solid match against another OS
      return false if set.include?("irix") and mod.refname.index("irix") and hos =~ /linux|solaris|hpux|aix|bsd/i
    end

    return true if set.include?("php")

    set.each do |mos|
      return true if hos.downcase.index(mos)
    end

    false
  end
end
