domain-probe 1.0.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.
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: []