whois 4.0.8 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.simplecov +4 -0
- data/.travis.yml +27 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/Rakefile +33 -0
- data/data/tld.json +21 -21
- data/lib/whois.rb +1 -1
- data/lib/whois/client.rb +1 -1
- data/lib/whois/errors.rb +1 -1
- data/lib/whois/record.rb +1 -1
- data/lib/whois/record/part.rb +1 -1
- data/lib/whois/server.rb +14 -2
- data/lib/whois/server/adapters/afilias.rb +1 -1
- data/lib/whois/server/adapters/arin.rb +1 -1
- data/lib/whois/server/adapters/arpa.rb +1 -1
- data/lib/whois/server/adapters/base.rb +1 -1
- data/lib/whois/server/adapters/formatted.rb +1 -1
- data/lib/whois/server/adapters/none.rb +1 -1
- data/lib/whois/server/adapters/not_implemented.rb +1 -1
- data/lib/whois/server/adapters/standard.rb +1 -1
- data/lib/whois/server/adapters/verisign.rb +1 -1
- data/lib/whois/server/adapters/web.rb +1 -1
- data/lib/whois/server/socket_handler.rb +1 -1
- data/lib/whois/version.rb +2 -2
- data/spec/fixtures/referrals/afilias.bz.txt +23 -0
- data/spec/fixtures/referrals/arin_referral_apnic.txt +78 -0
- data/spec/fixtures/referrals/arin_referral_missing.txt +52 -0
- data/spec/fixtures/referrals/arin_referral_ripe.txt +50 -0
- data/spec/fixtures/referrals/arin_referral_rwhois.txt +63 -0
- data/spec/fixtures/referrals/arin_referral_servernap.txt +63 -0
- data/spec/fixtures/referrals/arin_referral_whois.txt +56 -0
- data/spec/fixtures/referrals/crsnic.com.txt +60 -0
- data/spec/fixtures/referrals/crsnic.com_referral.txt +56 -0
- data/spec/fixtures/referrals/crsnic.com_referral_missing.txt +50 -0
- data/spec/integration/whois_spec.rb +73 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/helpers/connectivity_helper.rb +15 -0
- data/spec/support/helpers/spec_helper.rb +31 -0
- data/spec/whois/client_spec.rb +144 -0
- data/spec/whois/errors_spec.rb +23 -0
- data/spec/whois/record/part_spec.rb +38 -0
- data/spec/whois/record_spec.rb +158 -0
- data/spec/whois/server/adapters/afilias_spec.rb +49 -0
- data/spec/whois/server/adapters/arin_spec.rb +84 -0
- data/spec/whois/server/adapters/arpa_spec.rb +20 -0
- data/spec/whois/server/adapters/base_spec.rb +150 -0
- data/spec/whois/server/adapters/formatted_spec.rb +53 -0
- data/spec/whois/server/adapters/none_spec.rb +23 -0
- data/spec/whois/server/adapters/not_implemented_spec.rb +24 -0
- data/spec/whois/server/adapters/standard_spec.rb +42 -0
- data/spec/whois/server/adapters/verisign_spec.rb +60 -0
- data/spec/whois/server/adapters/web_spec.rb +24 -0
- data/spec/whois/server/socket_handler_spec.rb +27 -0
- data/spec/whois/server_spec.rb +300 -0
- data/spec/whois/whois_spec.rb +15 -0
- data/tasks/spec.rake +199 -0
- data/utils/compare-whois.rb +30 -0
- data/utils/deftld.rb +231 -0
- data/utils/defutils.rb +26 -0
- data/utils/fixupd.rb +60 -0
- data/utils/matrix.rb +68 -0
- data/utils/mkwhois.rb +31 -0
- data/whois.gemspec +19 -32
- 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
|
data/tasks/spec.rake
ADDED
@@ -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")
|
data/utils/deftld.rb
ADDED
@@ -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
|