rubydns 0.4.1 → 0.5.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.
- data/Gemfile +7 -0
- data/README.md +1 -0
- data/lib/rubydns.rb +6 -1
- data/lib/rubydns/chunked.rb +34 -0
- data/lib/rubydns/extensions/string-1.8.rb +6 -12
- data/lib/rubydns/extensions/string-1.9.2.rb +37 -0
- data/lib/rubydns/extensions/string-1.9.3.rb +33 -0
- data/lib/rubydns/extensions/{string-1.9.rb → string.rb} +3 -9
- data/lib/rubydns/handler.rb +9 -10
- data/lib/rubydns/message.rb +38 -0
- data/lib/rubydns/resolver.rb +219 -0
- data/lib/rubydns/server.rb +41 -14
- data/lib/rubydns/system.rb +146 -0
- data/lib/rubydns/transaction.rb +32 -14
- data/lib/rubydns/version.rb +2 -2
- data/rakefile.rb +10 -0
- data/test/TruncatedServer/log/daemon.log +445 -0
- data/test/fortune-dns.rb +6 -6
- data/test/helper.rb +9 -0
- data/test/hosts.txt +2 -0
- data/test/log/BasicTestServer.log +1453 -0
- data/test/log/FortuneDNS.log +265 -0
- data/test/log/TestPassthroughServer.log +790 -0
- data/test/log/TruncatedServer.log +959 -0
- data/test/test_daemon.rb +77 -0
- data/test/test_passthrough.rb +57 -0
- data/test/test_resolver.rb +35 -0
- data/test/test_system.rb +36 -0
- data/test/test_truncation.rb +55 -0
- metadata +52 -34
- data/test/log/FortuneDNS/stderr.log +0 -6881
- data/test/log/FortuneDNS/stdout.log +0 -24
- data/test/log/Server/stderr.log +0 -3557
- data/test/log/Server/stdout.log +0 -0
- data/test/run/Server/Server.pid +0 -1
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -4,6 +4,7 @@ RubyDNS
|
|
4
4
|
* Author: Samuel G. D. Williams (<http://www.oriontransfer.co.nz>)
|
5
5
|
* Copyright (C) 2009, 2011 Samuel G. D. Williams.
|
6
6
|
* Released under the MIT license.
|
7
|
+
* [](http://travis-ci.org/ioquatix/rubydns)
|
7
8
|
|
8
9
|
RubyDNS is a simple programmatic DSL (domain specific language) for configuring and running a DNS server. RubyDNS provides a daemon that runs a DNS server which can process DNS requests depending on specific policy. Rule selection is based on pattern matching, and results can be hard-coded, computed, fetched from a remote DNS server, fetched from a local cache, etc.
|
9
10
|
|
data/lib/rubydns.rb
CHANGED
@@ -23,12 +23,17 @@ require 'rubydns/version'
|
|
23
23
|
if RUBY_VERSION < "1.9"
|
24
24
|
require 'rubydns/extensions/resolv-1.8'
|
25
25
|
require 'rubydns/extensions/string-1.8'
|
26
|
+
elsif RUBY_VERSION < "1.9.3"
|
27
|
+
require 'rubydns/extensions/resolv-1.9'
|
28
|
+
require 'rubydns/extensions/string-1.9.2'
|
26
29
|
else
|
27
30
|
require 'rubydns/extensions/resolv-1.9'
|
28
|
-
require 'rubydns/extensions/string-1.9'
|
31
|
+
require 'rubydns/extensions/string-1.9.3'
|
29
32
|
end
|
30
33
|
|
31
34
|
require 'rubydns/server'
|
35
|
+
require 'rubydns/resolver'
|
36
|
+
require 'rubydns/handler'
|
32
37
|
|
33
38
|
require 'logger'
|
34
39
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright (c) 2009, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
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 RubyDNS
|
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
|
@@ -18,6 +18,8 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
+
require 'stringio'
|
22
|
+
|
21
23
|
class String
|
22
24
|
def bytesize
|
23
25
|
size
|
@@ -26,16 +28,8 @@ class String
|
|
26
28
|
def byteslice(*args)
|
27
29
|
self[*args]
|
28
30
|
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
offset = 0
|
34
|
-
while offset < bytesize
|
35
|
-
chunks << byteslice(offset, chunk_size)
|
36
|
-
offset += chunk_size
|
37
|
-
end
|
38
|
-
|
39
|
-
return chunks
|
40
|
-
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module RubyDNS
|
34
|
+
BinaryStringIO = StringIO
|
41
35
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Copyright (c) 2009, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
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 'stringio'
|
22
|
+
|
23
|
+
class String
|
24
|
+
def byteslice(*args)
|
25
|
+
self.dup.force_encoding("binary").slice(*args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module RubyDNS
|
30
|
+
class BinaryStringIO < StringIO
|
31
|
+
def initialize
|
32
|
+
super
|
33
|
+
|
34
|
+
set_encoding("binary")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright (c) 2009, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
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 'stringio'
|
22
|
+
|
23
|
+
module RubyDNS
|
24
|
+
class BinaryStringIO < StringIO
|
25
|
+
def initialize
|
26
|
+
super
|
27
|
+
|
28
|
+
set_encoding("BINARY")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
end
|
@@ -18,16 +18,10 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
+
require 'rubydns/chunked'
|
22
|
+
|
21
23
|
class String
|
22
24
|
def chunked(chunk_size = 255)
|
23
|
-
|
24
|
-
|
25
|
-
offset = 0
|
26
|
-
while offset < bytesize
|
27
|
-
chunks << byteslice(offset, chunk_size)
|
28
|
-
offset += chunk_size
|
29
|
-
end
|
30
|
-
|
31
|
-
return chunks
|
25
|
+
RubyDNS::chunked(self)
|
32
26
|
end
|
33
27
|
end
|
data/lib/rubydns/handler.rb
CHANGED
@@ -18,14 +18,10 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
require '
|
22
|
-
require 'stringio'
|
21
|
+
require 'rubydns/message'
|
23
22
|
|
24
23
|
module RubyDNS
|
25
|
-
|
26
24
|
module UDPHandler
|
27
|
-
UDP_TRUNCATION_SIZE = 512
|
28
|
-
|
29
25
|
def initialize(server)
|
30
26
|
@server = server
|
31
27
|
end
|
@@ -35,7 +31,7 @@ module RubyDNS
|
|
35
31
|
query = nil
|
36
32
|
|
37
33
|
begin
|
38
|
-
query =
|
34
|
+
query = RubyDNS::decode_message(data)
|
39
35
|
|
40
36
|
return server.process_query(query, &block)
|
41
37
|
rescue
|
@@ -69,8 +65,10 @@ module RubyDNS
|
|
69
65
|
@server.logger.warn "Response via UDP was larger than #{UDP_TRUNCATION_SIZE}!"
|
70
66
|
|
71
67
|
# Reencode data with truncation flag marked as true:
|
72
|
-
|
73
|
-
|
68
|
+
truncation_error = Resolv::DNS::Message.new(answer.id)
|
69
|
+
truncation_error.tc = 1
|
70
|
+
|
71
|
+
data = truncation_error.encode
|
74
72
|
end
|
75
73
|
|
76
74
|
self.send_data(data)
|
@@ -84,14 +82,15 @@ module RubyDNS
|
|
84
82
|
module TCPHandler
|
85
83
|
def initialize(server)
|
86
84
|
@server = server
|
87
|
-
|
85
|
+
|
86
|
+
@buffer = BinaryStringIO.new
|
87
|
+
|
88
88
|
@length = nil
|
89
89
|
@processed = 0
|
90
90
|
end
|
91
91
|
|
92
92
|
def receive_data(data)
|
93
93
|
# We buffer data until we've received the entire packet:
|
94
|
-
@buffer ||= StringIO.new
|
95
94
|
@buffer.write(data)
|
96
95
|
|
97
96
|
# Message includes a 16-bit length field.. we need to see if we have received it yet:
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Copyright (c) 2009, 2011 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
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 'eventmachine'
|
22
|
+
require 'stringio'
|
23
|
+
require 'resolv'
|
24
|
+
|
25
|
+
module RubyDNS
|
26
|
+
UDP_TRUNCATION_SIZE = 512
|
27
|
+
|
28
|
+
# The DNS message container.
|
29
|
+
Message = Resolv::DNS::Message
|
30
|
+
|
31
|
+
def self.decode_message(data)
|
32
|
+
if data.respond_to? :force_encoding
|
33
|
+
data.force_encoding("BINARY")
|
34
|
+
end
|
35
|
+
|
36
|
+
Message.decode(data)
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# Copyright (c) 2012 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
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 'rubydns/message'
|
22
|
+
|
23
|
+
module RubyDNS
|
24
|
+
class InvalidProtocolError < StandardError
|
25
|
+
end
|
26
|
+
|
27
|
+
class ResolutionFailure < StandardError
|
28
|
+
end
|
29
|
+
|
30
|
+
class Resolver
|
31
|
+
# Servers are specified in the same manor as options[:listen], e.g.
|
32
|
+
# [:tcp/:udp, address, port]
|
33
|
+
# In the case of multiple servers, they will be checked in sequence.
|
34
|
+
def initialize(servers, options = {})
|
35
|
+
@servers = servers
|
36
|
+
@sequence = 0
|
37
|
+
|
38
|
+
@options = options
|
39
|
+
end
|
40
|
+
|
41
|
+
# Provides the next sequence identification number which is used to keep track of DNS messages.
|
42
|
+
def next_id!
|
43
|
+
return (@sequence += 1)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Look up a named resource of the given resource_class.
|
47
|
+
def query(name, resource_class = Resolv::DNS::Resource::IN::A, &block)
|
48
|
+
message = Resolv::DNS::Message.new(next_id!)
|
49
|
+
message.rd = 1
|
50
|
+
message.add_question name, resource_class
|
51
|
+
|
52
|
+
Request.fetch(message, @servers, @options, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Manages a single DNS question message across one or more servers.
|
56
|
+
class Request
|
57
|
+
include EventMachine::Deferrable
|
58
|
+
|
59
|
+
def self.fetch(*args)
|
60
|
+
request = self.new(*args)
|
61
|
+
|
62
|
+
request.callback do |message|
|
63
|
+
yield message
|
64
|
+
end
|
65
|
+
|
66
|
+
request.errback do |error|
|
67
|
+
yield error
|
68
|
+
end
|
69
|
+
|
70
|
+
request.run!
|
71
|
+
end
|
72
|
+
|
73
|
+
def initialize(message, servers, options = {}, &block)
|
74
|
+
@message = message
|
75
|
+
@packet = message.encode
|
76
|
+
|
77
|
+
@servers = servers.dup
|
78
|
+
|
79
|
+
# We select the protocol based on the size of the data:
|
80
|
+
if @packet.bytesize > UDP_TRUNCATION_SIZE
|
81
|
+
@servers.delete_if{|server| server[0] == :udp}
|
82
|
+
end
|
83
|
+
|
84
|
+
# Measured in seconds:
|
85
|
+
@timeout = options[:timeout] || 5
|
86
|
+
|
87
|
+
@logger = options[:logger]
|
88
|
+
end
|
89
|
+
|
90
|
+
attr :message
|
91
|
+
attr :packet
|
92
|
+
attr :logger
|
93
|
+
|
94
|
+
def run!
|
95
|
+
try_next_server!
|
96
|
+
end
|
97
|
+
|
98
|
+
def process_response!(response)
|
99
|
+
if response.tc != 0
|
100
|
+
@logger.warn "[#{@message.id}] Received truncated response!" if @logger
|
101
|
+
# We hardcode this behaviour for now.
|
102
|
+
try_next_server!
|
103
|
+
else
|
104
|
+
@logger.warn "[#{@message.id}] Received valid response #{response.inspect}" if @logger
|
105
|
+
succeed response
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def try_next_server!
|
112
|
+
if @request
|
113
|
+
@request.close_connection
|
114
|
+
@request = nil
|
115
|
+
end
|
116
|
+
|
117
|
+
if @servers.size > 0
|
118
|
+
@server = @servers.shift
|
119
|
+
|
120
|
+
@logger.debug "[#{@message.id}] Sending request to server #{@server.inspect}" if @logger
|
121
|
+
|
122
|
+
# We make requests one at a time to the given server, naturally the servers are ordered in terms of priority.
|
123
|
+
case @server[0]
|
124
|
+
when :udp
|
125
|
+
@request = UDPRequestHandler.open(@server[1], @server[2], self)
|
126
|
+
when :tcp
|
127
|
+
@request = TCPRequestHandler.open(@server[1], @server[2], self)
|
128
|
+
else
|
129
|
+
raise InvalidProtocolError.new(@server)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Setting up the timeout...
|
133
|
+
EventMachine::Timer.new(@timeout) do
|
134
|
+
@logger.debug "[#{@message.id}] Request timed out!" if @logger
|
135
|
+
|
136
|
+
try_next_server!
|
137
|
+
end
|
138
|
+
else
|
139
|
+
fail ResolutionFailure.new("No available servers responded to the request.")
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
module UDPRequestHandler
|
144
|
+
def self.open(host, port, request)
|
145
|
+
# Open a datagram socket... EventMachine doesn't support connected datagram sockets, so we have to cheat a bit:
|
146
|
+
EventMachine::open_datagram_socket('', 0, self, request, host, port)
|
147
|
+
end
|
148
|
+
|
149
|
+
def initialize(request, host, port)
|
150
|
+
@request = request
|
151
|
+
@host = host
|
152
|
+
@port = port
|
153
|
+
end
|
154
|
+
|
155
|
+
def post_init
|
156
|
+
# Sending question to remote DNS server...
|
157
|
+
send_datagram(@request.packet, @host, @port)
|
158
|
+
end
|
159
|
+
|
160
|
+
def receive_data(data)
|
161
|
+
# Receiving response from remote DNS server...
|
162
|
+
message = RubyDNS::decode_message(data)
|
163
|
+
|
164
|
+
# The message id must match, and it can't be truncated:
|
165
|
+
if message.id == @request.message.id
|
166
|
+
@request.process_response!(message)
|
167
|
+
else
|
168
|
+
@request.logger.warn "[#{@request.message.id}] Received response with incorrect message id: #{message.id}" if @request.logger
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
module TCPRequestHandler
|
174
|
+
def self.open(host, port, request)
|
175
|
+
EventMachine::connect(host, port, TCPRequestHandler, request)
|
176
|
+
end
|
177
|
+
|
178
|
+
def initialize(request)
|
179
|
+
@request = request
|
180
|
+
@buffer = nil
|
181
|
+
@length = nil
|
182
|
+
end
|
183
|
+
|
184
|
+
def post_init
|
185
|
+
data = @request.packet
|
186
|
+
|
187
|
+
send_data([data.bytesize].pack('n'))
|
188
|
+
send_data data
|
189
|
+
end
|
190
|
+
|
191
|
+
def receive_data(data)
|
192
|
+
# We buffer data until we've received the entire packet:
|
193
|
+
@buffer ||= BinaryStringIO.new
|
194
|
+
@buffer.write(data)
|
195
|
+
|
196
|
+
if @length == nil
|
197
|
+
if @buffer.size > 2
|
198
|
+
@length = @buffer.string.byteslice(0, 2).unpack('n')[0]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
if @buffer.size == (@length + 2)
|
203
|
+
data = @buffer.string.byteslice(2, @length)
|
204
|
+
|
205
|
+
message = RubyDNS::decode_message(data)
|
206
|
+
|
207
|
+
if message.id == @request.message.id
|
208
|
+
@request.process_response!(message)
|
209
|
+
else
|
210
|
+
@request.logger.warn "[#{@request.message.id}] Received response with incorrect message id: #{message.id}" if @request.logger
|
211
|
+
end
|
212
|
+
elsif @buffer.size > (@length + 2)
|
213
|
+
@request.try_next_server!
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|