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.
@@ -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