rubydns 0.6.0 → 2.0.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.
Files changed (58) hide show
  1. checksums.yaml +6 -14
  2. data/.gitignore +23 -14
  3. data/.rspec +4 -0
  4. data/.simplecov +15 -0
  5. data/.travis.yml +9 -5
  6. data/.yardopts +1 -0
  7. data/Gemfile +6 -2
  8. data/README.md +82 -92
  9. data/Rakefile +2 -5
  10. data/bin/rubydns-check +374 -0
  11. data/examples/Gemfile +7 -0
  12. data/examples/README.md +137 -0
  13. data/examples/basic-dns.rb +24 -0
  14. data/examples/cname.rb +25 -0
  15. data/{test/examples/dropping-dns.rb → examples/flakey-dns.rb} +28 -31
  16. data/{test/examples → examples}/fortune-dns.rb +42 -46
  17. data/examples/geoip-dns.rb +115 -0
  18. data/examples/simple.rb +25 -0
  19. data/{test/examples → examples}/soa-dns.rb +27 -27
  20. data/{test/examples → examples}/test-dns-1.rb +26 -20
  21. data/{test/examples → examples}/test-dns-2.rb +17 -19
  22. data/examples/wikipedia-dns.rb +107 -0
  23. data/lib/rubydns/rule_based_server.rb +180 -0
  24. data/lib/rubydns/version.rb +1 -1
  25. data/lib/rubydns.rb +13 -63
  26. data/rubydns.gemspec +29 -23
  27. data/spec/rubydns/daemon_spec.rb +114 -0
  28. data/{test/test_system.rb → spec/rubydns/injected_supervisor_spec.rb} +32 -25
  29. data/spec/rubydns/passthrough_spec.rb +85 -0
  30. data/spec/rubydns/rules_spec.rb +74 -0
  31. data/spec/spec_helper.rb +30 -0
  32. metadata +101 -78
  33. data/bin/rd-dns-check +0 -374
  34. data/bin/rd-resolve-test +0 -160
  35. data/lib/rubydns/chunked.rb +0 -34
  36. data/lib/rubydns/extensions/hexdump.rb +0 -38
  37. data/lib/rubydns/extensions/logger.rb +0 -30
  38. data/lib/rubydns/extensions/resolv.rb +0 -53
  39. data/lib/rubydns/extensions/string-1.8.rb +0 -35
  40. data/lib/rubydns/extensions/string-1.9.2.rb +0 -29
  41. data/lib/rubydns/extensions/string-1.9.3.rb +0 -31
  42. data/lib/rubydns/extensions/string.rb +0 -27
  43. data/lib/rubydns/handler.rb +0 -140
  44. data/lib/rubydns/message.rb +0 -41
  45. data/lib/rubydns/resolver.rb +0 -239
  46. data/lib/rubydns/server.rb +0 -241
  47. data/lib/rubydns/system.rb +0 -146
  48. data/lib/rubydns/transaction.rb +0 -250
  49. data/test/examples/geoip-dns.rb +0 -86
  50. data/test/helper.rb +0 -9
  51. data/test/test_daemon.rb +0 -100
  52. data/test/test_domains.txt +0 -185
  53. data/test/test_passthrough.rb +0 -80
  54. data/test/test_resolver.rb +0 -105
  55. data/test/test_rules.rb +0 -74
  56. data/test/test_slow_server.rb +0 -98
  57. data/test/test_truncation.rb +0 -78
  58. /data/{test → spec/rubydns}/hosts.txt +0 -0
