whois 4.0.0.pre.beta2 → 4.0.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.
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # An intelligent pure Ruby WHOIS client and parser.
5
5
  #
6
- # Copyright (c) 2009-2015 Simone Carletti <weppos@weppos.net>
6
+ # Copyright (c) 2009-2016 Simone Carletti <weppos@weppos.net>
7
7
  #++
8
8
 
9
9
 
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # An intelligent pure Ruby WHOIS client and parser.
5
5
  #
6
- # Copyright (c) 2009-2015 Simone Carletti <weppos@weppos.net>
6
+ # Copyright (c) 2009-2016 Simone Carletti <weppos@weppos.net>
7
7
  #++
8
8
 
9
9
 
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # An intelligent pure Ruby WHOIS client and parser.
5
5
  #
6
- # Copyright (c) 2009-2015 Simone Carletti <weppos@weppos.net>
6
+ # Copyright (c) 2009-2016 Simone Carletti <weppos@weppos.net>
7
7
  #++
8
8
 
9
9
 
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # An intelligent pure Ruby WHOIS client and parser.
5
5
  #
6
- # Copyright (c) 2009-2015 Simone Carletti <weppos@weppos.net>
6
+ # Copyright (c) 2009-2016 Simone Carletti <weppos@weppos.net>
7
7
  #++
8
8
 
9
9
 
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # An intelligent pure Ruby WHOIS client and parser.
5
5
  #
6
- # Copyright (c) 2009-2015 Simone Carletti <weppos@weppos.net>
6
+ # Copyright (c) 2009-2016 Simone Carletti <weppos@weppos.net>
7
7
  #++
8
8
 
9
9
 
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # An intelligent pure Ruby WHOIS client and parser.
5
5
  #
6
- # Copyright (c) 2009-2015 Simone Carletti <weppos@weppos.net>
6
+ # Copyright (c) 2009-2016 Simone Carletti <weppos@weppos.net>
7
7
  #++
8
8
 
9
9
 
@@ -37,294 +37,347 @@ module Whois
37
37
  autoload :Web, "whois/server/adapters/web"
38
38
  end
39
39
 
40
+ # @return [Array<Symbol>] the definition types
41
+ TYPES = [
42
+ TYPE_TLD = :tld,
43
+ TYPE_IPV4 = :ipv4,
44
+ TYPE_IPV6 = :ipv6,
45
+ TYPE_ASN16 = :asn16,
46
+ TYPE_ASN32 = :asn32,
47
+ ].freeze
48
+
49
+ class << self
50
+
51
+ # Clears the definition and reset them to an empty list.
52
+ #
53
+ # @return [void]
54
+ def clear_definitions
55
+ @definitions = {}
56
+ end
40
57
 
41
- # The WHOIS server definitions.
42
- #
43
- # @return [{ Symbol => Array }] The definition Hash.
44
- # @private
45
- @@definitions = {}
58
+ # Searches the +/definitions+ folder for definition files and loads them.
59
+ # This method is automatically invoked when this file is parsed
60
+ # by the Ruby interpreter (scroll down to the bottom of this file).
61
+ #
62
+ # @return [void]
63
+ def load_definitions
64
+ clear_definitions
65
+ Dir[File.expand_path("../../../data/*.json", __FILE__)].each { |f| load_json(f) }
66
+ end
67
+
68
+ # Loads the definitions from a JSON file.
69
+ #
70
+ # @param [String] file The path to the definition file.
71
+ #
72
+ # @return [void]
73
+ def load_json(file)
74
+ type = File.basename(file, File.extname(file)).to_sym
75
+ JSON.load(File.read(file)).each do |allocation, settings|
76
+ next if allocation == "_"
77
+ settings.reject! { |k, _| k.start_with?("_") }
78
+ define(type, allocation, settings.delete("host"), Hash[settings.map { |k,v| [k.to_sym, v] }])
79
+ end
80
+ end
46
81
 
47
- # Searches the +/definitions+ folder for definition files and loads them.
48
- # This method is automatically invoked when this file is parsed
49
- # by the Ruby interpreter (scroll down to the bottom of this file).
50
- #
51
- # @return [void]
52
- def self.load_definitions
53
- Dir[File.expand_path("../../../data/*.json", __FILE__)].each { |f| load_json(f) }
54
- end
55
82
 
