whois-parser-next 0.4.7 → 0.4.9
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 +66 -0
- data/lib/whois/parser/version.rb +1 -1
- data/lib/whois/parsers/whois.aeda.net.ae.rb +47 -0
- data/lib/whois/parsers/whois.registry.qa.rb +35 -0
- data/lib/whois/parsers/whois.trabis.gov.tr.rb +32 -1
- data/lib/whois/scanners/whois.registry.qa.rb +29 -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: a5663fd18a8e122c6ccc8085b25e18be3b61fab9ff9cbd9bda64cdefbf2e10f5
|
|
4
|
+
data.tar.gz: a34398419d706543bf070bfa3edda47c501b06cb9ac0452c8e18c0c11d414204
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2ae5c942ba61ee7143b6eec11d41d33b16f0a13046b8c1c150be040fbcedc377fea3580caeab8a8bce22b063e7d09c8f6fa7436056dd1240e17250670478dfcc
|
|
7
|
+
data.tar.gz: '089dfc0682b7bee5730c452b7ffb7b4e9022b3b82cea695f0ac6532d789c052cf2eead16de4d8ca6f651f9909b2f4e9ac34f80f2f2f7bab9e4bd43d69d66c1d2'
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,72 @@
|
|
|
2
2
|
|
|
3
3
|
This project uses [Semantic Versioning 2.0.0](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
#### Release 0.4.9 (2026-10-25)
|
|
6
|
+
|
|
7
|
+
- ADDED: Added `registrar`, `registrant_contacts` and `technical_contacts` to
|
|
8
|
+
the `whois.aeda.net.ae` (`.ae`) parser. The registrar comes from the
|
|
9
|
+
`Registrar ID` and `Registrar Name` lines. Each contact comes from the
|
|
10
|
+
`<Role> Contact ID`, `Name` and `Organisation` lines. The registry publishes
|
|
11
|
+
no contact email over port 43; the `Contact Email` line of the legacy layout
|
|
12
|
+
only pointed to the web WHOIS, so it is not read. `admin_contacts` is not
|
|
13
|
+
supported because responses carry no admin contact. No-data responses yield
|
|
14
|
+
no registrar or contact, and blacklisted responses raise
|
|
15
|
+
`Whois::ResponseIsThrottled`.
|
|
16
|
+
- EVIDENCE: On 2026-10-24 a single lookup of `google.ae` on
|
|
17
|
+
`whois.aeda.net.ae` (TCP/43) returned the registrar, registrant and technical
|
|
18
|
+
contact lines in the new `current_contacts_registered.txt` fixture
|
|
19
|
+
(unmodified, CRLF preserved; it holds organisation and role data only, no
|
|
20
|
+
personal data).
|
|
21
|
+
- FIXED: The `whois.registry.qa` (`.qa`, `.com.qa` and the other `.qa`
|
|
22
|
+
second-level zones) parser raised `Whois::ParserError` on every current
|
|
23
|
+
response. The registry now opens each reply with the instructions of its web
|
|
24
|
+
lookup form and closes it with a copyright, terms of use and query limit
|
|
25
|
+
notice; the parser skips both. Current records also renamed their lines:
|
|
26
|
+
`Registrar` replaces `Registrar ID` and `Registrar Name`, `Domain Status`
|
|
27
|
+
(an EPP code followed by its ICANN URL) replaces `Status`, and
|
|
28
|
+
`Registrant Name` and `Tech Name` replace the `<Role> Contact ID/Name/Email`
|
|
29
|
+
lines. The parser reads both layouts: `status` returns the EPP codes,
|
|
30
|
+
`registered?` no longer requires the legacy `Status` line, and the registrar
|
|
31
|
+
and contacts carry a name only, because current records publish no
|
|
32
|
+
registrar ID, contact ID or email.
|
|
33
|
+
- EVIDENCE: On 2026-10-24 single lookups on `whois.registry.qa` (TCP/43)
|
|
34
|
+
returned the registered `google.qa` and `ooredoo.com.qa` responses and the
|
|
35
|
+
`No Data Found` responses for one generated unregistered name in each zone,
|
|
36
|
+
stored unmodified (CRLF preserved) as the new `current_status_registered.txt`,
|
|
37
|
+
`current_com_qa_status_registered.txt`, `current_status_available.txt` and
|
|
38
|
+
`current_com_qa_status_available.txt` fixtures. The registered fixtures hold
|
|
39
|
+
role and organisation names only, no personal data. The 2026-09-19 probes
|
|
40
|
+
that received empty responses no longer reproduce.
|
|
41
|
+
|
|
42
|
+
#### Release 0.4.8 (2026-10-24)
|
|
43
|
+
|
|
44
|
+
- ADDED: Added `registrant_contacts` to the `whois.trabis.gov.tr` (`.tr`)
|
|
45
|
+
parser. The `** Registrant:` block yields the name, and the email when one is
|
|
46
|
+
published. Address, city, country, phone and fax are read only from the fully
|
|
47
|
+
published eight-line layout, because hidden fields ("Hidden upon user
|
|
48
|
+
request") shorten the block. Fully hidden blocks, no-match responses and
|
|
49
|
+
throttled responses yield no contact.
|
|
50
|
+
- EVIDENCE: Bounded 2026-10-22 observations of `whois.trabis.gov.tr` returned a
|
|
51
|
+
fully published registrant block, a block with only the name visible
|
|
52
|
+
(`google.com.tr`), a fully hidden block, and the `No match found` response.
|
|
53
|
+
The fully published and fully hidden fixtures are redacted: the registrant
|
|
54
|
+
name, address and email, the domain name and the name servers are replaced
|
|
55
|
+
with placeholders while the line layout and whitespace are preserved.
|
|
56
|
+
- FIXED: The `whois.aeda.net.ae` (`.ae`) parser no longer reports the
|
|
57
|
+
registry's rate-limit reply ("BLACKLISTED: You have exceeded the query limit
|
|
58
|
+
...") as registered. `registered?` was the negation of `available?`, so any
|
|
59
|
+
reply other than an explicit absence marker counted as a registration. The
|
|
60
|
+
parser now includes `RegistryResponseSafety` and treats the `BLACKLISTED:`
|
|
61
|
+
reply as throttled, so status, availability and registration raise
|
|
62
|
+
`Whois::ResponseIsThrottled`. Empty and access-denied replies now raise
|
|
63
|
+
`Whois::ResponseIsUnavailable` instead of counting as registered.
|
|
64
|
+
- EVIDENCE: On 2026-10-22, after a bounded series of lookups, `whois.aeda.net.ae`
|
|
65
|
+
answered every query from the client network, including `google.ae` and
|
|
66
|
+
generated unregistered names, with the single `BLACKLISTED:` line in the new
|
|
67
|
+
`response_blacklisted.txt` fixture (unmodified). Earlier the same day,
|
|
68
|
+
registered names and generated unregistered names returned the regular
|
|
69
|
+
registered and `No Data Found` responses.
|
|
70
|
+
|
|
5
71
|
#### Release 0.4.7 (2026-10-19)
|
|
6
72
|
|
|
7
73
|
- ADDED: Added strict parsing for the official `.gt` and `.com.gt` web WHOIS
|
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/
|
|
@@ -53,12 +62,50 @@ module Whois
|
|
|
53
62
|
property_not_supported :expires_on
|
|
54
63
|
|
|
55
64
|
|
|
65
|
+
property_supported :registrar do
|
|
66
|
+
id = field("Registrar ID")
|
|
67
|
+
name = field("Registrar Name")
|
|
68
|
+
Parser::Registrar.new(id: id, name: name) if id || name
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
property_supported :registrant_contacts do
|
|
73
|
+
build_contact("Registrant", Parser::Contact::TYPE_REGISTRANT)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
property_not_supported :admin_contacts
|
|
77
|
+
|
|
78
|
+
property_supported :technical_contacts do
|
|
79
|
+
build_contact("Tech", Parser::Contact::TYPE_TECHNICAL)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
56
83
|
property_supported :nameservers do
|
|
57
84
|
content_for_scanner.scan(/Name Server:\s+(.+)\n/).flatten.map do |name|
|
|
58
85
|
Parser::Nameserver.new(:name => name)
|
|
59
86
|
end
|
|
60
87
|
end
|
|
61
88
|
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def field(label)
|
|
93
|
+
content_for_scanner[/^#{label}:[ \t]+(.+)$/, 1]&.strip.presence
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# A contact is the "<Role> Contact ID/Name/Organisation" lines. The
|
|
97
|
+
# registry publishes no contact email over port 43: the "Contact Email"
|
|
98
|
+
# line of the legacy layout only pointed to the web WHOIS, so it is not
|
|
99
|
+
# read.
|
|
100
|
+
def build_contact(role, type)
|
|
101
|
+
id = field("#{role} Contact ID")
|
|
102
|
+
name = field("#{role} Contact Name")
|
|
103
|
+
organization = field("#{role} Contact Organisation")
|
|
104
|
+
return [] unless id || name || organization
|
|
105
|
+
|
|
106
|
+
[Parser::Contact.new(type: type, id: id, name: name, organization: organization)]
|
|
107
|
+
end
|
|
108
|
+
|
|
62
109
|
end
|
|
63
110
|
|
|
64
111
|
end
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
require_relative 'base_shared1'
|
|
11
|
+
require 'whois/scanners/whois.registry.qa'
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
module Whois
|
|
@@ -15,10 +16,44 @@ module Whois
|
|
|
15
16
|
|
|
16
17
|
# Parser for the whois.registry.qa server.
|
|
17
18
|
#
|
|
19
|
+
# Current responses use the "Registrar", "Domain Status" and
|
|
20
|
+
# "<Role> Name" lines; the legacy layout used "Registrar ID/Name",
|
|
21
|
+
# "Status" and "<Role> Contact ID/Name/Email". Both are read.
|
|
22
|
+
#
|
|
18
23
|
# @see Whois::Parsers::Example
|
|
19
24
|
# The Example parser for the list of all available methods.
|
|
20
25
|
#
|
|
21
26
|
class WhoisRegistryQa < BaseShared1
|
|
27
|
+
|
|
28
|
+
self.scanner = Scanners::WhoisRegistryQa
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# "Domain Status" carries the EPP status code followed by its ICANN URL.
|
|
32
|
+
property_supported :status do
|
|
33
|
+
Array.wrap(node("Status") || node("Domain Status")).map { |value| value.split.first }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
property_supported :registered? do
|
|
37
|
+
!available? && !status.empty? && !node("Domain Name").to_s.strip.empty?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
property_supported :registrar do
|
|
42
|
+
name = node("Registrar Name") || node("Registrar")
|
|
43
|
+
Parser::Registrar.new(id: node("Registrar ID"), name: name, organization: name) if name.present?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def build_contact(element, type)
|
|
50
|
+
return super if node("#{element} Contact ID")
|
|
51
|
+
|
|
52
|
+
node("#{element} Name") do |name|
|
|
53
|
+
Parser::Contact.new(type: type, name: name) if name.present?
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
22
57
|
end
|
|
23
58
|
|
|
24
59
|
end
|
|
@@ -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,29 @@
|
|
|
1
|
+
require_relative 'base_shared1'
|
|
2
|
+
|
|
3
|
+
module Whois
|
|
4
|
+
module Scanners
|
|
5
|
+
|
|
6
|
+
# Scanner for the whois.registry.qa records.
|
|
7
|
+
class WhoisRegistryQa < BaseShared1
|
|
8
|
+
|
|
9
|
+
self.tokenizers = [:skip_head, :skip_end] + tokenizers
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Current responses open with the instructions of the registry's web
|
|
13
|
+
# lookup form.
|
|
14
|
+
tokenizer :skip_head do
|
|
15
|
+
@input.skip(/^Welcome to Qatar Domains Registry WHOIS Lookup\n(?:.*\n)*?Type the word you see into the verification box to proceed\.\n/)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# The status code reference ends the record; the copyright, terms of use
|
|
19
|
+
# and query limit notice follow it.
|
|
20
|
+
tokenizer :skip_end do
|
|
21
|
+
if @input.check(/^For more information on Whois status codes, please visit /)
|
|
22
|
+
@input.terminate
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
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.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simone Carletti
|
|
@@ -783,6 +783,7 @@ files:
|
|
|
783
783
|
- lib/whois/scanners/whois.nic.it.rb
|
|
784
784
|
- lib/whois/scanners/whois.nic.net.jo.rb
|
|
785
785
|
- lib/whois/scanners/whois.pir.org.rb
|
|
786
|
+
- lib/whois/scanners/whois.registry.qa.rb
|
|
786
787
|
- lib/whois/scanners/whois.rnids.rs.rb
|
|
787
788
|
- lib/whois/scanners/whois.smallregistry.net.rb
|
|
788
789
|
- lib/whois/scanners/whois.srs.net.nz.rb
|