async-dns 1.0.0 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a69c3e892e1022681eb81ec89cf6953b27f2dd65
4
- data.tar.gz: f4b1bbfe1e36b9bae37681343c4eb1bc71f5b621
2
+ SHA256:
3
+ metadata.gz: 75dfa7c3b610755410a52e52a8d17f3b98ffafcd0c74949b1f276e654cdf0677
4
+ data.tar.gz: 3d3434f03a5cd4492dcb0d1ba5ea2257952ba756a3a284ad021b9e58105e615c
5
5
  SHA512:
6
- metadata.gz: 0dc107b7e33810d8a45fdb660dfc7421ad95383d8e14db6006175a335b0cc80500065e2c78c3d80ef53291a2ae8f3617a5cdb081475009ddff5c0edf3ff06576
7
- data.tar.gz: 3209d74dc8a3ae956acb687da50ac5049f5c5a4be820ae8e5f1ca88c3c02b1bcd077e02ba33a23a498d90f8cd86b2ad52b2582907c734ccfee45f4372487cb42
6
+ metadata.gz: ad1b54307fc07a786260573d9d97e619a1662af47786460f04d24c35220ef72787f719b174bb676089a8c4f1680f23c2a05c60b256097182ed63935fbfe7263b
7
+ data.tar.gz: 332b76c624b5965f41c8d9242252fd4b9e713d9e6cab8a1ccc593101eea6a8a57327f81ea069642d8cafc63460e995a72cae858a66c7427bc79e43e3cd0a1acb
@@ -7,6 +7,8 @@ addons:
7
7
  packages:
8
8
  - bind9
9
9
 
10
+ before_script: sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'
11
+
10
12
  matrix:
11
13
  include:
12
14
  - rvm: 2.1
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
  spec.require_paths = ["lib"]
22
22
  spec.has_rdoc = "yard"
23
23
 
24
- spec.add_dependency("async-io", "~> 1.0")
24
+ spec.add_dependency("async-io", "~> 1.3")
25
25
 
26
26
  spec.add_development_dependency "async-rspec", "~> 1.0"
27
27
  spec.add_development_dependency "process-daemon", "~> 1.0"
@@ -22,15 +22,15 @@ require_relative 'transport'
22
22
 
23
23
  module Async::DNS
24
24
  class GenericHandler
25
- def initialize(server, endpoint)
25
+ def initialize(server, socket)
26
26
  @server = server
27
- @endpoint = endpoint
27
+ @socket = socket
28
28
 
29
29
  @logger = @server.logger || Async.logger
30
30
  end
31
31
 
32
32
  attr :server
33
- attr :endpoint
33
+ attr :socket
34
34
 
35
35
  def error_response(query = nil, code = Resolv::DNS::RCode::ServFail)
36
36
  # Encoding may fail, so we need to handle this particular case:
@@ -68,15 +68,11 @@ module Async::DNS
68
68
  # Handling incoming UDP requests, which are single data packets, and pass them on to the given server.
69
69
  class DatagramHandler < GenericHandler
70
70
  def run(task: Async::Task.current)
71
- @endpoint.bind do |socket|
72
- while true
73
- Async.logger.debug(self.class.name) {"-> socket.recvfrom"}
74
- input_data, remote_address = socket.recvmsg(UDP_TRUNCATION_SIZE)
75
- Async.logger.debug(self.class.name) {"<- socket.recvfrom"}
76
-
77
- task.async do
78
- respond(socket, input_data, remote_address)
79
- end
71
+ while true
72
+ input_data, remote_address = @socket.recvmsg(UDP_TRUNCATION_SIZE)
73
+
74
+ task.async do
75
+ respond(@socket, input_data, remote_address)
80
76
  end
81
77
  end
82
78
  end
@@ -109,15 +105,15 @@ module Async::DNS
109
105
  end
110
106
 
111
107
  class StreamHandler < GenericHandler
112
- def run(task: Async::Task.current)
113
- @endpoint.accept do |client, address|
108
+ def run(backlog = Socket::SOMAXCONN)
109
+ @socket.listen(backlog)
110
+
111
+ @socket.accept_each do |client, address|
114
112
  handle_connection(client)
115
113
  end
116
114
  end
117
115
 
118
116
  def handle_connection(socket)
119
- context = Async::Task.current
120
-
121
117
  input_data = StreamTransport.read_chunk(socket)
122
118
 
123
119
  response = process_query(input_data, remote_address: socket.remote_address)
@@ -185,13 +185,15 @@ module Async::DNS
185
185
  end
186
186
 
187
187
  def try_server(request, endpoint)