56
- # Loads the definitions from a JSON file.
57
- #
58
- # @param [String] file The path to the definition file.
59
- #
60
- # @return [void]
61
- def self.load_json(file)
62
- type = File.basename(file, File.extname(file)).to_sym
63
- JSON.load(File.read(file)).each do |allocation, settings|
64
- define(type, allocation, settings.delete("host"), Hash[settings.map { |k,v| [k.to_sym, v] }])
83
+ # Lookup and returns the definition list for given `type`.
84
+ #
85
+ # @param [Symbol] type The type of WHOIS server to lookup.
86
+ # See Whois::Server::TYPES for valid types.
87
+ #
88
+ # @return [{ Symbol => Array }]
89
+ # The definition Hash if +type+ is +nil+.
90
+ # @return [Array<Hash>]
91
+ # The definitions for given +type+ if +type+ is not +nil+ and +type+ exists.
92
+ #
93
+ # @example Return the definitions for given key.
94
+ #
95
+ # Whois::Server.definitions(:tld)
96
+ # # => [...]
97
+ #
98
+ # Whois::Server.definitions(:invalid)
99
+ # # => nil
100
+ #
101
+ def definitions(type)
102
+ TYPES.include?(type) or
103
+ raise(ArgumentError, "`#{type}` is not a valid definition type")
104
+
105
+ _definitions(type).values
65
106
  end
66
- end
67
107
 
108
+ # Defines a new server for <tt>:type</tt> queries.
109
+ #
110
+ # @param [Symbol] type
111
+ # The type of WHOIS server to define.
112
+ # Known values are :tld, :ipv4, :ipv6.
113
+ # @param [String] allocation
114
+ # The allocation, range or hostname, this server is responsible for.
115
+ # @param [String, nil] host
116
+ # The server hostname. Use nil if unknown or not available.
117
+ # @param [Hash] options Optional definition properties.
118
+ # @option options [Class] :adapter (Whois::Server::Adapters::Standard)
119
+ # This option has a special meaning and determines the adapter Class to use.
120
+ # Defaults to {Whois::Server::Adapters::Standard} unless specified.
121
+ # All the other options are passed directly to the adapter which can decide how to use them.
122
+ #
123
+ # @return [void]
124
+ #
125
+ # @example
126
+ #
127
+ # # Define a server for the .it extension
128
+ # Whois::Server.define :tld, "it", "whois.nic.it"
129
+ #
130
+ # # Define a new server for an range of IPv4 addresses
131
+ # Whois::Server.define :ipv4, "61.192.0.0/12", "whois.nic.ad.jp"
132
+ #
133
+ # # Define a new server for an range of IPv6 addresses
134
+ # Whois::Server.define :ipv6, "2001:2000::/19", "whois.ripe.net"
135
+ #
136
+ # # Define a new server with a custom adapter
137
+ # Whois::Server.define :tld, "test", nil,
138
+ # :adapter => Whois::Server::Adapter::None
139
+ #
140
+ # # Define a new server with a custom adapter and options
141
+ # Whois::Server.define :tld, "ar", nil,
142
+ # :adapter => Whois::Server::Adapters::Web,
143
+ # :url => "http://www.nic.ar/"
144
+ #
145
+ def define(type, allocation, host, options = {})
146
+ TYPES.include?(type) or
147
+ raise(ArgumentError, "`#{type}` is not a valid definition type")
148
+
149
+ _definitions(type)[allocation] = [allocation, host, options]
150
+ end
68
151
 
