whois-parser-next 0.3.1 → 0.4.0
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 -6
- data/README.md +20 -15
- data/RELEASING.md +2 -2
- data/lib/whois/parser/version.rb +1 -1
- data/lib/whois/parsers/registry_response_safety.rb +7 -5
- data/lib/whois/parsers/whois.auda.org.au.rb +43 -1
- data/lib/whois/parsers/whois.cctld.uz.rb +6 -0
- data/lib/whois/parsers/whois.eu.rb +33 -7
- data/lib/whois/parsers/whois.id.rb +22 -2
- data/lib/whois/parsers/whois.nic.africa.rb +23 -0
- data/lib/whois/parsers/whois.nic.biz.rb +38 -0
- data/lib/whois/parsers/whois.nic.cl.rb +32 -8
- data/lib/whois/parsers/whois.nic.foundation.rb +4 -0
- data/lib/whois/parsers/whois.nic.ge.rb +6 -0
- data/lib/whois/parsers/whois.nic.gifts.rb +13 -0
- data/lib/whois/parsers/whois.nic.gives.rb +46 -0
- data/lib/whois/parsers/whois.nic.giving.rb +44 -0
- data/lib/whois/parsers/whois.nic.help.rb +12 -0
- data/lib/whois/parsers/whois.nic.homes.rb +12 -0
- data/lib/whois/parsers/whois.nic.icu.rb +11 -1
- data/lib/whois/parsers/whois.nic.lat.rb +15 -1
- data/lib/whois/parsers/whois.nic.love.rb +19 -0
- data/lib/whois/parsers/whois.nic.mn.rb +51 -0
- data/lib/whois/parsers/whois.nic.mx.rb +47 -7
- data/lib/whois/parsers/whois.nic.net.sa.rb +6 -0
- data/lib/whois/parsers/whois.nic.news.rb +2 -0
- data/lib/whois/parsers/whois.nic.one.rb +7 -1
- data/lib/whois/parsers/whois.nic.online.rb +25 -1
- data/lib/whois/parsers/whois.nic.open.rb +95 -0
- data/lib/whois/parsers/whois.nic.quest.rb +17 -0
- data/lib/whois/parsers/whois.nic.sbs.rb +28 -0
- data/lib/whois/parsers/whois.nic.store.rb +28 -0
- data/lib/whois/parsers/whois.nic.top.rb +25 -3
- data/lib/whois/parsers/whois.nic.tv.rb +21 -0
- data/lib/whois/parsers/whois.nic.uk.rb +84 -23
- data/lib/whois/parsers/whois.nic.vip.rb +21 -0
- data/lib/whois/parsers/whois.nic.wiki.rb +21 -0
- data/lib/whois/parsers/whois.nic.win.rb +27 -0
- data/lib/whois/parsers/whois.publicinterestregistry.org.rb +52 -0
- data/lib/whois/parsers/whois.punktum.dk.rb +53 -0
- data/lib/whois/parsers/whois.registry.click.rb +9 -0
- data/lib/whois/parsers/whois.registry.gift.rb +10 -0
- data/lib/whois/parsers/whois.sr.rb +44 -0
- data/lib/whois/parsers/whois.tcinet.ru.rb +83 -8
- data/lib/whois/parsers/whois.tld.mu.rb +20 -0
- data/lib/whois/parsers/whois.tonicregistry.to.rb +55 -1
- data/lib/whois/parsers/whois.uniregistry.net.rb +15 -2
- data/lib/whois/parsers/whois.weare.ie.rb +12 -0
- metadata +16 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require_relative 'base_identity_digital'
|
|
2
|
+
|
|
3
|
+
module Whois
|
|
4
|
+
class Parsers
|
|
5
|
+
# CentralNic's .HOMES registry endpoint uses its exact no-object marker.
|
|
6
|
+
class WhoisNicHomes < BaseIdentityDigital
|
|
7
|
+
self.scanner = Scanners::BaseIcannCompliant, {
|
|
8
|
+
pattern_available: /^The queried object does not exist: DOMAIN NOT FOUND\s*$/i,
|
|
9
|
+
}
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -4,9 +4,19 @@ module Whois
|
|
|
4
4
|
class Parsers
|
|
5
5
|
# Parser for the Identity Digital endpoint serving .ICU.
|
|
6
6
|
class WhoisNicIcu < BaseIcannCompliant
|
|
7
|
+
NO_RECORD_MARKER = /^The queried object does not exist: DOMAIN NOT FOUND\s*$/i
|
|
8
|
+
RECORD_EVIDENCE = /^(?:Domain Name|Registry Domain ID|Creation Date|Registrar|Domain Status|Name Server):[ \t]*\S/i
|
|
9
|
+
|
|
7
10
|
self.scanner = Scanners::BaseIcannCompliant, {
|
|
8
|
-
pattern_available:
|
|
11
|
+
pattern_available: NO_RECORD_MARKER,
|
|
9
12
|
}
|
|
13
|
+
|
|
14
|
+
def response_unavailable?
|
|
15
|
+
body = content_for_scanner
|
|
16
|
+
marker_count = body.lines.count { |line| line.match?(NO_RECORD_MARKER) }
|
|
17
|
+
|
|
18
|
+
super || (marker_count.positive? && (marker_count != 1 || body.match?(RECORD_EVIDENCE)))
|
|
19
|
+
end
|
|
10
20
|
end
|
|
11
21
|
end
|
|
12
22
|
end
|
|
@@ -2,7 +2,21 @@ require_relative 'base_identity_digital'
|
|
|
2
2
|
module Whois
|
|
3
3
|
class Parsers
|
|
4
4
|
class WhoisNicLat < BaseIdentityDigital
|
|
5
|
-
|
|
5
|
+
NO_RECORD_MARKER = /^The queried object does not exist: DOMAIN NOT FOUND\s*$/i
|
|
6
|
+
RECORD_EVIDENCE = /^[ \t]*(?:Domain Name|Registry Domain ID|Creation Date|Registrar|Domain Status|Name Server):[ \t]*\S/i
|
|
7
|
+
|
|
8
|
+
self.scanner = Scanners::BaseIcannCompliant, { pattern_available: NO_RECORD_MARKER }
|
|
9
|
+
|
|
10
|
+
property_supported :available? do
|
|
11
|
+
content_for_scanner.lines.one? { |line| line.match?(NO_RECORD_MARKER) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def response_unavailable?
|
|
15
|
+
body = content_for_scanner
|
|
16
|
+
marker_count = body.lines.count { |line| line.match?(NO_RECORD_MARKER) }
|
|
17
|
+
|
|
18
|
+
super || (marker_count.positive? && (marker_count != 1 || body.match?(RECORD_EVIDENCE)))
|
|
19
|
+
end
|
|
6
20
|
end
|
|
7
21
|
end
|
|
8
22
|
end
|
|
@@ -3,6 +3,25 @@ require_relative 'base_top1000_icann'
|
|
|
3
3
|
module Whois
|
|
4
4
|
class Parsers
|
|
5
5
|
class WhoisNicLove < BaseTop1000Icann
|
|
6
|
+
AVAILABLE_DOMAIN_LINE = /^>>> Domain (?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+love is available for registration[ \t]*$/i
|
|
7
|
+
RECORD_EVIDENCE = /^[ \t]*(?:Domain Name|Registry Domain ID|Creation Date|Registrar|Sponsoring Registrar|Domain Status|Name Server):[ \t]*\S/i
|
|
8
|
+
|
|
9
|
+
property_supported :available? do
|
|
10
|
+
available_marker_count == 1
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def response_unavailable?
|
|
14
|
+
body = content_for_scanner
|
|
15
|
+
marker_count = available_marker_count
|
|
16
|
+
|
|
17
|
+
super || (marker_count.positive? && (marker_count != 1 || body.match?(RECORD_EVIDENCE)))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def available_marker_count
|
|
23
|
+
content_for_scanner.lines.count { |line| line.match?(AVAILABLE_DOMAIN_LINE) }
|
|
24
|
+
end
|
|
6
25
|
end
|
|
7
26
|
end
|
|
8
27
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Ruby Whois
|
|
3
|
+
#
|
|
4
|
+
# An intelligent pure Ruby WHOIS client and parser.
|
|
5
|
+
#++
|
|
6
|
+
|
|
7
|
+
require_relative 'base_icann_compliant'
|
|
8
|
+
|
|
9
|
+
module Whois
|
|
10
|
+
class Parsers
|
|
11
|
+
# Parser for the IANA-listed .mn WHOIS host, whois.nic.mn.
|
|
12
|
+
class WhoisNicMn < BaseIcannCompliant
|
|
13
|
+
self.scanner = Scanners::BaseIcannCompliant, {
|
|
14
|
+
pattern_available: /^[ \t]*Domain not found\.[ \t\r]*$/i,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
property_supported :status do
|
|
18
|
+
classify_status
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
property_supported :available? do
|
|
22
|
+
classify_status == :available
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
property_supported :registered? do
|
|
26
|
+
classify_status == :registered
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def classify_status
|
|
32
|
+
cached_properties_fetch(:classified_status) do
|
|
33
|
+
missing = content_for_scanner.match?(/\A[ \t]*Domain not found\.[ \t\r]*(?:\n|\z)/i)
|
|
34
|
+
denial = content_for_scanner.match?(/^[ \t]*(?:[%#][ \t]*)?(?:access denied|permission denied|request denied|forbidden)\b/i)
|
|
35
|
+
domains = content_for_scanner.scan(/^[ \t]*Domain Name:[ \t]*((?:[a-z0-9-]+\.)+mn)[ \t\r]*$/i).flatten.uniq
|
|
36
|
+
has_registrar = content_for_scanner.match?(/^[ \t]*Registrar:[ \t]*\S/i)
|
|
37
|
+
has_record_detail = content_for_scanner.match?(/^[ \t]*(?:Creation Date|Updated Date|Registry Expiry Date|Name Server):[ \t]*\S/i)
|
|
38
|
+
has_record = domains.one? && has_registrar && has_record_detail
|
|
39
|
+
|
|
40
|
+
if missing && domains.empty? && !denial && !response_throttled?
|
|
41
|
+
:available
|
|
42
|
+
elsif has_record && !missing && !denial && !response_throttled?
|
|
43
|
+
:registered
|
|
44
|
+
else
|
|
45
|
+
:unknown
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -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
|
|
@@ -25,21 +26,22 @@ module Whois
|
|
|
25
26
|
# and examples.
|
|
26
27
|
#
|
|
27
28
|
class WhoisNicMx < Base
|
|
29
|
+
include RegistryResponseSafety
|
|
30
|
+
|
|
31
|
+
DOMAIN_PATTERN = /\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+mx\z/i
|
|
32
|
+
NO_RECORD_MARKER = %r{^[ \t]*No_Se_Encontro_El_Objeto/Object_Not_Found[ \t]*$}i
|
|
33
|
+
RECORD_EVIDENCE = /^[ \t]*(?:Created On|Expiration Date|Registrar|Name Servers|DNS):/i
|
|
28
34
|
|
|
29
35
|
property_supported :status do
|
|
30
|
-
|
|
31
|
-
:available
|
|
32
|
-
else
|
|
33
|
-
:registered
|
|
34
|
-
end
|
|
36
|
+
classify_status
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
property_supported :available? do
|
|
38
|
-
|
|
40
|
+
classify_status == :available
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
property_supported :registered? do
|
|
42
|
-
|
|
44
|
+
classify_status == :registered
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
|
|
@@ -73,6 +75,44 @@ module Whois
|
|
|
73
75
|
end
|
|
74
76
|
end
|
|
75
77
|
|
|
78
|
+
def response_unavailable?
|
|
79
|
+
super || content_for_scanner.match?(
|
|
80
|
+
/^\s*(?:access denied|permission denied|request denied|forbidden)\b/i
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def classify_status
|
|
87
|
+
cached_properties_fetch(:classified_status) do
|
|
88
|
+
response = content_for_scanner
|
|
89
|
+
absent = response.lines.one? { |line| line.match?(NO_RECORD_MARKER) }
|
|
90
|
+
domain_rows = response.scan(/^[ \t]*Domain Name:[ \t]*(.*?)[ \t\r]*$/i).flatten
|
|
91
|
+
domains = domain_rows.grep(DOMAIN_PATTERN)
|
|
92
|
+
has_created = response.match?(/^[ \t]*Created On:[ \t]*\S/i)
|
|
93
|
+
has_expires = response.match?(/^[ \t]*Expiration Date:[ \t]*\S/i)
|
|
94
|
+
has_registrar = response.match?(/^[ \t]*Registrar:[ \t]*\S/i)
|
|
95
|
+
registered = [
|
|
96
|
+
domain_rows.one?,
|
|
97
|
+
domains.one?,
|
|
98
|
+
has_created,
|
|
99
|
+
has_expires,
|
|
100
|
+
has_registrar,
|
|
101
|
+
!response_unavailable?,
|
|
102
|
+
!response_throttled?,
|
|
103
|
+
].all?
|
|
104
|
+
has_record_evidence = response.match?(RECORD_EVIDENCE)
|
|
105
|
+
|
|
106
|
+
if absent && domain_rows.empty? && !has_record_evidence && !response_unavailable? && !response_throttled?
|
|
107
|
+
:available
|
|
108
|
+
elsif registered && !absent
|
|
109
|
+
:registered
|
|
110
|
+
else
|
|
111
|
+
:unknown
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
76
116
|
end
|
|
77
117
|
|
|
78
118
|
end
|
|
@@ -65,6 +65,12 @@ module Whois
|
|
|
65
65
|
private
|
|
66
66
|
|
|
67
67
|
def classify_status
|
|
68
|
+
cached_properties_fetch(:classified_status) do
|
|
69
|
+
classify_status_from_content
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def classify_status_from_content
|
|
68
74
|
registered = registered_domain_names
|
|
69
75
|
absent = absence_domain_names
|
|
70
76
|
|
|
@@ -2,6 +2,8 @@ require_relative 'base_unsupported_registry'
|
|
|
2
2
|
|
|
3
3
|
module Whois
|
|
4
4
|
class Parsers
|
|
5
|
+
# IANA currently lists RDAP only for .news. Keep this adapter only so a
|
|
6
|
+
# legacy upstream WHOIS route fails explicitly instead of claiming status.
|
|
5
7
|
class WhoisNicNews < BaseUnsupportedRegistry
|
|
6
8
|
end
|
|
7
9
|
end
|
|
@@ -8,7 +8,9 @@ module Whois
|
|
|
8
8
|
# footer. Keep that exact evidence separate from an unparseable response.
|
|
9
9
|
class WhoisNicOne < BaseIcannCompliant
|
|
10
10
|
property_supported :status do
|
|
11
|
-
if
|
|
11
|
+
if contradictory_registration_and_absence?
|
|
12
|
+
:unknown
|
|
13
|
+
elsif no_data_found?
|
|
12
14
|
:available
|
|
13
15
|
elsif registered_response?
|
|
14
16
|
:registered
|
|
@@ -31,6 +33,10 @@ module Whois
|
|
|
31
33
|
content_for_scanner.lines.first.to_s.strip.casecmp?("No Data Found")
|
|
32
34
|
end
|
|
33
35
|
|
|
36
|
+
def contradictory_registration_and_absence?
|
|
37
|
+
no_data_found? && registered_response?
|
|
38
|
+
end
|
|
39
|
+
|
|
34
40
|
def registered_response?
|
|
35
41
|
content_for_scanner.match?(/^Domain Name:\s+\S+/) &&
|
|
36
42
|
content_for_scanner.match?(/^Domain Status:\s+\S+/)
|
|
@@ -12,8 +12,25 @@ module Whois
|
|
|
12
12
|
|
|
13
13
|
self.scanner = Scanners::BaseIcannCompliant
|
|
14
14
|
|
|
15
|
+
property_supported :status do
|
|
16
|
+
if contradictory_registration_and_absence?
|
|
17
|
+
:unknown
|
|
18
|
+
elsif available?
|
|
19
|
+
:available
|
|
20
|
+
elsif node('Domain Name').to_s.strip.empty? ||
|
|
21
|
+
[node('Registry Domain ID'), node('Creation Date'), node('Registrar')].all? { |value| value.to_s.strip.empty? }
|
|
22
|
+
:unknown
|
|
23
|
+
else
|
|
24
|
+
:registered
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
15
28
|
property_supported :available? do
|
|
16
|
-
!!(content_for_scanner =~ /^>>> Domain \S+ is available for registration\s*$/i)
|
|
29
|
+
!contradictory_registration_and_absence? && !!(content_for_scanner =~ /^>>> Domain \S+ is available for registration\s*$/i)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
property_supported :registered? do
|
|
33
|
+
status == :registered
|
|
17
34
|
end
|
|
18
35
|
|
|
19
36
|
# Radix can answer with a limit or client-denial notice that contains no
|
|
@@ -33,6 +50,13 @@ module Whois
|
|
|
33
50
|
node("Registry Expiry Date") { |value| parse_time(value) }
|
|
34
51
|
end
|
|
35
52
|
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def contradictory_registration_and_absence?
|
|
56
|
+
!node('Domain Name').to_s.strip.empty? &&
|
|
57
|
+
content_for_scanner.match?(/^>>> Domain \S+ is available for registration\s*$/i)
|
|
58
|
+
end
|
|
59
|
+
|
|
36
60
|
end
|
|
37
61
|
|
|
38
62
|
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require_relative 'base_icann_compliant'
|
|
2
|
+
|
|
3
|
+
module Whois
|
|
4
|
+
class Parsers
|
|
5
|
+
# Conservative parser for American Express's restricted .OPEN registry.
|
|
6
|
+
# The registry's exact "No Data Found" response is not public availability:
|
|
7
|
+
# the published policy restricts registrations to the operator, qualified
|
|
8
|
+
# affiliates, and trademark licensees.
|
|
9
|
+
class WhoisNicOpen < BaseIcannCompliant
|
|
10
|
+
DOMAIN_PATTERN = /\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+open\z/i
|
|
11
|
+
DOMAIN_ID_PATTERN = /\AD\d+-OPEN\z/i
|
|
12
|
+
NO_DATA_MARKER = /^[ \t]*No Data Found[ \t]*$/i
|
|
13
|
+
RECORD_FIELD = /^[ \t]*(?:Domain Name|Registry Domain ID|Updated Date|Creation Date|Registry Expiry Date|Registrar|Registrar IANA ID|Domain Status|Name Server|DNSSEC):[ \t]*\S/i
|
|
14
|
+
REGISTRAR_NAME = /\AAmerican Express Travel Related Services, Inc\.\z/i
|
|
15
|
+
NAMESERVER_PATTERN = /\A(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.?\z/i
|
|
16
|
+
|
|
17
|
+
property_supported :domain do
|
|
18
|
+
values = field_values('Domain Name')
|
|
19
|
+
values.one? && values.first.match?(DOMAIN_PATTERN) ? values.first.downcase : nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
property_supported :domain_id do
|
|
23
|
+
values = field_values('Registry Domain ID')
|
|
24
|
+
values.one? && values.first.match?(DOMAIN_ID_PATTERN) ? values.first : nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
property_supported :status do
|
|
28
|
+
classify_status
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
property_supported :available? do
|
|
32
|
+
classify_status == :available
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
property_supported :registered? do
|
|
36
|
+
classify_status == :registered
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
property_supported :expires_on do
|
|
40
|
+
values = field_values('Registry Expiry Date')
|
|
41
|
+
parse_time(values.first) if values.one?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def response_unavailable?
|
|
45
|
+
body = content_for_scanner
|
|
46
|
+
marker_count = body.lines.count { |line| line.match?(NO_DATA_MARKER) }
|
|
47
|
+
|
|
48
|
+
super || (marker_count.positive? && (marker_count != 1 || body.match?(RECORD_FIELD)))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def classify_status
|
|
54
|
+
cached_properties_fetch(:classified_status) do
|
|
55
|
+
complete_registered_record? ? :registered : :unknown
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def complete_registered_record?
|
|
60
|
+
domains = field_values('Domain Name')
|
|
61
|
+
ids = field_values('Registry Domain ID')
|
|
62
|
+
registrars = field_values('Registrar')
|
|
63
|
+
registrar_ids = field_values('Registrar IANA ID')
|
|
64
|
+
|
|
65
|
+
domains.one? && domains.first.match?(DOMAIN_PATTERN) &&
|
|
66
|
+
ids.one? && ids.first.match?(DOMAIN_ID_PATTERN) &&
|
|
67
|
+
registrars.one? && registrars.first.match?(REGISTRAR_NAME) &&
|
|
68
|
+
registrar_ids == ['9999'] &&
|
|
69
|
+
valid_date_field?('Creation Date') &&
|
|
70
|
+
valid_date_field?('Updated Date') &&
|
|
71
|
+
valid_date_field?('Registry Expiry Date') &&
|
|
72
|
+
valid_nameservers? && field_values('Domain Status').any?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def valid_date_field?(name)
|
|
76
|
+
values = field_values(name)
|
|
77
|
+
return false unless values.one?
|
|
78
|
+
|
|
79
|
+
Time.iso8601(values.first)
|
|
80
|
+
true
|
|
81
|
+
rescue ArgumentError
|
|
82
|
+
false
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def valid_nameservers?
|
|
86
|
+
values = field_values('Name Server')
|
|
87
|
+
values.any? && values.all? { |value| value.match?(NAMESERVER_PATTERN) }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def field_values(name)
|
|
91
|
+
content_for_scanner.scan(/^[ \t]*#{Regexp.escape(name)}:[ \t]*(.*?)[ \t\r]*$/i).flatten
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -3,6 +3,23 @@ require_relative 'whois.centralnic.com'
|
|
|
3
3
|
module Whois
|
|
4
4
|
class Parsers
|
|
5
5
|
class WhoisNicQuest < WhoisCentralnicCom
|
|
6
|
+
property_supported :available? do
|
|
7
|
+
!contradictory_registration_and_absence? && !!node('status:available')
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
property_supported :registered? do
|
|
11
|
+
!contradictory_registration_and_absence? &&
|
|
12
|
+
!available? && !Array.wrap(node('Status') || node('Domain Status')).empty? &&
|
|
13
|
+
!node('Domain Name').to_s.strip.empty? &&
|
|
14
|
+
Array.wrap(node('Status') || node('Domain Status')).none? { |value| value.to_s.strip.casecmp('unknown').zero? }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def contradictory_registration_and_absence?
|
|
20
|
+
!node('Domain Name').to_s.strip.empty? &&
|
|
21
|
+
content_for_scanner.match?(/^(?:DOMAIN NOT FOUND|The queried object does not exist: DOMAIN NOT FOUND|>>> Domain \S+ is available for registration)\s*$/i)
|
|
22
|
+
end
|
|
6
23
|
end
|
|
7
24
|
end
|
|
8
25
|
end
|
|
@@ -3,6 +3,34 @@ module Whois
|
|
|
3
3
|
class Parsers
|
|
4
4
|
class WhoisNicSbs < BaseIdentityDigital
|
|
5
5
|
self.scanner = Scanners::BaseIcannCompliant, { pattern_available: /^The queried object does not exist: DOMAIN NOT FOUND\s*$/i }
|
|
6
|
+
|
|
7
|
+
property_supported :status do
|
|
8
|
+
if contradictory_registration_and_absence?
|
|
9
|
+
:unknown
|
|
10
|
+
elsif available?
|
|
11
|
+
:available
|
|
12
|
+
elsif node('Domain Name').to_s.strip.empty? ||
|
|
13
|
+
[node('Registry Domain ID'), node('Creation Date'), node('Registrar')].all? { |value| value.to_s.strip.empty? }
|
|
14
|
+
:unknown
|
|
15
|
+
else
|
|
16
|
+
:registered
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
property_supported :available? do
|
|
21
|
+
!contradictory_registration_and_absence? && !!node('status:available')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
property_supported :registered? do
|
|
25
|
+
status == :registered
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def contradictory_registration_and_absence?
|
|
31
|
+
!node('Domain Name').to_s.strip.empty? &&
|
|
32
|
+
content_for_scanner.match?(/^The queried object does not exist: DOMAIN NOT FOUND\s*$/i)
|
|
33
|
+
end
|
|
6
34
|
end
|
|
7
35
|
end
|
|
8
36
|
end
|
|
@@ -10,6 +10,34 @@ module Whois
|
|
|
10
10
|
self.scanner = Scanners::BaseIcannCompliant, {
|
|
11
11
|
pattern_available: /^>>> Domain \S+ is available for registration\s*$/i,
|
|
12
12
|
}
|
|
13
|
+
|
|
14
|
+
property_supported :status do
|
|
15
|
+
if contradictory_registration_and_absence?
|
|
16
|
+
:unknown
|
|
17
|
+
elsif available?
|
|
18
|
+
:available
|
|
19
|
+
elsif node('Domain Name').to_s.strip.empty? ||
|
|
20
|
+
[node('Registry Domain ID'), node('Creation Date'), node('Registrar')].all? { |value| value.to_s.strip.empty? }
|
|
21
|
+
:unknown
|
|
22
|
+
else
|
|
23
|
+
:registered
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
property_supported :available? do
|
|
28
|
+
!contradictory_registration_and_absence? && !!node('status:available')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
property_supported :registered? do
|
|
32
|
+
status == :registered
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def contradictory_registration_and_absence?
|
|
38
|
+
!node('Domain Name').to_s.strip.empty? &&
|
|
39
|
+
content_for_scanner.match?(/^>>> Domain \S+ is available for registration\s*$/i)
|
|
40
|
+
end
|
|
13
41
|
end
|
|
14
42
|
end
|
|
15
43
|
end
|
|
@@ -5,10 +5,32 @@ module Whois
|
|
|
5
5
|
# The .top registry uses the shared ICANN record shape and returns the full
|
|
6
6
|
# queried .top name in its exact no-record response.
|
|
7
7
|
class WhoisNicTop < BaseTop1000Icann
|
|
8
|
+
RECORD_EVIDENCE = /^(?:Domain Name|Registry Domain ID|Creation Date|Registrar|Domain Status):[ \t]*\S/i
|
|
9
|
+
NO_RECORD_MARKER = /
|
|
10
|
+
^The\ queried\ object\ does\ not\ exist:\s+
|
|
11
|
+
(?:DOMAIN\ NOT\ FOUND|no\ matching\ objects\ found|
|
|
12
|
+
(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\.)*
|
|
13
|
+
[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\.top)
|
|
14
|
+
\s*$
|
|
15
|
+
/ix
|
|
16
|
+
|
|
8
17
|
property_supported :available? do
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
raise ResponseIsThrottled if response_throttled?
|
|
19
|
+
raise ResponseIsUnavailable if response_unavailable?
|
|
20
|
+
|
|
21
|
+
super() || content_for_scanner.match?(NO_RECORD_MARKER)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def response_unavailable?
|
|
25
|
+
body = content_for_scanner
|
|
26
|
+
ambiguous_absence = body.match?(NO_RECORD_MARKER) && body.match?(RECORD_EVIDENCE)
|
|
27
|
+
|
|
28
|
+
super || body.match?(/^\s*access denied\b/i) ||
|
|
29
|
+
ambiguous_absence
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def response_throttled?
|
|
33
|
+
super || content_for_scanner.match?(/^\s*too many requests\b/i)
|
|
12
34
|
end
|
|
13
35
|
end
|
|
14
36
|
end
|
|
@@ -7,9 +7,30 @@ module Whois
|
|
|
7
7
|
class WhoisNicTv < BaseIcannCompliant
|
|
8
8
|
include RegistryResponseSafety
|
|
9
9
|
|
|
10
|
+
RECORD_EVIDENCE = /^(?:Domain Name|Registry Domain ID|Creation Date|Registrar|Domain Status):[ \t]*\S/i
|
|
11
|
+
|
|
10
12
|
self.scanner = Scanners::BaseIcannCompliant, {
|
|
11
13
|
pattern_available: /^No Data Found\n/,
|
|
12
14
|
}
|
|
15
|
+
|
|
16
|
+
property_supported :available? do
|
|
17
|
+
raise ResponseIsThrottled if response_throttled?
|
|
18
|
+
raise ResponseIsUnavailable if response_unavailable?
|
|
19
|
+
|
|
20
|
+
super()
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def response_unavailable?
|
|
24
|
+
body = content_for_scanner
|
|
25
|
+
ambiguous_absence = body.match?(/^No Data Found\s*$/i) && body.match?(RECORD_EVIDENCE)
|
|
26
|
+
|
|
27
|
+
super || body.match?(/^\s*access denied\b/i) ||
|
|
28
|
+
ambiguous_absence
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def response_throttled?
|
|
32
|
+
super || content_for_scanner.match?(/^\s*too many requests\b/i)
|
|
33
|
+
end
|
|
13
34
|
end
|
|
14
35
|
end
|
|
15
36
|
end
|