passivedns-client 2.0.6 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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