whois-parser-next 0.4.8 → 0.4.10
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 +55 -0
- data/lib/whois/parser/version.rb +1 -1
- data/lib/whois/parsers/base_nic_fr.rb +1 -1
- data/lib/whois/parsers/whois.aeda.net.ae.rb +38 -0
- data/lib/whois/parsers/whois.registro.br.rb +1 -1
- data/lib/whois/parsers/whois.registry.qa.rb +35 -0
- data/lib/whois/parsers/whois.ua.rb +2 -2
- data/lib/whois/scanners/base.rb +18 -1
- data/lib/whois/scanners/iana.rb +3 -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: 9e023caaec0b71465576d51e1aab35e0fa956f22fe435c9ebe91e7eae29e0d18
|
|
4
|
+
data.tar.gz: e90fe07318d7c90413f88ba7201eade76e7fc8ada882da016f1bcfb9574bc68e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2a55f20c2508a3a06d2a704e255532940ddf67b7ae3c9249b1a893b60c1f664c56ddd497aeac46cb1e14b4f6a9a8576db3411d318f768504027f0131916232c2
|
|
7
|
+
data.tar.gz: 3bd083076d0699b8520cd1cd232f09a8c755dcd33192198d7512974f44d73c50044201e34a919eb617c6132079710628c4fa3069e4306b4754292fbc8446f406
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,61 @@
|
|
|
2
2
|
|
|
3
3
|
This project uses [Semantic Versioning 2.0.0](http://semver.org/).
|
|
4
4
|
|
|
5
|
+
#### Release 0.4.10 (2026-11-02)
|
|
6
|
+
|
|
7
|
+
- SAFETY: A response no longer keeps a scanner looping forever. A reply cut
|
|
8
|
+
off right after a disclaimer or notice header, with no final newline (for
|
|
9
|
+
example a Verisign body of just `TERMS OF USE:`, or the CIRA, `.ie`, ATI,
|
|
10
|
+
SX, PIR and configured Afilias disclaimers), made the tokenizer report
|
|
11
|
+
success without consuming input, so parsing never finished. The scanner now
|
|
12
|
+
raises `Whois::ParserError` when tokens stop consuming input, while still
|
|
13
|
+
allowing the short in-place state changes some scanners use to close a
|
|
14
|
+
section.
|
|
15
|
+
- SAFETY: The `whois.iana.org` scanner matches repeated field keys literally.
|
|
16
|
+
A key taken from the response was used as a regular expression, so a key
|
|
17
|
+
such as `()|^` matched without consuming input and looped forever. Contact
|
|
18
|
+
handles in the `.fr`-family, `.br` and `.ua` parsers are also matched
|
|
19
|
+
literally instead of as patterns.
|
|
20
|
+
- TESTED: `spec/whois/scanners/progress_spec.rb` covers each truncated
|
|
21
|
+
prefix and the regex-shaped IANA keys under a deadline.
|
|
22
|
+
|
|
23
|
+
#### Release 0.4.9 (2026-10-25)
|
|
24
|
+
|
|
25
|
+
- ADDED: Added `registrar`, `registrant_contacts` and `technical_contacts` to
|
|
26
|
+
the `whois.aeda.net.ae` (`.ae`) parser. The registrar comes from the
|
|
27
|
+
`Registrar ID` and `Registrar Name` lines. Each contact comes from the
|
|
28
|
+
`<Role> Contact ID`, `Name` and `Organisation` lines. The registry publishes
|
|
29
|
+
no contact email over port 43; the `Contact Email` line of the legacy layout
|
|
30
|
+
only pointed to the web WHOIS, so it is not read. `admin_contacts` is not
|
|
31
|
+
supported because responses carry no admin contact. No-data responses yield
|
|
32
|
+
no registrar or contact, and blacklisted responses raise
|
|
33
|
+
`Whois::ResponseIsThrottled`.
|
|
34
|
+
- EVIDENCE: On 2026-10-24 a single lookup of `google.ae` on
|
|
35
|
+
`whois.aeda.net.ae` (TCP/43) returned the registrar, registrant and technical
|
|
36
|
+
contact lines in the new `current_contacts_registered.txt` fixture
|
|
37
|
+
(unmodified, CRLF preserved; it holds organisation and role data only, no
|
|
38
|
+
personal data).
|
|
39
|
+
- FIXED: The `whois.registry.qa` (`.qa`, `.com.qa` and the other `.qa`
|
|
40
|
+
second-level zones) parser raised `Whois::ParserError` on every current
|
|
41
|
+
response. The registry now opens each reply with the instructions of its web
|
|
42
|
+
lookup form and closes it with a copyright, terms of use and query limit
|
|
43
|
+
notice; the parser skips both. Current records also renamed their lines:
|
|
44
|
+
`Registrar` replaces `Registrar ID` and `Registrar Name`, `Domain Status`
|
|
45
|
+
(an EPP code followed by its ICANN URL) replaces `Status`, and
|
|
46
|
+
`Registrant Name` and `Tech Name` replace the `<Role> Contact ID/Name/Email`
|
|
47
|
+
lines. The parser reads both layouts: `status` returns the EPP codes,
|
|
48
|
+
`registered?` no longer requires the legacy `Status` line, and the registrar
|
|
49
|
+
and contacts carry a name only, because current records publish no
|
|
50
|
+
registrar ID, contact ID or email.
|
|
51
|
+
- EVIDENCE: On 2026-10-24 single lookups on `whois.registry.qa` (TCP/43)
|
|
52
|
+
returned the registered `google.qa` and `ooredoo.com.qa` responses and the
|
|
53
|
+
`No Data Found` responses for one generated unregistered name in each zone,
|
|
54
|
+
stored unmodified (CRLF preserved) as the new `current_status_registered.txt`,
|
|
55
|
+
`current_com_qa_status_registered.txt`, `current_status_available.txt` and
|
|
56
|
+
`current_com_qa_status_available.txt` fixtures. The registered fixtures hold
|
|
57
|
+
role and organisation names only, no personal data. The 2026-09-19 probes
|
|
58
|
+
that received empty responses no longer reproduce.
|
|
59
|
+
|
|
5
60
|
#### Release 0.4.8 (2026-10-24)
|
|
6
61
|
|
|
7
62
|
- ADDED: Added `registrant_contacts` to the `whois.trabis.gov.tr` (`.tr`)
|
data/lib/whois/parser/version.rb
CHANGED
|
@@ -117,7 +117,7 @@ module Whois
|
|
|
117
117
|
return unless content_for_scanner =~ /#{element}:\s+(.+)\n/
|
|
118
118
|
|
|
119
119
|
id = ::Regexp.last_match(1)
|
|
120
|
-
content_for_scanner.scan(/nic-hdl:\s+#{id}\n((.+\n)+)\n/).any? ||
|
|
120
|
+
content_for_scanner.scan(/nic-hdl:\s+#{Regexp.escape(id)}\n((.+\n)+)\n/).any? ||
|
|
121
121
|
Whois::Parser.bug!(ParserError, "Unable to parse contact block for nic-hdl: #{id}")
|
|
122
122
|
values = build_hash(::Regexp.last_match(1).scan(/(.+?):\s+(.+?)\n/))
|
|
123
123
|
|
|
@@ -62,12 +62,50 @@ module Whois
|
|
|
62
62
|
property_not_supported :expires_on
|
|
63
63
|
|
|
64
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
|
+
|
|
65
83
|
property_supported :nameservers do
|
|
66
84
|
content_for_scanner.scan(/Name Server:\s+(.+)\n/).flatten.map do |name|
|
|
67
85
|
Parser::Nameserver.new(:name => name)
|
|
68
86
|
end
|
|
69
87
|
end
|
|
70
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
|
+
|
|
71
109
|
end
|
|
72
110
|
|
|
73
111
|
end
|
|
@@ -96,7 +96,7 @@ module Whois
|
|
|
96
96
|
return unless content_for_scanner =~ /#{element}:\s+(.+)\n/
|
|
97
97
|
|
|
98
98
|
id = ::Regexp.last_match(1)
|
|
99
|
-
content_for_scanner.scan(/nic-hdl-br:\s+#{id}\n((.+\n)+)\n/).any? ||
|
|
99
|
+
content_for_scanner.scan(/nic-hdl-br:\s+#{Regexp.escape(id)}\n((.+\n)+)\n/).any? ||
|
|
100
100
|
Whois.bug!(ParserError, "Unable to parse contact block for nic-hdl-br: #{id}")
|
|
101
101
|
values = build_hash(::Regexp.last_match(1).scan(/(.+?):\s+(.+?)\n/))
|
|
102
102
|
|
|
@@ -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
|
|
@@ -65,7 +65,7 @@ module Whois
|
|
|
65
65
|
return if contact_ids.empty?
|
|
66
66
|
|
|
67
67
|
contact_ids.map do |contact_id|
|
|
68
|
-
textblock = content.slice(/contact-id:\s+#{contact_id}\n((?:.+\n)+)\n/, 1)
|
|
68
|
+
textblock = content.slice(/contact-id:\s+#{Regexp.escape(contact_id)}\n((?:.+\n)+)\n/, 1)
|
|
69
69
|
|
|
70
70
|
address = textblock.scan(/address:\s+(.+)\n/).flatten
|
|
71
71
|
address = address.reject { |a| a == "n/a" }
|
|
@@ -137,7 +137,7 @@ module Whois
|
|
|
137
137
|
return if contact_ids.empty?
|
|
138
138
|
|
|
139
139
|
contact_ids.map do |contact_id|
|
|
140
|
-
textblock = content.slice(/nic-handle:\s+#{contact_id}\n((?:.+\n)+)\n/, 1)
|
|
140
|
+
textblock = content.slice(/nic-handle:\s+#{Regexp.escape(contact_id)}\n((?:.+\n)+)\n/, 1)
|
|
141
141
|
|
|
142
142
|
address = textblock.scan(/address:\s+(.+)\n/).flatten
|
|
143
143
|
zip = nil
|
data/lib/whois/scanners/base.rb
CHANGED
|
@@ -6,6 +6,9 @@ module Whois
|
|
|
6
6
|
class_attribute :tokenizers
|
|
7
7
|
self.tokenizers = []
|
|
8
8
|
|
|
9
|
+
# Consecutive tokens allowed to succeed without consuming input.
|
|
10
|
+
MAX_IDLE_TOKENS = 100
|
|
11
|
+
|
|
9
12
|
def self.tokenizer(name, &block)
|
|
10
13
|
define_method(name, &block)
|
|
11
14
|
end
|
|
@@ -26,7 +29,21 @@ module Whois
|
|
|
26
29
|
@ast = {}
|
|
27
30
|
|
|
28
31
|
@input = StringScanner.new(content)
|
|
29
|
-
|
|
32
|
+
idle = 0
|
|
33
|
+
until @input.eos?
|
|
34
|
+
position = @input.pos
|
|
35
|
+
tokenize
|
|
36
|
+
if @input.pos == position
|
|
37
|
+
# Some tokenizers succeed once without consuming input to change
|
|
38
|
+
# state (e.g. close a section) for the next one. One that keeps
|
|
39
|
+
# succeeding in place loops forever, e.g. a disclaimer header matched
|
|
40
|
+
# by lookahead in a reply truncated before its final newline.
|
|
41
|
+
idle += 1
|
|
42
|
+
unexpected_token if idle > MAX_IDLE_TOKENS
|
|
43
|
+
else
|
|
44
|
+
idle = 0
|
|
45
|
+
end
|
|
46
|
+
end
|
|
30
47
|
|
|
31
48
|
@ast
|
|
32
49
|
end
|
data/lib/whois/scanners/iana.rb
CHANGED
|
@@ -79,7 +79,9 @@ module Whois
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
def parse_section_pair_newlinevalue(key)
|
|
82
|
-
|
|
82
|
+
# The key comes from the response: match it literally. As a pattern, a
|
|
83
|
+
# key such as "()|^" matches without consuming input and loops forever.
|
|
84
|
+
if @input.scan(/^#{Regexp.escape(key)}:\s*(.+)\n/)
|
|
83
85
|
@input[1].strip
|
|
84
86
|
end
|
|
85
87
|
end
|
|
@@ -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.10
|
|
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
|