rubydns 0.1.8 → 0.2.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/lib/rubydns/extensions/hexdump.rb +19 -0
- data/lib/rubydns/handler.rb +86 -0
- data/lib/rubydns/server.rb +24 -2
- data/lib/rubydns/transaction.rb +1 -1
- data/lib/rubydns/version.rb +2 -2
- data/lib/rubydns.rb +16 -46
- data/test/daemon2.rb +32 -33
- data/test/log/Server/stderr.log +496 -657
- data/test/run/Server/Server.pid +1 -0
- metadata +31 -7
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class String
|
5
|
+
def hexdump
|
6
|
+
i = 1
|
7
|
+
out = StringIO.new
|
8
|
+
|
9
|
+
out.puts "Size: #{self.size}"
|
10
|
+
while (self.length > 16*(i-1))
|
11
|
+
a = self.slice(16*(i-1)..(16*i)-1)
|
12
|
+
out.printf("%06x: %4.4x %4.4x %4.4x %4.4x %4.4x %4.4x %4.4x %4.4x ", (i-1)*16, *a.unpack("n16"))
|
13
|
+
out.printf("|%s|\n", a.tr("^\040-\176","."))
|
14
|
+
i += 1
|
15
|
+
end
|
16
|
+
|
17
|
+
return out.string
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
require 'rubydns/extensions/hexdump'
|
6
|
+
|
7
|
+
module RubyDNS
|
8
|
+
|
9
|
+
UDP_TRUNCATION_SIZE = 512
|
10
|
+
|
11
|
+
module UDPHandler
|
12
|
+
def initialize(server)
|
13
|
+
@server = server
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.process(server, data, &block)
|
17
|
+
server.logger.debug "Receiving incoming query..."
|
18
|
+
|
19
|
+
server.logger.debug data.hexdump
|
20
|
+
|
21
|
+
begin
|
22
|
+
server.receive_data(data) do |answer|
|
23
|
+
yield answer.encode
|
24
|
+
end
|
25
|
+
rescue
|
26
|
+
server.logger.error "Error processing request!"
|
27
|
+
server.logger.error "#{$!.class}: #{$!.message}"
|
28
|
+
|
29
|
+
$!.backtrace.each { |at| server.logger.error at }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def receive_data(data)
|
34
|
+
UDPHandler.process(@server, data) do |result|
|
35
|
+
self.send_data(result)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class LengthError < StandardError
|
41
|
+
end
|
42
|
+
|
43
|
+
module TCPHandler
|
44
|
+
def initialize(server)
|
45
|
+
@server = server
|
46
|
+
@buffer = nil
|
47
|
+
@length = nil
|
48
|
+
@processed = 0
|
49
|
+
end
|
50
|
+
|
51
|
+
def receive_data(data)
|
52
|
+
@buffer ||= StringIO.new
|
53
|
+
@buffer.write(data)
|
54
|
+
|
55
|
+
# Message includes a 16-bit length field
|
56
|
+
if @length == nil
|
57
|
+
if (@buffer.size - @processed) < 2
|
58
|
+
raise LengthError.new("Malformed message smaller than two bytes received")
|
59
|
+
end
|
60
|
+
|
61
|
+
@length = @buffer.string[@processed, 2].unpack('n')[0]
|
62
|
+
@processed += 2
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
if (@buffer.size - @processed) >= @length
|
67
|
+
data = @buffer.string[@processed, @length]
|
68
|
+
|
69
|
+
UDPHandler.process(@server, data) do |result|
|
70
|
+
self.send_data([result.size].pack('n'))
|
71
|
+
self.send_data(result)
|
72
|
+
end
|
73
|
+
|
74
|
+
@processed += @length
|
75
|
+
@length = nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def unbind
|
80
|
+
if @processed != @buffer.size
|
81
|
+
raise LengthError.new("Unprocessed data remaining (#{@buffer.size - @processed} bytes unprocessed)")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/lib/rubydns/server.rb
CHANGED
@@ -31,6 +31,7 @@ module RubyDNS
|
|
31
31
|
# end
|
32
32
|
#
|
33
33
|
def initialize(&block)
|
34
|
+
@events = {}
|
34
35
|
@rules = []
|
35
36
|
@otherwise = nil
|
36
37
|
|
@@ -68,6 +69,23 @@ module RubyDNS
|
|
68
69
|
@rules << [pattern, Proc.new(&block)]
|
69
70
|
end
|
70
71
|
|
72
|
+
# Register a named event which may be invoked later using #fire
|
73
|
+
# on(:start) do |server|
|
74
|
+
# RExec.change_user(RUN_AS)
|
75
|
+
# end
|
76
|
+
def on(event_name, &block)
|
77
|
+
@events[event_name] = Proc.new(&block)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Fire the named event, which must have been registered using on.
|
81
|
+
def fire(event_name)
|
82
|
+
callback = @events[event_name]
|
83
|
+
|
84
|
+
if callback
|
85
|
+
callback.call(self)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
71
89
|
# Specify a default block to execute if all other rules fail to match.
|
72
90
|
# This block is typially used to pass the request on to another server
|
73
91
|
# (i.e. recursive request).
|
@@ -138,7 +156,7 @@ module RubyDNS
|
|
138
156
|
|
139
157
|
# Process an incoming DNS message. Returns a serialized message to be
|
140
158
|
# sent back to the client.
|
141
|
-
def receive_data(data)
|
159
|
+
def receive_data(data, &block)
|
142
160
|
query = Resolv::DNS::Message::decode(data)
|
143
161
|
|
144
162
|
# Setup answer
|
@@ -164,7 +182,11 @@ module RubyDNS
|
|
164
182
|
end
|
165
183
|
end
|
166
184
|
|
167
|
-
|
185
|
+
if block_given?
|
186
|
+
yield answer
|
187
|
+
else
|
188
|
+
return answer
|
189
|
+
end
|
168
190
|
end
|
169
191
|
end
|
170
192
|
end
|
data/lib/rubydns/transaction.rb
CHANGED
data/lib/rubydns/version.rb
CHANGED
data/lib/rubydns.rb
CHANGED
@@ -25,6 +25,8 @@ require 'logger'
|
|
25
25
|
require 'rexec'
|
26
26
|
require 'rexec/daemon'
|
27
27
|
|
28
|
+
require 'rubydns/handler'
|
29
|
+
|
28
30
|
module RubyDNS
|
29
31
|
|
30
32
|
# Run a server with the given rules. A number of options can be supplied:
|
@@ -51,58 +53,26 @@ module RubyDNS
|
|
51
53
|
#
|
52
54
|
def self.run_server (options = {}, &block)
|
53
55
|
server = RubyDNS::Server.new(&block)
|
54
|
-
threads = ThreadGroup.new
|
55
|
-
|
56
56
|
server.logger.info "Starting server..."
|
57
|
-
|
58
|
-
options[:listen] ||= [[:udp, "0.0.0.0", 53]]
|
59
|
-
|
60
|
-
sockets = []
|
61
57
|
|
62
|
-
|
63
|
-
options[:listen].each do |spec|
|
64
|
-
if spec.kind_of?(BasicSocket)
|
65
|
-
sockets << spec
|
66
|
-
elsif spec[0] == :udp
|
67
|
-
socket = UDPSocket.new
|
68
|
-
socket.bind(spec[1], spec[2])
|
69
|
-
|
70
|
-
sockets << socket
|
71
|
-
elsif spec[0] == :tcp
|
72
|
-
server.logger.warn "Sorry, TCP is not currently supported!"
|
73
|
-
end
|
74
|
-
end
|
58
|
+
options[:listen] ||= [[:udp, "0.0.0.0", 53], [:tcp, "0.0.0.0", 53]]
|
75
59
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
thr = Thread.new do
|
86
|
-
begin
|
87
|
-
result = server.receive_data(packet)
|
88
|
-
|
89
|
-
server.logger.debug "Sending result to #{sender.inspect}:"
|
90
|
-
server.logger.debug "#{result.inspect}"
|
91
|
-
socket.send(result, 0, sender[2], sender[1])
|
92
|
-
rescue
|
93
|
-
server.logger.error "Error processing request!"
|
94
|
-
server.logger.error "#{$!.class}: #{$!.message}"
|
95
|
-
$!.backtrace.each { |at| server.logger.error at }
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
threads.add thr
|
60
|
+
EventMachine.run do
|
61
|
+
server.fire(:setup)
|
62
|
+
|
63
|
+
# Setup server sockets
|
64
|
+
options[:listen].each do |spec|
|
65
|
+
if spec[0] == :udp
|
66
|
+
EventMachine.open_datagram_socket(spec[1], spec[2], UDPHandler, server)
|
67
|
+
elsif spec[0] == :tcp
|
68
|
+
EventMachine.start_server(spec[1], spec[2], TCPHandler, server)
|
100
69
|
end
|
101
70
|
end
|
102
|
-
|
103
|
-
server.
|
104
|
-
threads.list.each { |thr| thr.join }
|
71
|
+
|
72
|
+
server.fire(:start)
|
105
73
|
end
|
74
|
+
|
75
|
+
server.fire(:stop)
|
106
76
|
end
|
107
77
|
end
|
108
78
|
|
data/test/daemon2.rb
CHANGED
@@ -36,46 +36,45 @@ require 'rubydns'
|
|
36
36
|
# You might need to change the user name "daemon". This can be a user name or a user id.
|
37
37
|
RUN_AS = "daemon"
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
INTERFACES = [
|
40
|
+
[:udp, "0.0.0.0", 53],
|
41
|
+
[:tcp, "0.0.0.0", 53]
|
42
|
+
]
|
41
43
|
|
42
44
|
# We need to be root in order to bind to privileged port
|
43
45
|
if RExec.current_user != "root"
|
44
|
-
|
45
|
-
|
46
|
+
$stderr.puts "Sorry, this command needs to be run as root!"
|
47
|
+
exit 1
|
46
48
|
end
|
47
49
|
|
48
50
|
# The Daemon itself
|
49
51
|
class Server < RExec::Daemon::Base
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
52
|
+
@@var_directory = File.dirname(__FILE__)
|
53
|
+
|
54
|
+
def self.run
|
55
|
+
# Don't buffer output (for debug purposes)
|
56
|
+
$stderr.sync = true
|
57
|
+
|
58
|
+
# Use upstream DNS for name resolution (These ones are Orcon DNS in NZ)
|
59
|
+
$R = Resolv::DNS.new(:nameserver => ["60.234.1.1", "60.234.2.2"])
|
60
|
+
|
61
|
+
# Start the RubyDNS server
|
62
|
+
RubyDNS::run_server(:listen => INTERFACES) do
|
63
|
+
on(:start) do
|
64
|
+
RExec.change_user(RUN_AS)
|
65
|
+
end
|
66
|
+
|
67
|
+
match("test.mydomain.org", :A) do |transaction|
|
68
|
+
transaction.respond!("10.0.0.80")
|
69
|
+
end
|
70
|
+
|
71
|
+
# Default DNS handler
|
72
|
+
otherwise do |transaction|
|
73
|
+
logger.info "Passthrough: #{transaction}"
|
74
|
+
transaction.passthrough!($R)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
79
78
|
end
|
80
79
|
|
81
80
|
# RExec daemon runner
|