rubydns 0.6.7 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -4
- data/.yardopts +1 -0
- data/README.md +98 -92
- data/lib/rubydns.rb +5 -47
- data/lib/rubydns/{extensions/string-1.9.3.rb → binary_string.rb} +2 -0
- data/lib/rubydns/extensions/logger.rb +1 -1
- data/lib/rubydns/extensions/resolv.rb +1 -5
- data/lib/rubydns/extensions/string.rb +1 -0
- data/lib/rubydns/handler.rb +7 -2
- data/lib/rubydns/message.rb +17 -4
- data/lib/rubydns/resolver.rb +3 -0
- data/lib/rubydns/server.rb +182 -93
- data/lib/rubydns/transaction.rb +110 -149
- data/lib/rubydns/version.rb +1 -1
- data/rubydns.gemspec +1 -1
- data/test/examples/dropping-dns.rb +2 -2
- data/test/examples/fortune-dns.rb +2 -2
- data/test/examples/soa-dns.rb +1 -1
- data/test/examples/wikipedia-dns.rb +2 -2
- data/test/test_daemon.rb +3 -3
- data/test/test_message.rb +41 -0
- data/test/test_passthrough.rb +5 -1
- data/test/test_rules.rb +3 -3
- data/test/test_slow_server.rb +8 -6
- data/test/test_truncation.rb +3 -3
- metadata +10 -9
- data/lib/rubydns/extensions/hexdump.rb +0 -38
- data/lib/rubydns/extensions/string-1.8.rb +0 -35
- data/lib/rubydns/extensions/string-1.9.2.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 190faf76114f4bfe4bcbff8896f6d54ff4d75667
|
4
|
+
data.tar.gz: 76d443584858fdfb9d8223215c47b3935d22f53c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d850649e8bb3f66638a30519898ece93a710c08925e83a8ca6ef15fce6474f80795133d71dfac0f67756f8945596771ddc6c0e98d6fd554a40e081b6ba41e61
|
7
|
+
data.tar.gz: e797f439ff7aaa0de6b2cbe15621ebf514a24a39441b200ccc09a15cc8baabc6fbfdf5ab95e69c40f31ef2925addce7f436bf5ea8598861bbbb7e072e922f2a5
|
data/.travis.yml
CHANGED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup=markdown
|
data/README.md
CHANGED
@@ -29,122 +29,128 @@ Or install it yourself as:
|
|
29
29
|
|
30
30
|
This is copied from `test/examples/test-dns-2.rb`. It has been simplified slightly.
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
run
|
60
|
-
```
|
32
|
+
#!/usr/bin/env ruby
|
33
|
+
require 'rubydns'
|
34
|
+
|
35
|
+
INTERFACES = [
|
36
|
+
[:udp, "0.0.0.0", 53],
|
37
|
+
[:tcp, "0.0.0.0", 53]
|
38
|
+
]
|
39
|
+
Name = Resolv::DNS::Name
|
40
|
+
IN = Resolv::DNS::Resource::IN
|
41
|
+
|
42
|
+
# Use upstream DNS for name resolution.
|
43
|
+
UPSTREAM = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
|
44
|
+
|
45
|
+
def self.run
|
46
|
+
# Start the RubyDNS server
|
47
|
+
RubyDNS::run_server(:listen => INTERFACES) do
|
48
|
+
match(/test.mydomain.org/, IN::A) do |transaction|
|
49
|
+
transaction.respond!("10.0.0.80")
|
50
|
+
end
|
51
|
+
|
52
|
+
# Default DNS handler
|
53
|
+
otherwise do |transaction|
|
54
|
+
transaction.passthrough!(UPSTREAM)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
run
|
61
59
|
|
62
60
|
Start the server using `rvmsudo ./test.rb`. You can then test it using dig:
|
63
61
|
|
64
|
-
|
65
|
-
$ dig @localhost
|
66
|
-
$ dig @localhost
|
67
|
-
$ dig @localhost google.com
|
68
|
-
```
|
62
|
+
$ dig @localhost test1.mydomain.org
|
63
|
+
$ dig @localhost dev.mydomain.org
|
64
|
+
$ dig @localhost google.com
|
69
65
|
|
70
66
|
## Compatibility
|
71
67
|
|
72
|
-
### Migrating from RubyDNS 0.
|
68
|
+
### Migrating from RubyDNS 0.6.x to 0.7.x
|
69
|
+
|
70
|
+
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.
|
71
|
+
|
72
|
+
RubyDNS::run_server(:listen => SERVER_PORTS) do
|
73
|
+
match(/\.*.com/, IN::A) do |transaction|
|
74
|
+
# Won't block and won't continue until handle.resume is called.
|
75
|
+
defer do |fiber|
|
76
|
+
# No domain exists, after 5 seconds:
|
77
|
+
EventMachine::Timer.new(5) do
|
78
|
+
transaction.fail!(:NXDomain)
|
79
|
+
|
80
|
+
fiber.resume
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
otherwise do
|
86
|
+
transaction.fail!(:NXDomain)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
You can see a complete example in `test/test_slow_server.rb`.
|
91
|
+
|
92
|
+
#### Server structure changes
|
93
|
+
|
94
|
+
When integrating RubyDNS into another project, the rule based DSL is often a hurdle rather than a feature. Thus, the rule-based DSL component of `RubyDNS::Server` class has been separated into a derived `RubyDNS::RuleBasedServer` class. `RubyDNS::Server` can be derived and the `RubyDNS::Server#process` method can be overridden to provide a single entry point for DNS processing.
|
95
|
+
|
96
|
+
In addition, `RubyDNS::Server#run` can now start the server, provided you are within an `EventMachine#run` context. The existing entry point, `RubyDNS::run_server` provides the same rule-based DSL as previous versions.
|
97
|
+
|
98
|
+
#### Method name changes
|
99
|
+
|
100
|
+
Some method names have changed to improve consistency.
|
101
|
+
|
102
|
+
- `failure!` became `fail!`
|
103
|
+
- `append` became `add`
|
104
|
+
- `append_query!` became `append!`
|
105
|
+
|
106
|
+
### Migrating from RubyDNS 0.5.x to 0.6.x
|
73
107
|
|
74
108
|
The order of arguments to pattern based rules has changed. For regular expression based rules, the arguments are now ordered `|transaction, match_data|`. The main reason for this change was that in many cases match_data is not important and can thus be ignored, e.g. `|transaction|`.
|
75
109
|
|
76
110
|
Going forward, Ruby 1.8.x is no longer supported.
|
77
111
|
|
78
|
-
### Migrating from RubyDNS 0.
|
79
|
-
|
80
|
-
Due to changes in `resolv.rb`, superficial parts of RubyDNS have changed. Rather than using `:A` to specify A-records, one must now use the class name.
|
81
|
-
```ruby
|
82
|
-
match(..., :A)
|
83
|
-
```
|
84
|
-
becomes
|
85
|
-
```ruby
|
86
|
-
IN = Resolv::DNS::Resource::IN
|
87
|
-
match(..., IN::A)
|
88
|
-
```
|
89
|
-
### Migrating from RubyDNS 0.4.x to 0.5.x ###
|
112
|
+
### Migrating from RubyDNS 0.4.x to 0.5.x
|
90
113
|
|
91
114
|
The system standard resolver was synchronous, and this could stall the server when making upstream requests to other DNS servers. A new resolver `RubyDNS::Resolver` now provides an asynchronous interface and the `Transaction::passthrough` makes exclusive use of this to provide high performance asynchonous resolution.
|
92
115
|
|
93
116
|
Here is a basic example of how to use the new resolver in full. It is important to provide both `:udp` and `:tcp` connection specifications, so that large requests will be handled correctly:
|
94
|
-
```ruby
|
95
|
-
resolver = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
|
96
|
-
|
97
|
-
EventMachine::run do
|
98
|
-
resolver.query('google.com', IN::A) do |response|
|
99
|
-
case response
|
100
|
-
when RubyDNS::Message
|
101
|
-
puts "Got response: #{response.answers.first}"
|
102
|
-
else
|
103
|
-
# Response is of class RubyDNS::ResolutionFailure
|
104
|
-
puts "Failed: #{response.message}"
|
105
|
-
end
|
106
117
|
|
107
|
-
|
118
|
+
resolver = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
|
119
|
+
|
120
|
+
EventMachine::run do
|
121
|
+
resolver.query('google.com', IN::A) do |response|
|
122
|
+
case response
|
123
|
+
when RubyDNS::Message
|
124
|
+
puts "Got response: #{response.answers.first}"
|
125
|
+
else
|
126
|
+
# Response is of class RubyDNS::ResolutionFailure
|
127
|
+
puts "Failed: #{response.message}"
|
128
|
+
end
|
129
|
+
|
130
|
+
EventMachine::stop
|
131
|
+
end
|
108
132
|
end
|
109
|
-
|
110
|
-
```
|
133
|
+
|
111
134
|
Existing code that uses `Resolv::DNS` as a resolver will need to be updated:
|
112
|
-
|
113
|
-
# 1/ Add this at the top of your file; Host specific system information:
|
114
|
-
require 'rubydns/system'
|
135
|
+
|
136
|
+
# 1/ Add this at the top of your file; Host specific system information:
|
137
|
+
require 'rubydns/system'
|
115
138
|
|
116
|
-
# 2/ Change from R = Resolv::DNS.new to:
|
117
|
-
R = RubyDNS::Resolver.new(RubyDNS::System::nameservers)
|
118
|
-
```
|
119
|
-
Everything else in the server can remain the same. You can see a complete example in `test/test_resolver.rb`.
|
139
|
+
# 2/ Change from R = Resolv::DNS.new to:
|
140
|
+
R = RubyDNS::Resolver.new(RubyDNS::System::nameservers)
|
120
141
|
|
121
|
-
|
142
|
+
Everything else in the server can remain the same. You can see a complete example in `test/test_resolver.rb`.
|
122
143
|
|
123
|
-
|
144
|
+
### Migrating from RubyDNS 0.3.x to 0.4.x
|
124
145
|
|
125
|
-
|
126
|
-
transaction.defer!
|
127
|
-
```
|
146
|
+
Due to changes in `resolv.rb`, superficial parts of RubyDNS have changed. Rather than using `:A` to specify A-records, one must now use the class name.
|
128
147
|
|
129
|
-
|
130
|
-
```ruby
|
131
|
-
RubyDNS::run_server(:listen => SERVER_PORTS) do
|
132
|
-
match(/\.*.com/, IN::A) do |transaction|
|
133
|
-
transaction.defer!
|
134
|
-
|
135
|
-
# No domain exists, after 5 seconds:
|
136
|
-
EventMachine::Timer.new(5) do
|
137
|
-
transaction.failure!(:NXDomain)
|
138
|
-
end
|
139
|
-
end
|
148
|
+
match(..., :A)
|
140
149
|
|
141
|
-
|
142
|
-
transaction.failure!(:NXDomain)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
```
|
150
|
+
becomes
|
146
151
|
|
147
|
-
|
152
|
+
IN = Resolv::DNS::Resource::IN
|
153
|
+
match(..., IN::A)
|
148
154
|
|
149
155
|
## Contributing
|
150
156
|
|
@@ -163,7 +169,7 @@ You can see a complete example in `test/test_slow_server.rb`.
|
|
163
169
|
|
164
170
|
Released under the MIT license.
|
165
171
|
|
166
|
-
Copyright, 2009, 2012, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
|
172
|
+
Copyright, 2009, 2012, 2014, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
|
167
173
|
|
168
174
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
169
175
|
of this software and associated documentation files (the "Software"), to deal
|
data/lib/rubydns.rb
CHANGED
@@ -20,14 +20,6 @@
|
|
20
20
|
|
21
21
|
require 'rubydns/version'
|
22
22
|
|
23
|
-
if RUBY_VERSION < "1.9"
|
24
|
-
require 'rubydns/extensions/string-1.8'
|
25
|
-
elsif RUBY_VERSION < "1.9.3"
|
26
|
-
require 'rubydns/extensions/string-1.9.2'
|
27
|
-
else
|
28
|
-
require 'rubydns/extensions/string-1.9.3'
|
29
|
-
end
|
30
|
-
|
31
23
|
require 'rubydns/message'
|
32
24
|
require 'rubydns/server'
|
33
25
|
require 'rubydns/resolver'
|
@@ -42,50 +34,16 @@ require 'rubydns/handler'
|
|
42
34
|
|
43
35
|
module RubyDNS
|
44
36
|
|
45
|
-
# Run a server with the given rules.
|
46
|
-
#
|
47
|
-
# <tt>:interfaces</tt>:: A set of sockets or addresses as defined below.
|
48
|
-
#
|
49
|
-
# One important feature of DNS is the port it runs on. The <tt>options[:listen]</tt>
|
50
|
-
# allows you to specify a set of network interfaces and ports to run the server on. This
|
51
|
-
# must be a list of <tt>[protocol, interface address, port]</tt>.
|
52
|
-
#
|
53
|
-
# INTERFACES = [[:udp, "0.0.0.0", 5300]]
|
54
|
-
# RubyDNS::run_server(:listen => INTERFACES) do
|
55
|
-
# ...
|
56
|
-
# end
|
57
|
-
#
|
58
|
-
# You can specify already connected sockets if need be:
|
59
|
-
#
|
60
|
-
# socket = UDPSocket.new; socket.bind("0.0.0.0", 53)
|
61
|
-
# Process::Sys.setuid(server_uid)
|
62
|
-
# INTERFACES = [socket]
|
63
|
-
#
|
64
|
-
# The default interface is <tt>[[:udp, "0.0.0.0", 53]]</tt>. The server typically needs
|
65
|
-
# to run as root for this to work, since port 53 is privileged.
|
66
|
-
#
|
37
|
+
# Run a server with the given rules.
|
67
38
|
def self.run_server (options = {}, &block)
|
68
|
-
server = RubyDNS::
|
69
|
-
server.logger.info "Starting RubyDNS server (v#{RubyDNS::VERSION})..."
|
70
|
-
|
71
|
-
options[:listen] ||= [[:udp, "0.0.0.0", 53], [:tcp, "0.0.0.0", 53]]
|
39
|
+
server = RubyDNS::RuleBasedServer.new(&block)
|
72
40
|
|
73
41
|
EventMachine.run do
|
74
|
-
server.
|
75
|
-
|
76
|
-
# Setup server sockets
|
77
|
-
options[:listen].each do |spec|
|
78
|
-
server.logger.info "Listening on #{spec.join(':')}"
|
79
|
-
if spec[0] == :udp
|
80
|
-
EventMachine.open_datagram_socket(spec[1], spec[2], UDPHandler, server)
|
81
|
-
elsif spec[0] == :tcp
|
82
|
-
EventMachine.start_server(spec[1], spec[2], TCPHandler, server)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
server.fire(:start)
|
42
|
+
server.run(options)
|
87
43
|
end
|
88
44
|
|
89
45
|
server.fire(:stop)
|
90
46
|
end
|
47
|
+
|
48
|
+
|
91
49
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
|
1
2
|
# Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
3
|
#
|
3
4
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
@@ -21,6 +22,7 @@
|
|
21
22
|
require 'stringio'
|
22
23
|
|
23
24
|
module RubyDNS
|
25
|
+
# A helper class for processing incoming network data.
|
24
26
|
class BinaryStringIO < StringIO
|
25
27
|
def initialize
|
26
28
|
super
|
@@ -23,11 +23,7 @@ require 'resolv'
|
|
23
23
|
class Resolv
|
24
24
|
class DNS
|
25
25
|
class Message
|
26
|
-
# Merge the given message with this message. A number of heuristics are
|
27
|
-
# applied in order to ensure that the result makes sense. For example,
|
28
|
-
# If the current message is not recursive but is being merged with a
|
29
|
-
# message that was recursive, this bit is maintained. If either message
|
30
|
-
# is authoritive, then the result is also authoritive.
|
26
|
+
# Merge the given message with this message. A number of heuristics are applied in order to ensure that the result makes sense. For example, If the current message is not recursive but is being merged with a message that was recursive, this bit is maintained. If either message is authoritive, then the result is also authoritive.
|
31
27
|
#
|
32
28
|
# Modifies the current message in place.
|
33
29
|
def merge! (other)
|
data/lib/rubydns/handler.rb
CHANGED
@@ -19,18 +19,21 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
require 'rubydns/message'
|
22
|
+
require 'rubydns/binary_string'
|
22
23
|
|
23
24
|
module RubyDNS
|
24
|
-
|
25
|
+
# @returns the [port, ip address] of the given connection.
|
25
26
|
def self.get_peer_details(connection)
|
26
27
|
Socket.unpack_sockaddr_in(connection.get_peername)
|
27
28
|
end
|
28
29
|
|
30
|
+
# Handling incoming UDP requests, which are single data packets, and pass them on to the given server.
|
29
31
|
module UDPHandler
|
30
32
|
def initialize(server)
|
31
33
|
@server = server
|
32
34
|
end
|
33
|
-
|
35
|
+
|
36
|
+
# Process a packet of data with the given server. If an exception is thrown, a failure message will be sent back.
|
34
37
|
def self.process(server, data, options = {}, &block)
|
35
38
|
server.logger.debug "Receiving incoming query (#{data.bytesize} bytes)..."
|
36
39
|
query = nil
|
@@ -98,6 +101,7 @@ module RubyDNS
|
|
98
101
|
@processed = 0
|
99
102
|
end
|
100
103
|
|
104
|
+
# Receive the data via a TCP connection, process messages when we receive the indicated amount of data.
|
101
105
|
def receive_data(data)
|
102
106
|
# We buffer data until we've received the entire packet:
|
103
107
|
@buffer.write(data)
|
@@ -132,6 +136,7 @@ module RubyDNS
|
|
132
136
|
end
|
133
137
|
end
|
134
138
|
|
139
|
+
# Check that all data received was processed.
|
135
140
|
def unbind
|
136
141
|
if @processed != @buffer.size
|
137
142
|
raise LengthError.new("Unprocessed data remaining (#{@buffer.size - @processed} bytes unprocessed)")
|
data/lib/rubydns/message.rb
CHANGED
@@ -46,18 +46,31 @@ module RubyDNS
|
|
46
46
|
RubyDNS.log_exception(bad_messages_log, error)
|
47
47
|
end
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
|
+
# Decodes binary data into a {Message}.
|
50
51
|
def self.decode_message(data)
|
52
|
+
# Otherwise the decode process might fail with non-binary data.
|
51
53
|
if data.respond_to? :force_encoding
|
52
54
|
data.force_encoding("BINARY")
|
53
55
|
end
|
54
56
|
|
55
|
-
|
57
|
+
begin
|
58
|
+
return Message.decode(data)
|
59
|
+
rescue DecodeError
|
60
|
+
raise
|
61
|
+
rescue StandardError => error
|
62
|
+
new_error = DecodeError.new(error.message)
|
63
|
+
new_error.set_backtrace(error.backtrace)
|
64
|
+
|
65
|
+
raise new_error
|
66
|
+
end
|
67
|
+
|
56
68
|
rescue => error
|
69
|
+
# Log the bad messsage if required:
|
57
70
|
if @dump_bad_message
|
58
|
-
@dump_bad_message.call(
|
71
|
+
@dump_bad_message.call(error, data)
|
59
72
|
end
|
60
73
|
|
61
74
|
raise
|
62
75
|
end
|
63
|
-
end
|
76
|
+
end
|