188
- case endpoint.socket_type
189
- when Socket::SOCK_DGRAM
190
- try_datagram_server(request, endpoint)
191
- when Socket::SOCK_STREAM
192
- try_stream_server(request, endpoint)
193
- else
194
- raise InvalidProtocolError.new(endpoint)
188
+ endpoint.connect do |socket|
189
+ case socket.type
190
+ when Socket::SOCK_DGRAM
191
+ try_datagram_server(request, socket)
192
+ when Socket::SOCK_STREAM
193
+ try_stream_server(request, socket)
194
+ else
195
+ raise InvalidProtocolError.new(endpoint)
196
+ end
195
197
  end
196
198
  end
197
199
 
@@ -209,26 +211,20 @@ module Async::DNS
209
211
  return false
210
212
  end
211
213
 
212
- def try_datagram_server(request, endpoint, task: Async::Task.current)
213
- endpoint.connect do |socket|
214
- socket.sendmsg(request.packet, 0)
215
-
216
- data, peer = socket.recvmsg(UDP_TRUNCATION_SIZE)
217
-
218
- return Async::DNS::decode_message(data)
219
- end
214
+ def try_datagram_server(request, socket)
215
+ socket.sendmsg(request.packet, 0)
216
+
217
+ data, peer = socket.recvmsg(UDP_TRUNCATION_SIZE)
218
+
219
+ return Async::DNS::decode_message(data)
220
220
  end
221
221
 
222
- def try_stream_server(request, endpoint)
223
- context = Async::Task.current
222
+ def try_stream_server(request, socket)
223
+ StreamTransport.write_chunk(socket, request.packet)
224
224
 
225
- endpoint.connect do |socket|
226
- StreamTransport.write_chunk(socket, request.packet)
227
-
228
- input_data = StreamTransport.read_chunk(socket)
229
-
230
- return Async::DNS::decode_message(input_data)
231
- end
225
+ input_data = StreamTransport.read_chunk(socket)
226
+
227
+ return Async::DNS::decode_message(input_data)
232
228
  end
233
229
 
234
230
  # Manages a single DNS question message across one or more servers.
@@ -41,8 +41,6 @@ module Async::DNS
41
41
  @endpoints = endpoints
42
42
  @origin = origin
43
43
  @logger = logger
44
-
45
- @handlers = []
46
44
  end
47
45
 
48
46
  # Records are relative to this origin:
@@ -106,12 +104,23 @@ module Async::DNS
106
104
  def run(*args)
107
105
  @logger.info "Starting Async::DNS server (v#{Async::DNS::VERSION})..."
108
106
 
109
- setup_handlers if @handlers.empty?
110
-
111
107
  Async::Reactor.run do |task|
112
- @handlers.each do |handler|
108
+ fire(:setup)
109
+
110
+ Async::IO::Endpoint.each(@endpoints) do |endpoint|
113
111
  task.async do
114
- handler.run(*args)
112
+ endpoint.bind do |socket|
113
+ case socket.type
114
+ when Socket::SOCK_DGRAM
115
+ @logger.info "<> Listening for datagrams on #{socket.local_address.inspect}"
116
+ DatagramHandler.new(self, socket).run
117
+ when Socket::SOCK_STREAM
118
+ @logger.info "<> Listening for connections on #{socket.local_address.inspect}"
119
+ StreamHandler.new(self, socket).run
120
+ else
121
+ raise ArgumentError.new("Don't know how to handle #{address}")
122
+ end
123
+ end
115
124
  end
116
125
  end
117
126
 
@@ -119,23 +128,5 @@ module Async::DNS
119
128
  end
120
129
  end
121
130
 
122
- private
123
-
124
- def setup_handlers
125
- fire(:setup)
126
-
127
- Async::IO::Endpoint.each(@endpoints) do |endpoint|
128
- case endpoint.socket_type
129
- when Socket::SOCK_DGRAM
130
- @logger.info "<> Listening for datagrams on #{endpoint.inspect}"
131
- @handlers << DatagramHandler.new(self, endpoint)
132
- when Socket::SOCK_STREAM
133
- @logger.info "<> Listening for connections on #{endpoint.inspect}"
134
- @handlers << StreamHandler.new(self, endpoint)
135
- else
136
- raise ArgumentError.new("Don't know how to handle #{address}")
137
- end
138
- end
139
- end
140
131
  end
141
132
  end
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Async
22
22
  module DNS
23
- VERSION = '1.0.0'
23
+ VERSION = '1.1.0'
24
24
  end
25
25
  end
@@ -25,14 +25,14 @@ describe Async::DNS::StreamHandler do
25
25
  include_context Async::RSpec::Reactor
26
26
 
27
27
  let(:server) {Async::DNS::Server.new}
28
- let(:address) {Async::IO::Endpoint.tcp('127.0.0.1', 6666)}
29
-
30
- subject {described_class.new(server, address)}
28
+ let(:endpoint) {Async::IO::Endpoint.tcp('127.0.0.1', 6666, reuse_port: true)}
31
29
 
