whois-parser-next 0.3.0 → 0.3.1
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 +12 -0
- data/README.md +3 -4
- data/lib/whois/parser/version.rb +1 -1
- data/lib/whois/parsers/whois.nic.hn.rb +8 -2
- data/lib/whois/parsers/whois.pknic.net.pk.rb +4 -4
- data/lib/whois/parsers/whois.registre.ma.rb +89 -9
- data/lib/whois/scanners/base_cocca2.rb +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1ac79159293403464f7e386fd283634642e6eda102d7e486e105b399089e34cf
|
|
4
|
+
data.tar.gz: 0fb55415df4c88415bac870059b30f25fbf5f488526241b1358ce15257b8bcb9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 58999ed26705e0ee3ff4e78428b70ee0f20f45e184693debdee4a899617274662ba2f1074f8ca0bb6109ac7c6c702a81ed88ee32138a29e68dd7a1391cd13ffa
|
|
7
|
+
data.tar.gz: c537956618e781da069518bc6428e39f7953368f001c833582b349f19c3d4d6a7cc7a73e1e85f93d3973c978083497c2fc424c616ad660dcd4ffaf5d8669be92
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
This project uses [Semantic Versioning 2.0.0](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
#### Release 0.3.1 (2026-09-25)
|
|
6
|
+
|
|
7
|
+
- ADDED: Added paired registered and exact authoritative-absence parsing for
|
|
8
|
+
`.ma` and `.hn`. Empty and denied replies remain unavailable, rate limits
|
|
9
|
+
remain throttled, and conflicting or incomplete replies remain unknown.
|
|
10
|
+
- FIXED: Corrected `.pk` parsing for PKNIC's observed indented response
|
|
11
|
+
fields. Registration and availability still require explicit markers;
|
|
12
|
+
incomplete or conflicting replies remain unknown, and denied or throttled
|
|
13
|
+
replies remain non-positive.
|
|
14
|
+
- EVIDENCE: [Current `.ma` and `.hn` responses](docs/audits/top-126-150-tlds-2026-09-19.md)
|
|
15
|
+
and [production-shaped `.pk` responses](docs/audits/top1000-es-pk-shop-dev-app-2026-09-25.md).
|
|
16
|
+
|
|
5
17
|
|
|
6
18
|
#### Release 0.3.0 (2026-09-25)
|
|
7
19
|
|
data/README.md
CHANGED
|
@@ -38,7 +38,7 @@ of current WHOIS responses, explicit unknown states, registry-specific
|
|
|
38
38
|
fixtures, and safe handling of malformed, rate-limited, retired, or changed
|
|
39
39
|
server responses.
|
|
40
40
|
|
|
41
|
-
The current source version is `0.3.
|
|
41
|
+
The current source version is `0.3.1`. Check the [RubyGems project
|
|
42
42
|
page](https://rubygems.org/gems/whois-parser-next) for its publication status
|
|
43
43
|
and the latest published version.
|
|
44
44
|
|
|
@@ -54,11 +54,10 @@ Whois::Parser
|
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
Existing applications can retain `require "whois-parser"` and their
|
|
57
|
-
`Whois::Parser` integrations. To install version 0.3.
|
|
58
|
-
RubyGems, pin it as follows:
|
|
57
|
+
`Whois::Parser` integrations. To install version 0.3.1, pin it as follows:
|
|
59
58
|
|
|
60
59
|
```ruby
|
|
61
|
-
gem "whois-parser-next", "= 0.3.
|
|
60
|
+
gem "whois-parser-next", "= 0.3.1", require: "whois-parser"
|
|
62
61
|
```
|
|
63
62
|
|
|
64
63
|
## Requirements
|
data/lib/whois/parser/version.rb
CHANGED
|
@@ -24,14 +24,14 @@ module Whois
|
|
|
24
24
|
|
|
25
25
|
property_supported :status do
|
|
26
26
|
if no_object_found?
|
|
27
|
-
:available
|
|
27
|
+
registration_fields_present? ? :unknown : :available
|
|
28
28
|
else
|
|
29
29
|
super()
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
property_supported :available? do
|
|
34
|
-
|
|
34
|
+
status == :available
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
property_supported :registered? do
|
|
@@ -43,6 +43,12 @@ module Whois
|
|
|
43
43
|
def no_object_found?
|
|
44
44
|
content_for_scanner.match?(/^The queried object does not exist:\s*No Object Found\s*$/i)
|
|
45
45
|
end
|
|
46
|
+
|
|
47
|
+
def registration_fields_present?
|
|
48
|
+
content_for_scanner.match?(
|
|
49
|
+
/^[ \t]*(?:Domain Status|Domain ID|Creation Date|Updated Date|Registry Expiry Date|Name Server|(?:Sponsoring )?Registrar|Registrant|Admin|Billing|Tech):/i
|
|
50
|
+
)
|
|
51
|
+
end
|
|
46
52
|
end
|
|
47
53
|
|
|
48
54
|
end
|
|
@@ -52,17 +52,17 @@ module Whois
|
|
|
52
52
|
private
|
|
53
53
|
|
|
54
54
|
def echoed_domain
|
|
55
|
-
content_for_scanner[/^Domain
|
|
55
|
+
content_for_scanner[/^ {0,8}Domain:[ \t]*((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+pk)[ \t]*$/i, 1]
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def registered_evidence?
|
|
59
|
-
!!(echoed_domain && content_for_scanner.match?(/^Status: Domain is Registered\
|
|
59
|
+
!!(echoed_domain && content_for_scanner.match?(/^ {0,8}Status: Domain is Registered[ \t]*$/))
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def available_evidence?
|
|
63
63
|
!!(echoed_domain &&
|
|
64
|
-
content_for_scanner.match?(/^Status: Not Registered, and may be available if valid\
|
|
65
|
-
content_for_scanner.match?(/^Available: Yes
|
|
64
|
+
content_for_scanner.match?(/^ {0,8}Status: Not Registered, and may be available if valid[ \t]*$/) &&
|
|
65
|
+
content_for_scanner.match?(/^ {0,8}Available: Yes\.[ \t]*$/))
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
@@ -27,21 +27,15 @@ module Whois
|
|
|
27
27
|
include RegistryResponseSafety
|
|
28
28
|
|
|
29
29
|
property_supported :status do
|
|
30
|
-
|
|
31
|
-
:available
|
|
32
|
-
elsif content_for_scanner.match?(/^domain:/i)
|
|
33
|
-
:registered
|
|
34
|
-
else
|
|
35
|
-
:unknown
|
|
36
|
-
end
|
|
30
|
+
classify_status
|
|
37
31
|
end
|
|
38
32
|
|
|
39
33
|
property_supported :available? do
|
|
40
|
-
|
|
34
|
+
classify_status == :available
|
|
41
35
|
end
|
|
42
36
|
|
|
43
37
|
property_supported :registered? do
|
|
44
|
-
|
|
38
|
+
classify_status == :registered
|
|
45
39
|
end
|
|
46
40
|
|
|
47
41
|
|
|
@@ -59,6 +53,92 @@ module Whois
|
|
|
59
53
|
|
|
60
54
|
property_not_supported :expires_on
|
|
61
55
|
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def classify_status
|
|
59
|
+
return :unknown if conflicting_evidence?
|
|
60
|
+
|
|
61
|
+
return :registered if registered_evidence?
|
|
62
|
+
return :available if available_evidence?
|
|
63
|
+
|
|
64
|
+
:unknown
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def conflicting_evidence?
|
|
68
|
+
return false unless absence_markers.any?
|
|
69
|
+
|
|
70
|
+
record_details_present? || legacy_record_evidence?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def registered_evidence?
|
|
74
|
+
return false unless valid_domain_name_fields? && domain_names.one? && absence_markers.empty?
|
|
75
|
+
|
|
76
|
+
current_record_evidence? || legacy_record_evidence?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def current_record_evidence?
|
|
80
|
+
domain_status_values == ['clientTransferProhibited https://icann.org/epp#clientTransferProhibited']
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def legacy_record_evidence?
|
|
84
|
+
legacy_domain_names.one? &&
|
|
85
|
+
content_for_scanner.match?(/^[ \t]*domain:Class-Name:domain[ \t]*$/i) &&
|
|
86
|
+
content_for_scanner.match?(/^[ \t]*%ok[ \t]*$/i)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def available_evidence?
|
|
90
|
+
absence_markers.one? && valid_domain_name_fields? && !record_details_present? &&
|
|
91
|
+
!legacy_record_evidence?
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def valid_domain_name_fields?
|
|
95
|
+
fields = domain_name_field_values
|
|
96
|
+
return false if domain_name_field? && fields.empty?
|
|
97
|
+
|
|
98
|
+
fields.empty? || (fields.one? && domain_names.one?)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def domain_names
|
|
102
|
+
(current_domain_names + legacy_domain_names).uniq
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def domain_name_field_values
|
|
106
|
+
content_for_scanner.scan(/^[ \t]*Domain Name:[ \t]*(\S+)[ \t]*$/i).flatten +
|
|
107
|
+
content_for_scanner.scan(/^[ \t]*domain:Domain-Name:[ \t]*(\S+)[ \t]*$/i).flatten
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def current_domain_names
|
|
111
|
+
content_for_scanner.scan(
|
|
112
|
+
/^[ \t]*Domain Name:[ \t]*((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+ma)[ \t]*$/i
|
|
113
|
+
).flatten.map(&:downcase)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def legacy_domain_names
|
|
117
|
+
content_for_scanner.scan(
|
|
118
|
+
/^[ \t]*domain:Domain-Name:[ \t]*((?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+ma)[ \t]*$/i
|
|
119
|
+
).flatten.map(&:downcase)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def domain_status_values
|
|
123
|
+
content_for_scanner.scan(/^[ \t]*Domain Status:[ \t]*(.+?)[ \t]*$/i).flatten
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def absence_markers
|
|
127
|
+
content_for_scanner.scan(
|
|
128
|
+
/^[ \t]*(?:%error 230 No Objects Found|The queried object does not exist: No Object Found)[ \t]*$/i
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def domain_name_field?
|
|
133
|
+
content_for_scanner.match?(/^[ \t]*(?:Domain Name:|domain:Domain-Name:)/i)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def record_details_present?
|
|
137
|
+
content_for_scanner.match?(
|
|
138
|
+
/^[ \t]*(?:Domain Status|Domain ID|Registrar|Creation Date|Updated Date|Registry Expiry Date|Name Server|domain:Class-Name|domain:ID|domain:Auth-Area):/i
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
|
|
62
142
|
end
|
|
63
143
|
|
|
64
144
|
end
|
|
@@ -20,6 +20,7 @@ module Whois
|
|
|
20
20
|
DISCLAIMER_MATCHES = [
|
|
21
21
|
"TERMS OF USE:", # global
|
|
22
22
|
"Terminos de Uso:", # whois.nic.hn
|
|
23
|
+
"Queda absolutamente prohibido su uso", # current whois.nic.hn footer
|
|
23
24
|
"The data in the WHOIS database of Meridian", # whois.meridiantld.net
|
|
24
25
|
"This information is provided", # whois.gg
|
|
25
26
|
]
|