data/bin/rubydns-check ADDED
@@ -0,0 +1,374 @@
1
+ #!/usr/bin/env ruby
2
+ # Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+
22
+ # Pulls down DNS data from old-dns
23
+ # rd-dns-check -s old-dns.mydomain.com -d mydomain.com. -f old-dns.yml
24
+
25
+ # Check data against old-dns
26
+ # rd-dns-check -s old-dns.mydomain.com -d mydomain.com. -c old-dns.yml
27
+
28
+ # Check data against new DNS server
29
+ # rd-dns-check -s 10.0.0.36 -d mydomain.com. -c old-dns.yml
30
+
31
+ require 'yaml'
32
+ require 'optparse'
33
+ require 'set'
34
+
35
+ class DNSRecord
36
+ def initialize(arr)
37
+ @record = arr
38
+ normalize
39
+ end
40
+
41
+ def normalize
42
+ @record[0] = @record[0].downcase
43
+ @record[1] = @record[1].upcase
44
+ @record[2] = @record[2].upcase
45
+ @record[3] = @record[3].downcase
46
+ end
47
+
48
+ def hostname
49
+ @record[0]
50
+ end
51
+
52
+ def klass
53
+ @record[1]
54
+ end
55
+
56
+ def type
57
+ @record[2]
58
+ end
59
+
60
+ def value
61
+ @record[3]
62
+ end
63
+
64
+ def is_address?
65
+ ["A", "AAAA"].include?(type)
66
+ end
67
+
68
+ def is_cname?
69
+ return type == "CNAME"
70
+ end
71
+
72
+ def to_s
73
+ "#{hostname.ljust(50)} #{klass.rjust(4)} #{type.rjust(5)} #{value}"
74
+ end
75
+
76
+ def key
77
+ "#{hostname}:#{klass}:#{type}".downcase
78
+ end
79
+
80
+ def to_a
81
+ @record
82
+ end
83
+
84
+ def == other
85
+ return @record == other.to_a
86
+ end
87
+ end
88
+
89
+ def dig(dns_server, cmd, exclude = ["TXT", "HINFO", "SOA", "NS"])
90
+ records = []
91
+
92
+ IO.popen("dig @#{dns_server} +nottlid +nocmd +noall +answer " + cmd) do |p|
93
+ p.each do |line|
94
+ r = line.chomp.split(/\s/, 4)
95
+
96
+ next if exclude.include?(r[2])
97
+
98
+ records << DNSRecord.new(r)
99
+ end
100
+ end
101
+
102
+ return records
103
+ end
104
+
105
+ def retrieve_records(dns_server, dns_root)
106
+ return dig(dns_server, "#{dns_root} AXFR")
107
+ end
108
+
109
+ def resolve_hostname(dns_server, hostname)
110
+ return dig(dns_server, "#{hostname} A").first
111
+ end
112
+
113
+ def resolve_address(dns_server, address)
114
+ return dig(dns_server, "-x #{address}").first
115
+ end
116
+
117
+ def print_summary(records, errors, okay, &block)
118
+ puts "[ Summary ]".center(72, "=")
119
+ puts "Checked #{records.size} record(s). #{errors} errors."
120
+ if errors == 0
121
+ puts "Everything seemed okay."
122
+ else
123
+ puts "The following records are okay:"
124
+ okay.each do |r|
125
+ if block_given?
126
+ yield r
127
+ else
128
+ puts "".rjust(12) + r.to_s
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ # Resolve hostnames to IP address "A" or "AAAA" records.
135
+ # Works through CNAME records in order to find out the final
136
+ # address if possible. Checks for loops in CNAME records.
137
+ def resolve_addresses(records)
138
+ addresses = {}
139
+ cnames = {}
140
+
141
+ # Extract all hostname -> ip address mappings
142
+ records.each do |r|
143
+ if r.is_address?
144
+ addresses[r.hostname] = r
145
+ elsif r.is_cname?
146
+ cnames[r.hostname] = r
147
+ end
148
+ end
149
+
150
+ cnames.each do |hostname, r|
151
+ q = r
152
+ trail = []
153
+ failed = false
154
+
155
+ # Keep track of CNAME records to avoid loops
156
+ while q.is_cname?
157
+ trail << q
158
+ q = cnames[q.value] || addresses[q.value]
159
+
160
+ # Q could be nil at this point, which means there was no address record
161
+ # Q could be already part of the trail, which means there was a loop
162
+ if q == nil || trail.include?(q)
163
+ failed = true
164
+ break
165
+ end
166
+ end
167
+
168
+ if failed
169
+ q = trail.last
170
+ puts "*** Warning: CNAME record #{hostname} does not point to actual address!"
171
+ trail.each_with_index do |r, idx|
172
+ puts idx.to_s.rjust(10) + ": " + r.to_s
173
+ end
174
+ end
175
+
176
+ addresses[r.hostname] = q
177
+ end
178
+
179
+ return addresses, cnames
180
+ end
181
+
182
+ def check_reverse(records, dns_server)
183
+ errors = 0
184
+ okay = []
185
+
186
+ puts "[ Checking Reverse Lookups ]".center(72, "=")
187
+
188
+ records.each do |r|
189
+ next unless r.is_address?
190
+
191
+ sr = resolve_address(dns_server, r.value)
192
+
193
+ if sr == nil
194
+ puts "*** Could not resolve host"
195
+ puts "".rjust(12) + r.to_s
196
+ errors += 1
197
+ elsif r.hostname != sr.value
198
+ puts "*** Hostname does not match"
199
+ puts "Primary: ".rjust(12) + r.to_s
200
+ puts "Secondary: ".rjust(12) + sr.to_s
201
+ errors += 1
202
+ else
203
+ okay << [r, sr]
204
+ end
205
+ end
206
+
207
+ print_summary(records, errors, okay) do |r|
208
+ puts "Primary:".rjust(12) + r[0].to_s
209
+ puts "Secondary:".rjust(12) + r[1].to_s
210
+ end
211
+ end
212
+
213
+ def ping_records(records)
214
+ addresses, cnames = resolve_addresses(records)
215
+
216
+ errors = 0
217
+ okay = []
218
+
219
+ puts "[ Pinging Records ]".center(72, "=")
220
+
221
+ addresses.each do |hostname, r|
222
+ ping = "ping -c 5 -t 5 -i 1 -o #{r.value} > /dev/null"
223
+
224
+ system(ping)
225
+
226
+ if $?.exitstatus == 0
227
+ okay << r
228
+ else
229
+ puts "*** Could not ping host #{hostname.dump}: #{ping.dump}"
230
+ puts "".rjust(12) + r.to_s
231
+ errors += 1
232
+ end
233
+ end
234
+
235
+ print_summary(records, errors, okay)
236
+ end
237
+
238
+ def query_records(primary, secondary_server)
239
+ addresses, cnames = resolve_addresses(primary)
240
+
241
+ okay = []
242
+ errors = 0
243
+
244
+ primary.each do |r|
245
+ sr = resolve_hostname(secondary_server, r.hostname)
246
+
247
+ if sr == nil
248
+ puts "*** Could not resolve hostname #{r.hostname.dump}"
249
+ puts "Primary: ".rjust(12) + r.to_s
250
+
251
+ rsr = resolve_address(secondary_server, (addresses[r.value] || r).value)
252
+ puts "Address: ".rjust(12) + rsr.to_s if rsr
253
+
254
+ errors += 1
255
+ elsif sr.value != r.value
256
+ ra = addresses[r.value] if r.is_cname?
257
+ sra = addresses[sr.value] if sr.is_cname?
258
+
259
+ if (sra || sr).value != (ra || r).value
260
+ puts "*** IP Address does not match"
261
+ puts "Primary: ".rjust(12) + r.to_s
262
+ puts "Resolved: ".rjust(12) + ra.to_s if ra
263
+ puts "Secondary: ".rjust(12) + sr.to_s
264
+ puts "Resolved: ".rjust(12) + sra.to_s if sra
265
+ errors += 1
266
+ end
267
+ else
268
+ okay << r
269
+ end
270
+ end
271
+
272
+ print_summary(primary, errors, okay)
273
+ end
274
+
275
+ def check_records(primary, secondary)
276
+ s = {}
277
+ okay = []
278
+ errors = 0
279
+
280
+ secondary.each do |r|
281
+ s[r.key] = r
282
+ end
283
+
284
+ puts "[ Checking Records ]".center(72, "=")
285
+
286
+ primary.each do |r|
287
+ sr = s[r.key]
288
+
289
+ if sr == nil
290
+ puts "*** Could not find record"
291
+ puts "Primary: ".rjust(12) + r.to_s
292
+ errors += 1
293
+ elsif sr != r
294
+ puts "*** Records are different"
295
+ puts "Primary: ".rjust(12) + r.to_s
296
+ puts "Secondary: ".rjust(12) + sr.to_s
297
+ errors += 1
298
+ else
299
+ okay << r
300
+ end
301
+ end
302
+
303
+ print_summary(primary, errors, okay)
304
+ end
305
+
306
+ OPTIONS = {
307
+ :DNSServer => nil,
308
+ :DNSRoot => ".",
309
+ }
310
+
311
+ ARGV.options do |o|
312
+ script_name = File.basename($0)
313
+
314
+ o.set_summary_indent(' ')
315
+ o.banner = "Usage: #{script_name} [options]"
316
+ o.define_head "This script is designed to test and check DNS servers."
317
+
318
+ o.on("-s ns.my.domain.", "--server ns.my.domain.", String, "The DNS server to query.") { |host| OPTIONS[:DNSServer] = host }
319
+ o.on("-d my.domain.", "--domain my.domain.", String, "The DNS zone to transfer/test.") { |host| OPTIONS[:DNSRoot] = host }
320
+
321
+ o.on("-f output.yml", "--fetch output.yml", String, "Pull down a list of hosts. Filters TXT and HINFO records. DNS transfers must be enabled.") { |f|
322
+ records = retrieve_records(OPTIONS[:DNSServer], OPTIONS[:DNSRoot])
323
+
324
+ output = (f ? File.open(f, "w") : STDOUT)
325
+
326
+ output.write(YAML::dump(records))
327
+
328
+ puts "#{records.size} record(s) retrieved."
329
+ }
330
+
331
+ o.on("-c input.yml", "--check input.yml", String, "Check that the DNS server returns results as specified by the file.") { |f|
332
+ input = (f ? File.open(f) : STDIN)
333
+
334
+ master_records = YAML::load(input.read)
335
+ secondary_records = retrieve_records(OPTIONS[:DNSServer], OPTIONS[:DNSRoot])
336
+
337
+ check_records(master_records, secondary_records)
338
+ }
339
+
340
+ o.on("-q input.yml", "--query input.yml", String, "Query the remote DNS server with all hostnames in the given file, and checks the IP addresses are consistent.") { |f|
341
+ input = (f ? File.open(f) : STDIN)
342
+
343
+ master_records = YAML::load(input.read)
344
+
345
+ query_records(master_records, OPTIONS[:DNSServer])
346
+ }
347
+
348
+ o.on("-p input.yml", "--ping input.yml", String, "Ping all hosts to check if they are available or not.") { |f|
349
+ input = (f ? File.open(f) : STDIN)
350
+
351
+ master_records = YAML::load(input.read)
352
+
353
+ ping_records(master_records)
354
+ }
355
+
356
+ o.on("-r input.yml", "--reverse input.yml", String, "Check that all address records have appropriate reverse entries.") { |f|
357
+ input = (f ? File.open(f) : STDIN)
358
+
359
+ master_records = YAML::load(input.read)
360
+
361
+ check_reverse(master_records, OPTIONS[:DNSServer])
362
+ }
363
+
364
+ o.separator ""
365
+ o.separator "Help and Copyright information"
366
+
367
+ o.on_tail("--copy", "Display copyright information") {
368
+ puts "#{script_name}. Copyright (c) 2009, 2011 Samuel Williams. Released under the MIT license."
369
+ puts "See http://www.oriontransfer.co.nz/ for more information."
370
+ exit
371
+ }
372
+
373
+ o.on_tail("-h", "--help", "Show this help message.") { puts o; exit }
374
+ end.parse!
data/examples/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rubydns', path: '../'
4
+ gem "process-daemon"
5
+ gem 'nokogiri'
6
+ gem 'http'
7
+ gem 'geoip'
@@ -0,0 +1,137 @@
1
+ # RubyDNS Examples
2
+
3
+ This directory contains several examples of customized RubyDNS servers,
4
+ intended to demonstrate how RubyDNS can be easily customized to specific
5
+ needs.
6
+
7
+ ## FlakeyDNS (flakey-dns.rb)
8
+
9
+ A DNS server that selectively drops queries based on the requested domain name. Queries for domains that match specified regular expressions (like 'microsoft.com' or 'sco.com') return NXDomain, while all other queries are passed to upstream resolvers.
10
+
11
+ By default this server will listen for UDP requests on port 5300 and does not need to be started as root.
12
+
13
+ To start the server, ensure that you're in the examples subdirectory and type
14
+
15
+ bundle
16
+ bundle exec ./flakey-dns.rb start
17
+
18
+ To see it in action you can then query some domains. For example,
19
+
20
+ dig @localhost -p 5300 slashdot.org -t A
21
+ dig @localhost -p 5300 www.hackernews.com -t A
22
+
23
+ give the correct results. But
24
+
25
+ dig @localhost -p 5300 microsoft.com -t A
26
+ dig @localhost -p 5300 www.microsoft.com -t A
27
+ dig @localhost -p 5300 www.microsoft.com
28
+
29
+ all give an NXDomain result.
30
+
31
+ ## FortuneDNS (fortune-dns.rb)
32
+
33
+ A DNS server that allows a client to generate fortunes and fetch them with subsequent requests. The server
34
+ 'remembers' the fortunes it generates, and can serve them to future requests. The reason for this is because most fortunes won't fit over UDP (maximum size 512 bytes) and the client will request the same fortune via TCP.
35
+
36
+ You will need to have the `fortune` app installed on your system. It comes installed by default on
37
+ most Linux distributions, and can be installed on a Mac with Homebrew by typing:
38
+
39
+ # Homebrew
40
+ brew install fortune
41
+ # MacPorts
42
+ sudo port install fortune
43
+ # Arch Linux
44
+ sudo pacman -S fortune-mod
45
+
46
+ By default this server will listen for UDP and TCP requests on port 53, and needs to be started as root. It
47
+ assumes the existence of a user 'daemon', as whom the process will run. If such a user doesn't exist on your
48
+ system, you will need to either create such a user or update the script to use a user that exists on your
49
+ system.
50
+
51
+ To start the server, ensure that you're in the examples subdirectory and type
52
+
53
+ bundle
54
+ sudo bundle exec ./fortune-dns.rb start
55
+
56
+ To create a new fortune type
57
+
58
+ dig @localhost fortune -t TXT
59
+
60
+ This will result in an DNS answer that looks something like this:
61
+
62
+ fortune. 0 IN TXT "Text Size: 714 Byte Size: 714"
63
+ fortune. 0 IN CNAME 32bf3bf2b0a2255f2df00ed9e95c8185.fortune.
64
+
65
+ Take the CNAME from this result and query it. For our example this would be:
66
+
67
+ dig @localhost 32bf3bf2b0a2255f2df00ed9e95c8185.fortune -t TXT
68
+
69
+ And your answer will be a fortune.
70
+
71
+ You can also generate a 'short' fortune by typing the following:
72
+
73
+ dig @localhost short.fortune -t TXT
74
+
75
+ or view the fortune stats with:
76
+
77
+ dig @localhost stats.fortune -t TXT
78
+
79
+ ## GeoIPDNS (geoip-dns.rb)
80
+
81
+ A sample DNS daemon that demonstrates how to use RubyDNS to build responses
82
+ that vary based on the geolocation of the requesting peer. Clients of this
83
+ server who request A records will get an answer IP address based on the
84
+ continent of the client IP address.
85
+
86
+ Please note that use of this example requires that the peer have a public
87
+ IP address. IP addresses on private networks or the localhost IP (127.0.0.1)
88
+ cannot be resolved to a location, and so will always yield the unknown result.
89
+
90
+ This daemon requires the file downloaded from
91
+ [MaxMind](http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz)
92
+ For more information on the GeoIP library, please click [here](http://www.maxmind.com/en/geolite)
93
+ or [here](https://github.com/cjheath/geoip). This file should be unzipped and placed in the
94
+ examples root directory, i.e. `examples/GeoLiteCountry.dat`.
95
+
96
+ By default this server will listen for UDP requests on port 5300 and does not need to be started as root.
97
+
98
+ To start the server, ensure that you're in the examples subdirectory and type
99
+
100
+ bundle
101
+ sudo bundle exec ./geoip-dns.rb start
102
+
103
+ To see the behavior, run a DNS query against the server where you are running the GeoIPDNS
104
+ daemon. Depending on the continent to which the client machine's IP address is mapped,
105
+ you will receive a different IP address in the answer section:
106
+
107
+ Africa - 1.1.1.1
108
+ Antarctica - 1.1.2.1
109
+ Asia - 1.1.3.1
110
+ Europe - 1.1.4.1
111
+ North America - 1.1.5.1
112
+ Oceania - 1.1.6.1
113
+ South America - 1.1.7.1
114
+
115
+ ## WikipediaDNS (wikipedia-dns.rb)
116
+
117
+ A DNS server that queries Wikipedia and returns summaries for specifically crafted queries.
118
+
119
+ By default this server will listen for UDP and TCP requests on port 53, and needs to be started as root. It
120
+ assumes the existence of a user 'daemon', as whom the process will run. If such a user doesn't exist on your
121
+ system, you will need to either create such a user or update the script to use a user that exists on your
122
+ system.
123
+
124
+ To start the server, ensure that you're in the examples subdirectory and type
125
+
126
+ bundle
127
+ sudo bundle exec ./wikipedia-dns.rb start
128
+
129
+ To query Wikipedia, pick a term - say, 'helium' - and make a DNS query like
130
+
131
+ dig @localhost helium.wikipedia -t TXT
132
+
133
+ The answer section should contain the summary for this topic from Wikipedia
134
+
135
+ helium.wikipedia. 86400 IN TXT "Helium is a chemical element with symbol He and atomic number 2. It is a colorless, odorless, tasteless, non-toxic, inert, monatomic gas that heads the noble gas group in the periodic table. Its boiling and melting points are the lowest among the elements" " and it exists only as a gas except in extreme conditions."
136
+
137
+ Long blocks of text cannot be easily replied in DNS as they must be chunked into segments at most 255 bytes. Long replies must be sent back using TCP.
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubydns'
3
+
4
+ INTERFACES = [
5
+ [:udp, "0.0.0.0", 5300],
6
+ [:tcp, "0.0.0.0", 5300],
7
+ ]
8
+
9
+ IN = Resolv::DNS::Resource::IN
10
+
11
+ # Use upstream DNS for name resolution.
12
+ UPSTREAM = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
13
+
14
+ # Start the RubyDNS server
15
+ RubyDNS::run_server(INTERFACES) do
16
+ match(%r{test.local}, IN::A) do |transaction|
17
+ transaction.respond!("10.0.0.80")
18
+ end
19
+
20
+ # Default DNS handler
21
+ otherwise do |transaction|
22
+ transaction.passthrough!(UPSTREAM)
23
+ end
24
+ end
data/examples/cname.rb ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubydns'
4
+ require 'rubydns/system'
5
+
6
+ INTERFACES = [
7
+ [:udp, "0.0.0.0", 5300],
8
+ [:tcp, "0.0.0.0", 5300]
9
+ ]
10
+
11
+ Name = Resolv::DNS::Name
12
+ IN = Resolv::DNS::Resource::IN
13
+
14
+ UPSTREAM = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
15
+
16
+ RubyDNS::run_server(INTERFACES) do
17
+ # How to respond to something other than what was requested.
18
+ match(//, IN::A) do |transaction|
19
+ transaction.respond!(Name.create('foo.bar'), resource_class: IN::CNAME)
20
+ end
21
+
22
+ otherwise do |transaction|
23
+ transaction.passthrough!(UPSTREAM)
24
+ end
25
+ end
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  # Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
4
+ #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  # of this software and associated documentation files (the "Software"), to deal
7
7
  # in the Software without restriction, including without limitation the rights
8
8
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  # copies of the Software, and to permit persons to whom the Software is
10
10
  # furnished to do so, subject to the following conditions:
11
- #
11
+ #
12
12
  # The above copyright notice and this permission notice shall be included in
13
13
  # all copies or substantial portions of the Software.
14
- #
14
+ #
15
15
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -20,56 +20,53 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  # THE SOFTWARE.
22
22
 
23
- require 'rubygems'
23
+ require 'process/daemon'
24
24
 
25
- require 'rexec'
26
- require 'rexec/daemon'
27
-
28
- require 'rubygems'
29
25
  require 'rubydns'
30
-
31
- require 'rubydns/resolver'
32
26
  require 'rubydns/system'
33
27
 
34
28
  INTERFACES = [
35
- [:udp, "0.0.0.0", 5300]
29
+ [:udp, '0.0.0.0', 5300]
36
30
  ]
37
31
 
38
- class DroppingDaemon < RExec::Daemon::Base
39
- # You can specify a specific directory to use for run-time information (pid, logs, etc):
40
- # @@base_directory = File.expand_path("../", __FILE__)
41
- # @@base_directory = "/var"
42
-
32
+ # A DNS server that selectively drops queries based on the requested domain
33
+ # name. Queries for domains that match specified regular expresssions
34
+ # (like 'microsoft.com' or 'sco.com') return NXDomain, while all other
35
+ # queries are passed to upstream resolvers.
36
+ class FlakeyDNS < Process::Daemon
43
37
  Name = Resolv::DNS::Name
44
38
  IN = Resolv::DNS::Resource::IN
45
- R = RubyDNS::Resolver.new(RubyDNS::System::nameservers)
46
-
47
- def self.run
48
- RubyDNS::run_server(:listen => INTERFACES) do
39
+
40
+ def startup
41
+ RubyDNS.run_server(INTERFACES) do
42
+ # Use a Celluloid supervisor so the system recovers if the actor dies
43
+ fallback_resolver_supervisor =
44
+ RubyDNS::Resolver.supervise(RubyDNS::System.nameservers)
45
+
49
46
  # Fail the resolution of certain domains ;)
50
47
  match(/(m?i?c?r?o?s?o?f?t)/) do |transaction, match_data|
51
48
  if match_data[1].size > 7
52
- logger.info "Dropping domain MICROSOFT..."
53
- transaction.failure!(:NXDomain)
49
+ logger.info 'Dropping domain MICROSOFT...'
50
+ transaction.fail!(:NXDomain)
54
51
  else
55
- # Pass the request to the otherwise handler
56
- false
52
+ logger.info 'Passing DNS request upstream...'
53
+ transaction.passthrough!(fallback_resolver_supervisor.actors.first)
57
54
  end
58
55
  end
59
-
56
+
60
57
  # Hmm....
61
58
  match(/^(.+\.)?sco\./) do |transaction|
62
- logger.info "Dropping domain SCO..."
63
- transaction.failure!(:NXDomain)
59
+ logger.info 'Dropping domain SCO...'
60
+ transaction.fail!(:NXDomain)
64
61
  end
65
62
 
66
63
  # Default DNS handler
67
64
  otherwise do |transaction|
68
- logger.info "Passing DNS request upstream..."
69
- transaction.passthrough!(R)
65
+ logger.info 'Passing DNS request upstream...'
66
+ transaction.passthrough!(fallback_resolver_supervisor.actors.first)
70
67
  end
71
68
  end
72
- end
69
+ end
73
70
  end
74
71
 
75
- DroppingDaemon.daemonize
72
+ FlakeyDNS.daemonize