async-dns 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.travis.yml +18 -0
  4. data/Gemfile +14 -0
  5. data/README.md +144 -0
  6. data/Rakefile +32 -0
  7. data/async-dns.gemspec +31 -0
  8. data/lib/async/dns.rb +36 -0
  9. data/lib/async/dns/chunked.rb +34 -0
  10. data/lib/async/dns/extensions/resolv.rb +136 -0
  11. data/lib/async/dns/extensions/string.rb +28 -0
  12. data/lib/async/dns/handler.rb +229 -0
  13. data/lib/async/dns/logger.rb +31 -0
  14. data/lib/async/dns/message.rb +75 -0
  15. data/lib/async/dns/replace.rb +54 -0
  16. data/lib/async/dns/resolver.rb +280 -0
  17. data/lib/async/dns/server.rb +154 -0
  18. data/lib/async/dns/system.rb +146 -0
  19. data/lib/async/dns/transaction.rb +202 -0
  20. data/lib/async/dns/transport.rb +78 -0
  21. data/lib/async/dns/version.rb +25 -0
  22. data/spec/async/dns/handler_spec.rb +58 -0
  23. data/spec/async/dns/hosts.txt +2 -0
  24. data/spec/async/dns/ipv6_spec.rb +78 -0
  25. data/spec/async/dns/message_spec.rb +56 -0
  26. data/spec/async/dns/origin_spec.rb +106 -0
  27. data/spec/async/dns/replace_spec.rb +44 -0
  28. data/spec/async/dns/resolver_performance_spec.rb +110 -0
  29. data/spec/async/dns/resolver_spec.rb +151 -0
  30. data/spec/async/dns/server/bind9/generate-local.rb +25 -0
  31. data/spec/async/dns/server/bind9/local.zone +5014 -0
  32. data/spec/async/dns/server/bind9/named.conf +14 -0
  33. data/spec/async/dns/server/bind9/named.run +0 -0
  34. data/spec/async/dns/server/million.rb +85 -0
  35. data/spec/async/dns/server_performance_spec.rb +138 -0
  36. data/spec/async/dns/slow_server_spec.rb +97 -0
  37. data/spec/async/dns/socket_spec.rb +70 -0
  38. data/spec/async/dns/system_spec.rb +57 -0
  39. data/spec/async/dns/transaction_spec.rb +140 -0
  40. data/spec/async/dns/truncation_spec.rb +61 -0
  41. data/spec/spec_helper.rb +60 -0
  42. metadata +175 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0d06ded8c25acd3d3b198a45c291b47f70d26667