69
- # Lookup and returns the definition list for given <tt>type</tt>,
70
- # or all definitions if <tt>type</tt> is <tt>nil</tt>.
71
- #
72
- # @param [Symbol] type The type of WHOIS server to lookup.
73
- # Known values are :tld, :ipv4, :ipv6.
74
- #
75
- # @return [{ Symbol => Array }]
76
- # The definition Hash if +type+ is +nil+.
77
- # @return [Array<Hash>]
78
- # The definitions for given +type+ if +type+ is not +nil+ and +type+ exists.
79
- # @return [nil]
80
- # The definitions for given +type+ if +type+ is not +nil+ and +type+ doesn't exist.
81
- #
82
- # @example Return the definition database.
83
- #
84
- # Whois::Server.definitions
85
- # # => { :tld => [...], :ipv4 => [], ... }
86
- #
87
- # @example Return the definitions for given key.
88
- #
89
- # Whois::Server.definitions(:tld)
90
- # # => [...]
91
- #
92
- # Whois::Server.definitions(:invalid)
93
- # # => nil
94
- #
95
- def self.definitions(type = nil)
96
- if type.nil?
97
- @@definitions
98
- else
99
- @@definitions[type]
152
+ # Creates a new server adapter from given arguments
153
+ # and returns the server instance.
154
+ #
155
+ # By default, returns a new {Whois::Server::Adapters::Standard} instance.
156
+ # You can customize the behavior passing a custom adapter class
157
+ # as <tt>:adapter</tt> option.
158
+ #
159
+ # Whois::Server.factory :tld, "it", "whois.nic.it"
160
+ # # => #<Whois::Servers::Adapter::Standard>
161
+ #
162
+ # Whois::Server.factory :tld, "it", "whois.nic.it",
163
+ # :option => Whois::Servers::Adapter::Custom
164
+ # # => #<Whois::Servers::Adapter::Custom>
165
+ #
166
+ # Please note that any adapter is responsible for a limited set
167
+ # of queries, which should be included in the range of the <tt>allocation</tt> parameter.
168
+ # Use {Whois::Server.guess} if you are not sure which adapter
169
+ # is the right one for a specific string.
170
+ #
171
+ # @param [Symbol] type
172
+ # The type of WHOIS server to define.
173
+ # Known values are :tld, :ipv4, :ipv6.
174
+ # @param [String] allocation
175
+ # The allocation, range or hostname, this server is responsible for.
176
+ # @param [String, nil] host
177
+ # The server hostname. Use nil if unknown or not available.
178
+ # @param [Hash] options Optional definition properties.
179
+ # @option options [Class] :adapter (Whois::Server::Adapters::Standard)
180
+ # This option has a special meaning and determines the adapter Class to use.
181
+ # Defaults to {Whois::Server::Adapters::Standard} unless specified.
182
+ # All the other options are passed directly to the adapter which can decide how to use them.
183
+ #
184
+ # @return [Whois::Server::Adapters::Base]
185
+ # a server adapter that can be used to perform queries.
186
+ def factory(type, allocation, host, options = {})
187
+ options = options.dup
188
+ adapter = options.delete(:adapter) || Adapters::Standard
189
+ adapter = Adapters.const_get(camelize(adapter)) unless adapter.respond_to?(:new)
190
+ adapter.new(type, allocation, host, options)
100
191
  end
101
- end
102
192
 
103
- # Defines a new server for <tt>:type</tt> queries.
104
- #
105
- # @param [Symbol] type
106
- # The type of WHOIS server to define.
107
- # Known values are :tld, :ipv4, :ipv6.
108
- # @param [String] allocation
109
- # The allocation, range or hostname, this server is responsible for.
110
- # @param [String, nil] host
111
- # The server hostname. Use nil if unknown or not available.
112
- # @param [Hash] options Optional definition properties.
113
- # @option options [Class] :adapter (Whois::Server::Adapters::Standard)
114
- # This option has a special meaning and determines the adapter Class to use.
115
- # Defaults to {Whois::Server::Adapters::Standard} unless specified.
116
- # All the other options are passed directly to the adapter which can decide how to use them.
117
- #
118
- # @return [void]
119
- #
120
- # @example
121
- #
122
- # # Define a server for the .it extension
123
- # Whois::Server.define :tld, ".it", "whois.nic.it"
124
- #
125
- # # Define a new server for an range of IPv4 addresses
126
- # Whois::Server.define :ipv4, "61.192.0.0/12", "whois.nic.ad.jp"
127
- #
128
- # # Define a new server for an range of IPv6 addresses
129
- # Whois::Server.define :ipv6, "2001:2000::/19", "whois.ripe.net"
130
- #
131
- # # Define a new server with a custom adapter
132
- # Whois::Server.define :tld, ".test", nil,
133
- # :adapter => Whois::Server::Adapter::None
134
- #
135
- # # Define a new server with a custom adapter and options
136
- # Whois::Server.define :tld, ".ar", nil,
137
- # :adapter => Whois::Server::Adapters::Web,
138
- # :url => "http://www.nic.ar/"
139
- #
140
- def self.define(type, allocation, host, options = {})
141
- @@definitions[type] ||= []
142
- @@definitions[type] << [allocation, host, options]
143
- end
144
193
 
