monovm-whois-ruby 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +75 -0
- data/LICENSE +21 -0
- data/README.md +348 -0
- data/data/rdap_bootstrap.json +5337 -0
- data/data/whois_servers.json +1460 -0
- data/exe/monovm-whois +6 -0
- data/lib/monovm/whois/availability/analyzer.rb +91 -0
- data/lib/monovm/whois/availability/context.rb +137 -0
- data/lib/monovm/whois/availability/patterns.rb +415 -0
- data/lib/monovm/whois/availability/rule.rb +57 -0
- data/lib/monovm/whois/availability/rule_set.rb +137 -0
- data/lib/monovm/whois/availability/rules/availability_keywords.rb +32 -0
- data/lib/monovm/whois/availability/rules/explicit_unavailability.rb +43 -0
- data/lib/monovm/whois/availability/rules/no_match.rb +31 -0
- data/lib/monovm/whois/availability/rules/premium_name.rb +35 -0
- data/lib/monovm/whois/availability/rules/rdap_object.rb +94 -0
- data/lib/monovm/whois/availability/rules/recordless.rb +45 -0
- data/lib/monovm/whois/availability/rules/registration_fields.rb +37 -0
- data/lib/monovm/whois/availability/rules/registry_marker.rb +38 -0
- data/lib/monovm/whois/availability/rules/server_refusal.rb +46 -0
- data/lib/monovm/whois/availability/rules/status_field.rb +42 -0
- data/lib/monovm/whois/availability/rules/tld_specific.rb +38 -0
- data/lib/monovm/whois/availability/rules/wrong_registry.rb +48 -0
- data/lib/monovm/whois/availability/verdict.rb +100 -0
- data/lib/monovm/whois/checker.rb +165 -0
- data/lib/monovm/whois/cli.rb +250 -0
- data/lib/monovm/whois/client.rb +227 -0
- data/lib/monovm/whois/configuration.rb +160 -0
- data/lib/monovm/whois/domain_name.rb +168 -0
- data/lib/monovm/whois/endpoint.rb +131 -0
- data/lib/monovm/whois/errors.rb +63 -0
- data/lib/monovm/whois/parser/base.rb +126 -0
- data/lib/monovm/whois/parser/icann_rdd.rb +79 -0
- data/lib/monovm/whois/parser/key_value.rb +169 -0
- data/lib/monovm/whois/parser/rdap_json.rb +170 -0
- data/lib/monovm/whois/parser/record.rb +165 -0
- data/lib/monovm/whois/parser/selector.rb +74 -0
- data/lib/monovm/whois/paths.rb +31 -0
- data/lib/monovm/whois/punycode.rb +206 -0
- data/lib/monovm/whois/referral/follower.rb +90 -0
- data/lib/monovm/whois/registry/definition.rb +119 -0
- data/lib/monovm/whois/registry/resolution.rb +57 -0
- data/lib/monovm/whois/registry/server_registry.rb +164 -0
- data/lib/monovm/whois/registry/sources/base.rb +58 -0
- data/lib/monovm/whois/registry/sources/iana_bootstrap.rb +142 -0
- data/lib/monovm/whois/registry/sources/json_file.rb +137 -0
- data/lib/monovm/whois/response.rb +89 -0
- data/lib/monovm/whois/result.rb +114 -0
- data/lib/monovm/whois/transport/base.rb +51 -0
- data/lib/monovm/whois/transport/factory.rb +51 -0
- data/lib/monovm/whois/transport/middleware/base.rb +55 -0
- data/lib/monovm/whois/transport/middleware/cache.rb +92 -0
- data/lib/monovm/whois/transport/middleware/instrumentation.rb +63 -0
- data/lib/monovm/whois/transport/middleware/retry.rb +56 -0
- data/lib/monovm/whois/transport/middleware/throttle.rb +62 -0
- data/lib/monovm/whois/transport/rdap_http.rb +146 -0
- data/lib/monovm/whois/transport/whois_socket.rb +130 -0
- data/lib/monovm/whois/version.rb +7 -0
- data/lib/monovm/whois/whois_handler.rb +157 -0
- data/lib/monovm/whois.rb +142 -0
- data/lib/monovm-whois-ruby.rb +5 -0
- data/lib/monovm-whois.rb +5 -0
- metadata +114 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "socket"
|
|
4
|
+
require_relative "base"
|
|
5
|
+
|
|
6
|
+
module MonoVM
|
|
7
|
+
module Whois
|
|
8
|
+
module Transport
|
|
9
|
+
# WHOIS over TCP port 43 (RFC 3912).
|
|
10
|
+
#
|
|
11
|
+
# The protocol is as thin as protocols get: connect, send the query and a
|
|
12
|
+
# CRLF, read until the server closes. What needs care is everything around
|
|
13
|
+
# that — a registry that accepts the connection and then never answers, one
|
|
14
|
+
# that sends half a record and hangs up, one that replies in Latin-1.
|
|
15
|
+
class WhoisSocket < Base
|
|
16
|
+
DEFAULT_CONNECT_TIMEOUT = 10.0
|
|
17
|
+
DEFAULT_READ_TIMEOUT = 15.0
|
|
18
|
+
CHUNK_SIZE = 16_384
|
|
19
|
+
|
|
20
|
+
# Registries that answer in Latin-1. Trying UTF-8 first and falling back
|
|
21
|
+
# keeps accented registrant names readable rather than replacing them with
|
|
22
|
+
# U+FFFD.
|
|
23
|
+
FALLBACK_ENCODING = Encoding::ISO_8859_1
|
|
24
|
+
|
|
25
|
+
# IO::TimeoutError only exists from Ruby 3.2. Naming it directly in a rescue
|
|
26
|
+
# clause would raise NameError on 3.1 at the moment an exception occurred —
|
|
27
|
+
# turning a timeout into a crash — so the list is built once, here.
|
|
28
|
+
CONNECT_TIMEOUTS = [
|
|
29
|
+
Errno::ETIMEDOUT,
|
|
30
|
+
(IO::TimeoutError if defined?(IO::TimeoutError))
|
|
31
|
+
].compact.freeze
|
|
32
|
+
|
|
33
|
+
attr_reader :connect_timeout, :read_timeout
|
|
34
|
+
|
|
35
|
+
def initialize(connect_timeout: DEFAULT_CONNECT_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT)
|
|
36
|
+
@connect_timeout = connect_timeout
|
|
37
|
+
@read_timeout = read_timeout
|
|
38
|
+
super()
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def supports?(endpoint)
|
|
42
|
+
endpoint.socket?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [Response]
|
|
46
|
+
# @raise [ConnectionError, TimeoutError, EmptyResponseError]
|
|
47
|
+
def fetch(query:, endpoint:)
|
|
48
|
+
body, elapsed = measure { exchange(query, endpoint) }
|
|
49
|
+
|
|
50
|
+
if body.strip.empty?
|
|
51
|
+
raise EmptyResponseError,
|
|
52
|
+
"#{endpoint.host} closed the connection without answering for #{query}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Response.new(body: body, endpoint: endpoint, query: query, elapsed: elapsed)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def exchange(query, endpoint)
|
|
61
|
+
socket = connect(endpoint)
|
|
62
|
+
|
|
63
|
+
begin
|
|
64
|
+
socket.write("#{query}\r\n")
|
|
65
|
+
read_all(socket, endpoint)
|
|
66
|
+
ensure
|
|
67
|
+
begin
|
|
68
|
+
socket.close
|
|
69
|
+
rescue IOError, SystemCallError
|
|
70
|
+
# Already closed by the peer; nothing to salvage.
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def connect(endpoint)
|
|
76
|
+
Socket.tcp(endpoint.host, endpoint.port, connect_timeout: connect_timeout)
|
|
77
|
+
rescue *CONNECT_TIMEOUTS => e
|
|
78
|
+
raise TimeoutError, "timed out connecting to #{endpoint.host}:#{endpoint.port} (#{e.class})"
|
|
79
|
+
rescue SocketError, SystemCallError => e
|
|
80
|
+
raise ConnectionError, "cannot reach #{endpoint.host}:#{endpoint.port}: #{e.message}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Read until the peer closes, bounded by a single deadline for the whole
|
|
84
|
+
# response rather than per chunk — a server that dribbles one byte per
|
|
85
|
+
# second should not be able to hold the caller indefinitely.
|
|
86
|
+
def read_all(socket, endpoint)
|
|
87
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + read_timeout
|
|
88
|
+
chunks = []
|
|
89
|
+
|
|
90
|
+
loop do
|
|
91
|
+
remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
92
|
+
return partial(chunks, endpoint, "read timed out", TimeoutError) if remaining <= 0
|
|
93
|
+
|
|
94
|
+
unless socket.wait_readable(remaining)
|
|
95
|
+
return partial(chunks, endpoint, "read timed out",
|
|
96
|
+
TimeoutError)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
begin
|
|
100
|
+
chunks << socket.readpartial(CHUNK_SIZE)
|
|
101
|
+
rescue EOFError
|
|
102
|
+
break
|
|
103
|
+
rescue SystemCallError => e
|
|
104
|
+
return partial(chunks, endpoint, e.message, ConnectionError)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
decode(chunks.join)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# A truncated record still classifies correctly far more often than not, so
|
|
112
|
+
# whatever arrived is kept. Nothing at all is a failure, not a verdict.
|
|
113
|
+
def partial(chunks, endpoint, reason, error_class)
|
|
114
|
+
raise error_class, "#{endpoint.host}:#{endpoint.port} #{reason}" if chunks.empty?
|
|
115
|
+
|
|
116
|
+
decode(chunks.join)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def decode(raw)
|
|
120
|
+
text = raw.dup.force_encoding(Encoding::UTF_8)
|
|
121
|
+
return text if text.valid_encoding?
|
|
122
|
+
|
|
123
|
+
raw.dup.force_encoding(FALLBACK_ENCODING).encode(Encoding::UTF_8)
|
|
124
|
+
rescue EncodingError
|
|
125
|
+
raw.dup.force_encoding(Encoding::UTF_8).scrub("")
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "client"
|
|
4
|
+
|
|
5
|
+
module MonoVM
|
|
6
|
+
module Whois
|
|
7
|
+
# A single-domain handler that wraps one lookup in an object.
|
|
8
|
+
#
|
|
9
|
+
# Exists so that code written against camelCase WHOIS APIs reads the same way
|
|
10
|
+
# here — the camelCase method names are aliased at the bottom.
|
|
11
|
+
#
|
|
12
|
+
# handler = MonoVM::Whois::WhoisHandler.whois("monovm.com")
|
|
13
|
+
# handler.available? # => false
|
|
14
|
+
# handler.whois_message # => the raw registry response
|
|
15
|
+
# handler.record.expires_on
|
|
16
|
+
#
|
|
17
|
+
# One deliberate difference from boolean-style WHOIS APIs: there, an
|
|
18
|
+
# +isAvailable()+ returns false both for a registered domain and for a
|
|
19
|
+
# lookup that failed, and a permissive detector reports a rate-limited or
|
|
20
|
+
# unreachable server as *available*. Here {#available?} is true only when availability was
|
|
21
|
+
# positively established, and {#unknown?} exists to say "we could not find out".
|
|
22
|
+
# Code that branches on +available?+ alone is safe; code that treats
|
|
23
|
+
# +!available?+ as "registered" should ask {#registered?} instead.
|
|
24
|
+
class WhoisHandler
|
|
25
|
+
attr_reader :domain, :result
|
|
26
|
+
|
|
27
|
+
class << self
|
|
28
|
+
# @param domain [String]
|
|
29
|
+
# @param options [Hash] anything {Configuration} accepts
|
|
30
|
+
# @return [WhoisHandler]
|
|
31
|
+
def whois(domain, **options)
|
|
32
|
+
new(domain, **options)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def initialize(domain, client: nil, **options)
|
|
37
|
+
@domain = domain
|
|
38
|
+
@client = client || build_client(options)
|
|
39
|
+
@result = @client.lookup(domain)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [String] the TLD, with its leading dot, or "" when none was resolved
|
|
43
|
+
def tld
|
|
44
|
+
result.tld.to_s
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [String] the second-level label, or "" when none was resolved
|
|
48
|
+
def sld
|
|
49
|
+
result.sld.to_s
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# True only when the response positively established that the name is free.
|
|
53
|
+
def available?
|
|
54
|
+
result.available?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# True when the name exists. Distinct from +!available?+, which is also true
|
|
58
|
+
# when the lookup failed.
|
|
59
|
+
def registered?
|
|
60
|
+
result.registered?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# True when the registry flagged the name as premium or reserved: unregistered,
|
|
64
|
+
# but not obtainable at standard price.
|
|
65
|
+
def premium?
|
|
66
|
+
result.premium?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# True when no verdict could be reached — a refusal, a timeout, an unreadable
|
|
70
|
+
# response. Ask again later; do not treat as free.
|
|
71
|
+
def unknown?
|
|
72
|
+
result.unknown?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# True when the domain was well-formed and its TLD is one we can look up.
|
|
76
|
+
def valid?
|
|
77
|
+
!result.invalid?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# The raw registry response, or the explanatory message when none was obtained.
|
|
81
|
+
def whois_message
|
|
82
|
+
result.whois_message
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @return [Parser::Record, nil]
|
|
86
|
+
def record
|
|
87
|
+
result.record
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @return [Symbol] :available, :registered, :premium, :unknown or :invalid
|
|
91
|
+
def status
|
|
92
|
+
result.status
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Which rule decided, what it matched, and what every other rule said —
|
|
96
|
+
# the actual decision path rather than a fixed set of boolean flags.
|
|
97
|
+
#
|
|
98
|
+
# @return [Hash]
|
|
99
|
+
def availability_details
|
|
100
|
+
{
|
|
101
|
+
domain: result.name,
|
|
102
|
+
tld: result.tld,
|
|
103
|
+
sld: result.sld,
|
|
104
|
+
status: result.status,
|
|
105
|
+
decided_by: result.verdict&.rule,
|
|
106
|
+
reason: result.reason,
|
|
107
|
+
evidence: result.verdict&.evidence,
|
|
108
|
+
trace: result.verdict&.trace || [],
|
|
109
|
+
endpoint: result.response&.endpoint&.to_s,
|
|
110
|
+
response_length: result.response&.body&.length || 0,
|
|
111
|
+
response_preview: preview
|
|
112
|
+
}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def to_h
|
|
116
|
+
result.to_h
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def inspect
|
|
120
|
+
"#<#{self.class.name} #{result.name.inspect} #{status}>"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# ------------------------------------------------------------------
|
|
124
|
+
# camelCase aliases, for callers coming from camelCase WHOIS APIs
|
|
125
|
+
# ------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
alias isAvailable available?
|
|
128
|
+
alias isPremium premium?
|
|
129
|
+
alias isValid valid?
|
|
130
|
+
alias getTld tld
|
|
131
|
+
alias getSld sld
|
|
132
|
+
alias getWhoisMessage whois_message
|
|
133
|
+
alias getAvailabilityDetails availability_details
|
|
134
|
+
|
|
135
|
+
private
|
|
136
|
+
|
|
137
|
+
def build_client(options)
|
|
138
|
+
return Client.new if options.empty?
|
|
139
|
+
|
|
140
|
+
config = Configuration.new
|
|
141
|
+
options.each do |key, value|
|
|
142
|
+
setter = "#{key}="
|
|
143
|
+
raise ArgumentError, "unknown option #{key.inspect}" unless config.respond_to?(setter)
|
|
144
|
+
|
|
145
|
+
config.public_send(setter, value)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
Client.new(config: config)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def preview(limit = 200)
|
|
152
|
+
text = whois_message.to_s.strip.gsub(/\s+/, " ")
|
|
153
|
+
text.length > limit ? "#{text[0, limit]}..." : text
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
data/lib/monovm/whois.rb
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "whois/version"
|
|
4
|
+
require_relative "whois/errors"
|
|
5
|
+
require_relative "whois/paths"
|
|
6
|
+
require_relative "whois/punycode"
|
|
7
|
+
require_relative "whois/domain_name"
|
|
8
|
+
require_relative "whois/endpoint"
|
|
9
|
+
require_relative "whois/response"
|
|
10
|
+
require_relative "whois/availability/verdict"
|
|
11
|
+
require_relative "whois/result"
|
|
12
|
+
require_relative "whois/registry/server_registry"
|
|
13
|
+
require_relative "whois/transport/factory"
|
|
14
|
+
require_relative "whois/availability/analyzer"
|
|
15
|
+
require_relative "whois/parser/selector"
|
|
16
|
+
require_relative "whois/configuration"
|
|
17
|
+
require_relative "whois/referral/follower"
|
|
18
|
+
require_relative "whois/client"
|
|
19
|
+
require_relative "whois/checker"
|
|
20
|
+
require_relative "whois/whois_handler"
|
|
21
|
+
|
|
22
|
+
module MonoVM
|
|
23
|
+
# Domain WHOIS and RDAP lookups.
|
|
24
|
+
#
|
|
25
|
+
# The short way in:
|
|
26
|
+
#
|
|
27
|
+
# MonoVM::Whois.available?("monovm.com") # => false
|
|
28
|
+
# MonoVM::Whois.lookup("monovm.com") # => #<Result "monovm.com" registered>
|
|
29
|
+
# MonoVM::Whois.whois(["monovm", "bing.com"])
|
|
30
|
+
#
|
|
31
|
+
# Settings are global unless overridden per call:
|
|
32
|
+
#
|
|
33
|
+
# MonoVM::Whois.configure do |config|
|
|
34
|
+
# config.prefer = :whois # port 43 before RDAP
|
|
35
|
+
# config.throttle_interval = 1.0 # be gentler with registries
|
|
36
|
+
# config.instrumentation = ->(event) { Rails.logger.info(event) }
|
|
37
|
+
# end
|
|
38
|
+
module Whois
|
|
39
|
+
class << self
|
|
40
|
+
# The global configuration. Mutating it affects the shared {client}.
|
|
41
|
+
def config
|
|
42
|
+
@config ||= Configuration.new
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Yields {config} for editing, then discards the cached client so the next
|
|
46
|
+
# lookup picks the changes up.
|
|
47
|
+
#
|
|
48
|
+
# @yieldparam config [Configuration]
|
|
49
|
+
def configure
|
|
50
|
+
yield config if block_given?
|
|
51
|
+
reset_client!
|
|
52
|
+
config
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Throw away the global configuration and client. Mainly for tests.
|
|
56
|
+
def reset!
|
|
57
|
+
@config = nil
|
|
58
|
+
reset_client!
|
|
59
|
+
self
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The shared client. Built once, because it owns the definition tables and the
|
|
63
|
+
# caches and throttle clocks that make repeated lookups cheap and polite.
|
|
64
|
+
def client
|
|
65
|
+
@client || mutex.synchronize { @client ||= Client.new(config: config) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Look one domain up.
|
|
69
|
+
#
|
|
70
|
+
# @param domain [String]
|
|
71
|
+
# @return [Result]
|
|
72
|
+
def lookup(domain)
|
|
73
|
+
client.lookup(domain)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [Boolean] true only when availability was positively established.
|
|
77
|
+
# A refusal, a timeout or an unreadable response is false, not true.
|
|
78
|
+
def available?(domain)
|
|
79
|
+
client.available?(domain)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @return [Boolean] true when the name is registered
|
|
83
|
+
def registered?(domain)
|
|
84
|
+
lookup(domain).registered?
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Check one or many domains, expanding a bare name across popular TLDs.
|
|
88
|
+
#
|
|
89
|
+
# @param domains [String, Enumerable<String>]
|
|
90
|
+
# @param options [Hash]
|
|
91
|
+
# @return [Hash{String => Symbol}] name => status
|
|
92
|
+
def whois(domains, options = {})
|
|
93
|
+
return Checker.new(client: client, config: config).check(domains) if options.empty?
|
|
94
|
+
|
|
95
|
+
Checker.whois(domains, options)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Full results rather than statuses.
|
|
99
|
+
#
|
|
100
|
+
# @return [Hash{String => Result}]
|
|
101
|
+
def check(domains, options = {})
|
|
102
|
+
return Checker.new(client: client, config: config).check_detailed(domains) if options.empty?
|
|
103
|
+
|
|
104
|
+
Checker.lookup(domains, options)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# A single-domain handler object, camelCase aliases included.
|
|
108
|
+
#
|
|
109
|
+
# @return [WhoisHandler]
|
|
110
|
+
def handler(domain, **options)
|
|
111
|
+
WhoisHandler.whois(domain, **options)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Why a lookup came out the way it did, including every rule consulted.
|
|
115
|
+
#
|
|
116
|
+
# @return [Hash]
|
|
117
|
+
def explain(domain)
|
|
118
|
+
client.explain(domain)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# @return [Array<String>] every TLD that can be looked up, dotted and sorted
|
|
122
|
+
def supported_tlds
|
|
123
|
+
config.server_registry.supported_tlds
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# @return [Boolean]
|
|
127
|
+
def supports?(tld)
|
|
128
|
+
config.server_registry.supports?(tld)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def reset_client!
|
|
134
|
+
mutex.synchronize { @client = nil }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def mutex
|
|
138
|
+
@mutex ||= Mutex.new
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
data/lib/monovm-whois.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: monovm-whois-ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- MonoVM
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: |
|
|
13
|
+
Look up domain registration data and check availability over RDAP and WHOIS
|
|
14
|
+
(port 43). Prefers RDAP where a registry offers it, falls back to port 43,
|
|
15
|
+
follows registrar referrals for thin registries, and parses records into
|
|
16
|
+
structured objects. Availability detection is a priority-ordered rule chain
|
|
17
|
+
that reports "unknown" instead of guessing when a server refuses to answer.
|
|
18
|
+
No runtime dependencies.
|
|
19
|
+
email:
|
|
20
|
+
- dev@monovm.com
|
|
21
|
+
executables:
|
|
22
|
+
- monovm-whois
|
|
23
|
+
extensions: []
|
|
24
|
+
extra_rdoc_files: []
|
|
25
|
+
files:
|
|
26
|
+
- CHANGELOG.md
|
|
27
|
+
- LICENSE
|
|
28
|
+
- README.md
|
|
29
|
+
- data/rdap_bootstrap.json
|
|
30
|
+
- data/whois_servers.json
|
|
31
|
+
- exe/monovm-whois
|
|
32
|
+
- lib/monovm-whois-ruby.rb
|
|
33
|
+
- lib/monovm-whois.rb
|
|
34
|
+
- lib/monovm/whois.rb
|
|
35
|
+
- lib/monovm/whois/availability/analyzer.rb
|
|
36
|
+
- lib/monovm/whois/availability/context.rb
|
|
37
|
+
- lib/monovm/whois/availability/patterns.rb
|
|
38
|
+
- lib/monovm/whois/availability/rule.rb
|
|
39
|
+
- lib/monovm/whois/availability/rule_set.rb
|
|
40
|
+
- lib/monovm/whois/availability/rules/availability_keywords.rb
|
|
41
|
+
- lib/monovm/whois/availability/rules/explicit_unavailability.rb
|
|
42
|
+
- lib/monovm/whois/availability/rules/no_match.rb
|
|
43
|
+
- lib/monovm/whois/availability/rules/premium_name.rb
|
|
44
|
+
- lib/monovm/whois/availability/rules/rdap_object.rb
|
|
45
|
+
- lib/monovm/whois/availability/rules/recordless.rb
|
|
46
|
+
- lib/monovm/whois/availability/rules/registration_fields.rb
|
|
47
|
+
- lib/monovm/whois/availability/rules/registry_marker.rb
|
|
48
|
+
- lib/monovm/whois/availability/rules/server_refusal.rb
|
|
49
|
+
- lib/monovm/whois/availability/rules/status_field.rb
|
|
50
|
+
- lib/monovm/whois/availability/rules/tld_specific.rb
|
|
51
|
+
- lib/monovm/whois/availability/rules/wrong_registry.rb
|
|
52
|
+
- lib/monovm/whois/availability/verdict.rb
|
|
53
|
+
- lib/monovm/whois/checker.rb
|
|
54
|
+
- lib/monovm/whois/cli.rb
|
|
55
|
+
- lib/monovm/whois/client.rb
|
|
56
|
+
- lib/monovm/whois/configuration.rb
|
|
57
|
+
- lib/monovm/whois/domain_name.rb
|
|
58
|
+
- lib/monovm/whois/endpoint.rb
|
|
59
|
+
- lib/monovm/whois/errors.rb
|
|
60
|
+
- lib/monovm/whois/parser/base.rb
|
|
61
|
+
- lib/monovm/whois/parser/icann_rdd.rb
|
|
62
|
+
- lib/monovm/whois/parser/key_value.rb
|
|
63
|
+
- lib/monovm/whois/parser/rdap_json.rb
|
|
64
|
+
- lib/monovm/whois/parser/record.rb
|
|
65
|
+
- lib/monovm/whois/parser/selector.rb
|
|
66
|
+
- lib/monovm/whois/paths.rb
|
|
67
|
+
- lib/monovm/whois/punycode.rb
|
|
68
|
+
- lib/monovm/whois/referral/follower.rb
|
|
69
|
+
- lib/monovm/whois/registry/definition.rb
|
|
70
|
+
- lib/monovm/whois/registry/resolution.rb
|
|
71
|
+
- lib/monovm/whois/registry/server_registry.rb
|
|
72
|
+
- lib/monovm/whois/registry/sources/base.rb
|
|
73
|
+
- lib/monovm/whois/registry/sources/iana_bootstrap.rb
|
|
74
|
+
- lib/monovm/whois/registry/sources/json_file.rb
|
|
75
|
+
- lib/monovm/whois/response.rb
|
|
76
|
+
- lib/monovm/whois/result.rb
|
|
77
|
+
- lib/monovm/whois/transport/base.rb
|
|
78
|
+
- lib/monovm/whois/transport/factory.rb
|
|
79
|
+
- lib/monovm/whois/transport/middleware/base.rb
|
|
80
|
+
- lib/monovm/whois/transport/middleware/cache.rb
|
|
81
|
+
- lib/monovm/whois/transport/middleware/instrumentation.rb
|
|
82
|
+
- lib/monovm/whois/transport/middleware/retry.rb
|
|
83
|
+
- lib/monovm/whois/transport/middleware/throttle.rb
|
|
84
|
+
- lib/monovm/whois/transport/rdap_http.rb
|
|
85
|
+
- lib/monovm/whois/transport/whois_socket.rb
|
|
86
|
+
- lib/monovm/whois/version.rb
|
|
87
|
+
- lib/monovm/whois/whois_handler.rb
|
|
88
|
+
homepage: https://github.com/monovm/whois-ruby
|
|
89
|
+
licenses:
|
|
90
|
+
- MIT
|
|
91
|
+
metadata:
|
|
92
|
+
homepage_uri: https://github.com/monovm/whois-ruby
|
|
93
|
+
bug_tracker_uri: https://github.com/monovm/whois-ruby/issues
|
|
94
|
+
changelog_uri: https://github.com/monovm/whois-ruby/blob/main/CHANGELOG.md
|
|
95
|
+
documentation_uri: https://rubydoc.info/gems/monovm-whois-ruby
|
|
96
|
+
rubygems_mfa_required: 'true'
|
|
97
|
+
rdoc_options: []
|
|
98
|
+
require_paths:
|
|
99
|
+
- lib
|
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - ">="
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '3.1'
|
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
110
|
+
requirements: []
|
|
111
|
+
rubygems_version: 4.0.16
|
|
112
|
+
specification_version: 4
|
|
113
|
+
summary: Domain WHOIS and RDAP lookups with reliable availability detection.
|
|
114
|
+
test_files: []
|