rubydns 0.8.1 → 0.8.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dbd9b4d8a8eec2e1ae92fb9d8e5b57cf0eb120d4
4
- data.tar.gz: dc2b13bae0244a6bade2b299f13b7ce1524aafc1
3
+ metadata.gz: 204ba4e799634855d87e3a5b0d4969e17b7aafcf
4
+ data.tar.gz: 55f9a962495018de41eeb37656b61d2e9580e519
5
5
  SHA512:
6
- metadata.gz: 661f8462c7fd1f6769b1674a556d34c3859b8ef8832435d294c9879a49d230a27ec1cb961bfc4814d43e1d81d5ea5b2b701c7f423465ab0b24c9f72df4822f7e
7
- data.tar.gz: 99aab4506a8903ad037f6bcbfb1d0fb8c64a25c48b9599f14336e4bb4522649de45b24ff8d983cbf1ef598730ee70ea53b923e9f425d630921eb89da6f670113
6
+ metadata.gz: 9a08e54d3e38cb82e735a65750dae55452c94115d28a1ea115452069afacb6ac978645de0dd25a547c65168703249df4a6f5bbb75254e8a4d5b1e368db58229d
7
+ data.tar.gz: 18220b387aec7b2b145f1af7760947b08aee115ab87a8e04fdbc34d0d230c0d49a3396c0252b3e87bf94d956982bba1aa905106f368059db6c286fbb42c03924
data/.travis.yml CHANGED
@@ -1,4 +1,8 @@
1
1
  language: ruby
2
+ before_install:
3
+ - sudo apt-get update -qq
4
+ - sudo apt-get install -y bind9
2
5
  rvm:
3
- - 2.0.0
4
- - 2.1.0
6
+ - 1.9
7
+ - 2.0
8
+ - 2.1
data/README.md CHANGED
@@ -8,7 +8,7 @@ For examples and documentation please see the main [project page][1].
8
8
 
9
9
  [1]: http://www.codeotaku.com/projects/rubydns/
10
10
 
