rubydns 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
- return answer.encode
185
+ if block_given?
186
+ yield answer
187
+ else
188
+ return answer
189
+ end
168
190
  end
169
191
  end
170
192
  end
@@ -44,7 +44,7 @@ module RubyDNS
44
44
  @question = question
45
45
  @resource_class = resource_class
46
46
  @answer = answer
47
-
47
+
48
48
  @question_appended = false
49
49
  end
50
50
 
@@ -16,8 +16,8 @@
16
16
  module RubyDNS
17
17
  module VERSION #:nodoc:
18
18
  MAJOR = 0
19
- MINOR = 1
20
- TINY = 8
19
+ MINOR = 2
20
+ TINY = 0
21
21
 
22
22
  STRING = [MAJOR, MINOR, TINY].join('.')
23
23
  end
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
- # Setup server sockets
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
- begin
77
- # Listen for incoming packets
78
- while true
79
- ready = IO.select(sockets)
80
-
81
- ready[0].each do |socket|
82
- packet, sender = socket.recvfrom(1024*5)
83
- server.logger.debug "Receiving incoming query..."
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
- rescue Interrupt
103
- server.logger.info "Server interrupted - stopping #{threads.list.size} request(s)."
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
- # UDP Socket does per packet reverse lookups unless this is set.
40
- UDPSocket.do_not_reverse_lookup = true
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
- $stderr.puts "Sorry, this command needs to be run as root!"
45
- exit 1
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
- @@var_directory = File.dirname(__FILE__)
51
-
52
- def self.run
53
- # Bind to port 53 (UDP)
54
- socket = UDPSocket.new
55
- socket.bind("0.0.0.0", 53)
56
-
57
- # Drop priviledges
58
- RExec.change_user(RUN_AS)
59
-
60
- # Don't buffer output (for debug purposes)
61
- $stderr.sync = true
62
-
63
- # Use upstream DNS for name resolution (These ones are Orcon DNS in NZ)
64
- $R = Resolv::DNS.new(:nameserver => ["60.234.1.1", "60.234.2.2"])
65
-
66
- # Start the RubyDNS server
67
- RubyDNS::run_server(:listen => [socket]) do
68
- match("test.mydomain.org", :A) do |transaction|
69
- transaction.respond!("10.0.0.80")
70
- end
71
-
72
- # Default DNS handler
73
- otherwise do |transaction|
74
- logger.info "Passthrough: #{transaction}"
75
- transaction.passthrough!($R)
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