145
- # Creates a new server adapter from given arguments
146
- # and returns the server instance.
147
- #
148
- # By default, returns a new {Whois::Server::Adapters::Standard} instance.
149
- # You can customize the behavior passing a custom adapter class
150
- # as <tt>:adapter</tt> option.
151
- #
152
- # Whois::Server.factory :tld, ".it", "whois.nic.it"
153
- # # => #<Whois::Servers::Adapter::Standard>
154
- #
155
- # Whois::Server.factory :tld, ".it", "whois.nic.it",
156
- # :option => Whois::Servers::Adapter::Custom
157
- # # => #<Whois::Servers::Adapter::Custom>
158
- #
159
- # Please note that any adapter is responsible for a limited set
160
- # of queries, which should be included in the range of the <tt>allocation</tt> parameter.
161
- # Use {Whois::Server.guess} if you are not sure which adapter
162
- # is the right one for a specific string.
163
- #
164
- # @param [Symbol] type
165
- # The type of WHOIS server to define.
166
- # Known values are :tld, :ipv4, :ipv6.
167
- # @param [String] allocation
168
- # The allocation, range or hostname, this server is responsible for.
169
- # @param [String, nil] host
170
- # The server hostname. Use nil if unknown or not available.
171
- # @param [Hash] options Optional definition properties.
172
- # @option options [Class] :adapter (Whois::Server::Adapters::Standard)
173
- # This option has a special meaning and determines the adapter Class to use.
174
- # Defaults to {Whois::Server::Adapters::Standard} unless specified.
175
- # All the other options are passed directly to the adapter which can decide how to use them.
176
- #
177
- # @return [Whois::Server::Adapters::Standard]
178
- # An adapter that can be used to perform queries.
179
- #
180
- def self.factory(type, allocation, host, options = {})
181
- options = options.dup
182
- adapter = options.delete(:adapter) || Adapters::Standard
183
- adapter = Adapters.const_get(camelize(adapter)) unless adapter.respond_to?(:new)
184
- adapter.new(type, allocation, host, options)
185
- end
194
+ # Parses <tt>string</tt> and tries to guess the right server.
195
+ #
196
+ # It successfully detects the following query types:
197
+ # * ipv6
198
+ # * ipv4
199
+ # * top level domains (e.g. .com, .net, .it)
200
+ # * domain names (e.g. google.com, google.net, google.it)
201
+ # * emails
202
+ #
203
+ # Note that not all query types actually have a corresponding adapter.
204
+ # For instance, the following request will result in a
205
+ # {Whois::ServerNotSupported} exception.
206
+ #
207
+ # Whois::Server.guess "mail@example.com"
208
+ #
209
+ #
210
+ # @param string [String]
211
+ # @return [Whois::Server::Adapters::Base]
212
+ # a server adapter that can be used to perform queries.
213
+ #
214
+ # @raise [Whois::AllocationUnknown]
215
+ # when the input is an IP, but the IP doesn't have a specific known allocation
216
+ # that matches one of the existing server definitions.
217
+ # @raise [Whois::ServerNotFound]
218
+ # when unable to find an appropriate WHOIS adapter. In most of the cases, the input
219
+ # is not recognised as one of the supported query types.
220
+ # @raise [Whois::ServerNotSupported]
221
+ # when the string type is detected,
222
+ # but the object type doesn't have any supported WHOIS adapter associated.
223
+ def guess(string)
224
+ # Top Level Domain match
225
+ if matches_tld?(string)
226
+ return factory(:tld, ".", "whois.iana.org")
227
+ end
186
228
 
229
+ # IP address (secure match)
230
+ if matches_ip?(string)
231
+ return find_for_ip(string)
232
+ end
187
233
 
