whois 4.0.8 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +11 -0
  3. data/.rspec +1 -0
  4. data/.simplecov +4 -0
  5. data/.travis.yml +27 -0
  6. data/CHANGELOG.md +9 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE.txt +1 -1
  9. data/README.md +1 -1
  10. data/Rakefile +33 -0
  11. data/data/tld.json +21 -21
  12. data/lib/whois.rb +1 -1
  13. data/lib/whois/client.rb +1 -1
  14. data/lib/whois/errors.rb +1 -1
  15. data/lib/whois/record.rb +1 -1
  16. data/lib/whois/record/part.rb +1 -1
  17. data/lib/whois/server.rb +14 -2
  18. data/lib/whois/server/adapters/afilias.rb +1 -1
  19. data/lib/whois/server/adapters/arin.rb +1 -1
  20. data/lib/whois/server/adapters/arpa.rb +1 -1
  21. data/lib/whois/server/adapters/base.rb +1 -1
  22. data/lib/whois/server/adapters/formatted.rb +1 -1
  23. data/lib/whois/server/adapters/none.rb +1 -1
  24. data/lib/whois/server/adapters/not_implemented.rb +1 -1
  25. data/lib/whois/server/adapters/standard.rb +1 -1
  26. data/lib/whois/server/adapters/verisign.rb +1 -1
  27. data/lib/whois/server/adapters/web.rb +1 -1
  28. data/lib/whois/server/socket_handler.rb +1 -1
  29. data/lib/whois/version.rb +2 -2
  30. data/spec/fixtures/referrals/afilias.bz.txt +23 -0
  31. data/spec/fixtures/referrals/arin_referral_apnic.txt +78 -0
  32. data/spec/fixtures/referrals/arin_referral_missing.txt +52 -0
  33. data/spec/fixtures/referrals/arin_referral_ripe.txt +50 -0
  34. data/spec/fixtures/referrals/arin_referral_rwhois.txt +63 -0
  35. data/spec/fixtures/referrals/arin_referral_servernap.txt +63 -0
  36. data/spec/fixtures/referrals/arin_referral_whois.txt +56 -0
  37. data/spec/fixtures/referrals/crsnic.com.txt +60 -0
  38. data/spec/fixtures/referrals/crsnic.com_referral.txt +56 -0
  39. data/spec/fixtures/referrals/crsnic.com_referral_missing.txt +50 -0
  40. data/spec/integration/whois_spec.rb +73 -0
  41. data/spec/spec_helper.rb +24 -0
  42. data/spec/support/helpers/connectivity_helper.rb +15 -0
  43. data/spec/support/helpers/spec_helper.rb +31 -0
  44. data/spec/whois/client_spec.rb +144 -0
  45. data/spec/whois/errors_spec.rb +23 -0
  46. data/spec/whois/record/part_spec.rb +38 -0
  47. data/spec/whois/record_spec.rb +158 -0
  48. data/spec/whois/server/adapters/afilias_spec.rb +49 -0
  49. data/spec/whois/server/adapters/arin_spec.rb +84 -0
  50. data/spec/whois/server/adapters/arpa_spec.rb +20 -0
  51. data/spec/whois/server/adapters/base_spec.rb +150 -0
  52. data/spec/whois/server/adapters/formatted_spec.rb +53 -0
  53. data/spec/whois/server/adapters/none_spec.rb +23 -0
  54. data/spec/whois/server/adapters/not_implemented_spec.rb +24 -0
  55. data/spec/whois/server/adapters/standard_spec.rb +42 -0
  56. data/spec/whois/server/adapters/verisign_spec.rb +60 -0
  57. data/spec/whois/server/adapters/web_spec.rb +24 -0
  58. data/spec/whois/server/socket_handler_spec.rb +27 -0
  59. data/spec/whois/server_spec.rb +300 -0
  60. data/spec/whois/whois_spec.rb +15 -0
  61. data/tasks/spec.rake +199 -0
  62. data/utils/compare-whois.rb +30 -0
  63. data/utils/deftld.rb +231 -0
  64. data/utils/defutils.rb +26 -0
  65. data/utils/fixupd.rb +60 -0
  66. data/utils/matrix.rb +68 -0
  67. data/utils/mkwhois.rb +31 -0
  68. data/whois.gemspec +19 -32
  69. metadata +82 -5
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Whois do
4
+
5
+ describe ".lookup" do
6
+ it "delegates the lookup to a new client" do
7
+ client = double()
8
+ expect(client).to receive(:lookup).with("example.com").and_return(:result)
9
+ expect(Whois::Client).to receive(:new).and_return(client)
10
+
11
+ expect(described_class.lookup("example.com")).to eq(:result)
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,199 @@
1
+ require 'fileutils'
2
+
3
+ namespace :spec do
4
+
5
+ ROOT_DIR = File.expand_path("../../", __FILE__)
6
+ TARGET_DIR = File.join(ROOT_DIR, %w( spec whois record parser responses ))
7
+
8
+ SOURCE_DIR = File.join(ROOT_DIR, %w( spec fixtures responses ))
9
+ SOURCE_PARTS = SOURCE_DIR.split("/")
10
+
11
+
12
+ TPL_DESCRIBE = <<-RUBY.chomp!
13
+ # encoding: utf-8
14
+
15
+ # This file is autogenerated. Do not edit it manually.
16
+ # If you want change the content of this file, edit
17
+ #
18
+ # %{sfile}
19
+ #
20
+ # and regenerate the tests with the following rake task
21
+ #
22
+ # $ rake spec:generate
23
+ #
24
+
25
+ require 'spec_helper'
26
+ require 'whois/record/parser/%{khost}.rb'
27
+
28
+ describe %{described_class}, "%{descr}" do
29
+
30
+ subject do
31
+ file = fixture("responses", "%{fixture}")
32
+ part = Whois::Record::Part.new(body: File.read(file))
33
+ described_class.new(part)
34
+ end
35
+
36
+ %{contexts}
37
+ end
38
+ RUBY
39
+
40
+ TPL_CONTEXT = <<-RUBY.chomp!
41
+ describe "#%{descr}" do
42
+ it do
43
+ %{examples}
44
+ end
45
+ end
46
+ RUBY
47
+
48
+ TPL_MATCH = <<-RUBY.chomp!
49
+ expect(subject.%{attribute}).to %{match}
50
+ RUBY
51
+
52
+ TPL_MATCH_SIZE = <<-RUBY.chomp!
53
+ expect(subject.%{attribute}.size).to eq(%{size})
54
+ RUBY
55
+
56
+ TPL_MATCH_RAISE = <<-RUBY.chomp!
57
+ expect { subject.%{attribute} }.to %{match}
58
+ RUBY
59
+
60
+ def relativize(path)
61
+ path.gsub(ROOT_DIR, "")
62
+ end
63
+
64
+
65
+ task :generate => :generate_parsers
66
+
67
+ task :generate_parsers do
68
+ Dir["#{SOURCE_DIR}/**/*.expected"].each do |source_path|
69
+
70
+ # Generate the filename and described_class name from the test file.
71
+ parts = (source_path.split("/") - SOURCE_PARTS)
72
+ khost = parts.first
73
+ kfile = parts.last
74
+ described_class = Whois::Record::Parser.parser_klass(khost)
75
+
76
+ target_path = File.join(TARGET_DIR, *parts).gsub(".expected", "_spec.rb")
77
+
78
+ # Extract the tests from the test file
79
+ # and generates a Hash.
80
+ #
81
+ # {
82
+ # "domain" => [
83
+ # ["%s", "== \"google.biz\""]
84
+ # ],
85
+ # "created_on" => [
86
+ # ["%s", "be_a(Time)"],
87
+ # ["%s", "== Time.parse(\"2002-03-27 00:01:00 UTC\")"]
88
+ # ]
89
+ # }
90
+ #
91
+ tests = {}
92
+ match = nil
93
+ lines = File.open(source_path, "r:UTF-8")
94
+ lines.each do |line|
95
+ line.chomp!
96
+ case line
97
+ when ""
98
+ # skip empty line
99
+ when /^\s*$/, /^\s*\/\//
100
+ # skip comment line
101
+ when /^#([^\s]+)/
102
+ tests[match = $1] = []
103
+ when /^\s+(.+?) (.+)/
104
+ tests[match] << _parse_assertion($1, $2)
105
+ else
106
+ raise "Invalid Line `#{line}' in `#{source_path}'"
107
+ end
108
+ end
109
+
110
+ # Generate the RSpec content and
111
+ # write one file for every test.
112
+ contexts = tests.map do |attr, specs|
113
+ matches = specs.map do |method, condition|
114
+ attribute = method % attr
115
+ case condition
116
+ when /raise_error/
117
+ TPL_MATCH_RAISE % { attribute: attribute, match: condition }
118
+ when /^%SIZE\{(\d+)\}$/
119
+ TPL_MATCH_SIZE % { attribute: attribute, size: $1 }
120
+ else
121
+ TPL_MATCH % { attribute: attribute, match: condition }
122
+ end
123
+ end.join("\n")
124
+ TPL_CONTEXT % { descr: attr, examples: matches }
125
+ end.join("\n")
126
+
127
+ describe = <<-RUBY
128
+ #{TPL_DESCRIBE % {
129
+ :described_class => described_class,
130
+ :khost => khost,
131
+ :descr => kfile,
132
+ :sfile => relativize(source_path),
133
+ :fixture => parts.join("/").gsub(".expected", ".txt"),
134
+ :contexts => contexts
135
+ }}
136
+ RUBY
137
+
138
+ print "Generating #{relativize(target_path)}... "
139
+ File.dirname(target_path).tap { |d| File.exists?(d) || FileUtils.mkdir_p(d) }
140
+ File.open(target_path, "w+") { |f| f.write(describe) }
141
+ print "done!\n"
142
+ end
143
+
144
+ end
145
+
146
+
147
+ def _parse_assertion(method, condition)
148
+ m = method
149
+ c = condition.strip
150
+
151
+ case
152
+
153
+ # %s %CLASS{time} -> %s be_a(time)
154
+ when c =~ /^%CLASS\{(.+)\}$/
155
+ c = "be_a(#{_build_condition_typeof($1)})"
156
+
157
+ # %s %TIME{...} -> %s Time.parse(...)
158
+ when c =~ /^%TIME\{(.+)\}$/
159
+ c = "eq(Time.parse(\"#{$1}\"))"
160
+
161
+ # %s %ERROR{...} -> %s raise_error(...)
162
+ when c =~ /^%ERROR\{(.+)\}$/
163
+ c = "raise_error(Whois::#{$1})"
164
+
165
+ # %s =~ "foo"
166
+ when c =~ /^%MATCH\{(.+)\}$/
167
+ c = "match(/#{$1}/)"
168
+
169
+ # %s == "foo"
170
+ when c =~ /^== (.+)$/
171
+ c = "eq(#{$1})"
172
+
173
+ end
174
+
175
+ [m, c]
176
+ end
177
+
178
+ def _build_condition_typeof(described_class)
179
+ case described_class
180
+ when "array" then "Array"
181
+ when "time" then "Time"
182
+ when "contact" then "Whois::Record::Contact"
183
+ when "registrar" then "Whois::Record::Registrar"
184
+ when "nameserver" then "Whois::Record::Nameserver"
185
+ else
186
+ raise "Unknown class `#{described_class}'"
187
+ end
188
+ end
189
+
190
+ def _build_condition_typecast(described_class, value)
191
+ case described_class
192
+ when "time"
193
+ %Q{Time.parse("#{value}")}
194
+ else
195
+ raise "Unknown class `#{described_class}'"
196
+ end
197
+ end
198
+
199
+ end
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby -wKU
2
+
3
+ $:.unshift(File.expand_path("../../lib", __FILE__))
4
+
5
+ require 'whois'
6
+
7
+ IANAWHOIS_DIR = "~/Code/ianawhois"
8
+
9
+ servers = {}
10
+ definitions = Whois::Server.definitions(:tld).inject({}) do |hash, item|
11
+ hash.merge(item[0] => item[1])
12
+ end
13
+
14
+ Dir.glob("#{File.expand_path(IANAWHOIS_DIR)}/*").each do |entry|
15
+ basename = File.basename(entry)
16
+ next unless basename =~ /^[A-Z]+$/
17
+ content = File.read(entry)
18
+ server = content =~ /^whois:\s+(.+)\n$/ && $1
19
+ servers[".#{basename.downcase}"] = server
20
+ end
21
+
22
+ diffs = []
23
+ servers.each do |host, server|
24
+ iana, whois = server, definitions[host]
25
+ if iana != whois
26
+ diffs << "#{host}: #{whois.inspect} -> #{iana.inspect}"
27
+ end
28
+ end
29
+
30
+ puts diffs.join("\n")
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'pathname'
5
+ require 'json'
6
+
7
+ class TldDefs
8
+
9
+ KEY_SCHEMA = "_".freeze
10
+
11
+ # The current schema version for the definition file
12
+ #
13
+ # @return [String] version
14
+ SCHEMA_VERSION = "2".freeze
15
+
16
+
17
+ class ChangeError < StandardError
18
+ end
19
+
20
+ class TldDef
21
+ attr_accessor :name
22
+
23
+ attr_accessor :host
24
+ attr_accessor :adapter
25
+ attr_accessor :format
26
+ attr_accessor :url
27
+
28
+ attr_accessor :type
29
+ attr_accessor :group
30
+ attr_accessor :note
31
+
32
+ ATTRIBUTES = {
33
+ host: :host,
34
+ adapter: :adapter,
35
+ format: :format,
36
+ url: :url,
37
+
38
+ group: :_group,
39
+ note: :_note,
40
+ type: :_type,
41
+ }
42
+
43
+
44
+ # Normalizes the TLD name by appending the dot, if missing.
45
+ #
46
+ # @return [String] the normalized TLD name
47
+ def self.name(string)
48
+ string = string.to_str.downcase
49
+ string.start_with?(".") ? string[1..-1] : string
50
+ end
51
+
52
+ def initialize(name, attributes = {})
53
+ @name = self.class.name(name)
54
+ @attributes = {}
55
+
56
+ update(attributes)
57
+ end
58
+
59
+ def load(attributes = {})
60
+ validate(ATTRIBUTES.values, attributes)
61
+
62
+ attributes.each do |key, value|
63
+ @attributes[key.to_sym] = value
64
+ end
65
+
66
+ self
67
+ end
68
+
69
+ # Updates the definition attributes.
70
+ #
71
+ # @param attributes [Hash]
72
+ # @return [void]
73
+ def update(attributes = {})
74
+ validate(ATTRIBUTES.keys, attributes)
75
+
76
+ attributes.each do |key, value|
77
+ @attributes[ATTRIBUTES[key.to_sym]] = value
78
+ end
79
+
80
+ self
81
+ end
82
+
83
+ # Validates the definitions to make sure there are no unknown attributes.
84
+ #
85
+ # @param allowed [Array]
86
+ # @param attributes [Hash]
87
+ # @return [void]
88
+ # @raise [ArgumentError] when a definition attribute is unknown
89
+ def validate(allowed, attributes)
90
+ attributes.each do |key, _|
91
+ allowed.include?(key.to_sym) or
92
+ raise ArgumentError, "Invalid attribute `#{key}`"
93
+ end
94
+ end
95
+
96
+ # Dump the definition object into a serializable Hash.
97
+ #
98
+ # Private attributes (starting by _) are added on top.
99
+ # Keys are sorted alphabetically.
100
+ #
101
+ # @return [Hash] the serializable hash
102
+ def dump
103
+ Hash[@attributes.reject { |_, value| value.nil? }.sort]
104
+ end
105
+ end
106
+
107
+
108
+ # @param file_path [String] path to the TLD definition file
109
+ # @param ignore_missing [Boolean] set to true to silently skip missing TLDs on update.
110
+ # When set to false, an error will be raised.
111
+ def initialize(file_path, ignore_missing: true)
112
+ @path = Pathname.new(file_path)
113
+ @settings = {
114
+ ignore_missing: ignore_missing
115
+ }
116
+ end
117
+
118
+ def read
119
+ JSON.load(@path)
120
+ end
121
+
122
+ def write(data)
123
+ data[KEY_SCHEMA] = schema_attributes
124
+ data = Hash[data.sort_by { |tld, _| tld.split(".").reverse.join(".") }]
125
+ File.write(@path, JSON.pretty_generate(data))
126
+ end
127
+
128
+ def count
129
+ read.count
130
+ end
131
+
132
+ def tlds_add(*tlds, **attributes)
133
+ update do |defs|
134
+ tlds.each do |tld|
135
+ tld = TldDef.name(tld)
136
+ tlddef = TldDef.new(tld, attributes)
137
+ defs[tld] = tlddef.dump
138
+ end
139
+ end
140
+ end
141
+
142
+ def tlds_update(*tlds, **attributes)
143
+ update do |defs|
144
+ tlds.each do |tld|
145
+ tld = TldDef.name(tld)
146
+ # puts(tld) if !defs.key?(tld)
147
+ next if !defs.key?(tld) && @settings[:ignore_missing]
148
+ raise ChangeError, "error updating `#{tld}`, tld is missing" if !defs.key?(tld) && !@settings[:ignore_missing]
149
+
150
+ tlddef = TldDef.new(tld).load(defs[tld]).update(attributes)
151
+ defs[tld] = tlddef.dump
152
+ end
153
+ end
154
+ end
155
+
156
+ def update
157
+ data = read
158
+ puts "#{data.count} definitions read"
159
+ yield data if block_given?
160
+ write(data)
161
+ puts "#{data.count} definitions written"
162
+ data
163
+ end
164
+
165
+ def validate
166
+ read.each do |tld, data|
167
+ TldDef.new(tld, data)
168
+ end; nil
169
+ end
170
+
171
+ private
172
+
173
+ def schema_attributes
174
+ {
175
+ "schema" => SCHEMA_VERSION,
176
+ "updated" => Time.now.utc,
177
+ }
178
+ end
179
+
180
+ end
181
+
182
+
183
+ defs = TldDefs.new(File.expand_path("../../data/tld.json", __FILE__))
184
+
185
+ args = ARGV
186
+ options = {}
187
+ OptionParser.new do |opts|
188
+ opts.banner = "Usage: deftld command [options]"
189
+ opts.separator <<~EOS
190
+
191
+ Commands:
192
+ \tadd
193
+ \tupdate
194
+ \tvalidate
195
+ \tfmt
196
+
197
+ Options:
198
+ EOS
199
+
200
+ TldDefs::TldDef::ATTRIBUTES.each do |key, _|
201
+ opts.on("--#{key} [VALUE]", String, "set the #{key}") do |value|
202
+ options[key] = value
203
+ end
204
+ end
205
+
206
+ begin
207
+ opts.parse!
208
+ rescue OptionParser::ParseError
209
+ puts opts
210
+ exit 1
211
+ end
212
+
213
+ if args.size.zero?
214
+ puts opts
215
+ exit 1
216
+ end
217
+ end
218
+
219
+ case command = args.shift
220
+ when "validate"
221
+ defs.validate
222
+ when "fmt"
223
+ defs.update
224
+ when "add"
225
+ defs.tlds_add(*args, options)
226
+ when "update"
227
+ defs.tlds_update(*args, options)
228
+ else
229
+ puts "Unknown command `#{command}`"
230
+ exit 1
231
+ end