class PassiveDNS::Provider::VirusTotal

Queries VirusTotal's passive DNS database

Attributes

debug[RW]

:debug enables verbose logging to standard output

Public Class Methods

config_section_name() click to toggle source

Sets the configuration section name to “virustotal”

# File lib/passivedns/client/provider/virustotal.rb, line 16
def self.config_section_name
  "virustotal"
end
name() click to toggle source

Sets the modules self-reported name to “VirusTotal”

# File lib/passivedns/client/provider/virustotal.rb, line 12
def self.name
  "VirusTotal"
end
new(options={}) click to toggle source

Options

  • :debug Sets the debug flag for the module

  • “APIKEY” Mandatory. API Key associated with your VirusTotal account

  • “URL” Alternate url for testing. Defaults to www.virustotal.com/vtapi/v2/

Example Instantiation

options = {
  :debug => true,
  "APIKEY" => "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
  "URL" => "https://www.virustotal.com/vtapi/v2/"
}

PassiveDNS::Provider::VirusTotal.new(options)
# File lib/passivedns/client/provider/virustotal.rb, line 42
def initialize(options={})
  @debug = options[:debug] || false
  @timeout = options[:timeout] || 20
  @apikey = options["APIKEY"] || raise("#{self.class.name} requires an APIKEY.  See README.md")
  @url = options["URL"] || "https://www.virustotal.com/vtapi/v2/"
end
option_letter() click to toggle source

Sets the command line database argument to “v”

# File lib/passivedns/client/provider/virustotal.rb, line 20
def self.option_letter
  "v"
end

Public Instance Methods

lookup(label, limit=nil) click to toggle source

Takes a label (either a domain or an IP address) and returns an array of PassiveDNS::PDNSResult instances with the answers to the query

# File lib/passivedns/client/provider/virustotal.rb, line 51
def lookup(label, limit=nil)
  $stderr.puts "DEBUG: #{self.class.name}.lookup(#{label})" if @debug
  Timeout::timeout(@timeout) {
    url = nil
    if label =~ /^[\d\.]+$/
      url = "#{@url}ip-address/report?ip=#{label}&apikey=#{@apikey}"
    else
      url = "#{@url}domain/report?domain=#{label}&apikey=#{@apikey}"
    end
    $stderr.puts "DEBUG: #{self.class.name} url = #{url}" if @debug
    begin
      url = URI.parse url
    rescue URI::InvalidURIError
      $stderr.puts "ERROR: Invalid address: #{url}"
      return
    end
    http = Net::HTTP.new(url.host, url.port)
    http.use_ssl = (url.scheme == 'https')
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    http.verify_depth = 5
    request = Net::HTTP::Get.new(url.path+"?"+url.query)
    request.add_field("User-Agent", "Ruby/#{RUBY_VERSION} passivedns-client rubygem v#{PassiveDNS::Client::VERSION}")
    t1 = Time.now
    response = http.request(request)
    if response.code.to_i == 204
      $stderr.puts "DEBUG: empty response from server" if @debug
      return
    end
    t2 = Time.now
    recs = parse_json(response.body, label, t2-t1)
    if limit
      recs[0,limit]
    else
      recs
    end
  }
rescue Timeout::Error
  $stderr.puts "#{self.class.name} lookup timed out: #{label}"
end

Private Instance Methods

parse_json(page,query,response_time=0) click to toggle source

parses the response of virustotal's JSON reply to generate an array of PDNSResult

# File lib/passivedns/client/provider/virustotal.rb, line 94
def parse_json(page,query,response_time=0)
  res = []
  return res if !page
  data = JSON.parse(page)
  if data['resolutions']
    data['resolutions'].each do |row|
      lastseen = Time.parse(row['last_resolved']+" +0000")
      if row['ip_address']
        res << PDNSResult.new(self.class.name,response_time,query,row['ip_address'],'A',nil,nil,lastseen, 'yellow')
      elsif row['hostname']
        res << PDNSResult.new(self.class.name,response_time,row['hostname'],query,'A',nil,nil,lastseen, 'yellow')
      end
    end
  end
  if data['response_code'] == 0
    $stderr.puts "DEBUG: server returned error: #{data['verbose_msg']}" if @debug
  end
  res
rescue Exception => e
  $stderr.puts "VirusTotal Exception: #{e}"
  raise e
end