188
- # Parses <tt>string</tt> and tries to guess the right server.
189
- #
190
- # It successfully detects the following query types:
191
- # * ipv6
192
- # * ipv4
193
- # * top level domains (e.g. .com, .net, .it)
194
- # * domain names (e.g. google.com, google.net, google.it)
195
- # * emails
196
- #
197
- # Note that not all query types actually have a corresponding adapter.
198
- # For instance, the following request will result in a
199
- # {Whois::ServerNotSupported} exception.
200
- #
201
- # Whois::Server.guess "mail@example.com"
202
- #
203
- #
204
- # @param [String] string
205
- # @return [Whois::Server::Adapters::Base]
206
- # The adapter that can be used to perform
207
- # WHOIS queries for <tt>string</tt>.
208
- #
209
- # @raise [Whois::ServerNotFound]
210
- # When unable to find an appropriate WHOIS adapter
211
- # for <tt>string</tt>. Most of the cases, the <tt>string</tt>
212
- # haven't been recognised as one of the supported query types.
213
- # @raise [Whois::ServerNotSupported]
214
- # When the <tt>string</tt> type is detected,
215
- # but the object type doesn't have any supported WHOIS adapter associated.
216
- #
217
- def self.guess(string)
218
- # Top Level Domain match
219
- if matches_tld?(string)
220
- return factory(:tld, ".", "whois.iana.org")
221
- end
234
+ # Email Address (secure match)
235
+ if matches_email?(string)
236
+ return find_for_email(string)
237
+ end
222
238
 
223
- # IP address (secure match)
224
- if matches_ip?(string)
225
- return find_for_ip(string)
226
- end
239
+ # Domain Name match
240
+ if (server = find_for_domain(string))
241
+ return server
242
+ end
243
+
244
+ # ASN match
245
+ if matches_asn?(string)
246
+ return find_for_asn(string)
247
+ end
227
248
 
228
- # Email Address (secure match)
229
- if matches_email?(string)
230
- return find_for_email(string)
249
+ # Game Over
250
+ raise ServerNotFound, "Unable to find a WHOIS server for `#{string}'"
231
251
  end
232
252
 
233
- # Domain Name match
234
- if server = find_for_domain(string)
235
- return server
253
+
254
+ # Searches for definition that matches given IP.
255
+ #
256
+ # @param string [String]
257
+ # @return [Whois::Server::Adapters::Base, nil]
258
+ # a server adapter that can be used to perform queries.
259
+ # @raise [Whois::AllocationUnknown]
260
+ # when the IP doesn't have a specific known allocation
261
+ # that matches one of the existing server definitions.
262
+ def find_for_ip(string)
263
+ begin
264
+ ip = IPAddr.new(string)
265
+ type = ip.ipv4? ? TYPE_IPV4 : TYPE_IPV6
266
+ _definitions(type).each do |_, definition|
267
+ if IPAddr.new(definition.first).include?(ip)
268
+ return factory(type, *definition)
269
+ end
270
+ end
271
+ rescue ArgumentError
272
+ # continue
273
+ end
274
+ raise AllocationUnknown, "IP Allocation for `#{string}' unknown"
236
275
  end
237
276
 
238
- # ASN match
239
- if matches_asn?(string)
240
- return find_for_asn(string)
277
+ # Searches for definition that matches given email.
278
+ #
279
+ # @param string [String]
280
+ # @raise [Whois::ServerNotSupported]
281
+ # emails are not supported.
282
+ def find_for_email(string)
283
+ raise ServerNotSupported, "No WHOIS server is known for email objects"
241
284
  end
242
285
 
243
- # Game Over
244
- raise ServerNotFound, "Unable to find a WHOIS server for `#{string}'"
245
- end
286
+ # Searches for definition that matches given domain.
287
+ #
288
+ # @param string [String]
289
+ # @return [Whois::Server::Adapters::Base, nil]
290
+ # a server adapter that can be used to perform queries.
291
+ def find_for_domain(string)
292
+ token = string
293
+ defs = _definitions(TYPE_TLD)
294
+
295
+ while token != "" do
296
+ if (found = defs[token])
297
+ return factory(:tld, *found)
298
+ else
299
+ index = token.index(".")
300
+ break if index.nil?
301
+
302
+ token = token[(index + 1)..-1]
303
+ end
304
+ end
246
305
 
306
+ nil
307
+ end
247
308
 
