domain-probe 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in domain-probe.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ = Domain::Probe
2
+
3
+ Domain::Probe is a simple library to probe the DNS records under specific domain, as many as possible
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rake'
4
+ require File.expand_path('../lib/domain-probe/version', __FILE__)
5
+
6
+ task :default
7
+
8
+ desc 'Builds the gem'
9
+ task :build do
10
+ sh "gem build domain-probe.gemspec"
11
+ end
12
+
13
+ desc 'Builds and installs the gem'
14
+ task :install => :build do
15
+ sh "gem install domain-probe-#{Probe::VERSION}"
16
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "domain-probe/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "domain-probe"
7
+ s.version = Probe::VERSION
8
+ s.authors = ["ijammy"]
9
+ s.email = ["mzhang@yottaa.com"]
10
+ s.homepage = "http://www.yottaa.com"
11
+ s.summary = "A simple library to probe the dns records under specific domain"
12
+ s.description = "A simple library to probe the dns records under specific domain, as many as possilbe"
13
+
14
+ s.rubyforge_project = "domain-probe"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here
22
+ s.add_development_dependency "rspec"
23
+ s.add_development_dependency "rake"
24
+ s.add_runtime_dependency "net-dns"
25
+ s.add_runtime_dependency "nokogiri"
26
+ end
@@ -0,0 +1,97 @@
1
+ require 'set'
2
+ require 'nokogiri'
3
+ require 'open-uri'
4
+ require 'domain-probe/util'
5
+ require 'domain-probe/policy'
6
+
7
+ module Probe
8
+
9
+ #consts
10
+ DOUBLE_SLASH_LENGTH = 2
11
+
12
+ TAG_NAME_A = "a".freeze
13
+ TAG_NAME_SCRIPT = "script".freeze
14
+ TAG_NAME_IMG = "img".freeze
15
+ TAG_NAME_FORM = "form".freeze
16
+
17
+ ATTR_NAME_SRC = "src".freeze
18
+ ATTR_NAME_ACTION = "action".freeze
19
+ ATTR_NAME_HREF = "href".freeze
20
+
21
+ #
22
+ class CrawlingPolicy < Policy
23
+
24
+ #
25
+ # return a set of possilbe host under specific domain by crawling the URL within HTML tags
26
+ #
27
+ def possible_host_under_domain(domain)
28
+ #sanity check
29
+ return [].to_set if !domain
30
+
31
+ set = Set.new()
32
+ begin
33
+
34
+ #choose the default website to try
35
+ page = Nokogiri::HTML(open(Util.polish_url(domain)))
36
+
37
+ #a lambda to check if the attribute is of an absolute url or not
38
+ absolute_url_checker = lambda { |element,attribute|
39
+ attrib = element[attribute]
40
+ attrib && (attrib.downcase =~ /^http[s]?:\/\// || attrib.downcase.start_with?("//") )
41
+ }
42
+
43
+ #try to find some subdomains by a tags
44
+ links = page.css(TAG_NAME_A).select{ |element| absolute_url_checker.call(element,ATTR_NAME_HREF) }
45
+ set |= iterate_elements_with_attr(links,ATTR_NAME_HREF,domain)
46
+
47
+ #try to find some subdomains by script tags
48
+ scripts = page.css(TAG_NAME_SCRIPT).select{ |element| absolute_url_checker.call(element,ATTR_NAME_SRC) }
49
+ set |= iterate_elements_with_attr(scripts,ATTR_NAME_SRC,domain)
50
+
51
+ #try to find some subdomains by img tags
52
+ images = page.css(TAG_NAME_IMG).select{ |element| absolute_url_checker.call(element,ATTR_NAME_SRC) }
53
+ set |= iterate_elements_with_attr(images,ATTR_NAME_SRC,domain)
54
+
55
+ #try to find some subdomains by img tags
56
+ forms = page.css(TAG_NAME_FORM).select{ |element| absolute_url_checker.call(element,ATTR_NAME_ACTION) }
57
+ set |= iterate_elements_with_attr(forms,ATTR_NAME_ACTION,domain)
58
+
59
+ rescue => ex
60
+ puts "#{ex.class}: #{ex.message}"
61
+ end
62
+ set
63
+ end
64
+
65
+ private
66
+
67
+
68
+
69
+
70
+ #
71
+ #iterate the elements to find a set of host names
72
+ #
73
+ def iterate_elements_with_attr(links,attribute,domain)
74
+ raise ArgumentError, "Both attribute name and domain name are expected" if !attribute || !domain
75
+ return [].to_set if !links
76
+ set = Set.new()
77
+
78
+ links.each { |link|
79
+ next unless link[attribute]
80
+ head = link[attribute].index("//")
81
+ next unless head
82
+ tail = link[attribute].index(domain)
83
+ next unless tail
84
+
85
+ #slice the host in the url
86
+ host = link[attribute].slice(head+DOUBLE_SLASH_LENGTH,tail-head-DOUBLE_SLASH_LENGTH)
87
+
88
+ #remove the trailing dot if any
89
+ host.slice!(host.length-1) if host.end_with? "."
90
+ set.add host
91
+ }
92
+
93
+ set
94
+ end
95
+ end
96
+
97
+ end
@@ -0,0 +1,120 @@
1
+ require 'domain-probe/policy'
2
+ module Probe
3
+
4
+ #
5
+ # Popular host names, in alphabetic order
6
+ #
7
+ COMMON_NAMES = %w{
8
+ ad
9
+ ads
10
+ api
11
+ app
12
+ application
13
+ apps
14
+ ask
15
+ asset
16
+ assets
17
+ au
18
+ auto
19
+
20
+ baby
21
+ bbs
22
+ biz
23
+ blog
24
+ build
25
+ cal
26
+ calendar
27
+ cards
28
+ client
29
+ club
30
+ college
31
+ date
32
+ dev
33
+ develop
34
+ developer
35
+ dictionary
36
+ dir
37
+ direct
38
+ discovery
39
+ docs
40
+ download
41
+ downloads
42
+ email
43
+ english
44
+ events
45
+ feed
46
+ finance
47
+ food
48
+ fresh
49
+ ftp
50
+ help
51
+ home
52
+ hr
53
+ image
54
+ images
55
+ imap
56
+
57
+ info
58
+ ip
59
+ iphone
60
+ local
61
+ log
62
+ login
63
+ love
64
+ m
65
+ mail
66
+ maps
67
+ media
68
+ message
69
+ messages
70
+ mon
71
+ money
72
+ monitor
73
+ mp3
74
+ news
75
+ operation
76
+ operations
77
+ page
78
+ pages
79
+ php
80
+ play
81
+ plus
82
+ pops
83
+ production
84
+ qa
85
+ read
86
+ repo
87
+ resource
88
+ resources
89
+ sg
90
+ shopping
91
+ sms
92
+ smtp
93
+ sports
94
+ staging
95
+ static
96
+ stats
97
+ status
98
+ studios
99
+ support
100
+ trading
101
+ video
102
+ view
103
+ vip
104
+ web
105
+ webmail
106
+ wiki
107
+ wireless
108
+ www
109
+ www1
110
+ }
111
+
112
+ class GuessingPolicy < Policy
113
+
114
+ #
115
+ def possible_host_under_domain(domain)
116
+ COMMON_NAMES.to_set
117
+ end
118
+ end
119
+
120
+ end
@@ -0,0 +1,17 @@
1
+ module Probe
2
+
3
+ #const declaration
4
+ WWW_PREFIX = "www."
5
+
6
+ #
7
+ # The policy interface specification, each child class must implement the possible_host_under_domain method
8
+ #
9
+ class Policy
10
+
11
+ def possible_host_under_domain(domain)
12
+ [].to_set
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,34 @@
1
+ require 'net/dns/resolver'
2
+
3
+ #
4
+ # Hack: the RR in net-dns didn't implement the ==,eql?,hash.methods
5
+ #
6
+ module Net
7
+ module DNS
8
+ class RR
9
+ class TXT
10
+ def value
11
+ @txt.to_s
12
+ end
13
+
14
+ def get_inspect
15
+ value
16
+ end
17
+ end
18
+
19
+ def ==(o)
20
+ if o.is_a? RR
21
+ self.data == o.data
22
+ else
23
+ false
24
+ end
25
+ end
26
+
27
+ alias eql? ==
28
+
29
+ def hash
30
+ self.data.hash
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,157 @@
1
+ require 'domain-probe/resolver_patch'
2
+ #
3
+ #
4
+ #
5
+ module Probe
6
+
7
+ #
8
+ # Utility functions to assist domain-probing
9
+ #
10
+ class Util
11
+
12
+ #
13
+ # Detect the zone name the specific domain belongs to
14
+ #
15
+ def self.detect_zone domain
16
+
17
+ return nil if !domain
18
+ domain = append_trailing_dot_if_necessary domain
19
+ names = domain.split "."
20
+
21
+ zone = nil
22
+ begin
23
+ names.size.times { |index|
24
+ resovler = Net::DNS::Resolver.new
25
+ packet = resovler.query(domain, Net::DNS::NS)
26
+ packet.answer.each { |record|
27
+ next unless record.name == domain && record.type == "NS"
28
+ zone = domain
29
+ break
30
+ } if packet && packet.answer
31
+
32
+ return remove_trailing_dot_if_any(zone) unless !zone
33
+ domain = domain.slice(names[index].length + 1,domain.length - names[index].length - 1)
34
+ }
35
+ rescue => ex
36
+ puts "#{ex.class}: #{ex.message}"
37
+ end
38
+
39
+ zone
40
+ end
41
+
42
+ #
43
+ # Detect the nameservers against specific zone
44
+ #
45
+ def self.detect_nameservers zone
46
+ return [] if !zone
47
+ zone = append_trailing_dot_if_necessary zone
48
+
49
+ nameserver_ips = []
50
+ nameserver_names = []
51
+ begin
52
+ resovler = Net::DNS::Resolver.new
53
+ packet = resovler.query(zone, Net::DNS::NS)
54
+ packet.answer.each { |record|
55
+ next unless record.name == append_trailing_dot_if_necessary(zone) && record.type == "NS"
56
+ nameserver_names << record.nsdname
57
+ packet.additional.select{ |x| x.name == record.nsdname && x.type == "A" }.each{ |rr|
58
+ nameserver_ips << rr.address.to_s
59
+ }
60
+ } if packet && packet.answer
61
+
62
+ if nameserver_ips.empty?
63
+ nameserver_ips += resolve_addresses(nameserver_names)
64
+ end
65
+ rescue => ex
66
+ puts "#{ex.class}: #{ex.message}"
67
+ end
68
+
69
+ nameserver_ips
70
+ end
71
+
72
+ #
73
+ # Organize the authorities to assist recusive queries
74
+ #
75
+ def self.organize_authorities(authorities,additional)
76
+
77
+ return [] if !authorities
78
+ nameserver_ips = []
79
+ nameserver_names = []
80
+ authorities.each{ |authority|
81
+ next unless authority.type == "NS"
82
+ nameserver_names << authority.nsdname
83
+ additional.select{ |x| x.name == authority.nsdname && x.type == "A" }.each{ |rr|
84
+ nameserver_ips << rr.address.to_s
85
+ }
86
+ }
87
+
88
+ if nameserver_ips.empty?
89
+ nameserver_ips += resolve_addresses(nameserver_names)
90
+ end
91
+
92
+ nameserver_ips
93
+ end
94
+
95
+ #
96
+ #
97
+ #
98
+ def self.polish_url(domain)
99
+ return nil if !domain
100
+ zone = detect_zone domain
101
+ "http://" + (!zone || domain.start_with?(WWW_PREFIX) ? domain : WWW_PREFIX + zone)
102
+ end
103
+
104
+
105
+ #
106
+ # if the domain is not ended with dot, then append one
107
+ #
108
+ def self.append_trailing_dot_if_necessary domain
109
+ return domain if !domain || domain.end_with?(".")
110
+ domain + "."
111
+ end
112
+
113
+ #
114
+ # removing trailing dot, if there is one
115
+ #
116
+ def self.remove_trailing_dot_if_any domain
117
+ return domain unless !domain || domain.end_with?(".")
118
+ domain.chop
119
+ end
120
+
121
+ #
122
+ # to judge whether the domain is a subdomain of zone.
123
+ #
124
+ def self.is_subdomain?(domain,zone)
125
+ return false if !domain || !zone
126
+ zone_with_trailing_dot = Util.append_trailing_dot_if_necessary(zone)
127
+ return false if domain == zone_with_trailing_dot
128
+ domain.end_with?(zone_with_trailing_dot) && domain[domain.index(zone_with_trailing_dot) - 1,1] == "."
129
+ end
130
+
131
+ private
132
+
133
+ #
134
+ # resolve the names to ip addresses
135
+ #
136
+ def self.resolve_addresses names
137
+
138
+ return [] unless names && !names.empty?
139
+ addresses = []
140
+ begin
141
+ resovler = Net::DNS::Resolver.new
142
+ names.each{ |name|
143
+ packet = resovler.query(name, Net::DNS::A)
144
+ packet.answer.each { |record|
145
+ next unless record.type == "A"
146
+ addresses << record.address.to_s
147
+ }
148
+ }
149
+ rescue => ex
150
+ puts "#{ex.class}: #{ex.message}"
151
+ end
152
+ addresses
153
+ end
154
+
155
+ end
156
+
157
+ end
@@ -0,0 +1,4 @@
1
+ module Probe
2
+ VERSION = "1.0.0"
3
+ end
4
+
@@ -0,0 +1,222 @@
1
+ require 'domain-probe/resolver_patch'
2
+ require 'domain-probe/util'
3
+ require 'domain-probe/policy'
4
+ require 'domain-probe/guessing_policy'
5
+ require 'domain-probe/crawling_policy'
6
+ require 'set'
7
+
8
+ module Probe
9
+
10
+ class DomainProbe
11
+
12
+ MAX_RECURSIVE_QUERY_DEPTH = 6
13
+
14
+ @policies = []
15
+
16
+ class << self
17
+ attr_accessor :policies
18
+ end
19
+
20
+ #
21
+ #
22
+ def self.register_policy policy
23
+ raise ArgumentError, "policy need respond_to? possible_host_under_domain" unless policy.kind_of? Policy
24
+ self.policies << policy
25
+ end
26
+
27
+ def self.unregister_policy policy
28
+ self.policies.delete policy
29
+ end
30
+
31
+ #
32
+ # collect the records under domain domain
33
+ #
34
+ def self.collect_records domain
35
+ self.new.collect_records domain
36
+ end
37
+
38
+ #
39
+ # ----------------------------------------
40
+ #
41
+ #
42
+ #
43
+ def initialize()
44
+ @a_records = []
45
+ @aaaa_records = []
46
+ @cname_records = []
47
+ @mx_records = []
48
+ @mr_records = []
49
+ @txt_records = []
50
+ @hinfo_records = []
51
+ @srv_records = []
52
+ @ns_records = []
53
+ @semaphore = Mutex.new
54
+ end
55
+
56
+ attr_accessor :a_records
57
+ attr_accessor :aaaa_records
58
+ attr_accessor :cname_records
59
+ attr_accessor :mx_records
60
+ attr_accessor :mr_records
61
+ attr_accessor :txt_records
62
+ attr_accessor :hinfo_records
63
+ attr_accessor :srv_records
64
+ attr_accessor :ns_records
65
+ attr_accessor :semaphore
66
+
67
+ #
68
+ # collect the records under domain domain
69
+ #
70
+ def collect_records domain
71
+ raise ArgumentError, "Domain name is expected" unless domain
72
+ domain = zone = Util.detect_zone(domain)
73
+
74
+ threads = []
75
+ nameservers = Util.detect_nameservers zone
76
+ possible_domains = Set.new
77
+
78
+ DomainProbe.policies.each{ |policy|
79
+ possible_domains |= policy.possible_host_under_domain(zone)
80
+ }
81
+
82
+ #A/CNAME records
83
+ possible_domains.each{ |possible_host|
84
+ possible_domain = possible_host + "." + zone
85
+
86
+ threads << threaded_resolve(possible_domain,Net::DNS::A,zone,nameservers)
87
+ #AAAA
88
+ threads << threaded_resolve(possible_domain,Net::DNS::AAAA,zone,nameservers)
89
+ }
90
+
91
+ #A/CNAME records
92
+ threads << threaded_resolve(domain,Net::DNS::A,zone,nameservers)
93
+ #AAAA
94
+ threads << threaded_resolve(domain,Net::DNS::AAAA,zone,nameservers)
95
+
96
+ #MX records
97
+ threads << threaded_resolve(domain,Net::DNS::MX,zone,nameservers)
98
+
99
+ #MR records
100
+ threads << threaded_resolve(domain,Net::DNS::MR,zone,nameservers)
101
+
102
+ #TXT records
103
+ threads << threaded_resolve(domain,Net::DNS::TXT,zone,nameservers)
104
+
105
+ #HINFO records
106
+ threads << threaded_resolve(domain,Net::DNS::HINFO,zone,nameservers)
107
+
108
+ #SRV records
109
+ threads << threaded_resolve(domain,Net::DNS::SRV,zone,nameservers)
110
+
111
+ #NS records
112
+ threads << threaded_resolve(domain,Net::DNS::NS,zone,nameservers)
113
+
114
+ threads.each do |t|
115
+ t.join
116
+ end
117
+
118
+ self.a_records + self.aaaa_records + self.cname_records + self.mx_records + self.mr_records + self.txt_records + self.hinfo_records + self.srv_records + self.ns_records
119
+ end
120
+
121
+ private
122
+
123
+ #
124
+ # the method will be called under the threaded execution context
125
+ # Attention:
126
+ # 1. tail recursive call happened when we get an un-authorized reply
127
+ # 2. depth limit to avoid infinite recursion
128
+ #
129
+ def resolve_recursive(domain,type,zone,nameservers,depth)
130
+
131
+ depth += 1
132
+ return if depth > MAX_RECURSIVE_QUERY_DEPTH
133
+ single_record_handler = lambda{ |record|
134
+
135
+ if record.name && (record.name == Util.append_trailing_dot_if_necessary(zone) || Util.is_subdomain?(record.name,zone))
136
+ #A little overhead here
137
+ self.semaphore.synchronize {
138
+ case record.type
139
+ when "A"
140
+ self.a_records << record unless self.a_records.include? record
141
+ when "AAAA"
142
+ self.aaaa_records << record unless self.aaaa_records.include? record
143
+ when "CNAME"
144
+ self.cname_records << record unless self.cname_records.include? record
145
+ when "NS"
146
+ self.ns_records << record unless self.ns_records.include? record
147
+ when "MX"
148
+ self.mx_records << record unless self.mx_records.include? record
149
+ when "MR"
150
+ self.mr_records << record unless self.mr_records.include? record
151
+ when "TXT"
152
+ self.txt_records << record unless self.txt_records.include? record
153
+ when "SRV"
154
+ self.srv_records << record unless self.srv_records.include? record
155
+ when "HINFO"
156
+ self.hinfo_records << record unless self.hinfo_records.include? record
157
+ else
158
+ #nothing to do?
159
+ end
160
+ }
161
+ end
162
+ }
163
+
164
+ #
165
+ begin
166
+
167
+ resolver = nameservers.empty? ? Net::DNS::Resolver.new : Net::DNS::Resolver.new(:nameserver => nameservers)
168
+ resolver.log_level = Net::DNS::ERROR
169
+ packet = resolver.query(domain, type)
170
+ return unless packet
171
+
172
+ if packet.header.auth?
173
+ packet.answer.each &single_record_handler if packet.answer
174
+ packet.authority.each &single_record_handler if packet.authority
175
+ packet.additional.each &single_record_handler if packet.additional
176
+ elsif packet.answer.empty? && !packet.authority.empty?
177
+ #following down
178
+ nameservers = Util.organize_authorities(packet.authority,packet.additional)
179
+ #tail recursive
180
+ resolve_recursive(domain,type,zone,nameservers,depth)
181
+ end
182
+
183
+ rescue =>ex
184
+ puts "#{ex.class}: #{ex.message}"
185
+ end
186
+ end
187
+
188
+ #
189
+ # The thread should benifit us, IF NOT, disable it
190
+ #
191
+ def threaded_resolve(domain,type,zone,nameservers)
192
+ Thread.new(domain,type,zone,nameservers) { |domain,type,zone,nameservers|
193
+ depth = 0
194
+ resolve_recursive(domain,type,zone,nameservers,depth)
195
+ }
196
+ end
197
+
198
+ register_policy GuessingPolicy.new
199
+ register_policy CrawlingPolicy.new
200
+ end
201
+
202
+ #
203
+ #
204
+ #
205
+ def collect_records domain
206
+ DomainProbe.collect_records domain
207
+ end
208
+
209
+ def collect_records_print_beautifully domain
210
+ start = Time.now.to_i
211
+ records = DomainProbe.collect_records(domain)
212
+ finish = Time.now.to_i
213
+ print "-----------------------","\n"
214
+ records.each{ |record|
215
+ print record, "\n"
216
+ }
217
+ print "-----------------------","\n"
218
+ print "Time elapsed:\t",finish - start,"seconds. \n"
219
+ nil
220
+ end
221
+
222
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: domain-probe
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - ijammy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70294597444060 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70294597444060
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70294597442500 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70294597442500
36
+ - !ruby/object:Gem::Dependency
37
+ name: net-dns
38
+ requirement: &70294597440720 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70294597440720
47
+ - !ruby/object:Gem::Dependency
48
+ name: nokogiri
49
+ requirement: &70294597439640 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70294597439640
58
+ description: A simple library to probe the dns records under specific domain, as many
59
+ as possilbe
60
+ email:
61
+ - mzhang@yottaa.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - .gitignore
67
+ - Gemfile
68
+ - README.md
69
+ - Rakefile
70
+ - domain-probe.gemspec
71
+ - lib/domain-probe.rb
72
+ - lib/domain-probe/crawling_policy.rb
73
+ - lib/domain-probe/guessing_policy.rb
74
+ - lib/domain-probe/policy.rb
75
+ - lib/domain-probe/resolver_patch.rb
76
+ - lib/domain-probe/util.rb
77
+ - lib/domain-probe/version.rb
78
+ homepage: http://www.yottaa.com
79
+ licenses: []
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project: domain-probe
98
+ rubygems_version: 1.8.10
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: A simple library to probe the dns records under specific domain
102
+ test_files: []