whois-parser-next 0.4.6 → 0.4.8
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 +49 -0
- data/lib/whois/parser/version.rb +1 -1
- data/lib/whois/parsers/whois.aeda.net.ae.rb +9 -0
- data/lib/whois/parsers/whois.trabis.gov.tr.rb +32 -1
- data/lib/whois/parsers/www.gt.rb +107 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd5238487b0cc29b77e6d56f3594c009ea7712fe877fc31a41079dc1e49178be
|
|
4
|
+
data.tar.gz: 6f94667791431a36eb9b45ce8550143c89b91e9f08d88eafded9411722ae92fc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 454380ba261a261d3f13e91646c54fde3adf7141b5782580323c3511f45b13c1dde9fd5f3f29b03855a090d7d48585304d660788e5a2942f943706670ae104cf
|
|
7
|
+
data.tar.gz: 21b62020ab6c094190a908d3e3e926b38928589c1727266fb691743b06003662a8ee3c22a973c6904b38f37402f6596e68de25efcf2a2f2443f9079e709f2564
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,55 @@
|
|
|
2
2
|
|
|
3
3
|
This project uses [Semantic Versioning 2.0.0](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
#### Release 0.4.8 (2026-10-24)
|
|
6
|
+
|
|
7
|
+
- ADDED: Added `registrant_contacts` to the `whois.trabis.gov.tr` (`.tr`)
|
|
8
|
+
parser. The `** Registrant:` block yields the name, and the email when one is
|
|
9
|
+
published. Address, city, country, phone and fax are read only from the fully
|
|
10
|
+
published eight-line layout, because hidden fields ("Hidden upon user
|
|
11
|
+
request") shorten the block. Fully hidden blocks, no-match responses and
|
|
12
|
+
throttled responses yield no contact.
|
|
13
|
+
- EVIDENCE: Bounded 2026-10-22 observations of `whois.trabis.gov.tr` returned a
|
|
14
|
+
fully published registrant block, a block with only the name visible
|
|
15
|
+
(`google.com.tr`), a fully hidden block, and the `No match found` response.
|
|
16
|
+
The fully published and fully hidden fixtures are redacted: the registrant
|
|
17
|
+
name, address and email, the domain name and the name servers are replaced
|
|
18
|
+
with placeholders while the line layout and whitespace are preserved.
|
|
19
|
+
- FIXED: The `whois.aeda.net.ae` (`.ae`) parser no longer reports the
|
|
20
|
+
registry's rate-limit reply ("BLACKLISTED: You have exceeded the query limit
|
|
21
|
+
...") as registered. `registered?` was the negation of `available?`, so any
|
|
22
|
+
reply other than an explicit absence marker counted as a registration. The
|
|
23
|
+
parser now includes `RegistryResponseSafety` and treats the `BLACKLISTED:`
|
|
24
|
+
reply as throttled, so status, availability and registration raise
|
|
25
|
+
`Whois::ResponseIsThrottled`. Empty and access-denied replies now raise
|
|
26
|
+
`Whois::ResponseIsUnavailable` instead of counting as registered.
|
|
27
|
+
- EVIDENCE: On 2026-10-22, after a bounded series of lookups, `whois.aeda.net.ae`
|
|
28
|
+
answered every query from the client network, including `google.ae` and
|
|
29
|
+
generated unregistered names, with the single `BLACKLISTED:` line in the new
|
|
30
|
+
`response_blacklisted.txt` fixture (unmodified). Earlier the same day,
|
|
31
|
+
registered names and generated unregistered names returned the regular
|
|
32
|
+
registered and `No Data Found` responses.
|
|
33
|
+
|
|
34
|
+
#### Release 0.4.7 (2026-10-19)
|
|
35
|
+
|
|
36
|
+
- ADDED: Added strict parsing for the official `.gt` and `.com.gt` web WHOIS
|
|
37
|
+
response. Registration requires one result heading with a single valid domain
|
|
38
|
+
and `ACTIVO`; absence requires one `alert alert-danger` result with the same
|
|
39
|
+
domain and the registry's exact `no está registrado` marker. Duplicate,
|
|
40
|
+
contradictory, malformed, mismatched, incomplete, denied, throttled, or
|
|
41
|
+
challenge responses remain non-positive. The parser ignores scripts, styles,
|
|
42
|
+
comments, and unrelated page content; contacts and dates are not parsed.
|
|
43
|
+
- EVIDENCE: The official UVG registry lookup is
|
|
44
|
+
[`whois.php?dn=...`](https://www.gt/sitio/whois.php?dn={domain}). Bounded
|
|
45
|
+
2026-10-19 observations returned registered results for `google.gt` and
|
|
46
|
+
`google.com.gt`, and exact absence results for generated eight-letter names
|
|
47
|
+
under both suffixes. The registry's
|
|
48
|
+
[policy](https://www.gt/sitio/registration_policy.php?lang=en) limits lookup
|
|
49
|
+
to individual names rather than lists of registered names. Regression
|
|
50
|
+
examples use only minimal synthetic HTML. This is a web WHOIS source; no
|
|
51
|
+
TCP/43 or RDAP service is claimed. See the
|
|
52
|
+
[dated evidence report](docs/audits/guatemala-web-whois-2026-10-19.md).
|
|
53
|
+
|
|
5
54
|
#### Release 0.4.6 (2026-10-19)
|
|
6
55
|
|
|
7
56
|
- ADDED: Added `.ga` parsing for ANINF's current French WHOIS response, requiring
|
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
|
|
@@ -23,6 +24,14 @@ module Whois
|
|
|
23
24
|
# The Example parser for the list of all available methods.
|
|
24
25
|
#
|
|
25
26
|
class WhoisAedaNetAe < Base
|
|
27
|
+
include RegistryResponseSafety
|
|
28
|
+
|
|
29
|
+
# The registry answers rate-limited clients with a single "BLACKLISTED:"
|
|
30
|
+
# line. That reply carries no domain data, so it must not be read as
|
|
31
|
+
# either registered or available.
|
|
32
|
+
def response_throttled?
|
|
33
|
+
super || content_for_scanner.match?(/^BLACKLISTED:/i)
|
|
34
|
+
end
|
|
26
35
|
|
|
27
36
|
property_supported :status do
|
|
28
37
|
if content_for_scanner =~ /Status:\s+(.+?)\n/
|
|
@@ -7,6 +7,9 @@ module Whois
|
|
|
7
7
|
class WhoisTrabisGovTr < Base
|
|
8
8
|
include RegistryResponseSafety
|
|
9
9
|
|
|
10
|
+
HIDDEN = "Hidden upon user request".freeze
|
|
11
|
+
EMAIL = /[\w.+-]+@[\w-]+(?:\.[\w-]+)+/
|
|
12
|
+
|
|
10
13
|
property_supported :domain do
|
|
11
14
|
content_for_scanner.slice(/^\*\* Domain Name:\s+(.+)\n/, 1)
|
|
12
15
|
end
|
|
@@ -43,7 +46,35 @@ module Whois
|
|
|
43
46
|
|
|
44
47
|
property_not_supported :updated_on
|
|
45
48
|
property_not_supported :registrar
|
|
46
|
-
|
|
49
|
+
|
|
50
|
+
# The "** Registrant:" block runs until the first blank line. A fully
|
|
51
|
+
# published block has eight lines: name, two address lines, "city,",
|
|
52
|
+
# country, email, phone and fax. Each hidden field reads "Hidden upon user
|
|
53
|
+
# request" and hidden blocks are shorter (a visible name plus four hidden
|
|
54
|
+
# lines), so positional fields are read only from the eight-line layout.
|
|
55
|
+
# The name is always the first line and the email is matched anywhere in
|
|
56
|
+
# the block. A block with no visible field yields no contact.
|
|
57
|
+
property_supported :registrant_contacts do
|
|
58
|
+
block = content_for_scanner.slice(/^\*\* Registrant:\n((?:.+\n)+)/, 1)
|
|
59
|
+
return [] unless block
|
|
60
|
+
|
|
61
|
+
lines = block.lines.map(&:strip)
|
|
62
|
+
value = ->(i) { (v = lines[i].to_s).empty? || v == "+" || v == HIDDEN ? nil : v }
|
|
63
|
+
email = lines.find { |l| l.match?(EMAIL) }&.slice(EMAIL)
|
|
64
|
+
name = value.call(0)
|
|
65
|
+
full = lines.size == 8
|
|
66
|
+
|
|
67
|
+
address = full ? [value.call(1), value.call(2)].compact.join("\n").presence : nil
|
|
68
|
+
city = full ? value.call(3)&.chomp(",")&.strip : nil
|
|
69
|
+
country = full ? value.call(4) : nil
|
|
70
|
+
phone = full ? value.call(6) : nil
|
|
71
|
+
fax = full ? value.call(7) : nil
|
|
72
|
+
return [] if [name, email, address, city, country, phone, fax].all?(&:nil?)
|
|
73
|
+
|
|
74
|
+
[Parser::Contact.new(type: Parser::Contact::TYPE_REGISTRANT, name: name, address: address,
|
|
75
|
+
city: city, country: country, email: email, phone: phone, fax: fax)]
|
|
76
|
+
end
|
|
77
|
+
|
|
47
78
|
property_not_supported :admin_contacts
|
|
48
79
|
property_not_supported :technical_contacts
|
|
49
80
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cgi'
|
|
4
|
+
require 'strscan'
|
|
5
|
+
require_relative 'base'
|
|
6
|
+
require_relative 'registry_response_safety'
|
|
7
|
+
|
|
8
|
+
module Whois
|
|
9
|
+
class Parsers
|
|
10
|
+
# The registry's per-domain public web WHOIS, observed 2026-10-19.
|
|
11
|
+
# Inspect only its result heading/alert, never navigation or script text.
|
|
12
|
+
class WwwGt < Base
|
|
13
|
+
include RegistryResponseSafety
|
|
14
|
+
|
|
15
|
+
property_supported(:status) { web_result[:status] }
|
|
16
|
+
property_supported(:domain) { web_result[:domain] }
|
|
17
|
+
property_supported(:registered?) { status == :registered }
|
|
18
|
+
property_supported(:available?) { status == :available }
|
|
19
|
+
|
|
20
|
+
def response_unavailable?
|
|
21
|
+
super || content_for_scanner.match?(/<cap-widget\b|\bclass=["']g-recaptcha["']|cf-chl-/i)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def web_result
|
|
27
|
+
@web_result ||= classify_web_result
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def classify_web_result
|
|
31
|
+
blocks = result_blocks
|
|
32
|
+
return { status: :unknown } unless blocks
|
|
33
|
+
|
|
34
|
+
candidates = blocks.filter_map do |kind, text|
|
|
35
|
+
text = CGI.unescapeHTML(text.gsub('á', 'á').gsub('Á', 'Á')).gsub(/[ \t\r\n]+/, ' ').strip
|
|
36
|
+
if kind == 'h3' && (match = /\A(\S+)\. ACTIVO\z/i.match(text))
|
|
37
|
+
[match[1].downcase, :registered]
|
|
38
|
+
elsif kind == 'div' && (match = /\A(\S+)\. no está registrado \.\z/i.match(text))
|
|
39
|
+
[match[1].downcase, :available]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
return { status: :unknown } unless candidates.size == 1
|
|
43
|
+
|
|
44
|
+
name, state = candidates.first
|
|
45
|
+
return { status: :unknown } unless name.bytesize <= 253 &&
|
|
46
|
+
name.match?(/\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+gt\z/)
|
|
47
|
+
|
|
48
|
+
{ domain: name, status: state }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# One forward scan, including malformed tags. Limit each tag before
|
|
52
|
+
# inspecting attributes and skip scripts, styles and comments entirely.
|
|
53
|
+
def result_blocks
|
|
54
|
+
input = StringScanner.new(content_for_scanner)
|
|
55
|
+
blocks = []
|
|
56
|
+
active = nil
|
|
57
|
+
depth = 0
|
|
58
|
+
text = +''
|
|
59
|
+
until input.eos?
|
|
60
|
+
chunk = input.scan_until(/</)
|
|
61
|
+
unless chunk
|
|
62
|
+
text << input.rest if active
|
|
63
|
+
input.terminate
|
|
64
|
+
break
|
|
65
|
+
end
|
|
66
|
+
text << chunk[0...-1] if active
|
|
67
|
+
if input.peek(3) == '!--'
|
|
68
|
+
return nil unless input.scan_until(/-->/)
|
|
69
|
+
next
|
|
70
|
+
end
|
|
71
|
+
tag = input.scan_until(/>/)
|
|
72
|
+
return nil unless tag && tag.bytesize <= 16_384
|
|
73
|
+
match = /\A[ \t]*(\/?)[ \t]*([a-z0-9]+)/i.match(tag)
|
|
74
|
+
next unless match
|
|
75
|
+
closing = match[1] == '/'
|
|
76
|
+
name = match[2].downcase
|
|
77
|
+
if !closing && %w[script style].include?(name)
|
|
78
|
+
return nil if active
|
|
79
|
+
return nil unless input.scan_until(%r{</#{name}[ \t\r\n]*>}i)
|
|
80
|
+
next
|
|
81
|
+
end
|
|
82
|
+
if active
|
|
83
|
+
depth += 1 if !closing && name == active
|
|
84
|
+
depth -= 1 if closing && name == active
|
|
85
|
+
if depth.zero?
|
|
86
|
+
blocks << [active, text]
|
|
87
|
+
active = nil
|
|
88
|
+
text = +''
|
|
89
|
+
else
|
|
90
|
+
text << ' '
|
|
91
|
+
end
|
|
92
|
+
elsif !closing && (name == 'h3' || (name == 'div' && alert_classes?(tag)))
|
|
93
|
+
active = name
|
|
94
|
+
depth = 1
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
active ? nil : blocks
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def alert_classes?(tag)
|
|
101
|
+
value = tag[/\bclass[ \t]*=[ \t]*["']([^"']*)["']/i, 1]
|
|
102
|
+
classes = value.to_s.split
|
|
103
|
+
classes.include?('alert') && classes.include?('alert-danger')
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
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.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simone Carletti
|
|
@@ -753,6 +753,7 @@ files:
|
|
|
753
753
|
- lib/whois/parsers/whois.za.org.rb
|
|
754
754
|
- lib/whois/parsers/whois.zicta.zm.rb
|
|
755
755
|
- lib/whois/parsers/whois1.nic.bi.rb
|
|
756
|
+
- lib/whois/parsers/www.gt.rb
|
|
756
757
|
- lib/whois/parsers/za_central_registry.rb
|
|
757
758
|
- lib/whois/safe_record.rb
|
|
758
759
|
- lib/whois/scanners/base.rb
|