redns 0.2.0 → 0.2.1
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/VERSION +1 -1
- data/lib/redns/connection.rb +90 -48
- data/lib/redns/name.rb +8 -4
- data/redns.gemspec +5 -3
- data/test/examples/aspmx4.googlemail.com +0 -0
- data/test/examples/postageapp.com.mx +0 -0
- data/test/helper.rb +8 -0
- data/test/test_redns_connection.rb +18 -0
- data/test/test_redns_message.rb +8 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a285fa417ce8a6cd882a1a882dae99db88dea5e3
|
4
|
+
data.tar.gz: a9182c4722a3e97dc04c7ae18f3eeb998fe06034
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83d57d0a82ef870f6b40269388d203bc85169071a48648352bf758e5b35756ec4d8b3aee678e68599fe602cb9340a55f918bb12e08bb10d2523610d79b5b284d
|
7
|
+
data.tar.gz: ec67b76f94fb65d902e6ae437d33e6301a8b04d1798935015f0b67d8758b97a85d10b96d90497274bd4070a8c9e3c11d9aaca726f3f56aaf34227fd6d26f07a7
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.1
|
data/lib/redns/connection.rb
CHANGED
@@ -3,8 +3,8 @@ require 'socket'
|
|
3
3
|
class ReDNS::Connection < EventMachine::Connection
|
4
4
|
# == Constants ============================================================
|
5
5
|
|
6
|
-
TIMEOUT_DEFAULT = 5
|
7
|
-
ATTEMPTS_DEFAULT =
|
6
|
+
TIMEOUT_DEFAULT = 2.5
|
7
|
+
ATTEMPTS_DEFAULT = 10
|
8
8
|
SEQUENCE_LIMIT = 0x10000
|
9
9
|
|
10
10
|
# == Properties ===========================================================
|
@@ -54,6 +54,10 @@ class ReDNS::Connection < EventMachine::Connection
|
|
54
54
|
@nameservers ||= ReDNS::Support.default_nameservers
|
55
55
|
end
|
56
56
|
|
57
|
+
def nameserver_score
|
58
|
+
@nameserver_score ||= Hash.new(0)
|
59
|
+
end
|
60
|
+
|
57
61
|
# Configure the nameservers to use. Supplied value can be either a string
|
58
62
|
# containing one IP address, an Array containing multiple IP addresses, or
|
59
63
|
# nil which reverts to defaults.
|
@@ -64,7 +68,7 @@ class ReDNS::Connection < EventMachine::Connection
|
|
64
68
|
|
65
69
|
# Picks a random nameserver from the configured list.
|
66
70
|
def random_nameserver
|
67
|
-
nameservers
|
71
|
+
nameservers.sample
|
68
72
|
end
|
69
73
|
|
70
74
|
# Returns the current port in use.
|
@@ -83,28 +87,24 @@ class ReDNS::Connection < EventMachine::Connection
|
|
83
87
|
end
|
84
88
|
|
85
89
|
serialized_message = message.serialize.to_s
|
86
|
-
target_nameserver = random_nameserver
|
87
90
|
|
88
|
-
|
89
|
-
serialized_message,
|
90
|
-
target_nameserver,
|
91
|
-
ReDNS::Support.dns_port
|
92
|
-
)
|
91
|
+
nameservers = self.nameservers_by_score
|
93
92
|
|
94
|
-
|
95
|
-
@
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
callback.call(nil)
|
106
|
-
end
|
93
|
+
entry = @callback[@sequence] = {
|
94
|
+
id: @sequence,
|
95
|
+
serialized_message: serialized_message,
|
96
|
+
type: type,
|
97
|
+
filter_by_type: (type == :any || !filter) ? false : type,
|
98
|
+
nameservers: nameservers,
|
99
|
+
nameserver: nameservers.pop,
|
100
|
+
attempts: self.attempts,
|
101
|
+
callback: callback,
|
102
|
+
at: Time.now
|
103
|
+
}
|
107
104
|
|
105
|
+
send_request!(entry)
|
106
|
+
|
107
|
+
ensure
|
108
108
|
@sequence += 1
|
109
109
|
@sequence %= SEQUENCE_LIMIT
|
110
110
|
end
|
@@ -124,6 +124,14 @@ class ReDNS::Connection < EventMachine::Connection
|
|
124
124
|
EventMachine.add_periodic_timer(1) do
|
125
125
|
check_for_timeouts!
|
126
126
|
end
|
127
|
+
|
128
|
+
EventMachine.add_periodic_timer(30) do
|
129
|
+
update_nameserver_scores!
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def peer_addr
|
134
|
+
Socket.unpack_sockaddr_in(self.get_peername)
|
127
135
|
end
|
128
136
|
|
129
137
|
# EventMachine: Called when data is received on the active socket.
|
@@ -131,6 +139,10 @@ class ReDNS::Connection < EventMachine::Connection
|
|
131
139
|
message = ReDNS::Message.new(ReDNS::Buffer.new(data))
|
132
140
|
|
133
141
|
if (callback = @callback.delete(message.id))
|
142
|
+
_, ip = self.peer_addr
|
143
|
+
|
144
|
+
self.nameserver_score[ip] += 1
|
145
|
+
|
134
146
|
answers = message.answers
|
135
147
|
|
136
148
|
if (type = callback[:filter_by_type])
|
@@ -146,10 +158,35 @@ class ReDNS::Connection < EventMachine::Connection
|
|
146
158
|
end
|
147
159
|
|
148
160
|
protected
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
161
|
+
def nameservers_by_score
|
162
|
+
self.nameservers.sort_by do |nameserver|
|
163
|
+
self.nameserver_score[nameserver]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def send_request!(params)
|
168
|
+
rv = send_datagram(
|
169
|
+
params[:serialized_message],
|
170
|
+
params[:nameserver],
|
171
|
+
ReDNS::Support.dns_port
|
172
|
+
)
|
173
|
+
|
174
|
+
# A non-positive result means there was some kind of error.
|
175
|
+
params[:retry] = rv <= 0
|
176
|
+
|
177
|
+
rescue EventMachine::ConnectionError
|
178
|
+
# This is thrown if an invalid address is configured in the nameservers.
|
179
|
+
params[:retry] = true
|
180
|
+
ensure
|
181
|
+
params[:attempts] -= 1
|
182
|
+
end
|
183
|
+
|
184
|
+
def update_nameserver_scores!
|
185
|
+
# Sorts nameservers by least to most timeouts, also shaves number of
|
186
|
+
# timeouts in half to ignore temporary problems.
|
187
|
+
self.nameservers.each do |nameserver|
|
188
|
+
self.nameserver_score[nameserver] /= 2
|
189
|
+
end
|
153
190
|
end
|
154
191
|
|
155
192
|
# Checks all pending queries for timeouts and triggers callbacks or retries
|
@@ -157,32 +194,37 @@ protected
|
|
157
194
|
def check_for_timeouts!
|
158
195
|
timeout_at = Time.now - (@timeout || TIMEOUT_DEFAULT)
|
159
196
|
|
197
|
+
# Iterate over a copy of the keys to avoid issues with deleting entries
|
198
|
+
# from a Hash being iterated.
|
160
199
|
@callback.keys.each do |k|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
# If this request can be retried, find a different nameserver.
|
165
|
-
target_nameserver = nameserver_after(params[:target_nameserver])
|
166
|
-
|
167
|
-
# Send exactly the same request to it so that the request ID will
|
168
|
-
# match to the same callback.
|
169
|
-
send_datagram(
|
170
|
-
params[:serialized_message],
|
171
|
-
target_nameserver,
|
172
|
-
ReDNS::Support.dns_port
|
173
|
-
)
|
174
|
-
|
175
|
-
params[:target_nameserver] = target_nameserver
|
176
|
-
params[:attempts] -= 1
|
177
|
-
else
|
178
|
-
params[:callback].call(nil)
|
179
|
-
@callback.delete(k)
|
180
|
-
end
|
181
|
-
end
|
182
|
-
else
|
200
|
+
params = @callback[k]
|
201
|
+
|
202
|
+
unless (params)
|
183
203
|
# Was missing params so should be deleted if not already removed.
|
184
204
|
@callback.delete(k)
|
185
205
|
end
|
206
|
+
|
207
|
+
if (params[:at] < timeout_at or params[:retry])
|
208
|
+
nameserver_score[params[:nameserver]] -= 1
|
209
|
+
|
210
|
+
if (params[:attempts] > 0)
|
211
|
+
if (params[:nameservers].empty?)
|
212
|
+
params[:nameservers] = self.nameservers_by_score
|
213
|
+
end
|
214
|
+
|
215
|
+
# If this request can be retried, find a different nameserver.
|
216
|
+
params[:nameserver] = params[:nameservers].pop
|
217
|
+
|
218
|
+
# Send exactly the same request to it so that the request ID will
|
219
|
+
# match to the same callback. The first successful response is
|
220
|
+
# considered valid.
|
221
|
+
send_request!(params)
|
222
|
+
else
|
223
|
+
params[:callback].call(nil)
|
224
|
+
|
225
|
+
@callback.delete(k)
|
226
|
+
end
|
227
|
+
end
|
186
228
|
end
|
187
229
|
end
|
188
230
|
end
|
data/lib/redns/name.rb
CHANGED
@@ -18,7 +18,7 @@ class ReDNS::Name < ReDNS::Fragment
|
|
18
18
|
when String
|
19
19
|
super(name: contents)
|
20
20
|
|
21
|
-
unless (ReDNS::Support.is_ip?(name) or self.name.match(
|
21
|
+
unless (ReDNS::Support.is_ip?(name) or self.name.match(/\.\z/))
|
22
22
|
self.name += '.'
|
23
23
|
end
|
24
24
|
else
|
@@ -64,8 +64,12 @@ class ReDNS::Name < ReDNS::Fragment
|
|
64
64
|
# point and read from there, but preserve the position where the
|
65
65
|
# pointer was found to leave the buffer in that final state.
|
66
66
|
|
67
|
-
|
68
|
-
|
67
|
+
|
68
|
+
|
69
|
+
# The pointer is encoded as two sequential bytes representing the
|
70
|
+
# positional offset.
|
71
|
+
if (byte = buffer.unpack('C')[0])
|
72
|
+
pointer = (c & 0x3F) << 8 | byte
|
69
73
|
|
70
74
|
return_to_offset ||= buffer.offset
|
71
75
|
buffer.rewind
|
@@ -100,7 +104,7 @@ class ReDNS::Name < ReDNS::Fragment
|
|
100
104
|
buffer.advance(return_to_offset)
|
101
105
|
end
|
102
106
|
|
103
|
-
self.name.encode!('UTF-8')
|
107
|
+
self.name.encode!('UTF-8', undef: :replace, invalid: :replace)
|
104
108
|
|
105
109
|
self
|
106
110
|
end
|
data/redns.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: redns 0.2.
|
5
|
+
# stub: redns 0.2.1 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "redns".freeze
|
9
|
-
s.version = "0.2.
|
9
|
+
s.version = "0.2.1"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["tadman".freeze]
|
14
|
-
s.date = "2017-09-
|
14
|
+
s.date = "2017-09-19"
|
15
15
|
s.description = "ReDNS is a pure Ruby DNS library with drivers for reactor-model engines such as EventMachine".freeze
|
16
16
|
s.email = "github@tadman.ca".freeze
|
17
17
|
s.executables = ["redig".freeze]
|
@@ -46,6 +46,8 @@ Gem::Specification.new do |s|
|
|
46
46
|
"lib/redns/resource.rb",
|
47
47
|
"lib/redns/support.rb",
|
48
48
|
"redns.gemspec",
|
49
|
+
"test/examples/aspmx4.googlemail.com",
|
50
|
+
"test/examples/postageapp.com.mx",
|
49
51
|
"test/helper.rb",
|
50
52
|
"test/test_redns.rb",
|
51
53
|
"test/test_redns_address.rb",
|
Binary file
|
Binary file
|
data/test/helper.rb
CHANGED
@@ -94,6 +94,24 @@ class TestReDNSConnection < Test::Unit::TestCase
|
|
94
94
|
EventMachine.stop_event_loop
|
95
95
|
end
|
96
96
|
end
|
97
|
+
|
98
|
+
def test_broken_ns
|
99
|
+
address = :fail
|
100
|
+
|
101
|
+
EventMachine.run do
|
102
|
+
dns = ReDNS::Connection.instance do |c|
|
103
|
+
c.nameservers = '129.1.1.1'
|
104
|
+
end
|
105
|
+
|
106
|
+
dns.resolve('example.com') do |result|
|
107
|
+
address = result
|
108
|
+
|
109
|
+
EventMachine.stop_event_loop
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
assert_nil address
|
114
|
+
end
|
97
115
|
|
98
116
|
def test_simple_attempts
|
99
117
|
address = :fail
|
data/test/test_redns_message.rb
CHANGED
@@ -168,4 +168,12 @@ class TestReDNSMessage < Test::Unit::TestCase
|
|
168
168
|
|
169
169
|
assert_equal 45532, question.id
|
170
170
|
end
|
171
|
+
|
172
|
+
def test_decode_example
|
173
|
+
message = ReDNS::Message.new(example_buffer('postageapp.com.mx'))
|
174
|
+
|
175
|
+
message.answers.each do |answer|
|
176
|
+
assert_equal 'UTF-8', answer.rdata.to_a[0].encoding.to_s
|
177
|
+
end
|
178
|
+
end
|
171
179
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- tadman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
11
|
+
date: 2017-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -74,6 +74,8 @@ files:
|
|
74
74
|
- lib/redns/resource.rb
|
75
75
|
- lib/redns/support.rb
|
76
76
|
- redns.gemspec
|
77
|
+
- test/examples/aspmx4.googlemail.com
|
78
|
+
- test/examples/postageapp.com.mx
|
77
79
|
- test/helper.rb
|
78
80
|
- test/test_redns.rb
|
79
81
|
- test/test_redns_address.rb
|