4
+ data.tar.gz: 708bfec661ce872d73137142ad338198f548a750
5
+ SHA512:
6
+ metadata.gz: 33fe2f59bf6907af89631f9b6bf13e05e8312df6d1d197cc2b530edc2e7d9a29581988830494127aaef4d95f5cdf775511e828b55c210ab07d8a6337c1281fba
7
+ data.tar.gz: b61ea4a2c6a9a336690165dbaa6e2789aaaccc53fb78e65e0dabc8a8ea99221b3c7d137020fac0f0e4dbe768f832fb5ab0ae72b9e9578989b3818107beba5c5c
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --format documentation
3
+ --warnings
4
+ --require spec_helper
@@ -0,0 +1,18 @@
1
+ language: ruby
2
+ sudo: false
3
+ addons:
4
+ apt:
5
+ packages:
6
+ - bind9
7
+ rvm:
8
+ - 2.2.6
9
+ - 2.3.3
10
+ - 2.4.0
11
+ - rbx-2
12
+ - ruby-head
13
+ - jruby-head
14
+ matrix:
15
+ allow_failures:
16
+ - rvm: rbx-2
17
+ - rvm: ruby-head
18
+ - rvm: jruby-head
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem "async"
6
+
7
+ group :development do
8
+ gem "pry"
9
+ end
10
+
11
+ group :test do
12
+ gem 'simplecov'
13
+ gem 'coveralls', require: false
14
+ end
@@ -0,0 +1,144 @@
1
+ # Async::DNS
2
+
3
+ Async::DNS is a high-performance DNS client resolver and server which can be easily integrated into other projects or used as a stand-alone daemon. It was forked from [RubyDNS] which is now implemented in terms of this library.
4
+
5
+ [RubyDNS]: https://github.com/ioquatix/rubydns
6
+
7
+ [![Build Status](https://secure.travis-ci.org/socketry/async-dns.svg)](http://travis-ci.org/socketry/async-dns)
8
+ [![Code Climate](https://codeclimate.com/github/socketry/async-dns.svg)](https://codeclimate.com/github/socketry/async-dns)
9
+ [![Coverage Status](https://coveralls.io/repos/socketry/async-dns/badge.svg)](https://coveralls.io/r/socketry/async-dns)
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'async-dns'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install async-dns
24
+
25
+ ## Usage
26
+
27
+ ### Resolver
28
+
29
+ Here is a simple example showing how to use the resolver:
30
+
31
+ Async::Reactor.run do
32
+ resolver = Async::DNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
33
+
34
+ addresses = resolver.addresses_for("www.google.com.")
35
+
36
+ puts addresses.inspect
37
+ end
38
+ => [#<Resolv::IPv4 202.124.127.240>, #<Resolv::IPv4 202.124.127.216>, #<Resolv::IPv4 202.124.127.223>, #<Resolv::IPv4 202.124.127.227>, #<Resolv::IPv4 202.124.127.234>, #<Resolv::IPv4 202.124.127.230>, #<Resolv::IPv4 202.124.127.208>, #<Resolv::IPv4 202.124.127.249>, #<Resolv::IPv4 202.124.127.219>, #<Resolv::IPv4 202.124.127.218>, #<Resolv::IPv4 202.124.127.212>, #<Resolv::IPv4 202.124.127.241>, #<Resolv::IPv4 202.124.127.238>, #<Resolv::IPv4 202.124.127.245>, #<Resolv::IPv4 202.124.127.251>, #<Resolv::IPv4 202.124.127.229>]
39
+
40
+ ### Server
41
+
42
+ Here is a simple example showing how to use the server:
43
+
44
+ require 'async/dns'
45
+
46
+ class TestServer < Async::DNS::Server
47
+ def process(name, resource_class, transaction)
48
+ @resolver ||= Async::DNS::Resolver.new([[:udp, '8.8.8.8', 53], [:tcp, '8.8.8.8', 53]])
49
+
50
+ transaction.passthrough!(@resolver)
51
+ end
52
+ end
53
+
54
+ server = TestServer.new(listen: [[:udp, '127.0.0.1', 2346]])
55
+
56
+ server.run
57
+
58
+ Then to test you could use `dig` like so:
59
+
60
+ dig @localhost -p 2346 google.com
61
+
62
+ ## FAQ
63
+
64
+ ### File Handle Limitations
65
+
66
+ I get the error `Errno::EMFILE: Too many open files - socket(2) - udp` when trying to run a server. What should I do?
67
+
68
+ On some platforms (e.g. Mac OS X) the number of file descriptors is relatively low by default and should be increased by calling `ulimit -n 10000` before running tests or even before starting a server which expects a large number of concurrent incoming connections.
69
+
70
+ ### Server
71
+
72
+ The performance is on the same magnitude as `bind9`. Some basic benchmarks resolving 1000 names concurrently, repeated 5 times, using `Async::DNS::Resolver` gives the following:
73
+
74
+ user system total real
75
+ Async::DNS::Server 4.280000 0.450000 4.730000 ( 4.854862)
76
+ Bind9 4.970000 0.520000 5.490000 ( 5.541213)
77
+
78
+ These benchmarks are included in the unit tests. To test bind9 performance, it must be installed and `which named` must return the executable.
79
+
80
+
81
+ ## Performance
82
+
83
+ We welcome additional benchmarks and feedback regarding Async::DNS performance. To check the current performance results, consult the [travis build job output](https://travis-ci.org/socketry/async-dns).
84
+
85
+ ### Resolver
86
+
87
+ The `Async::DNS::Resolver` is highly concurrent and can resolve individual names as fast as the built in `Resolv::DNS` resolver. Because the resolver is asynchronous, when dealing with multiple names, it can work more efficiently:
88
+
89
+ user system total real
90
+ Async::DNS::Resolver 0.020000 0.010000 0.030000 ( 0.030507)
91
+ Resolv::DNS 0.070000 0.010000 0.080000 ( 1.465975)
92
+
93
+ These benchmarks are included in the unit tests.
94
+
95
+ ### Server
96
+
97
+ The performance is on the same magnitude as `bind9`. Some basic benchmarks resolving 1000 names concurrently, repeated 5 times, using `Async::DNS::Resolver` gives the following:
98
+
99
+ user system total real
100
+ Async::DNS::Server 4.280000 0.450000 4.730000 ( 4.854862)
101
+ Bind9 4.970000 0.520000 5.490000 ( 5.541213)
102
+
103
+ These benchmarks are included in the unit tests. To test bind9 performance, it must be installed and `which named` must return the executable.
104
+
105
+ ### DNSSEC support
106
+
107
+ DNSSEC is currently not supported and is [unlikely to be supported in the future](http://sockpuppet.org/blog/2015/01/15/against-dnssec/). Feel free to submit a PR.
108
+
109
+ ## Contributing
110
+
111
+ 1. Fork it
112
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
113
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
114
+ 4. Push to the branch (`git push origin my-new-feature`)
115
+ 5. Create new Pull Request
116
+
117
+ ### Desired Features
118
+
119
+ * Support for more features of DNS such as zone transfer.
120
+ * Some kind of system level integration, e.g. registering a DNS server with the currently running system resolver.
121
+
122
+ ## License
123
+
124
+ Released under the MIT license.
125
+
126
+ Copyright, 2015, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
127
+
128
+ Permission is hereby granted, free of charge, to any person obtaining a copy
129
+ of this software and associated documentation files (the "Software"), to deal
130
+ in the Software without restriction, including without limitation the rights
131
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
132
+ copies of the Software, and to permit persons to whom the Software is
133
+ furnished to do so, subject to the following conditions:
134
+
135
+ The above copyright notice and this permission notice shall be included in
136
+ all copies or substantial portions of the Software.
137
+
138
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
139
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
140
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
141
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
142
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
143
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
144
+ THE SOFTWARE.
@@ -0,0 +1,32 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:test)
5
+
6
+ task :default => :test
7
+
8
+ task :console do
9
+ require 'pry'
10
+
11
+ require_relative 'lib/async/dns'
12
+
13
+ Pry.start
14
+ end
15
+
16
+ task :server do
17
+ require_relative 'lib/async/dns'
18
+
19
+ class TestServer < Async::DNS::Server
20
+ def process(name, resource_class, transaction)
21
+ @resolver ||= Async::DNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
22
+
23
+ transaction.passthrough!(@resolver)
24
+ end
25
+ end
26
+
27
+ server = TestServer.new(listen: [[:udp, '127.0.0.1', 2346]])
28
+
29
+ Async::Reactor.run do
30
+ server.run
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require_relative 'lib/async/dns/version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "async-dns"
6
+ spec.version = Async::DNS::VERSION
7
+ spec.authors = ["Samuel Williams"]
8
+ spec.email = ["samuel.williams@oriontransfer.co.nz"]
9
+ spec.description = <<-EOF
10
+ Async::DNS provides a high-performance DNS client resolver and server
11
+ which can be easily integrated into other projects or used as a stand-alone
12
+ daemon.
13
+ EOF
14
+ spec.summary = "An easy to use DNS client resolver and server for Ruby."
15
+ spec.homepage = "https://github.com/async/async-dns"
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+ spec.has_rdoc = "yard"
23
+
24
+ spec.add_dependency("async", "~> 0.10")
25
+
26
+ spec.add_development_dependency "process-daemon", "~> 1.0.0"
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.3"
29
+ spec.add_development_dependency "rspec", "~> 3.4.0"
30
+ spec.add_development_dependency "rake"
31
+ end
@@ -0,0 +1,36 @@
1
+ # Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'async'
22
+ require 'async/tcp_socket'
23
+ require 'async/udp_socket'
24
+
25
+ require_relative 'dns/version'
26
+
27
+ require_relative 'dns/message'
28
+ require_relative 'dns/server'
29
+ require_relative 'dns/resolver'
30
+ require_relative 'dns/handler'
31
+ require_relative 'dns/logger'
32
+
33
+ module Async
34
+ module DNS
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ # Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module Async::DNS
22
+ # Produces an array of arrays of binary data with each sub-array a maximum of chunk_size bytes.
23
+ def self.chunked(string, chunk_size = 255)
24
+ chunks = []
25
+
26
+ offset = 0
27
+ while offset < string.bytesize
28
+ chunks << string.byteslice(offset, chunk_size)
29
+ offset += chunk_size
30
+ end
31
+
32
+ return chunks
33
+ end
34
+ end
@@ -0,0 +1,136 @@
1
+ # Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'resolv'
22
+
23
+ class Resolv
24
+ class DNS
25
+ class Message
26
+ # Merge the given message with this message. A number of heuristics are applied in order to ensure that the result makes sense. For example, If the current message is not recursive but is being merged with a message that was recursive, this bit is maintained. If either message is authoritive, then the result is also authoritive.
27
+ #
28
+ # Modifies the current message in place.
29
+ def merge! (other)
30
+ # Authoritive Answer
31
+ @aa = @aa && other.aa
32
+
33
+ @question += other.question
34
+ @answer += other.answer
35
+ @authority += other.authority
36
+ @additional += other.additional
37
+
38
+ # Recursion Available
39
+ @ra = @ra || other.ra
40
+
41
+ # Result Code (Error Code)
42
+ @rcode = other.rcode unless other.rcode == 0
43
+
44
+ # Recursion Desired
45
+ @rd = @rd || other.rd
46
+ end
47
+ end
48
+
49
+ class OriginError < ArgumentError
50
+ end
51
+
52
+ class Name
53
+ def to_s
54
+ "#{@labels.join('.')}#{@absolute ? '.' : ''}"
55
+ end
56
+
57
+ def inspect
58
+ "#<#{self.class}: #{self.to_s}>"
59
+ end
60
+
61
+ # Return the name, typically absolute, with the specified origin as a suffix. If the origin is nil, don't change the name, but change it to absolute (as specified).
62
+ def with_origin(origin, absolute = true)
63
+ return self.class.new(@labels, absolute) if origin == nil
64
+
65
+ origin = Label.split(origin) if String === origin
66
+
67
+ return self.class.new(@labels + origin, absolute)
68
+ end
69
+
70
+
71
+ # Return the name, typically relative, without the specified origin suffix. If the origin is nil, don't change the name, but change it to absolute (as specified).
72
+ def without_origin(origin, absolute = false)
73
+ return self.class.new(@labels, absolute) if origin == nil
74
+
75
+ origin = Label.split(origin) if String === origin
76
+
77
+ if @labels.last(origin.length) == origin
78
+ return self.class.new(@labels.first(@labels.length - origin.length), absolute)
79
+ else
80
+ raise OriginError.new("#{self} does not end with #{origin.join('.')}")
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ if RUBY_VERSION == "2.3.0"
87
+ # Clearly, the Ruby 2.3.0 release was throughly tested.
88
+ class IPv6
89
+ def self.create(arg)
90
+ case arg
91
+ when IPv6
92
+ return arg
93
+ when String
94
+ address = ''.b
95
+ if Regex_8Hex =~ arg
96
+ arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
97
+ elsif Regex_CompressedHex =~ arg
98
+ prefix = $1
99
+ suffix = $2
100
+ a1 = ''.b
101
+ a2 = ''.b
102
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
103
+ suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
104
+ omitlen = 16 - a1.length - a2.length
105
+ address << a1 << "\0" * omitlen << a2
106
+ elsif Regex_6Hex4Dec =~ arg
107
+ prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i
108
+ if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
109
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
110
+ address << [a, b, c, d].pack('CCCC')
111
+ else
112
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
113
+ end
114
+ elsif Regex_CompressedHex4Dec =~ arg
115
+ prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i
116
+ if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
117
+ a1 = ''.b
118
+ a2 = ''.b
119
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
120
+ suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
121
+ omitlen = 12 - a1.length - a2.length
122
+ address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC')
123
+ else
124
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
125
+ end
126
+ else
127
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
128
+ end
129
+ return IPv6.new(address)
130
+ else
131
+ raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}")
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end