11
- [![Build Status](https://travis-ci.org/ioquatix/rubydns.svg)](https://travis-ci.org/ioquatix/rubydns)
11
+ [![Build Status](https://travis-ci.org/ioquatix/rubydns.svg?branch=master)](https://travis-ci.org/ioquatix/rubydns)
12
12
  [![Code Climate](https://codeclimate.com/github/ioquatix/rubydns.png)](https://codeclimate.com/github/ioquatix/rubydns)
13
13
 
14
14
  ## Installation
@@ -63,6 +63,10 @@ Start the server using `rvmsudo ./test.rb`. You can then test it using dig:
63
63
  $ dig @localhost dev.mydomain.org
64
64
  $ dig @localhost google.com
65
65
 
66
+ ### File Handle Limitations
67
+
68
+ On some platforms (e.g. Mac OS X) the number of file descriptors is relatively low by default and should be increased by calling `ulimit -n 10000` before running tests or even before starting a server which expects a large number of concurrent incoming connections.
69
+
66
70
  ### Custom servers
67
71
 
68
72
  It is possible to create and integrate your own custom servers.
@@ -79,12 +83,44 @@ It is possible to create and integrate your own custom servers.
79
83
 
80
84
  This is the best way to integrate with other projects.
81
85
 
86
+ ## Performance
87
+
88
+ We welcome additional benchmarks and feedback regarding RubyDNS performance.
89
+
90
+ ### Server
91
+
92
+ The performance is on the same magnitude as `bind9` however `bind9` supports multiple CPUs and thus can scale more easily than RubyDNS using MRI. Some basic benchmarks resolving 1000 names concurrently, repeated 5 times, using `RubyDNS::Resolver` gives the following:
93
+
94
+ Testing server performance...
95
+ user system total real
96
+ RubyDNS::Server 1.040000 0.320000 1.360000 ( 6.469213)
97
+ Bind9 1.940000 0.120000 2.060000 ( 2.062983)
98
+
99
+ These benchmarks are included in the unit tests. To test bind9 performance, it must be installed and `which named` must return the executable.
100
+
101
+ ### Resolver
102
+
103
+ The `RubyDNS::Resolver` is highly concurrent and can resolve individual names as fast as the built in `Resolv::DNS` resolver. Because the resolver is asynchronous, when dealing with multiple names, it can work more efficiently:
104
+
105
+ Comparing resolvers...
106
+ user system total real
107
+ RubyDNS::Resolver 0.020000 0.010000 0.030000 ( 0.280679)
108
+ Resolv::DNS 0.030000 0.010000 0.040000 ( 2.801773)
109
+
110
+ These benchmarks are included in the unit tests.
111
+
82
112
  ## Compatibility
83
113
 
84
114
  ### Migrating from RubyDNS 0.7.x to 0.8.x
85
115
 
86
116
  The primary change is the removal of the dependency on `RExec` which was used for daemons and the addition of the testing dependency `process-daemon`. In order to create and run your own daemon, you may use `process-daemon` or another tool of your choice.
87
117
 
118
+ The transaction options are now conveniently available:
119
+
120
+ transaction.options[key] == transaction[key]
121
+
122
+ The remote peer address used to be available directly via `transaction[:peer]` but profiling revealed that the `EventMachine::Connection#get_peername` was moderately expensive. Therefore, the incoming connection is now available in `transaction[:connection]` and more specifically `transaction[:peer]` is no longer available and replaced by `transaction[:connection].peername` which gives `[ip_address, port]`.
123
+
88
124
  ### Migrating from RubyDNS 0.6.x to 0.7.x
89
125
 
90
126
  The asynchronous deferred processing became the default and only method for processing requests in `0.7.0`. This simplifies the API but there were a few changes, notably the removal of `defer!` and the addition of `defer`. The reason for this was due to issues relating to deferred processing and the flow of control, which were confusing and introduced bugs in specific situations. Now, you can assume flow control through the entire block even with non-blocking functions.
@@ -22,20 +22,24 @@ require_relative 'message'
22
22
  require_relative 'binary_string'
23
23
 
24
24
  module RubyDNS
25
- # @returns the [port, ip address] of the given connection.
26
- def self.get_peer_details(connection)
27
- Socket.unpack_sockaddr_in(connection.get_peername)
25
+ module Peername
26
+ # Available for both UDP and TCP connections, returns `[address, port]`.
27
+ def peername
28
+ Socket.unpack_sockaddr_in(self.get_peername).reverse
29
+ end
28
30
  end
29
31
 
30
32
  # Handling incoming UDP requests, which are single data packets, and pass them on to the given server.
31
33
  module UDPHandler
34
+ include Peername
35
+
32
36
  def initialize(server)
33
37
  @server = server
34
38
  end
35
39
 
36
40
  # Process a packet of data with the given server. If an exception is thrown, a failure message will be sent back.
37
41
  def self.process(server, data, options = {}, &block)
38
- server.logger.debug "Receiving incoming query (#{data.bytesize} bytes)..."
42
+ server.logger.debug {"Receiving incoming query (#{data.bytesize} bytes)..."}
39
43
  query = nil
40
44
 
41
45
  begin
@@ -63,17 +67,20 @@ module RubyDNS
63
67
  end
64
68
  end
65
69
 
70
+ def peername
71
+ Socket.unpack_sockaddr_in(self.get_peername)
72
+ end
73
+
66
74
  def receive_data(data)
67
- peer_port, peer_ip = RubyDNS::get_peer_details(self)
68
- options = {:peer => peer_ip}
75
+ options = {:connection => self}
69
76
 
70
77
  UDPHandler.process(@server, data, options) do |answer|
71
78
  data = answer.encode
72
79
 
73
- @server.logger.debug "Writing response to client (#{data.bytesize} bytes) via UDP..."
80
+ @server.logger.debug {"Writing response to client (#{data.bytesize} bytes) via UDP..."}
74
81
 
75
82
  if data.bytesize > UDP_TRUNCATION_SIZE
76
- @server.logger.warn "Response via UDP was larger than #{UDP_TRUNCATION_SIZE}!"
83
+ @server.logger.warn {"Response via UDP was larger than #{UDP_TRUNCATION_SIZE}!"}
77
84
 
78
85
  # Reencode data with truncation flag marked as true:
79
86
  truncation_error = Resolv::DNS::Message.new(answer.id)
@@ -83,7 +90,8 @@ module RubyDNS
83
90
  end
84
91
 
85
92
  # We explicitly use the ip and port given, because we found that send_data was unreliable in a callback.
86
- self.send_datagram(data, peer_ip, peer_port)
93
+ # self.send_datagram(data, peer_ip, peer_port)
94
+ self.send_data(data)
87
95
  end
88
96
  end
89
97
  end
@@ -92,6 +100,8 @@ module RubyDNS
92
100
  end
93
101
 
94
102
  module TCPHandler
103
+ include Peername
104
+
95
105
  def initialize(server)
96
106
  @server = server
97
107
 
@@ -120,7 +130,7 @@ module RubyDNS
120
130
  if (@buffer.size - @processed) >= @length
121
131
  data = @buffer.string.byteslice(@processed, @length)
122
132
 
123
- options = {:peer => RubyDNS::get_peer_details(self)}
133
+ options = {:connection => self}
124
134
 
125
135
  UDPHandler.process(@server, data, options) do |answer|
126
136
  data = answer.encode
@@ -63,6 +63,8 @@ module RubyDNS
63
63
 
64
64
  # Process an incoming DNS message. Returns a serialized message to be sent back to the client.
65
65
  def process_query(query, options = {}, &block)
66
+ start_time = Time.now
67
+
66
68
  # Setup answer
67
69
  answer = Resolv::DNS::Message::new(query.id)
68
70
  answer.qr = 1 # 0 = Query, 1 = Response
@@ -77,20 +79,23 @@ module RubyDNS
77
79
 
78
80
  begin
79
81
  query.question.each do |question, resource_class|
80
- @logger.debug "Processing question #{question} #{resource_class}..."
82
+ @logger.debug {"[#{query.id}] Processing question #{question} #{resource_class}..."}
81
83
 
82
84
  transaction = Transaction.new(self, query, question, resource_class, answer, options)
83
85
 
84
86
  transaction.process
85
87
  end
86
88
  rescue
87
- @logger.error "Exception thrown while processing #{transaction}!"
89
+ @logger.error {"[#{query.id}] Exception thrown while processing #{transaction}!"}
88
90
  RubyDNS.log_exception(@logger, $!)
89
91
 
90
92
  answer.rcode = Resolv::DNS::RCode::ServFail
91
93
  end
92
-
94
+
93
95
  yield answer
96
+
97
+ end_time = Time.now
98
+ @logger.debug {"[#{query.id}] Time to process request: #{end_time - start_time}s"}
94
99
  end.resume
95
100
  end
96
101
 
@@ -267,10 +272,10 @@ module RubyDNS
267
272
 
268
273
  # Give a name and a record type, try to match a rule and use it for processing the given arguments.
269
274
  def process(name, resource_class, *args)
270
- @logger.debug "Searching for #{name} #{resource_class.name}"
275
+ @logger.debug {"Searching for #{name} #{resource_class.name}"}
271
276
 
272
277
  @rules.each do |rule|
273
- @logger.debug "Checking rule #{rule}..."
278
+ @logger.debug {"Checking rule #{rule}..."}
274
279
 
275
280
  catch (:next) do
276
281
  # If the rule returns true, we assume that it was successful and no further rules need to be evaluated.
@@ -61,6 +61,10 @@ module RubyDNS
61
61
  # Any options or configuration associated with the given transaction.
62
62
  attr :options
63
63
 
64
+ def [] key
65
+ @options[key]
66
+ end
67
+
64
68
  # The name of the question, which is typically the requested hostname.
65
69
  def name
66
70
  @question.to_s
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module RubyDNS
22
- VERSION = "0.8.1"
22
+ VERSION = "0.8.4"
23
23
  end
data/lib/rubydns.rb CHANGED
@@ -31,6 +31,10 @@ module RubyDNS
31
31
  server = RubyDNS::RuleBasedServer.new(&block)
32
32
 
33
33
  EventMachine.run do
34
+ trap("INT") do
35
+ EventMachine::stop
36
+ end
37
+
34
38
  server.run(options)
35
39
  end
36
40
 
data/rubydns.gemspec CHANGED
@@ -33,6 +33,6 @@ Gem::Specification.new do |spec|
33
33
  spec.add_dependency("eventmachine", "~> 1.0.0")
34
34
 
35
35
  spec.add_development_dependency "bundler", "~> 1.3"
36
- spec.add_development_dependency "process-daemon", "~> 0.5.0"
36
+ spec.add_development_dependency "process-daemon", "~> 0.5.3"
37
37
  spec.add_development_dependency "rake"
38
38
  end
@@ -30,10 +30,6 @@ INTERFACES = [
30
30
  ]
31
31
 
32
32
  class DroppingDaemon < Process::Daemon
33
- # You can specify a specific directory to use for run-time information (pid, logs, etc):
34
- # @@base_directory = File.expand_path("../", __FILE__)
35
- # @@base_directory = "/var"
36
-
37
33
  Name = Resolv::DNS::Name
38
34
  IN = Resolv::DNS::Resource::IN
39
35
  R = RubyDNS::Resolver.new(RubyDNS::System::nameservers)
@@ -43,8 +43,6 @@ end
43
43
  # To use, start the daemon and try:
44
44
  # dig @localhost fortune CNAME
45
45
  class FortuneDNS < Process::Daemon
46
- @@base_directory = File.dirname(__FILE__)
47
-
48
46
  Name = Resolv::DNS::Name
49
47
  IN = Resolv::DNS::Resource::IN
50
48
 
@@ -35,10 +35,6 @@ INTERFACES = [
35
35
  # For more information, please see http://www.maxmind.com/en/geolite and http://geoip.rubyforge.org
36
36
 
37
37
  class GeoIPDNSDaemon < Process::Daemon
38
- # You can specify a specific directory to use for run-time information (pid, logs, etc):
39
- # @@base_directory = File.expand_path("../", __FILE__)
40
- # @@base_directory = "/var"
41
-
42
38
  Name = Resolv::DNS::Name
43
39
  IN = Resolv::DNS::Resource::IN
44
40
  R = RubyDNS::Resolver.new(RubyDNS::System::nameservers)
@@ -48,7 +44,7 @@ class GeoIPDNSDaemon < Process::Daemon
48
44
  RubyDNS::run_server(:listen => INTERFACES) do
49
45
  match(//, IN::A) do |transaction|
50
46
  location = nil
51
- peer = transaction.options[:peer]
47
+ peer = transaction.options[:connection].peername[0]
52
48
 
53
49
  if peer
54
50
  logger.debug "Looking up geographic information for peer #{peer}"
@@ -57,8 +57,6 @@ end
57
57
  # To use, start the daemon and try:
58
58
  # dig @localhost fortune CNAME
59
59
  class WikipediaDNS < Process::Daemon
60
- @@base_directory = File.dirname(__FILE__)
61
-
62
60
  Name = Resolv::DNS::Name
63
61
  IN = Resolv::DNS::Resource::IN
64
62
 
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'benchmark'
4
+ require 'rubydns'
5
+
6
+ Benchmark.bm(20) do |x|
7
+ DOMAINS = (1..1000).collect do |i|
8
+ "domain#{i}.local"
9
+ end
10
+
11
+ resolved = {}
12
+
13
+ x.report("RubyDNS::Resolver") do
14
+ resolver = RubyDNS::Resolver.new([[:udp, '127.0.0.1', 5300]])
15
+
16
+ # Number of requests remaining since this is an asynchronous event loop:
17
+ pending = DOMAINS.size
18
+
19
+ EventMachine::run do
20
+ DOMAINS.each do |domain|
21
+ resolver.addresses_for(domain) do |addresses|
22
+ resolved[domain] = addresses
23
+
24
+ EventMachine::stop if (pending -= 1) == 0
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ File.open("local.zone", "w") do |f|
4
+ f.write(DATA.read)
5
+
6
+ (1..5_000).each do |i|
7
+ f.puts "domain#{i} IN A #{69}.#{(i >> 16)%256}.#{(i >> 8)%256}.#{i%256}"
8
+ end
9
+ end
10
+
11
+ __END__
12
+ ;
13
+ ; BIND data file for local loopback interface
14
+ ;
15
+ $TTL 604800
16
+ @ IN SOA ns.local. postmaster.localhost (
17
+ 2 ; Serial
18
+ 604800 ; Refresh
19
+ 86400 ; Retry
20
+ 2419200 ; Expire
21
+ 604800 ) ; Negative Cache TTL
22
+ ;
23
+ local. IN NS ns.local.
24
+ ns.local. IN A 127.0.0.1
25
+