whois-parser-next 0.4.4 → 0.4.6
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 +4 -4
- data/CHANGELOG.md +64 -0
- data/lib/whois/parser/version.rb +1 -1
- data/lib/whois/parsers/whois.isoc.org.il.rb +13 -11
- data/lib/whois/parsers/whois.nic.ga.rb +64 -0
- data/lib/whois/parsers/whois.nic.kn.rb +9 -0
- data/lib/whois/parsers/whois.nic.mc.rb +46 -0
- data/lib/whois/parsers/whois.nic.mr.rb +15 -0
- data/lib/whois/parsers/whois.nic.net.jo.rb +41 -0
- data/lib/whois/parsers/whois.nic.td.rb +15 -0
- data/lib/whois/parsers/whois.registre.bf.rb +15 -0
- data/lib/whois/parsers/whois.sx.rb +34 -1
- data/lib/whois/parsers/whois.website.ws.rb +21 -10
- data/lib/whois/scanners/whois.nic.net.jo.rb +15 -0
- data/lib/whois/scanners/whois.sx.rb +5 -0
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 66e7767b738ae0cf7df9736dc9a6a7685f9ddf7d344506a3a4233accf905f0d2
|
|
4
|
+
data.tar.gz: c15b010e832b1e52cebdb02fd473e8c75176a0f232c777ba385e39b17676b327
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 587e231a29331459c3d14a9cd655ef2fa0230b819cbbcde397be5465500fd4a8eef4b5742407f402ed203d7104a9dc235553795427ec1b2daa1d08a34a24e82e
|
|
7
|
+
data.tar.gz: d4e01866484c46fcf88ef68889d677459ae34db5c0920dcabfc4bb2eaf390f16af8d5524436056a543154320742d51cc445dfb3553de4eb4386adc5c0ef511f1
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,70 @@
|
|
|
2
2
|
|
|
3
3
|
This project uses [Semantic Versioning 2.0.0](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
#### Release 0.4.6 (2026-10-19)
|
|
6
|
+
|
|
7
|
+
- ADDED: Added `.ga` parsing for ANINF's current French WHOIS response, requiring
|
|
8
|
+
one valid domain, `Statut: actif`, and a parseable creation date; absence
|
|
9
|
+
requires the exact `%% NOT FOUND` marker. Added `.jo` parsing for a single
|
|
10
|
+
valid domain with a creation date and `Domain Status: Active`; absence
|
|
11
|
+
requires the exact quoted-name `No match for domain` response and matching
|
|
12
|
+
echoed domain.
|
|
13
|
+
- UPDATED: `.sx` recognizes the current CIRA EPP statuses
|
|
14
|
+
`clientDeleteProhibited` and `clientTransferProhibited`, plus the exact
|
|
15
|
+
`Not found: <name>` absence response, while retaining its legacy format.
|
|
16
|
+
Duplicate, contradictory, malformed, or incomplete evidence remains
|
|
17
|
+
unknown; explicit denials and throttling remain blocked.
|
|
18
|
+
- SAFETY: `.il` no longer treats a missing status as absence. Production
|
|
19
|
+
registered and absent queries returned the same 790-byte disclaimer-only
|
|
20
|
+
reply without an absence marker. Absence now requires the exact
|
|
21
|
+
`% No data was found to match the request criteria.` marker; registration
|
|
22
|
+
requires one valid domain and one `Transfer Locked` or `Transfer Allowed`
|
|
23
|
+
status. Empty, denied, throttled, duplicate, mixed, or incomplete replies
|
|
24
|
+
remain unknown. This corrects false absence; `.il` coverage remains
|
|
25
|
+
unverified.
|
|
26
|
+
- EVIDENCE: Bounded production-host and managed-proxy checks on 2026-10-19 UTC
|
|
27
|
+
observed registered and absent replies for all three hosts. IANA lists
|
|
28
|
+
`whois.nic.net.jo` for [`.jo`](https://www.iana.org/domains/root/db/jo.html)
|
|
29
|
+
and `whois.sx` for [`.sx`](https://www.iana.org/domains/root/db/sx.html).
|
|
30
|
+
IANA has no WHOIS listing for `.ga`; ANINF's current [official
|
|
31
|
+
webWHOIS](https://whois.nic.ga/) and [registration
|
|
32
|
+
policy](https://www.mon.ga/policy/registry_policies.pdf) establish its provenance.
|
|
33
|
+
The `.ga` and `.sx` source terms restrict circulation or use of WHOIS data,
|
|
34
|
+
so the repository fixtures are synthetic technical-shape controls, not
|
|
35
|
+
copies of the captured replies. See the fixture README for details.
|
|
36
|
+
|
|
37
|
+
#### Release 0.4.5 (2026-10-18)
|
|
38
|
+
|
|
39
|
+
- ADDED: Added current CoCCA response parsing for `.bf`, `.td`, and `.mr`,
|
|
40
|
+
requiring one valid domain identity and an exact `active` or `ok` EPP
|
|
41
|
+
registration state. `.bf` accepts `Domain Status: No Object Found`; `.td`,
|
|
42
|
+
`.mr`, and `.kn` require the exact absence line
|
|
43
|
+
`The queried object does not exist: No Object Found`. `.kn` also recognizes
|
|
44
|
+
its current CoCCA response while retaining the legacy format. Mixed or
|
|
45
|
+
incomplete replies remain unknown.
|
|
46
|
+
- ADDED: Added strict `.mc` parsing: registration requires one valid domain,
|
|
47
|
+
`Status: ACTIVE`, and a parseable `Created` value; absence requires the exact
|
|
48
|
+
`>> No data found for domain : <name>` line. Malformed or mixed replies
|
|
49
|
+
remain unknown.
|
|
50
|
+
- SAFETY: `.ws` recognizes the current `Domain Name` and `Creation Date` format
|
|
51
|
+
while preserving the legacy `Domain Created` format and exact absence
|
|
52
|
+
response. Invalid, inconsistent, or duplicate fields remain unknown.
|
|
53
|
+
- EVIDENCE: IANA lists the official hosts `whois.registre.bf`, `whois.nic.td`,
|
|
54
|
+
`whois.nic.mr`, `whois.nic.kn`, `whois.nic.mc`, and `whois.website.ws` for
|
|
55
|
+
[`.bf`](https://www.iana.org/domains/root/db/bf.html),
|
|
56
|
+
[`.td`](https://www.iana.org/domains/root/db/td.html),
|
|
57
|
+
[`.mr`](https://www.iana.org/domains/root/db/mr.html),
|
|
58
|
+
[`.kn`](https://www.iana.org/domains/root/db/kn.html),
|
|
59
|
+
[`.mc`](https://www.iana.org/domains/root/db/mc.html), and
|
|
60
|
+
[`.ws`](https://www.iana.org/domains/root/db/ws.html). Production-host queries
|
|
61
|
+
on 2026-10-18 observed registered and absent pairs for `.bf`, `.td`, `.mr`,
|
|
62
|
+
`.kn`, and `.mc`. Local-network queries observed both `.ws` states; the
|
|
63
|
+
production-proxy query confirmed a registered response only, so a production
|
|
64
|
+
registered/absent pair remains unverified. Committed examples are minimal
|
|
65
|
+
synthetic technical-shape controls without contact details.
|
|
66
|
+
- TESTED: The full suite passes 7,074 examples with 0 failures on Ruby 3.2.2
|
|
67
|
+
and 3.4.5. Final package smoke and supported-Ruby CI remain release gates.
|
|
68
|
+
|
|
5
69
|
#### Release 0.4.4 (2026-10-18)
|
|
6
70
|
|
|
7
71
|
- ADDED: Added parsers for the current `.gh` and `.do` CoCCA WHOIS response
|
data/lib/whois/parser/version.rb
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
require_relative 'base'
|
|
11
|
+
require_relative 'registry_response_safety'
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
module Whois
|
|
@@ -25,19 +26,20 @@ module Whois
|
|
|
25
26
|
# and examples.
|
|
26
27
|
#
|
|
27
28
|
class WhoisIsocOrgIl < Base
|
|
29
|
+
include RegistryResponseSafety
|
|
28
30
|
|
|
29
31
|
property_supported :status do
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
when "transfer allowed"
|
|
35
|
-
:registered
|
|
36
|
-
else
|
|
37
|
-
Whois::Parser.bug!(ParserError, "Unknown status `#{::Regexp.last_match(1)}'.")
|
|
38
|
-
end
|
|
39
|
-
else
|
|
32
|
+
names = content_for_scanner.scan(/^domain:[ \t]*(.+)$/i).flatten.map(&:strip)
|
|
33
|
+
states = content_for_scanner.scan(/^status:[ \t]*(.+)$/i).flatten.map { |value| value.strip.downcase }
|
|
34
|
+
missing = content_for_scanner.scan(/^% No data was found to match the request criteria\.[ \t]*$/).size
|
|
35
|
+
if missing == 1 && names.empty? && states.empty?
|
|
40
36
|
:available
|
|
37
|
+
elsif missing.zero? && names.size == 1 && states.size == 1 &&
|
|
38
|
+
names.first.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+il\z/i) &&
|
|
39
|
+
['transfer locked', 'transfer allowed'].include?(states.first)
|
|
40
|
+
:registered
|
|
41
|
+
else
|
|
42
|
+
:unknown
|
|
41
43
|
end
|
|
42
44
|
end
|
|
43
45
|
|
|
@@ -46,7 +48,7 @@ module Whois
|
|
|
46
48
|
end
|
|
47
49
|
|
|
48
50
|
property_supported :registered? do
|
|
49
|
-
|
|
51
|
+
status == :registered
|
|
50
52
|
end
|
|
51
53
|
|
|
52
54
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative 'registry_response_safety'
|
|
5
|
+
|
|
6
|
+
module Whois
|
|
7
|
+
class Parsers
|
|
8
|
+
# ANINF's current French-language registry response, observed 2026-10-19.
|
|
9
|
+
class WhoisNicGa < Base
|
|
10
|
+
include RegistryResponseSafety
|
|
11
|
+
|
|
12
|
+
property_supported(:domain) { header_field('Nom de domaine')&.downcase }
|
|
13
|
+
property_supported(:domain_id) { header_field('Domain ID') }
|
|
14
|
+
property_supported(:status) do
|
|
15
|
+
names = content_for_scanner.scan(/^Nom de domaine:[ \t]*(.+)$/).flatten
|
|
16
|
+
missing = content_for_scanner.scan(/^%% NOT FOUND[ \t]*$/).size
|
|
17
|
+
if missing == 1 && names.empty? && !header_field('Domain ID')
|
|
18
|
+
:available
|
|
19
|
+
elsif missing.zero? && names.size == 1 &&
|
|
20
|
+
names.first.strip.match?(/\A[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.ga\z/i) &&
|
|
21
|
+
header_field('Domain ID') && header_field('Statut') == 'actif' &&
|
|
22
|
+
valid_creation_date?
|
|
23
|
+
:registered
|
|
24
|
+
else
|
|
25
|
+
:unknown
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
property_supported(:available?) { status == :available }
|
|
29
|
+
property_supported(:registered?) { status == :registered }
|
|
30
|
+
property_supported(:created_on) { header_date('Date de création') }
|
|
31
|
+
property_supported(:updated_on) { header_date('Dernière modification') }
|
|
32
|
+
property_supported(:expires_on) { header_date("Date d'expiration") }
|
|
33
|
+
property_supported(:registrar) do
|
|
34
|
+
name = header_field('Registrar')
|
|
35
|
+
Parser::Registrar.new(name: name) if name
|
|
36
|
+
end
|
|
37
|
+
property_supported(:nameservers) do
|
|
38
|
+
content_for_scanner.scan(/^Serveur de noms:[ \t]*(\S+)[ \t]*$/).flatten.map do |name|
|
|
39
|
+
Parser::Nameserver.new(name: name.downcase)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def header_field(label)
|
|
46
|
+
header = content_for_scanner.split(/^\[/, 2).first.to_s
|
|
47
|
+
values = header.scan(/^#{Regexp.escape(label)}:[ \t]*(.+)$/).flatten.map(&:strip)
|
|
48
|
+
values.first if values.size == 1 && !values.first.empty?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def header_date(label)
|
|
52
|
+
value = header_field(label)
|
|
53
|
+
parse_time(value) if value
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def valid_creation_date?
|
|
57
|
+
value = header_field('Date de création')
|
|
58
|
+
value && value.match?(/\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z\z/) && Date.iso8601(value[0, 10]) && Time.iso8601(value)
|
|
59
|
+
rescue ArgumentError
|
|
60
|
+
false
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
require_relative 'base_icann_compliant'
|
|
2
|
+
require_relative 'cocca_epp_status'
|
|
2
3
|
|
|
3
4
|
module Whois
|
|
4
5
|
class Parsers
|
|
5
6
|
class WhoisNicKn < BaseIcannCompliant
|
|
7
|
+
include CoccaEppStatus
|
|
8
|
+
CURRENT_COCCA_SUFFIX = "kn".freeze
|
|
9
|
+
|
|
10
|
+
property_supported(:status) { current_cocca_status || super() }
|
|
11
|
+
property_supported :available? do
|
|
12
|
+
current = current_cocca_status
|
|
13
|
+
current.nil? ? super() : current == :available
|
|
14
|
+
end
|
|
6
15
|
end
|
|
7
16
|
end
|
|
8
17
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require_relative 'base'
|
|
2
|
+
require_relative 'registry_response_safety'
|
|
3
|
+
|
|
4
|
+
module Whois
|
|
5
|
+
class Parsers
|
|
6
|
+
class WhoisNicMc < Base
|
|
7
|
+
include RegistryResponseSafety
|
|
8
|
+
|
|
9
|
+
property_supported(:domain) { content_for_scanner[/^Domain[ \t]*:[ \t]*(\S+)[ \t]*\r?$/i, 1]&.downcase }
|
|
10
|
+
|
|
11
|
+
property_supported :status do
|
|
12
|
+
text = content_for_scanner
|
|
13
|
+
names = text.scan(/^Domain[ \t]*:[ \t]*(\S+)[ \t]*\r?$/i).flatten
|
|
14
|
+
absent = text.scan(/^>> No data found for domain : (\S+)[ \t]*\r?$/i).flatten
|
|
15
|
+
dates = text.scan(/^Created[ \t]*:[ \t]*([^\r\n]+)$/i).flatten
|
|
16
|
+
statuses = text.scan(/^Status[ \t]*:[ \t]*([^\r\n]+)$/i).flatten.map(&:strip)
|
|
17
|
+
if absent.any?
|
|
18
|
+
absent.one? && valid_mc_domain?(absent.first) && names.empty? && dates.empty? && statuses.empty? ? :available : :unknown
|
|
19
|
+
else
|
|
20
|
+
valid_date = dates.one? && begin
|
|
21
|
+
!!parse_time(dates.first.strip)
|
|
22
|
+
rescue ArgumentError
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
names.one? && valid_mc_domain?(names.first) && statuses == ['ACTIVE'] && valid_date ? :registered : :unknown
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
property_supported(:registered?) { status == :registered }
|
|
29
|
+
property_supported(:available?) { status == :available }
|
|
30
|
+
property_supported :created_on do
|
|
31
|
+
value = content_for_scanner[/^Created[ \t]*:[ \t]*([^\r\n]+)$/i, 1]
|
|
32
|
+
parse_time(value.strip) if value
|
|
33
|
+
end
|
|
34
|
+
property_supported :expires_on do
|
|
35
|
+
value = content_for_scanner[/^Expires on[ \t]*:[ \t]*([^\r\n]+)$/i, 1]
|
|
36
|
+
parse_time(value.strip) if value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def valid_mc_domain?(name)
|
|
42
|
+
name.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+mc\z/i)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require_relative 'base_cocca2'
|
|
2
|
+
require_relative 'cocca_epp_status'
|
|
3
|
+
|
|
4
|
+
module Whois
|
|
5
|
+
class Parsers
|
|
6
|
+
class WhoisNicMr < BaseCocca2
|
|
7
|
+
include CoccaEppStatus
|
|
8
|
+
CURRENT_COCCA_SUFFIX = "mr".freeze
|
|
9
|
+
|
|
10
|
+
property_supported(:status) { current_cocca_status || :unknown }
|
|
11
|
+
property_supported(:available?) { status == :available }
|
|
12
|
+
property_supported(:registered?) { status == :registered }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_cocca2'
|
|
4
|
+
require_relative 'registry_response_safety'
|
|
5
|
+
require 'whois/scanners/whois.nic.net.jo'
|
|
6
|
+
|
|
7
|
+
module Whois
|
|
8
|
+
class Parsers
|
|
9
|
+
# Jordan's official registry endpoint, observed 2026-10-19.
|
|
10
|
+
class WhoisNicNetJo < BaseCocca2
|
|
11
|
+
include RegistryResponseSafety
|
|
12
|
+
self.scanner = Scanners::WhoisNicNetJo
|
|
13
|
+
|
|
14
|
+
property_supported(:status) do
|
|
15
|
+
names = content_for_scanner.scan(/^Domain Name:[ \t]*(.+)$/i).flatten.map(&:strip)
|
|
16
|
+
absent = content_for_scanner.scan(/^No match for domain "([^"\n]+)"[ \t]*$/i).flatten
|
|
17
|
+
statuses = content_for_scanner.scan(/^Domain Status:[ \t]*(.+)$/i).flatten.map(&:strip)
|
|
18
|
+
valid_name = names.size == 1 && names.first.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+jo\z/i)
|
|
19
|
+
if valid_name && absent.size == 1 && absent.first.casecmp?(names.first) && statuses.empty? &&
|
|
20
|
+
!content_for_scanner.match?(/^Creation Date:/i)
|
|
21
|
+
:available
|
|
22
|
+
elsif valid_name && absent.empty? && statuses == ['Active'] && valid_creation_date?
|
|
23
|
+
:registered
|
|
24
|
+
else
|
|
25
|
+
:unknown
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
property_supported(:available?) { status == :available }
|
|
29
|
+
property_supported(:registered?) { status == :registered }
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def valid_creation_date?
|
|
34
|
+
values = content_for_scanner.scan(/^Creation Date:[ \t]*(.+)$/i).flatten.map(&:strip)
|
|
35
|
+
values.size == 1 && values.first.match?(/\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\z/) && Date.iso8601(values.first[0, 10]) && Time.iso8601(values.first)
|
|
36
|
+
rescue ArgumentError
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require_relative 'base_cocca2'
|
|
2
|
+
require_relative 'cocca_epp_status'
|
|
3
|
+
|
|
4
|
+
module Whois
|
|
5
|
+
class Parsers
|
|
6
|
+
class WhoisNicTd < BaseCocca2
|
|
7
|
+
include CoccaEppStatus
|
|
8
|
+
CURRENT_COCCA_SUFFIX = "td".freeze
|
|
9
|
+
|
|
10
|
+
property_supported(:status) { current_cocca_status || :unknown }
|
|
11
|
+
property_supported(:available?) { status == :available }
|
|
12
|
+
property_supported(:registered?) { status == :registered }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require_relative 'base_cocca2'
|
|
2
|
+
require_relative 'cocca_epp_status'
|
|
3
|
+
|
|
4
|
+
module Whois
|
|
5
|
+
class Parsers
|
|
6
|
+
class WhoisRegistreBf < BaseCocca2
|
|
7
|
+
include CoccaEppStatus
|
|
8
|
+
CURRENT_COCCA_SUFFIX = "bf".freeze
|
|
9
|
+
|
|
10
|
+
property_supported(:status) { current_cocca_status || :unknown }
|
|
11
|
+
property_supported(:available?) { status == :available }
|
|
12
|
+
property_supported(:registered?) { status == :registered }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
require_relative 'base'
|
|
11
|
+
require_relative 'registry_response_safety'
|
|
11
12
|
require 'whois/scanners/whois.sx.rb'
|
|
12
13
|
|
|
13
14
|
|
|
@@ -20,6 +21,7 @@ module Whois
|
|
|
20
21
|
# The Example parser for the list of all available methods.
|
|
21
22
|
#
|
|
22
23
|
class WhoisSx < Base
|
|
24
|
+
include RegistryResponseSafety
|
|
23
25
|
include Scanners::Scannable
|
|
24
26
|
|
|
25
27
|
self.scanner = Scanners::WhoisSx
|
|
@@ -31,7 +33,7 @@ module Whois
|
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
property_supported :domain do
|
|
34
|
-
node("Domain Name"
|
|
36
|
+
(node("Domain Name") || node("Not found"))&.downcase
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
property_supported :domain_id do
|
|
@@ -40,6 +42,9 @@ module Whois
|
|
|
40
42
|
|
|
41
43
|
|
|
42
44
|
property_supported :status do
|
|
45
|
+
current = current_registry_status
|
|
46
|
+
return current if current
|
|
47
|
+
|
|
43
48
|
case (s = node("Domain Status", &:downcase))
|
|
44
49
|
when "available"
|
|
45
50
|
:available
|
|
@@ -103,6 +108,34 @@ module Whois
|
|
|
103
108
|
end
|
|
104
109
|
|
|
105
110
|
|
|
111
|
+
# Keep the historical scanner for old fixtures, while recognizing the
|
|
112
|
+
# current EPP record and exact missing reply before scanning legacy text.
|
|
113
|
+
def current_registry_status
|
|
114
|
+
names = content_for_scanner.scan(/^Domain Name:[ \t]*(.+)$/i).flatten.map(&:strip)
|
|
115
|
+
absent = content_for_scanner.scan(/^Not found:[ \t]*(\S+)[ \t]*$/i).flatten
|
|
116
|
+
return nil if absent.empty? && !content_for_scanner.match?(/^Registry Domain ID:/i)
|
|
117
|
+
|
|
118
|
+
valid = ->(name) { name.match?(/\A[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.sx\z/i) }
|
|
119
|
+
if absent.size == 1 && names.empty? && valid.call(absent.first) &&
|
|
120
|
+
!content_for_scanner.match?(/^(?:Creation Date|Registry Domain ID|Domain Status):/i)
|
|
121
|
+
return :available
|
|
122
|
+
end
|
|
123
|
+
return :unknown unless absent.empty? && names.size == 1 && valid.call(names.first)
|
|
124
|
+
|
|
125
|
+
statuses = content_for_scanner.scan(/^Domain Status:[ \t]*(\S+)(?:[^\n]*)$/i).flatten
|
|
126
|
+
dates = content_for_scanner.scan(/^Creation Date:[ \t]*(.+)$/i).flatten.map(&:strip)
|
|
127
|
+
return :unknown unless dates.size == 1 && dates.first.match?(/\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\z/)
|
|
128
|
+
Date.iso8601(dates.first[0, 10])
|
|
129
|
+
Time.iso8601(dates.first)
|
|
130
|
+
if statuses.any? && statuses.all? { |value| value.match?(/\A(?:ok|active|(?:client|server)(?:Delete|Transfer|Update|Renew)Prohibited)\z/) }
|
|
131
|
+
:registered
|
|
132
|
+
else
|
|
133
|
+
:unknown
|
|
134
|
+
end
|
|
135
|
+
rescue ArgumentError
|
|
136
|
+
:unknown
|
|
137
|
+
end
|
|
138
|
+
|
|
106
139
|
private
|
|
107
140
|
|
|
108
141
|
def build_contact(element, type)
|
|
@@ -26,19 +26,25 @@ module Whois
|
|
|
26
26
|
class WhoisWebsiteWs < Base
|
|
27
27
|
include RegistryResponseSafety
|
|
28
28
|
|
|
29
|
+
property_supported :domain do
|
|
30
|
+
content_for_scanner[/^[ \t]*Domain Name:[ \t]*(\S+)[ \t]*\r?$/i, 1]&.downcase
|
|
31
|
+
end
|
|
32
|
+
|
|
29
33
|
property_supported :status do
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
body = content_for_scanner
|
|
35
|
+
names = body.scan(/^[ \t]*Domain Name:[ \t]*(\S+)[ \t]*\r?$/i).flatten
|
|
36
|
+
missing = body.scan(/^(?:No match for "([^"\r\n]+)"\.|The queried object does not exist: ([^\r\n]+)\.)[ \t]*\r?$/i).map { |parts| parts.compact.first }
|
|
37
|
+
dates = body.scan(/^[ \t]*(?:Creation Date|Domain Created):[ \t]*([^\r\n]+)$/i).flatten
|
|
38
|
+
if missing.any?
|
|
39
|
+
missing.one? && names.empty? && dates.empty? && valid_ws_domain?(missing.first) ? :available : :unknown
|
|
34
40
|
else
|
|
35
|
-
|
|
41
|
+
times = dates.map { |date| begin parse_time(date.strip); rescue ArgumentError; nil; end }
|
|
42
|
+
names.one? && valid_ws_domain?(names.first) && times.any? && times.none?(&:nil?) && times.uniq.one? ? :registered : :unknown
|
|
36
43
|
end
|
|
37
44
|
end
|
|
38
45
|
|
|
39
46
|
property_supported :available? do
|
|
40
|
-
|
|
41
|
-
content_for_scanner =~ /^The queried object does not exist: \S+\.$/)
|
|
47
|
+
status == :available
|
|
42
48
|
end
|
|
43
49
|
|
|
44
50
|
property_supported :registered? do
|
|
@@ -47,9 +53,8 @@ module Whois
|
|
|
47
53
|
|
|
48
54
|
|
|
49
55
|
property_supported :created_on do
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
end
|
|
56
|
+
value = content_for_scanner[/^[ \t]*(?:Creation Date|Domain Created):[ \t]*([^\r\n]+)$/i, 1]
|
|
57
|
+
parse_time(value.strip) if value
|
|
53
58
|
end
|
|
54
59
|
|
|
55
60
|
property_supported :updated_on do
|
|
@@ -73,6 +78,12 @@ module Whois
|
|
|
73
78
|
end
|
|
74
79
|
end
|
|
75
80
|
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def valid_ws_domain?(name)
|
|
84
|
+
name.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+ws\z/i)
|
|
85
|
+
end
|
|
86
|
+
|
|
76
87
|
end
|
|
77
88
|
|
|
78
89
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_cocca2'
|
|
4
|
+
|
|
5
|
+
module Whois
|
|
6
|
+
module Scanners
|
|
7
|
+
class WhoisNicNetJo < BaseCocca2
|
|
8
|
+
self.tokenizers += [:skip_jordan_absence]
|
|
9
|
+
|
|
10
|
+
tokenizer :skip_jordan_absence do
|
|
11
|
+
@input.skip(/^No match for domain "[^"\n]+"[ \t]*\n/i)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -11,6 +11,7 @@ module Whois
|
|
|
11
11
|
:scan_keyvalue,
|
|
12
12
|
:skip_lastupdate,
|
|
13
13
|
:scan_disclaimer,
|
|
14
|
+
:skip_comment,
|
|
14
15
|
]
|
|
15
16
|
|
|
16
17
|
|
|
@@ -25,6 +26,10 @@ module Whois
|
|
|
25
26
|
@input.skip(/>>>(.+?)<<<\n/)
|
|
26
27
|
end
|
|
27
28
|
|
|
29
|
+
tokenizer :skip_comment do
|
|
30
|
+
@input.skip(/^%[^\n]*\n/)
|
|
31
|
+
end
|
|
32
|
+
|
|
28
33
|
tokenizer :scan_disclaimer do
|
|
29
34
|
if @input.match?(/^% WHOIS LEGAL STATEMENT/)
|
|
30
35
|
@ast["field:disclaimer"] = _scan_lines_to_array(/%(.*)\n/).map(&:strip).join("\n")
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: whois-parser-next
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simone Carletti
|
|
@@ -439,6 +439,7 @@ files:
|
|
|
439
439
|
- lib/whois/parsers/whois.nic.furniture.rb
|
|
440
440
|
- lib/whois/parsers/whois.nic.futbol.rb
|
|
441
441
|
- lib/whois/parsers/whois.nic.fyi.rb
|
|
442
|
+
- lib/whois/parsers/whois.nic.ga.rb
|
|
442
443
|
- lib/whois/parsers/whois.nic.gal.rb
|
|
443
444
|
- lib/whois/parsers/whois.nic.games.rb
|
|
444
445
|
- lib/whois/parsers/whois.nic.gd.rb
|
|
@@ -513,6 +514,7 @@ files:
|
|
|
513
514
|
- lib/whois/parsers/whois.nic.management.rb
|
|
514
515
|
- lib/whois/parsers/whois.nic.market.rb
|
|
515
516
|
- lib/whois/parsers/whois.nic.mba.rb
|
|
517
|
+
- lib/whois/parsers/whois.nic.mc.rb
|
|
516
518
|
- lib/whois/parsers/whois.nic.md.rb
|
|
517
519
|
- lib/whois/parsers/whois.nic.me.rb
|
|
518
520
|
- lib/whois/parsers/whois.nic.media.rb
|
|
@@ -528,6 +530,7 @@ files:
|
|
|
528
530
|
- lib/whois/parsers/whois.nic.monster.rb
|
|
529
531
|
- lib/whois/parsers/whois.nic.mortgage.rb
|
|
530
532
|
- lib/whois/parsers/whois.nic.moscow.rb
|
|
533
|
+
- lib/whois/parsers/whois.nic.mr.rb
|
|
531
534
|
- lib/whois/parsers/whois.nic.ms.rb
|
|
532
535
|
- lib/whois/parsers/whois.nic.mu.rb
|
|
533
536
|
- lib/whois/parsers/whois.nic.museum.rb
|
|
@@ -535,6 +538,7 @@ files:
|
|
|
535
538
|
- lib/whois/parsers/whois.nic.mx.rb
|
|
536
539
|
- lib/whois/parsers/whois.nic.name.rb
|
|
537
540
|
- lib/whois/parsers/whois.nic.net.bw.rb
|
|
541
|
+
- lib/whois/parsers/whois.nic.net.jo.rb
|
|
538
542
|
- lib/whois/parsers/whois.nic.net.ng.rb
|
|
539
543
|
- lib/whois/parsers/whois.nic.net.sa.rb
|
|
540
544
|
- lib/whois/parsers/whois.nic.net.sb.rb
|
|
@@ -624,6 +628,7 @@ files:
|
|
|
624
628
|
- lib/whois/parsers/whois.nic.taipei.rb
|
|
625
629
|
- lib/whois/parsers/whois.nic.tatar.rb
|
|
626
630
|
- lib/whois/parsers/whois.nic.tc.rb
|
|
631
|
+
- lib/whois/parsers/whois.nic.td.rb
|
|
627
632
|
- lib/whois/parsers/whois.nic.team.rb
|
|
628
633
|
- lib/whois/parsers/whois.nic.tech.rb
|
|
629
634
|
- lib/whois/parsers/whois.nic.technology.rb
|
|
@@ -698,6 +703,7 @@ files:
|
|
|
698
703
|
- lib/whois/parsers/whois.register.bg.rb
|
|
699
704
|
- lib/whois/parsers/whois.register.com.rb
|
|
700
705
|
- lib/whois/parsers/whois.register.si.rb
|
|
706
|
+
- lib/whois/parsers/whois.registre.bf.rb
|
|
701
707
|
- lib/whois/parsers/whois.registre.ma.rb
|
|
702
708
|
- lib/whois/parsers/whois.registro.br.rb
|
|
703
709
|
- lib/whois/parsers/whois.registry.click.rb
|
|
@@ -774,6 +780,7 @@ files:
|
|
|
774
780
|
- lib/whois/scanners/whois.nc.rb
|
|
775
781
|
- lib/whois/scanners/whois.nic.cz.rb
|
|
776
782
|
- lib/whois/scanners/whois.nic.it.rb
|
|
783
|
+
- lib/whois/scanners/whois.nic.net.jo.rb
|
|
777
784
|
- lib/whois/scanners/whois.pir.org.rb
|
|
778
785
|
- lib/whois/scanners/whois.rnids.rs.rb
|
|
779
786
|
- lib/whois/scanners/whois.smallregistry.net.rb
|