async-dns 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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