248
- private
309
+ # Searches for definition that matches given ASN string.
310
+ #
311
+ # @param string [String]
312
+ # @return [Whois::Server::Adapters::Base, nil]
313
+ # a server adapter that can be used to perform queries.
314
+ # @raise [Whois::AllocationUnknown]
315
+ # when the IP doesn't have a specific known allocation
316
+ # that matches one of the existing server definitions.
317
+ def find_for_asn(string)
318
+ asn = string[/\d+/].to_i
319
+ asn_type = asn <= 65535 ? TYPE_ASN16 : TYPE_ASN32
320
+ _definitions(asn_type).each do |_, definition|
321
+ if (range = definition.first.split.map(&:to_i)) && asn >= range.first && asn <= range.last
322
+ return factory(asn_type, *definition)
323
+ end
324
+ end
325
+ raise AllocationUnknown, "Unknown AS number - `#{asn}'."
326
+ end
249
327
 
250
- def self.camelize(string)
251
- string.to_s.split("_").collect(&:capitalize).join
252
- end
253
328
 
329
+ private
254
330
 
255
- def self.matches_tld?(string)
256
- string =~ /^\.(xn--)?[a-z0-9]+$/
257
- end
331
+ def _definitions(type = nil)
332
+ if type.nil?
333
+ @definitions
334
+ else
335
+ @definitions[type] ||= {}
336
+ end
337
+ end
258
338
 
259
- def self.matches_ip?(string)
260
- valid_ipv4?(string) || valid_ipv6?(string)
261
- end
262
339
 
263
- def self.matches_email?(string)
264
- string =~ /@/
265
- end
340
+ def camelize(string)
341
+ string.to_s.split("_").collect(&:capitalize).join
342
+ end
266
343
 
267
- def self.matches_asn?(string)
268
- string =~ /^as\d+$/i
269
- end
344
+ def matches_tld?(string)
345
+ string =~ /^\.(xn--)?[a-z0-9]+$/
346
+ end
270
347
 
271
- def self.find_for_ip(string)
272
- begin
273
- ip = IPAddr.new(string)
274
- type = ip.ipv4? ? :ipv4 : :ipv6
275
- definitions(type).each do |definition|
276
- if IPAddr.new(definition.first).include?(ip)
277
- return factory(type, *definition)
278
- end
279
- end
280
- rescue ArgumentError
281
- # continue
348
+ def matches_ip?(string)
349
+ valid_ipv4?(string) || valid_ipv6?(string)
282
350
  end
283
- raise AllocationUnknown, "IP Allocation for `#{string}' unknown. Server definitions might be outdated."
284
- end
285
351
 
286
- def self.find_for_email(string)
287
- raise ServerNotSupported, "No WHOIS server is known for email objects"
288
- end
352
+ def matches_email?(string)
353
+ string =~ /@/
354
+ end
289
355
 
290
- def self.find_for_domain(string)
291
- definitions(:tld).each do |definition|
292
- return factory(:tld, *definition) if /#{Regexp.escape(definition.first)}$/ =~ string
356
+ def matches_asn?(string)
357
+ string =~ /^as\d+$/i
293
358
  end
294
- nil
295
- end
296
359
 
297
- def self.find_for_asn(string)
298
- asn = string[/\d+/].to_i
299
- asn_type = asn <= 65535 ? :asn16 : :asn32
300
- definitions(asn_type).each do |definition|
301
- if (range = definition.first.split.map(&:to_i)) && asn >= range.first && asn <= range.last
302
- return factory(asn_type, *definition)
360
+
361
+ def valid_ipv4?(addr)
362
+ if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
363
+ return $~.captures.all? {|i| i.to_i < 256}
303
364
  end
365
+ false
304
366
  end
305
- raise AllocationUnknown, "Unknown AS number - `#{asn}'."
306
- end
307
367
 
308
-
309
- def self.valid_ipv4?(addr)
310
- if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
311
- return $~.captures.all? {|i| i.to_i < 256}
368
+ def valid_ipv6?(addr)
369
+ # IPv6 (normal)
370
+ return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
371
+ return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
372
+ return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
373
+ # IPv6 (IPv4 compat)
374
+ return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_ipv4?($')
375
+ return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($')
376
+ return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($')
377
+ false
312
378
  end
313
- false
314
- end
315
379
 
316
- def self.valid_ipv6?(addr)
317
- # IPv6 (normal)
318
- return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
319
- return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
320
- return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
321
- # IPv6 (IPv4 compat)
322
- return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_ipv4?($')
323
- return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($')
324
- return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($')
325
- false
326
380
  end
327
-
328
381
  end
329
382
 
330
383
  end