32
30
  it "can rebind port" do
33
31
  2.times do
34
32
  task = reactor.async do
35
- subject.run
33
+ endpoint.bind do |socket|
34
+ described_class.new(server, socket).run
35
+ end
36
36
  end
37
37
 
38
38
  task.stop
@@ -44,14 +44,14 @@ describe Async::DNS::DatagramHandler do
44
44
  include_context Async::RSpec::Reactor
45
45
 
46
46
  let(:server) {Async::DNS::Server.new}
47
- let(:address) {Async::IO::Endpoint.udp('127.0.0.1', 6666)}
48
-
49
- subject {described_class.new(server, address)}
47
+ let(:endpoint) {Async::IO::Endpoint.udp('127.0.0.1', 6666)}
50
48
 
51
49
  it "can rebind port" do
52
50
  2.times do
53
51
  task = reactor.async do
54
- subject.run
52
+ endpoint.bind do |socket|
53
+ described_class.new(server, socket).run
54
+ end
55
55
  end
56
56
 
57
57
  task.stop
@@ -0,0 +1,62 @@
1
+ # Copyright, 2018, 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
+ RSpec.shared_context "Junk UDP Server" do
22
+ let(:server_endpoint) {Async::IO::Endpoint.udp('0.0.0.0', 6060, reuse_port: true)}
23
+
24
+ let!(:server_task) do
25
+ reactor.async do
26
+ server_endpoint.bind do |socket|
27
+ begin
28
+ while true
29
+ data, address = socket.recvfrom(1024)
30
+ socket.send("foobar", 0, address)
31
+ end
32
+ rescue
33
+ socket.close
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ after(:each) do
40
+ server_task.stop
41
+ end
42
+ end
43
+
44
+ RSpec.shared_context "Junk TCP Server" do
45
+ let(:server_endpoint) {Async::IO::Endpoint.tcp('0.0.0.0', 6060, reuse_port: true)}
46
+
47
+ let!(:server_task) do
48
+ reactor.async do
49
+ server_endpoint.accept do |socket|
50
+ begin
51
+ socket.write("f\0\0bar")
52
+ rescue
53
+ socket.close
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ after(:each) do
60
+ server_task.stop
61
+ end
62
+ end
@@ -22,118 +22,83 @@
22
22
 
23
23
  require 'async/dns'
24
24
 
25
- module Async::DNS::ResolverSpec
26
- describe Async::DNS::Resolver do
27
- include_context Async::RSpec::Reactor
28
-
29
- class JunkUDPServer
30
- def initialize(server_address = nil)
31
- @server_address = server_address || Addrinfo.udp('0.0.0.0', 6060)
32
- end
33
-
34
- def run(task: Async::Task.current)
35
- task.async do
36
- Async::IO::Socket.bind(@server_address) do |socket|
37
- while true
38
- data, address = socket.recvfrom(1024)
39
- socket.send("foobar", 0, address)
40
- end
41
- end
42
- end
43
- end
44
- end
25
+ require_relative 'junk_server_context'
45
26
 
46
- class JunkTCPServer
47
- def initialize(server_address = nil)
48
- @server_address = server_address || Addrinfo.tcp('0.0.0.0', 6060)
49
- end
50
-
51
- def run(task: Async::Task.current)
52
- task.async do
53
- Async::IO::Socket.accept(@server_address, backlog: 10) do |socket|
54
- socket.write("f\0\0bar")
55
- end
56
- end
57
- end
58
- end
59
-
60
- it "should result in non-existent domain" do
61
- resolver = Async::DNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
62
-
63
- response = resolver.query('foobar.oriontransfer.org')
27
+ RSpec.describe Async::DNS::Resolver do
28
+ include_context Async::RSpec::Reactor
64
29
 
65
- expect(response.rcode).to be == Resolv::DNS::RCode::NXDomain
66
- end
30
+ it "should result in non-existent domain" do
31
+ resolver = Async::DNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
67
32
 
68
- it "should result in some answers" do
69
- resolver = Async::DNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
70
-
71
- response = resolver.query('google.com')
72
-
73
- expect(response.class).to be == Async::DNS::Message
74
- expect(response.answer.size).to be > 0
75
- end
33
+ response = resolver.query('foobar.oriontransfer.org')
76
34
 
