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