whois-parser-next 0.4.1 → 0.4.3
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 +43 -0
- data/lib/whois/parser/version.rb +1 -1
- data/lib/whois/parsers/cocca_epp_status.rb +33 -0
- data/lib/whois/parsers/whois.ax.rb +18 -4
- data/lib/whois/parsers/whois.nic.ci.rb +9 -3
- data/lib/whois/parsers/whois.nic.ki.rb +16 -0
- data/lib/whois/parsers/whois.nic.ls.rb +42 -0
- data/lib/whois/parsers/whois.nic.sh.rb +27 -0
- data/lib/whois/parsers/whois.registre.ma.rb +4 -1
- data/lib/whois/parsers/whois1.nic.bi.rb +16 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c58e47a473ff8911ebe65e7a94e538d557585d23196f8116cbeb9720a45f4421
|
|
4
|
+
data.tar.gz: b2e930eecc35340b1812c7a6e351d87e22adf67f1a9a1efaa48c53d736452382
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb08e46591a409452ee668eb3087b02221afaeec040bba662654e26f8b3b5ea2e02213efe68a7bad508d5cf9a74686534af72394bad0207b4c6ec784111eeeb9
|
|
7
|
+
data.tar.gz: 632d2fe08d217051da515695dedec0f62f41bc145aad9cca96429adc7a822361c886a3b8414d99c517e71df6d56518a26528f96f72caf819d130cbcde1ffbdfb
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
This project uses [Semantic Versioning 2.0.0](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
#### Release 0.4.3 (2026-10-18)
|
|
6
|
+
|
|
7
|
+
- ADDED: Add the official `.ls` WHOIS parser for the registered `domain`,
|
|
8
|
+
`registered`, and `expire` fields and the registry's exact generated-name
|
|
9
|
+
absence response. Mixed and incomplete replies remain unknown.
|
|
10
|
+
- UPDATED: Recognize the current `.ax` dotted-field registered format and
|
|
11
|
+
exact `Domain not found` absence marker. For `.ki`, `.bi`, and `.ci`, accept
|
|
12
|
+
current CoCCA EPP status and absence shapes only with a single valid domain
|
|
13
|
+
identity; contradictory or malformed replies remain unknown.
|
|
14
|
+
- EVIDENCE: IANA lists the WHOIS hosts for [`.ki`](https://www.iana.org/domains/root/db/ki.html),
|
|
15
|
+
[`.bi`](https://www.iana.org/domains/root/db/bi.html),
|
|
16
|
+
[`.ci`](https://www.iana.org/domains/root/db/ci.html),
|
|
17
|
+
[`.ax`](https://www.iana.org/domains/root/db/ax.html), and
|
|
18
|
+
[`.ls`](https://www.iana.org/domains/root/db/ls.html). CoCCA fixtures are
|
|
19
|
+
synthetic technical-shape controls because record redistribution is
|
|
20
|
+
restricted; `.ax` and `.ls` fixtures contain reduced technical excerpts.
|
|
21
|
+
- TESTED: The focused suite passes 84 examples. The full suite passes 7,040
|
|
22
|
+
examples with 0 failures on Ruby 3.2.2 and 3.4.5; package smoke passes on
|
|
23
|
+
both and loads every packaged parser. The 633-file gem contains the CoCCA
|
|
24
|
+
helper and `.ls` parser and has no native extensions. Supported-Ruby CI
|
|
25
|
+
passed on Ruby 3.2, 3.3, 3.4, and 4.0.7 for implementation commit
|
|
26
|
+
`7b32576500609aecd8f69b9ae7941bdd41213647` (run 39172234679).
|
|
27
|
+
|
|
28
|
+
#### Release 0.4.2 (2026-10-18)
|
|
29
|
+
|
|
30
|
+
- SAFETY: `.ma` accepts the observed `active https://icann.org/epp#active`
|
|
31
|
+
status as registered only with exactly one valid `.ma` domain name and no
|
|
32
|
+
contradictory absence marker. Incomplete, multiple-domain, or mixed replies
|
|
33
|
+
remain unknown.
|
|
34
|
+
- SAFETY: `.sh` accepts the exact `Domain not found.` response as absence while
|
|
35
|
+
retaining the legacy `NOT FOUND` marker. Partial text, repeated absence
|
|
36
|
+
markers, record data mixed with absence, and error replies do not become
|
|
37
|
+
available.
|
|
38
|
+
- EVIDENCE: The `.ma` active-state meaning is documented by
|
|
39
|
+
[Registre.ma](https://www.registre.ma/language/en/domain-name/e-statuts);
|
|
40
|
+
IANA lists `whois.registre.ma` for `.ma` and `whois.nic.sh` for `.sh`
|
|
41
|
+
([`.ma`](https://www.iana.org/domains/root/db/ma.html),
|
|
42
|
+
[`.sh`](https://www.iana.org/domains/root/db/sh.html)). Only minimal public
|
|
43
|
+
technical fields and synthetic ambiguity cases are used in tests; no
|
|
44
|
+
personal response data is included.
|
|
45
|
+
- TESTED: The full suite passes 7,024 examples with 0 failures on Ruby 3.2.2
|
|
46
|
+
and 3.4.5. The package smoke check passes on both versions.
|
|
47
|
+
|
|
5
48
|
#### Release 0.4.1 (2026-10-17)
|
|
6
49
|
|
|
7
50
|
- SAFETY: `.lu` records are registered only when the response contains a
|
data/lib/whois/parser/version.rb
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require_relative 'registry_response_safety'
|
|
2
|
+
|
|
3
|
+
module Whois
|
|
4
|
+
class Parsers
|
|
5
|
+
# Current CoCCA responses annotate status values with exact EPP links and
|
|
6
|
+
# can identify an absent query using a Domain Name echo plus one marker.
|
|
7
|
+
module CoccaEppStatus
|
|
8
|
+
include RegistryResponseSafety
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def current_cocca_status
|
|
13
|
+
text = content_for_scanner
|
|
14
|
+
absent = text.scan(/^(?:The queried object does not exist:|Domain Status:)[ \t]*No Object Found[ \t]*$/i)
|
|
15
|
+
return nil unless absent.any? || text.match?(/^Domain Status:.*https:\/\/icann\.org\/epp#/i)
|
|
16
|
+
|
|
17
|
+
names = text.scan(/^Domain Name:[ \t]*([^\r\n]+)$/i).flatten.map(&:strip)
|
|
18
|
+
return :unknown unless names.one? && names.first.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+#{self.class::CURRENT_COCCA_SUFFIX}\z/i)
|
|
19
|
+
|
|
20
|
+
statuses = text.scan(/^Domain Status:[ \t]*([^\r\n]+)$/i).flatten.map(&:strip)
|
|
21
|
+
details = text.match?(/^(?:Creation Date|Updated Date|Registry Expiry Date|Name Server|Sponsoring Registrar):[ \t]*\S/i)
|
|
22
|
+
return :available if absent.one? && statuses.all? { |value| value.casecmp('No Object Found').zero? } && !details
|
|
23
|
+
return :unknown if absent.any?
|
|
24
|
+
|
|
25
|
+
valid = statuses.all? do |status|
|
|
26
|
+
status.match?(/\A(?<token>ok|active|(?:client|server)(?:Delete|Renew|Transfer|Update)Prohibited)(?:[ \t]+https:\/\/icann\.org\/epp#\k<token>)?\z/i)
|
|
27
|
+
end
|
|
28
|
+
present = statuses.any? { |status| status.match?(/\A(?:ok|active)(?:[ \t]|\z)/i) }
|
|
29
|
+
valid && present ? :registered : :unknown
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -27,7 +27,9 @@ module Whois
|
|
|
27
27
|
include RegistryResponseSafety
|
|
28
28
|
|
|
29
29
|
property_supported :status do
|
|
30
|
-
if
|
|
30
|
+
if contradictory_evidence?
|
|
31
|
+
:unknown
|
|
32
|
+
elsif available?
|
|
31
33
|
:available
|
|
32
34
|
elsif registered_evidence?
|
|
33
35
|
:registered
|
|
@@ -37,7 +39,7 @@ module Whois
|
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
property_supported :available? do
|
|
40
|
-
!!(content_for_scanner =~ /^No records matching \S+ found\.\s*$/i ||
|
|
42
|
+
!contradictory_evidence? && !!(content_for_scanner =~ /^No records matching \S+ found\.\s*$/i ||
|
|
41
43
|
content_for_scanner =~ /^Domain not found\s*$/i)
|
|
42
44
|
end
|
|
43
45
|
|
|
@@ -65,9 +67,21 @@ module Whois
|
|
|
65
67
|
|
|
66
68
|
private
|
|
67
69
|
|
|
70
|
+
def contradictory_evidence?
|
|
71
|
+
markers = content_for_scanner.scan(/^(?:No records matching \S+ found\.|Domain not found)[ \t]*$/i)
|
|
72
|
+
markers.length > 1 || markers.any? && content_for_scanner.match?(/^(?:Domain Name:|domain\.+:|status\.+:|Created:|created\.+:|Name Server \d:)/i)
|
|
73
|
+
end
|
|
74
|
+
|
|
68
75
|
def registered_evidence?
|
|
69
|
-
content_for_scanner.
|
|
70
|
-
|
|
76
|
+
dotted = content_for_scanner.scan(/^domain\.+:[ \t]*(\S+)[ \t]*$/i).flatten
|
|
77
|
+
if dotted.any?
|
|
78
|
+
dotted.one? && dotted.first.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+ax\z/i) &&
|
|
79
|
+
content_for_scanner.scan(/^status\.+:[ \t]*(.+)$/i).flatten.map(&:strip) == ['Registered'] &&
|
|
80
|
+
content_for_scanner.match?(/^created\.+:[ \t]*\d{2}\.\d{2}\.\d{4}[ \t]*$/i)
|
|
81
|
+
else
|
|
82
|
+
content_for_scanner.match?(/^Domain Name:\s+\S+/i) &&
|
|
83
|
+
content_for_scanner.match?(/^(?:Created|Name Server \d):\s+\S+/i)
|
|
84
|
+
end
|
|
71
85
|
end
|
|
72
86
|
|
|
73
87
|
end
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
require_relative 'base'
|
|
11
11
|
require_relative 'registry_response_safety'
|
|
12
|
+
require_relative 'cocca_epp_status'
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
module Whois
|
|
@@ -26,10 +27,14 @@ module Whois
|
|
|
26
27
|
# and examples.
|
|
27
28
|
#
|
|
28
29
|
class WhoisNicCi < Base
|
|
29
|
-
include
|
|
30
|
+
include CoccaEppStatus
|
|
31
|
+
|
|
32
|
+
CURRENT_COCCA_SUFFIX = "ci".freeze
|
|
30
33
|
|
|
31
34
|
property_supported :status do
|
|
32
|
-
if
|
|
35
|
+
if (current = current_cocca_status)
|
|
36
|
+
current
|
|
37
|
+
elsif available?
|
|
33
38
|
:available
|
|
34
39
|
elsif content_for_scanner.match?(/^Domain:\s*\S+/i) && content_for_scanner.match?(/^Created:\s*\S+/i)
|
|
35
40
|
:registered
|
|
@@ -39,7 +44,8 @@ module Whois
|
|
|
39
44
|
end
|
|
40
45
|
|
|
41
46
|
property_supported :available? do
|
|
42
|
-
|
|
47
|
+
current = current_cocca_status
|
|
48
|
+
current ? current == :available : !!(content_for_scanner =~ /^Domain \S+ not found\s*$/i)
|
|
43
49
|
end
|
|
44
50
|
|
|
45
51
|
property_supported :registered? do
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
require_relative 'base_cocca2'
|
|
11
|
+
require_relative 'cocca_epp_status'
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
module Whois
|
|
@@ -19,6 +20,21 @@ module Whois
|
|
|
19
20
|
# The Example parser for the list of all available methods.
|
|
20
21
|
#
|
|
21
22
|
class WhoisNicKi < BaseCocca2
|
|
23
|
+
include CoccaEppStatus
|
|
24
|
+
|
|
25
|
+
CURRENT_COCCA_SUFFIX = "ki".freeze
|
|
26
|
+
|
|
27
|
+
property_supported :status do
|
|
28
|
+
current_cocca_status || super()
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
property_supported :available? do
|
|
32
|
+
status == :available
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
property_supported :registered? do
|
|
36
|
+
status == :registered
|
|
37
|
+
end
|
|
22
38
|
end
|
|
23
39
|
|
|
24
40
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Ruby Whois
|
|
3
|
+
#++
|
|
4
|
+
|
|
5
|
+
require_relative 'whois.nic.cr'
|
|
6
|
+
|
|
7
|
+
module Whois
|
|
8
|
+
class Parsers
|
|
9
|
+
# The official LSNIC response uses NIC-style registration fields.
|
|
10
|
+
class WhoisNicLs < WhoisNicCr
|
|
11
|
+
property_supported :status do
|
|
12
|
+
classify_ls_status
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
property_supported :available? do
|
|
16
|
+
classify_ls_status == :available
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def classify_ls_status
|
|
22
|
+
text = content_for_scanner
|
|
23
|
+
domains = text.scan(/^domain:[ \t]*(\S+)[ \t]*$/i).flatten
|
|
24
|
+
absent = text.scan(/^No record found for '([^']+)'\.[ \t]*$/i).flatten
|
|
25
|
+
details = text.match?(/^(?:domain|registered|expire|nserver|registrar):/i)
|
|
26
|
+
if absent.one? && valid_ls_domain?(absent.first) && !details
|
|
27
|
+
:available
|
|
28
|
+
elsif absent.empty? && domains.one? && valid_ls_domain?(domains.first) &&
|
|
29
|
+
text.scan(/^registered:[ \t]*\d{2}\.\d{2}\.\d{4}(?:[ \t][^\r\n]*)?$/i).one? &&
|
|
30
|
+
text.scan(/^expire:[ \t]*\d{2}\.\d{2}\.\d{4}[ \t]*$/i).one?
|
|
31
|
+
:registered
|
|
32
|
+
else
|
|
33
|
+
:unknown
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def valid_ls_domain?(name)
|
|
38
|
+
name.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+ls\z/i)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -19,6 +19,33 @@ module Whois
|
|
|
19
19
|
# The Example parser for the list of all available methods.
|
|
20
20
|
#
|
|
21
21
|
class WhoisNicSh < BaseIcb
|
|
22
|
+
self.scanner = Scanners::BaseIcannCompliant, {
|
|
23
|
+
pattern_available: /^(?:NOT FOUND|Domain not found\.)[ \t]*\r?$/i,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
property_supported :status do
|
|
27
|
+
if absence_marker_count.positive?
|
|
28
|
+
available? ? :available : :unknown
|
|
29
|
+
else
|
|
30
|
+
super()
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
property_supported :available? do
|
|
35
|
+
absence_marker_count == 1 && !record_details_present?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def absence_marker_count
|
|
41
|
+
content_for_scanner.scan(/^(?:NOT FOUND|Domain not found\.)[ \t]*\r?$/i).length
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def record_details_present?
|
|
45
|
+
content_for_scanner.match?(
|
|
46
|
+
/^[ \t]*(?:Domain Name|Registry Domain ID|Domain Status|Creation Date|Updated Date|Registry Expiry Date|Registrar|Name Server):/i
|
|
47
|
+
)
|
|
48
|
+
end
|
|
22
49
|
end
|
|
23
50
|
|
|
24
51
|
end
|
|
@@ -77,7 +77,10 @@ module Whois
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def current_record_evidence?
|
|
80
|
-
|
|
80
|
+
[
|
|
81
|
+
['clientTransferProhibited https://icann.org/epp#clientTransferProhibited'],
|
|
82
|
+
['active https://icann.org/epp#active'],
|
|
83
|
+
].include?(domain_status_values)
|
|
81
84
|
end
|
|
82
85
|
|
|
83
86
|
def legacy_record_evidence?
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
require_relative 'base_cocca2'
|
|
11
|
+
require_relative 'cocca_epp_status'
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
module Whois
|
|
@@ -19,6 +20,21 @@ module Whois
|
|
|
19
20
|
# The Example parser for the list of all available methods.
|
|
20
21
|
#
|
|
21
22
|
class Whois1NicBi < BaseCocca2
|
|
23
|
+
include CoccaEppStatus
|
|
24
|
+
|
|
25
|
+
CURRENT_COCCA_SUFFIX = "bi".freeze
|
|
26
|
+
|
|
27
|
+
property_supported :status do
|
|
28
|
+
current_cocca_status || super()
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
property_supported :available? do
|
|
32
|
+
status == :available
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
property_supported :registered? do
|
|
36
|
+
status == :registered
|
|
37
|
+
end
|
|
22
38
|
end
|
|
23
39
|
|
|
24
40
|
end
|
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.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simone Carletti
|
|
@@ -196,6 +196,7 @@ files:
|
|
|
196
196
|
- lib/whois/parsers/capetown-whois.registry.net.za.rb
|
|
197
197
|
- lib/whois/parsers/ccwhois.ksregistry.net.rb
|
|
198
198
|
- lib/whois/parsers/ccwhois.verisign-grs.com.rb
|
|
199
|
+
- lib/whois/parsers/cocca_epp_status.rb
|
|
199
200
|
- lib/whois/parsers/coza-whois.registry.net.za.rb
|
|
200
201
|
- lib/whois/parsers/cwhois.cnnic.cn.rb
|
|
201
202
|
- lib/whois/parsers/durban-whois.registry.net.za.rb
|
|
@@ -498,6 +499,7 @@ files:
|
|
|
498
499
|
- lib/whois/parsers/whois.nic.lk.rb
|
|
499
500
|
- lib/whois/parsers/whois.nic.london.rb
|
|
500
501
|
- lib/whois/parsers/whois.nic.love.rb
|
|
502
|
+
- lib/whois/parsers/whois.nic.ls.rb
|
|
501
503
|
- lib/whois/parsers/whois.nic.ltd.rb
|
|
502
504
|
- lib/whois/parsers/whois.nic.luxury.rb
|
|
503
505
|
- lib/whois/parsers/whois.nic.lv.rb
|