77
- it "should return no results" do
78
- resolver = Async::DNS::Resolver.new([])
79
-
80
- response = resolver.query('google.com')
81
-
82
- expect(response).to be == nil
83
- end
35
+ expect(response.rcode).to be == Resolv::DNS::RCode::NXDomain
36
+ end
37
+
38
+ it "should result in some answers" do
39
+ resolver = Async::DNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
40
+
41
+ response = resolver.query('google.com')
42
+
43
+ expect(response.class).to be == Async::DNS::Message
44
+ expect(response.answer.size).to be > 0
45
+ end
46
+
47
+ it "should return no results" do
48
+ resolver = Async::DNS::Resolver.new([])
49
+
50
+ response = resolver.query('google.com')
51
+
52
+ expect(response).to be == nil
53
+ end
54
+
55
+ it "should fail to get addresses" do
56
+ resolver = Async::DNS::Resolver.new([])
84
57
 
85
- it "should fail to get addresses" do
86
- resolver = Async::DNS::Resolver.new([])
58
+ expect{resolver.addresses_for('google.com')}.to raise_error(Async::DNS::ResolutionFailure)
59
+ end
87
60
 
88
- expect{resolver.addresses_for('google.com')}.to raise_error(Async::DNS::ResolutionFailure)
89
- end
61
+ context 'with junk UDP server' do
62
+ include_context 'Junk UDP Server'
90
63
 
91
- let(:udp_server) {JunkUDPServer.new}
92
-
93
- it "should fail with decode error from bad udp server" do
94
- server = udp_server.run
95
-
64
+ it "should fail with decode error" do
96
65
  resolver = Async::DNS::Resolver.new([[:udp, "0.0.0.0", 6060]])
97
66
 
98
67
  response = resolver.query('google.com')
99
68
 
100
69
  expect(response).to be == nil
101
-
102
- server.stop
103
70
  end
71
+ end
72
+
73
+ context 'with junk TCP server' do
74
+ include_context 'Junk TCP Server'
104
75
 
105
- let(:tcp_server) {JunkTCPServer.new}
106
-
107
- it "should fail with decode error from bad tcp server" do
108
- server = tcp_server.run
109
-
76
+ it "should fail with decode error" do
110
77
  resolver = Async::DNS::Resolver.new([[:tcp, "0.0.0.0", 6060]])
111
78
 
112
79
  response = resolver.query('google.com')
113
80
 
114
81
  expect(response).to be == nil
115
-
116
- server.stop
117
82
  end
118
-
119
- it "should return some IPv4 and IPv6 addresses" do
120
- resolver = Async::DNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
121
-
122
- addresses = resolver.addresses_for("www.google.com.")
123
-
124
- expect(addresses.size).to be > 0
83
+ end
125
84
 
126
- addresses.each do |address|
127
- expect(address).to be_kind_of(Resolv::IPv4) | be_kind_of(Resolv::IPv6)
128
- end
85
+ it "should return some IPv4 and IPv6 addresses" do
86
+ resolver = Async::DNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
87
+
88
+ addresses = resolver.addresses_for("www.google.com.")
89
+
90
+ expect(addresses.size).to be > 0
91
+
92
+ addresses.each do |address|
93
+ expect(address).to be_kind_of(Resolv::IPv4) | be_kind_of(Resolv::IPv6)
129
94
  end
95
+ end
96
+
97
+ it "should recursively resolve CNAME records" do
98
+ resolver = Async::DNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
130
99
 
131
- it "should recursively resolve CNAME records" do
132
- resolver = Async::DNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
133
-
134
- addresses = resolver.addresses_for('www.baidu.com')
135
-
136
- expect(addresses.size).to be > 0
137
- end
100
+ addresses = resolver.addresses_for('www.baidu.com')
101
+
102
+ expect(addresses.size).to be > 0
138
103
  end
139
104
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-dns
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-30 00:00:00.000000000 Z
11
+ date: 2018-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-io
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
19
+ version: '1.3'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.0'
26
+ version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: async-rspec
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -126,6 +126,7 @@ files:
126
126
  - spec/async/dns/handler_spec.rb
127
127
  - spec/async/dns/hosts.txt
128
128
  - spec/async/dns/ipv6_spec.rb
129
+ - spec/async/dns/junk_server_context.rb
129
130
  - spec/async/dns/message_spec.rb
130
131
  - spec/async/dns/origin_spec.rb
131
132
  - spec/async/dns/replace_spec.rb
@@ -163,7 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
164
  version: '0'
164
165
  requirements: []
165
166
  rubyforge_project:
166
- rubygems_version: 2.6.10
167
+ rubygems_version: 2.7.6
167
168
  signing_key:
168
169
  specification_version: 4
169
170
  summary: An easy to use DNS client resolver and server for Ruby.
@@ -171,6 +172,7 @@ test_files:
171
172
  - spec/async/dns/handler_spec.rb
172
173
  - spec/async/dns/hosts.txt
173
174
  - spec/async/dns/ipv6_spec.rb
175
+ - spec/async/dns/junk_server_context.rb
174
176
  - spec/async/dns/message_spec.rb
175
177
  - spec/async/dns/origin_spec.rb
176
178
  - spec/async/dns/replace_spec.rb