passivedns-client 2.0.6 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +6 -0
- data/Rakefile +8 -0
- data/lib/passivedns/client.rb +14 -7
- data/lib/passivedns/client/cli.rb +34 -6
- data/lib/passivedns/client/passivedb.rb +6 -1
- data/lib/passivedns/client/provider/bfk.rb +102 -0
- data/lib/passivedns/client/provider/circl.rb +111 -0
- data/lib/passivedns/client/provider/cn360.rb +108 -0
- data/lib/passivedns/client/provider/dnsdb.rb +110 -0
- data/lib/passivedns/client/provider/mnemonic.rb +98 -0
- data/lib/passivedns/client/provider/passivetotal.rb +103 -0
- data/lib/passivedns/client/provider/riskiq.rb +130 -0
- data/lib/passivedns/client/provider/tcpiputils.rb +118 -0
- data/lib/passivedns/client/provider/virustotal.rb +105 -0
- data/lib/passivedns/client/state.rb +30 -2
- data/lib/passivedns/client/version.rb +4 -2
- data/test/test_cli.rb +6 -5
- data/test/test_passivedns-client.rb +33 -8
- metadata +11 -10
- data/lib/passivedns/client/providers/bfk.rb +0 -77
- data/lib/passivedns/client/providers/circl.rb +0 -79
- data/lib/passivedns/client/providers/cn360.rb +0 -80
- data/lib/passivedns/client/providers/dnsdb.rb +0 -85
- data/lib/passivedns/client/providers/mnemonic.rb +0 -72
- data/lib/passivedns/client/providers/passivetotal.rb +0 -77
- data/lib/passivedns/client/providers/tcpiputils.rb +0 -92
- data/lib/passivedns/client/providers/virustotal.rb +0 -78
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'openssl'
|
4
|
+
require 'json'
|
5
|
+
require 'digest/md5'
|
6
|
+
|
7
|
+
module PassiveDNS #:nodoc: don't document this
|
8
|
+
# The Provider module contains all the Passive DNS provider client code
|
9
|
+
module Provider
|
10
|
+
# Queries 360.cn's passive DNS database
|
11
|
+
class CN360 < PassiveDB
|
12
|
+
# Sets the modules self-reported name to "360.cn"
|
13
|
+
def self.name
|
14
|
+
"360.cn"
|
15
|
+
end
|
16
|
+
# Sets the configuration section name to "cn360"
|
17
|
+
def self.config_section_name
|
18
|
+
"cn360"
|
19
|
+
end
|
20
|
+
# Sets the command line database argument to "3"
|
21
|
+
def self.option_letter
|
22
|
+
"3"
|
23
|
+
end
|
24
|
+
|
25
|
+
# :debug enables verbose logging to standard output
|
26
|
+
attr_accessor :debug
|
27
|
+
# === Options
|
28
|
+
# * :debug Sets the debug flag for the module
|
29
|
+
# * "API" REQUIRED: http://some.web.address.for.their.api
|
30
|
+
# * "API_ID" REQUIRED: a username that is given when you register
|
31
|
+
# * "API_KEY" REQUIRED: a long and random password of sorts that is used along with the page request to generate a per page API key
|
32
|
+
#
|
33
|
+
# === Example Instantiation
|
34
|
+
#
|
35
|
+
# options = {
|
36
|
+
# :debug => true,
|
37
|
+
# "API" => "http://some.web.address.for.their.api",
|
38
|
+
# "API_ID" => "360user",
|
39
|
+
# "API_KEY" => "360apikey"
|
40
|
+
# }
|
41
|
+
#
|
42
|
+
# PassiveDNS::Provider::CN360.new(options)
|
43
|
+
#
|
44
|
+
def initialize(options={})
|
45
|
+
@debug = options[:debug] || false
|
46
|
+
["API", "API_ID", "API_KEY"].each do |opt|
|
47
|
+
if not options[opt]
|
48
|
+
raise "Field #{opt} is required. See README.md"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
@cp = options
|
52
|
+
end
|
53
|
+
|
54
|
+
# Takes a label (either a domain or an IP address) and returns
|
55
|
+
# an array of PassiveDNS::PDNSResult instances with the answers to the query
|
56
|
+
def lookup(label, limit=10000)
|
57
|
+
table = "rrset"
|
58
|
+
if label =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ or label =~ /^[0-9a-fA-F]+:[0-9a-fA-F:]+[0-9a-fA-F]$/
|
59
|
+
table = "rdata"
|
60
|
+
end
|
61
|
+
limit ||= 10000
|
62
|
+
path = "/api/#{table}/keyword/#{label}/count/#{limit}/"
|
63
|
+
url = @cp["API"]+path
|
64
|
+
url = URI.parse url
|
65
|
+
http = Net::HTTP.new(url.host, url.port)
|
66
|
+
http.use_ssl = (url.scheme == 'https')
|
67
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # I hate doing this
|
68
|
+
http.verify_depth = 5
|
69
|
+
request = Net::HTTP::Get.new(url.path)
|
70
|
+
request.add_field("User-Agent", "Ruby/#{RUBY_VERSION} passivedns-client rubygem v#{PassiveDNS::Client::VERSION}")
|
71
|
+
request.add_field('Accept', 'application/json')
|
72
|
+
request.add_field("X-BashTokid", @cp["API_ID"])
|
73
|
+
token = Digest::MD5.hexdigest(path+@cp["API_KEY"])
|
74
|
+
$stderr.puts "DEBUG: cn360 url = #{url} token = #{token}" if @debug
|
75
|
+
request.add_field("X-BashToken", token)
|
76
|
+
t1 = Time.now
|
77
|
+
response = http.request(request)
|
78
|
+
t2 = Time.now
|
79
|
+
recs = parse_json(response.body, label, t2-t1)
|
80
|
+
if limit
|
81
|
+
recs[0,limit]
|
82
|
+
else
|
83
|
+
recs
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# parses the response of 360.cn's JSON reply to generate an array of PDNSResult
|
89
|
+
def parse_json(page,query,response_time=0)
|
90
|
+
res = []
|
91
|
+
# need to remove the json_class tag or the parser will crap itself trying to find a class to align it to
|
92
|
+
data = JSON.parse(page)
|
93
|
+
data.each do |row|
|
94
|
+
time_first = (row["time_first"]) ? Time.at(row["time_first"].to_i) : nil
|
95
|
+
time_last = (row["time_last"]) ? Time.at(row["time_last"].to_i) : nil
|
96
|
+
count = row["count"] || 0
|
97
|
+
res << PDNSResult.new(self.class.name, response_time, row["rrname"], row["rdata"], row["rrtype"], time_first, time_last, count)
|
98
|
+
end
|
99
|
+
res
|
100
|
+
rescue Exception => e
|
101
|
+
$stderr.puts "#{self.class.name} Exception: #{e}"
|
102
|
+
raise e
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# DESCRIPTION: this is a module for pdns.rb, primarily used by pdnstool.rb, to query the Farsight Security passive DNS database
|
2
|
+
# details on the API are at https://api.dnsdb.info/
|
3
|
+
# to request an API key, please email dnsdb-api at farsightsecurity dot com.
|
4
|
+
require 'net/http'
|
5
|
+
require 'net/https'
|
6
|
+
|
7
|
+
module PassiveDNS #:nodoc: don't document this
|
8
|
+
# The Provider module contains all the Passive DNS provider client code
|
9
|
+
module Provider
|
10
|
+
# Queries FarSight's passive DNS database
|
11
|
+
class DNSDB < PassiveDB
|
12
|
+
# Sets the modules self-reported name to "DNSDB"
|
13
|
+
def self.name
|
14
|
+
"DNSDB"
|
15
|
+
end
|
16
|
+
# Sets the configuration section name to "dnsdb"
|
17
|
+
def self.config_section_name
|
18
|
+
"dnsdb"
|
19
|
+
end
|
20
|
+
# Sets the command line database argument to "d"
|
21
|
+
def self.option_letter
|
22
|
+
"d"
|
23
|
+
end
|
24
|
+
|
25
|
+
# :debug enables verbose logging to standard output
|
26
|
+
attr_accessor :debug
|
27
|
+
# === Options
|
28
|
+
# * :debug Sets the debug flag for the module
|
29
|
+
# * "APIKEY" REQUIRED: The API key associated with DNSDB
|
30
|
+
# * "URL" Alternate url for testing. Defaults to "https://api.dnsdb.info/lookup"
|
31
|
+
#
|
32
|
+
# === Example Instantiation
|
33
|
+
#
|
34
|
+
# options = {
|
35
|
+
# :debug => true,
|
36
|
+
# "APIKEY" => "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
37
|
+
# "URL" => "https://api.dnsdb.info/lookup"
|
38
|
+
# }
|
39
|
+
#
|
40
|
+
# PassiveDNS::Provider::DNSDB.new(options)
|
41
|
+
#
|
42
|
+
def initialize(options={})
|
43
|
+
@debug = options[:debug] || false
|
44
|
+
@key = options["APIKEY"] || raise("APIKEY option required for #{self.class}")
|
45
|
+
@base = options["URL"] || "https://api.dnsdb.info/lookup"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Takes a label (either a domain or an IP address) and returns
|
49
|
+
# an array of PassiveDNS::PDNSResult instances with the answers to the query
|
50
|
+
def lookup(label, limit=nil)
|
51
|
+
$stderr.puts "DEBUG: #{self.class.name}.lookup(#{label})" if @debug
|
52
|
+
Timeout::timeout(240) {
|
53
|
+
url = nil
|
54
|
+
if label =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$/
|
55
|
+
label = label.gsub(/\//,',')
|
56
|
+
url = "#{@base}/rdata/ip/#{label}"
|
57
|
+
else
|
58
|
+
url = "#{@base}/rrset/name/#{label}"
|
59
|
+
end
|
60
|
+
url = URI.parse url
|
61
|
+
http = Net::HTTP.new(url.host, url.port)
|
62
|
+
http.use_ssl = (url.scheme == 'https')
|
63
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
64
|
+
http.verify_depth = 5
|
65
|
+
path = url.path
|
66
|
+
if limit
|
67
|
+
path << "?limit=#{limit}"
|
68
|
+
end
|
69
|
+
request = Net::HTTP::Get.new(path)
|
70
|
+
request.add_field("User-Agent", "Ruby/#{RUBY_VERSION} passivedns-client rubygem v#{PassiveDNS::Client::VERSION}")
|
71
|
+
request.add_field("X-API-Key", @key)
|
72
|
+
request.add_field("Accept", "application/json")
|
73
|
+
t1 = Time.now
|
74
|
+
response = http.request(request)
|
75
|
+
t2 = Time.now
|
76
|
+
$stderr.puts response.body if @debug
|
77
|
+
parse_json(response.body,t2-t1)
|
78
|
+
}
|
79
|
+
rescue Timeout::Error => e
|
80
|
+
$stderr.puts "#{self.class.name} lookup timed out: #{label}"
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# parses the response of DNSDB's JSON reply to generate an array of PDNSResult
|
86
|
+
def parse_json(page,response_time)
|
87
|
+
res = []
|
88
|
+
raise "Error: unable to parse request" if page =~ /Error: unable to parse request/
|
89
|
+
# need to remove the json_class tag or the parser will crap itself trying to find a class to align it to
|
90
|
+
rows = page.split(/\n/)
|
91
|
+
rows.each do |row|
|
92
|
+
record = JSON.parse(row)
|
93
|
+
record['rdata'] = [record['rdata']] if record['rdata'].class == String
|
94
|
+
record['rdata'].each do |rdata|
|
95
|
+
if record['time_first']
|
96
|
+
res << PDNSResult.new(self.class.name,response_time,record['rrname'],rdata,record['rrtype'],0,Time.at(record['time_first'].to_i).utc.strftime("%Y-%m-%dT%H:%M:%SZ"),Time.at(record['time_last'].to_i).utc.strftime("%Y-%m-%dT%H:%M:%SZ"),record['count'])
|
97
|
+
else
|
98
|
+
res << PDNSResult.new(self.class.name,response_time,record['rrname'],rdata,record['rrtype'])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
res
|
103
|
+
rescue Exception => e
|
104
|
+
$stderr.puts "#{self.class.name} Exception: #{e}"
|
105
|
+
$stderr.puts page
|
106
|
+
raise e
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# DESCRIPTION: Module to query Mnemonic's passive DNS repository
|
2
|
+
# CONTRIBUTOR: Drew Hunt (pinowudi@yahoo.com)
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'openssl'
|
6
|
+
|
7
|
+
module PassiveDNS #:nodoc: don't document this
|
8
|
+
# The Provider module contains all the Passive DNS provider client code
|
9
|
+
module Provider
|
10
|
+
# Queries Mnemonic's passive DNS database
|
11
|
+
class Mnemonic < PassiveDB
|
12
|
+
# Sets the modules self-reported name to "Mnemonic"
|
13
|
+
def self.name
|
14
|
+
"Mnemonic"
|
15
|
+
end
|
16
|
+
# Sets the configuration section name to "mnemonic"
|
17
|
+
def self.config_section_name
|
18
|
+
"mnemonic"
|
19
|
+
end
|
20
|
+
# Sets the command line database argument to "m"
|
21
|
+
def self.option_letter
|
22
|
+
"m"
|
23
|
+
end
|
24
|
+
|
25
|
+
# :debug enables verbose logging to standard output
|
26
|
+
attr_accessor :debug
|
27
|
+
# === Options
|
28
|
+
# * :debug Sets the debug flag for the module
|
29
|
+
# * "APIKEY" REQUIRED: The API key associated with Mnemonic
|
30
|
+
# * "URL" Alternate url for testing. Defaults to "https://passivedns.mnemonic.no/api1/?apikey="
|
31
|
+
#
|
32
|
+
# === Example Instantiation
|
33
|
+
#
|
34
|
+
# options = {
|
35
|
+
# :debug => true,
|
36
|
+
# "APIKEY" => "01234567890abcdef01234567890abcdef012345",
|
37
|
+
# "URL" => "https://passivedns.mnemonic.no/api1/?apikey="
|
38
|
+
# }
|
39
|
+
#
|
40
|
+
# PassiveDNS::Provider::Mnemonic.new(options)
|
41
|
+
#
|
42
|
+
def initialize(options={})
|
43
|
+
@debug = options[:debug] || false
|
44
|
+
@apikey = options["APIKEY"] || raise("#{self.class.name} requires an APIKEY")
|
45
|
+
@url = options["URL"] || "https://passivedns.mnemonic.no/api1/?apikey="
|
46
|
+
end
|
47
|
+
|
48
|
+
# Takes a label (either a domain or an IP address) and returns
|
49
|
+
# an array of PassiveDNS::PDNSResult instances with the answers to the query
|
50
|
+
def lookup(label, limit=nil)
|
51
|
+
$stderr.puts "DEBUG: #{self.class.name}.lookup(#{label})" if @debug
|
52
|
+
Timeout::timeout(240) {
|
53
|
+
url = "#{@url}#{@apikey}&query=#{label}&method=exact"
|
54
|
+
$stderr.puts "DEBUG: #{self.class.name} url = #{url}" if @debug
|
55
|
+
url = URI.parse url
|
56
|
+
http = Net::HTTP.new(url.host, url.port)
|
57
|
+
http.use_ssl = (url.scheme == 'https')
|
58
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
59
|
+
http.verify_depth = 5
|
60
|
+
request = Net::HTTP::Get.new(url.path+"?"+url.query)
|
61
|
+
request.add_field("User-Agent", "Ruby/#{RUBY_VERSION} passivedns-client rubygem v#{PassiveDNS::Client::VERSION}")
|
62
|
+
t1 = Time.now
|
63
|
+
response = http.request(request)
|
64
|
+
t2 = Time.now
|
65
|
+
recs = parse_json(response.body, label, t2-t1)
|
66
|
+
if limit
|
67
|
+
recs[0,limit]
|
68
|
+
else
|
69
|
+
recs
|
70
|
+
end
|
71
|
+
}
|
72
|
+
rescue Timeout::Error => e
|
73
|
+
$stderr.puts "#{self.class.name} lookup timed out: #{label}"
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# parses the response of mnemonic's JSON reply to generate an array of PDNSResult
|
79
|
+
def parse_json(page,query,response_time=0)
|
80
|
+
res = []
|
81
|
+
# need to remove the json_class tag or the parser will crap itself trying to find a class to align it to
|
82
|
+
data = JSON.parse(page)
|
83
|
+
if data['result']
|
84
|
+
data['result'].each do |row|
|
85
|
+
if row['query']
|
86
|
+
res << PDNSResult.new(self.class.name,response_time,row['query'],row['answer'],row['type'].upcase,row['ttl'],row['first'],row['last'])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
res
|
91
|
+
rescue Exception => e
|
92
|
+
$stderr.puts "#{self.class.name} Exception: #{e}"
|
93
|
+
raise e
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# DESCRIPTION: Module to query PassiveTotal's passive DNS repository
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'openssl'
|
6
|
+
|
7
|
+
module PassiveDNS #:nodoc: don't document this
|
8
|
+
# The Provider module contains all the Passive DNS provider client code
|
9
|
+
module Provider
|
10
|
+
# Queries PassiveTotal's passive DNS database
|
11
|
+
class PassiveTotal < PassiveDB
|
12
|
+
# Sets the modules self-reported name to "PassiveTotal"
|
13
|
+
def self.name
|
14
|
+
"PassiveTotal"
|
15
|
+
end
|
16
|
+
# Sets the configuration section name to "passivetotal"
|
17
|
+
def self.config_section_name
|
18
|
+
"passivetotal"
|
19
|
+
end
|
20
|
+
# Sets the command line database argument to "p"
|
21
|
+
def self.option_letter
|
22
|
+
"p"
|
23
|
+
end
|
24
|
+
|
25
|
+
# :debug enables verbose logging to standard output
|
26
|
+
attr_accessor :debug
|
27
|
+
# === Options
|
28
|
+
# * :debug Sets the debug flag for the module
|
29
|
+
# * "APIKEY" REQUIRED: The API key associated with PassiveTotal
|
30
|
+
# * "URL" Alternate url for testing. Defaults to "https://www.passivetotal.org/api/passive"
|
31
|
+
#
|
32
|
+
# === Example Instantiation
|
33
|
+
#
|
34
|
+
# options = {
|
35
|
+
# :debug => true,
|
36
|
+
# "APIKEY" => "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
37
|
+
# "URL" => "https://www.passivetotal.org/api/passive"
|
38
|
+
# }
|
39
|
+
#
|
40
|
+
# PassiveDNS::Provider::PassiveTotal.new(options)
|
41
|
+
#
|
42
|
+
def initialize(options={})
|
43
|
+
@debug = options[:debug] || false
|
44
|
+
@apikey = options["APIKEY"] || raise("#{self.class.name} requires an APIKEY")
|
45
|
+
@url = options["URL"] || "https://www.passivetotal.org/api/passive"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Takes a label (either a domain or an IP address) and returns
|
49
|
+
# an array of PassiveDNS::PDNSResult instances with the answers to the query
|
50
|
+
def lookup(label, limit=nil)
|
51
|
+
$stderr.puts "DEBUG: #{self.class.name}.lookup(#{label})" if @debug
|
52
|
+
Timeout::timeout(240) {
|
53
|
+
url = @url
|
54
|
+
$stderr.puts "DEBUG: #{self.class.name} url = #{url}" if @debug
|
55
|
+
url = URI.parse url
|
56
|
+
http = Net::HTTP.new(url.host, url.port)
|
57
|
+
http.use_ssl = (url.scheme == 'https')
|
58
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
59
|
+
http.verify_depth = 5
|
60
|
+
request = Net::HTTP::Post.new(url.request_uri)
|
61
|
+
request.add_field("User-Agent", "Ruby/#{RUBY_VERSION} passivedns-client rubygem v#{PassiveDNS::Client::VERSION}")
|
62
|
+
request.set_form_data({"apikey" => @apikey, "value" => label})
|
63
|
+
t1 = Time.now
|
64
|
+
response = http.request(request)
|
65
|
+
t2 = Time.now
|
66
|
+
recs = parse_json(response.body, label, t2-t1)
|
67
|
+
if limit
|
68
|
+
recs[0,limit]
|
69
|
+
else
|
70
|
+
recs
|
71
|
+
end
|
72
|
+
}
|
73
|
+
rescue Timeout::Error => e
|
74
|
+
$stderr.puts "#{self.class.name} lookup timed out: #{label}"
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# parses the response of passivetotals's JSON reply to generate an array of PDNSResult
|
80
|
+
def parse_json(page,query,response_time=0)
|
81
|
+
res = []
|
82
|
+
# need to remove the json_class tag or the parser will crap itself trying to find a class to align it to
|
83
|
+
data = JSON.parse(page)
|
84
|
+
if data['results']
|
85
|
+
query = data['results']['value']
|
86
|
+
data['results']['resolutions'].each do |row|
|
87
|
+
first_seen = row['firstSeen']
|
88
|
+
last_seen = row['lastSeen']
|
89
|
+
value = row['value']
|
90
|
+
source = row['source'].join(",")
|
91
|
+
res << PDNSResult.new(self.class.name+"/"+source,response_time,
|
92
|
+
query, value, "A", 0, first_seen, last_seen)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
res
|
96
|
+
rescue Exception => e
|
97
|
+
$stderr.puts "#{self.class.name} Exception: #{e}"
|
98
|
+
raise e
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# DESCRIPTION: Module to query PassiveTotal's passive DNS repository
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'openssl'
|
6
|
+
#require 'pp'
|
7
|
+
|
8
|
+
module PassiveDNS #:nodoc: don't document this
|
9
|
+
# The Provider module contains all the Passive DNS provider client code
|
10
|
+
module Provider
|
11
|
+
# Queries RiskIQ's passive DNS database
|
12
|
+
class RiskIQ < PassiveDB
|
13
|
+
# Sets the modules self-reported name to "RiskIQ"
|
14
|
+
def self.name
|
15
|
+
"RiskIQ"
|
16
|
+
end
|
17
|
+
# Sets the configuration section name to "riskiq"
|
18
|
+
def self.config_section_name
|
19
|
+
"riskiq"
|
20
|
+
end
|
21
|
+
# Sets the command line database argument to "r"
|
22
|
+
def self.option_letter
|
23
|
+
"r"
|
24
|
+
end
|
25
|
+
|
26
|
+
# :debug enables verbose logging to standard output
|
27
|
+
attr_accessor :debug
|
28
|
+
# === Options
|
29
|
+
# * :debug Sets the debug flag for the module
|
30
|
+
# * "API_TOKEN" REQUIRED: User name associated with your RiskIQ account
|
31
|
+
# * "API_PRIVATE_KEY" REQUIRED: Password associated with your RiskIQ account
|
32
|
+
# * "API_SERVER" Alternate server for testing. Defaults to "ws.riskiq.net"
|
33
|
+
# * "API_VERSION" Alternate version of the API to test. Defaults to "V1"
|
34
|
+
#
|
35
|
+
# === Example Instantiation
|
36
|
+
#
|
37
|
+
# options = {
|
38
|
+
# :debug => true,
|
39
|
+
# "API_TOKEN" => "riskiq_token",
|
40
|
+
# "API_PRIVATE_KEY" => "riskiq_private_key",
|
41
|
+
# "API_SERVER" => "ws.riskiq.net",
|
42
|
+
# "API_VERSION" => "v1"
|
43
|
+
# }
|
44
|
+
#
|
45
|
+
# PassiveDNS::Provider::RiskIQ.new(options)
|
46
|
+
#
|
47
|
+
def initialize(options={})
|
48
|
+
@debug = options[:debug] || false
|
49
|
+
@token = options["API_TOKEN"] || raise("#{self.class.name} requires an API_TOKEN")
|
50
|
+
@privkey = options["API_PRIVATE_KEY"] || raise("#{self.class.name} requires an API_PRIVATE_KEY")
|
51
|
+
@server = options["API_SERVER"] || "ws.riskiq.net"
|
52
|
+
@version = options["API_VERSION"] || "v1"
|
53
|
+
@url = "https://#{@server}/#{@version}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Takes a label (either a domain or an IP address) and returns
|
57
|
+
# an array of PassiveDNS::PDNSResult instances with the answers to the query
|
58
|
+
def lookup(label, limit=nil)
|
59
|
+
$stderr.puts "DEBUG: #{self.class.name}.lookup(#{label})" if @debug
|
60
|
+
Timeout::timeout(240) {
|
61
|
+
url = nil
|
62
|
+
params = {"rrType" => "", "maxResults" => limit || 1000}
|
63
|
+
|
64
|
+
if label =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
|
65
|
+
url = @url+"/dns/data"
|
66
|
+
params["ip"] = label
|
67
|
+
else
|
68
|
+
url = @url+"/dns/name"
|
69
|
+
params["name"] = label
|
70
|
+
end
|
71
|
+
url << "?"
|
72
|
+
params.each do |k,v|
|
73
|
+
url << "#{k}=#{v}&"
|
74
|
+
end
|
75
|
+
url.gsub!(/\&$/,"")
|
76
|
+
|
77
|
+
$stderr.puts "DEBUG: #{self.class.name} url = #{url}" if @debug
|
78
|
+
url = URI.parse url
|
79
|
+
http = Net::HTTP.new(url.host, url.port)
|
80
|
+
http.use_ssl = (url.scheme == 'https')
|
81
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
82
|
+
http.verify_depth = 5
|
83
|
+
request = Net::HTTP::Get.new(url.request_uri)
|
84
|
+
request.add_field("User-Agent", "Ruby/#{RUBY_VERSION} passivedns-client rubygem v#{PassiveDNS::Client::VERSION}")
|
85
|
+
request.add_field('Accept', 'Application/JSON')
|
86
|
+
request.add_field('Content-Type', 'Application/JSON')
|
87
|
+
request.basic_auth(@token, @privkey)
|
88
|
+
t1 = Time.now
|
89
|
+
response = http.request(request)
|
90
|
+
t2 = Time.now
|
91
|
+
recs = parse_json(response.body, label, t2-t1)
|
92
|
+
if limit
|
93
|
+
recs[0,limit]
|
94
|
+
else
|
95
|
+
recs
|
96
|
+
end
|
97
|
+
}
|
98
|
+
rescue Timeout::Error => e
|
99
|
+
$stderr.puts "#{self.class.name} lookup timed out: #{label}"
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
# parses the response of riskiq's JSON reply to generate an array of PDNSResult
|
105
|
+
def parse_json(page,query,response_time=0)
|
106
|
+
#pp page
|
107
|
+
res = []
|
108
|
+
# need to remove the json_class tag or the parser will crap itself trying to find a class to align it to
|
109
|
+
data = JSON.parse(page)
|
110
|
+
if data['records']
|
111
|
+
data['records'].each do |record|
|
112
|
+
name = record['name']
|
113
|
+
type = record['rrtype']
|
114
|
+
last_seen = record['lastSeen']
|
115
|
+
first_seen = record['firstSeen']
|
116
|
+
record['data'].each do |datum|
|
117
|
+
res << PDNSResult.new(self.class.name,response_time,
|
118
|
+
name, datum, type, 0, first_seen, last_seen)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
res
|
123
|
+
rescue Exception => e
|
124
|
+
$stderr.puts "#{self.class.name} Exception: #{e}"
|
125
|
+
raise e
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|