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
@@ -2,17 +2,17 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  # Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
5
- #
5
+ #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  # of this software and associated documentation files (the "Software"), to deal
8
8
  # in the Software without restriction, including without limitation the rights
9
9
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
10
  # copies of the Software, and to permit persons to whom the Software is
11
11
  # furnished to do so, subject to the following conditions:
12
- #
12
+ #
13
13
  # The above copyright notice and this permission notice shall be included in
14
14
  # all copies or substantial portions of the Software.
15
- #
15
+ #
16
16
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
17
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
18
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -21,90 +21,86 @@
21
21
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
22
  # THE SOFTWARE.
23
23
 
24
- require 'rubygems'
25
-
26
- require 'rexec'
27
- require 'rexec/daemon'
28
-
29
- require 'rubygems'
24
+ require 'process/daemon'
25
+ require 'process/daemon/privileges'
30
26
 
31
27
  require 'rubydns'
32
28
  require 'rubydns/extensions/string'
33
29
 
34
30
  require 'digest/md5'
35
31
 
36
- # You might need to change the user name "daemon". This can be a user name or a user id.
37
- RUN_AS = "daemon"
32
+ # You might need to change the user name "daemon". This can be a user name
33
+ # or a user id.
34
+ RUN_AS = 'daemon'
38
35
 
39
- if RExec.current_user != "root"
40
- $stderr.puts "Sorry, this command needs to be run as root!"
36
+ if Process::Daemon::Privileges.current_user != 'root'
37
+ $stderr.puts 'Sorry, this command needs to be run as root!'
41
38
  exit 1
42
39
  end
43
40
 
44
- # To use, start the daemon and try:
45
- # dig @localhost fortune CNAME
46
- class FortuneDNS < RExec::Daemon::Base
47
- @@base_directory = File.dirname(__FILE__)
48
-
41
+ # A DNS server that allows a client to generate fortunes and fetch them with
42
+ # subsequent requests. The server 'remembers' the fortunes it generates,
43
+ # and can serve them to future requests.
44
+ class FortuneDNS < Process::Daemon
49
45
  Name = Resolv::DNS::Name
50
46
  IN = Resolv::DNS::Resource::IN
51
47
 
52
- def self.run
53
- # Don't buffer output (for debug purposes)
54
- $stderr.sync = true
55
-
48
+ def startup
56
49
  cache = {}
57
- stats = {:requested => 0}
58
-
50
+ stats = { requested: 0 }
51
+
59
52
  # Start the RubyDNS server
60
- RubyDNS::run_server do
53
+ RubyDNS.run_server do
61
54
  on(:start) do
62
- RExec.change_user(RUN_AS)
63
- if ARGV.include?("--debug")
55
+ Process::Daemon::Privileges.change_user(RUN_AS)
56
+
57
+ if ARGV.include?('--debug')
64
58
  @logger.level = Logger::DEBUG
59
+ $stderr.sync = true
65
60
  else
66
61
  @logger.level = Logger::WARN
67
62
  end
68
63
  end
69
-
64
+
65
+ match(/short\.fortune/, IN::TXT) do |transaction|
66
+ fortune = `fortune -s`.gsub(/\s+/, ' ').strip
67
+
68
+ transaction.respond!(*fortune.chunked, ttl: 0)
69
+ end
70
+
70
71
  match(/stats\.fortune/, IN::TXT) do |transaction|
71
72
  $stderr.puts "Sending stats: #{stats.inspect}"
72
73
  transaction.respond!(stats.inspect)
73
74
  end
74
-
75
- match(/(.+)\.fortune/, IN::TXT) do |transaction|
75
+
76
+ match(/([a-f0-9]*)\.fortune/, IN::TXT) do |transaction, match|
76
77
  fortune = cache[match[1]]
77
78
  stats[:requested] += 1
78
-
79
+
79
80
  if fortune
80
81
  transaction.respond!(*fortune.chunked)
81
82
  else
82
- transaction.failure!(:NXDomain)
83
+ transaction.fail!(:NXDomain)
83
84
  end
84
85
  end
85
-
86
+
86
87
  match(/fortune/, [IN::CNAME, IN::TXT]) do |transaction|
87
- fortune = `fortune`.gsub(/\s+/, " ").strip
88
+ fortune = `fortune`.gsub(/\s+/, ' ').strip
88
89
  checksum = Digest::MD5.hexdigest(fortune)
89
90
  cache[checksum] = fortune
90
-
91
- transaction.respond!("Text Size: #{fortune.size} Byte Size: #{fortune.bytesize}", :resource_class => IN::TXT, :ttl => 0)
92
- transaction.respond!(Name.create(checksum + ".fortune"), :resource_class => IN::CNAME, :ttl => 0)
93
- end
94
-
95
- match(/short.fortune/, IN::TXT) do |match, transation|
96
- fortune = `fortune -s`.gsub(/\s+/, " ").strip
97
-
98
- transaction.respond!(*fortune.chunked, :ttl => 0)
91
+
92
+ answer_txt = "Text Size: #{fortune.size} Byte Size: #{fortune.bytesize}"
93
+ transaction.respond!(answer_txt, resource_class: IN::TXT, ttl: 0)
94
+ answer_cname = Name.create(checksum + '.fortune')
95
+ transaction.respond!(answer_cname, resource_class: IN::CNAME, ttl: 0)
99
96
  end
100
-
97
+
101
98
  # Default DNS handler
102
99
  otherwise do |transaction|
103
- transaction.failure!(:NXDomain)
100
+ transaction.fail!(:NXDomain)
104
101
  end
105
102
  end
106
103
  end
107
104
  end
108
105
 
109
- # RExec daemon runner
110
106
  FortuneDNS.daemonize
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require 'geoip'
24
+
25
+ require 'process/daemon'
26
+
27
+ require 'rubydns'
28
+ require 'rubydns/system'
29
+
30
+ INTERFACES = [
31
+ [:udp, '0.0.0.0', 5300]
32
+ ]
33
+
34
+ # Path to the GeoIP file downloaded from
35
+ # http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
36
+ # If you have renamed the ungzipped file, or have placed it somewhere other than
37
+ # the repository root directory you will need to update this path.
38
+ PATH_TO_GEOIP_DAT_FILE =
39
+ File.expand_path('../GeoIP.dat', File.dirname(__FILE__))
40
+
41
+ # A sample DNS daemon that demonstrates how to use RubyDNS to build responses
42
+ # that vary based on the geolocation of the requesting peer. Clients of
43
+ # this server who request A records will get an answer IP address based
44
+ # on the continent of the client IP address.
45
+ #
46
+ # Please note that use of this example requires that the peer have a public
47
+ # IP address. IP addresses on private networks or the localhost IP (127.0.0.1)
48
+ # cannot be resolved to a location, and so will always yield the unknown result.
49
+ # This daemon requires the file downloaded from
50
+ # http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
51
+ # For more information, please see http://www.maxmind.com/en/geolite and
52
+ # http://geoip.rubyforge.org
53
+ class GeoIPDNS < Process::Daemon
54
+ GEO = GeoIP.new(PATH_TO_GEOIP_DAT_FILE)
55
+
56
+ Name = Resolv::DNS::Name
57
+ IN = Resolv::DNS::Resource::IN
58
+
59
+ def startup
60
+ RubyDNS.run_server(INTERFACES) do
61
+ fallback_resolver_supervisor = RubyDNS::Resolver.supervise(RubyDNS::System.nameservers)
62
+
63
+ match(//, IN::A) do |transaction|
64
+ logger.debug 'In block'
65
+
66
+ # The IP Address of the peer is stored in the transaction options
67
+ # with the key :remote_address
68
+ ip_address = transaction.options[:remote_address].ip_address
69
+ logger.debug "Looking up geographic information for peer #{ip_address}"
70
+ location = GeoIPDNS.ip_to_location(ip_address)
71
+
72
+ if location
73
+ logger.debug "Found location #{location} for #{ip_address}"
74
+ else
75
+ logger.debug "Could not resolve location for #{ip_address}"
76
+ end
77
+
78
+ code = location ? location.continent_code : nil
79
+ answer = GeoIPDNS.answer_for_continent_code(code)
80
+ logger.debug "Answer is #{answer}"
81
+ transaction.respond!(answer)
82
+ end
83
+
84
+ # Default DNS handler
85
+ otherwise do |transaction|
86
+ logger.debug 'In otherwise'
87
+ transaction.passthrough!(fallback_resolver_supervisor.actors.first)
88
+ end
89
+ end
90
+ end
91
+
92
+ # Maps each continent code to a fixed IP address for the response.
93
+ # A simple mapper to demonstrate the behavior.
94
+ def self.answer_for_continent_code(code)
95
+ case code
96
+ when 'AF' then '1.1.1.1'
97
+ when 'AN' then '1.1.2.1'
98
+ when 'AS' then '1.1.3.1'
99
+ when 'EU' then '1.1.4.1'
100
+ when 'NA' then '1.1.5.1'
101
+ when 'OC' then '1.1.6.1'
102
+ when 'SA' then '1.1.7.1'
103
+ else '1.1.8.1'
104
+ end
105
+ end
106
+
107
+ # Finds the continent code for the specified IP address.
108
+ # Returns nil if the IP address cannot be mapped to a location.
109
+ def self.ip_to_location(ip_address)
110
+ return nil unless ip_address
111
+ GEO.country(ip_address)
112
+ end
113
+ end
114
+
115
+ GeoIPDNS.daemonize
@@ -0,0 +1,25 @@
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
+ Name = Resolv::DNS::Name
10
+ IN = Resolv::DNS::Resource::IN
11
+
12
+ # Use upstream DNS for name resolution.
13
+ UPSTREAM = RubyDNS::Resolver.new([[:udp, '8.8.8.8', 53], [:tcp, '8.8.8.8', 53]])
14
+
15
+ # Start the RubyDNS server
16
+ RubyDNS.run_server(INTERFACES) do
17
+ match(/test.mydomain.org/, IN::A) do |transaction|
18
+ transaction.respond!('10.0.0.80')
19
+ end
20
+
21
+ # Default DNS handler
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
@@ -27,56 +27,56 @@ $R = Resolv::DNS.new
27
27
  Name = Resolv::DNS::Name
28
28
  IN = Resolv::DNS::Resource::IN
29
29
 
30
- RubyDNS::run_server(:listen => [[:udp, "0.0.0.0", 5400]]) do
30
+ RubyDNS.run_server([[:udp, '0.0.0.0', 5400]]) do
31
31
  # SOA Record
32
- # dig @localhost -p 5300 SOA mydomain.org
33
- match("mydomain.org", IN::SOA) do |transaction|
32
+ # dig @localhost -p 5400 SOA mydomain.org
33
+ match('mydomain.org', IN::SOA) do |transaction|
34
34
  #
35
35
  # For more details about these headers please see:
36
36
  # http://www.ripe.net/ripe/docs/ripe-203.html
37
37
  #
38
38
 
39
39
  transaction.respond!(
40
- Name.create("ns.mydomain.org."), # Master Name
41
- Name.create("admin.mydomain.org."), # Responsible Name
40
+ Name.create('ns.mydomain.org.'), # Master Name
41
+ Name.create('admin.mydomain.org.'), # Responsible Name
42
42
  File.mtime(__FILE__).to_i, # Serial Number
43
43
  1200, # Refresh Time
44
44
  900, # Retry Time
45
- 3600000, # Maximum TTL / Expiry Time
46
- 172800 # Minimum TTL
45
+ 3_600_000, # Maximum TTL / Expiry Time
46
+ 172_800 # Minimum TTL
47
47
  )
48
48
 
49
- transaction.append_query!(transaction.question, IN::NS, :section => :authority)
49
+ transaction.append!(transaction.question, IN::NS, section: :authority)
50
50
  end
51
-
51
+
52
52
  # Default NS record
53
- # dig @localhost -p 5300 mydomain.org NS
54
- match("mydomain.org", IN::NS) do |transaction|
55
- transaction.respond!(Name.create("ns.mydomain.org."))
53
+ # dig @localhost -p 5400 mydomain.org NS
54
+ match('mydomain.org', IN::NS) do |transaction|
55
+ transaction.respond!(Name.create('ns.mydomain.org.'))
56
56
  end
57
57
 
58
58
  # For this exact address record, return an IP address
59
- # dig @localhost -p 5300 CNAME bob.mydomain.org
59
+ # dig @localhost -p 5400 CNAME bob.mydomain.org
60
60
  match(/([^.]+).mydomain.org/, IN::CNAME) do |transaction|
61
- transaction.respond!(Name.create("www.mydomain.org"))
62
- transaction.append_query!("www.mydomain.org", IN::A)
61
+ transaction.respond!(Name.create('www.mydomain.org'))
62
+ transaction.append!('www.mydomain.org', IN::A)
63
63
  end
64
64
 
65
- match("80.0.0.10.in-addr.arpa", IN::PTR) do |transaction|
66
- transaction.respond!(Name.create("www.mydomain.org."))
65
+ match('80.0.0.10.in-addr.arpa', IN::PTR) do |transaction|
66
+ transaction.respond!(Name.create('www.mydomain.org.'))
67
67
  end
68
68
 
69
- match("www.mydomain.org", IN::A) do |transaction|
70
- transaction.respond!("10.0.0.80")
69
+ match('www.mydomain.org', IN::A) do |transaction|
70
+ transaction.respond!('10.0.0.80')
71
71
  end
72
-
73
- match("ns.mydomain.org", IN::A) do |transaction|
74
- transaction.respond!("10.0.0.10")
72
+
73
+ match('ns.mydomain.org', IN::A) do |transaction|
74
+ transaction.respond!('10.0.0.10')
75
75
  end
76
-
76
+
77
77
  # Default DNS handler
78
78
  otherwise do |transaction|
79
79
  # Non-Existant Domain
80
- transaction.failure!(:NXDomain)
80
+ transaction.fail!(:NXDomain)
81
81
  end
82
82
  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
@@ -27,42 +27,48 @@ require 'rubydns/system'
27
27
  # You can specify other DNS servers easily
28
28
  # $R = Resolv::DNS.new(:nameserver => ["xx.xx.1.1", "xx.xx.2.2"])
29
29
 
30
- R = RubyDNS::Resolver.new(RubyDNS::System::nameservers)
30
+ R = RubyDNS::Resolver.new(RubyDNS::System.nameservers)
31
31
  Name = Resolv::DNS::Name
32
32
  IN = Resolv::DNS::Resource::IN
33
+ INTERFACES = [
34
+ [:udp, '0.0.0.0', 5300],
35
+ [:tcp, '0.0.0.0', 5300],
36
+ # [:udp, '::0', 5300],
37
+ # [:tcp, '::0', 5300],
38
+ ]
33
39
 
34
- RubyDNS::run_server do
40
+ RubyDNS.run_server(INTERFACES) do
35
41
  # % dig +nocmd +noall +answer @localhost ANY dev.mydomain.org
36
42
  # dev.mydomain.org. 16000 IN A 10.0.0.80
37
43
  # dev.mydomain.org. 16000 IN MX 10 mail.mydomain.org.
38
44
  match(/dev.mydomain.org/, IN::ANY) do |transaction|
39
45
  transaction.append_question!
40
-
46
+
41
47
  [IN::A, IN::CNAME, IN::MX].each do |resource_class|
42
48
  logger.debug "Appending query for #{resource_class}..."
43
- transaction.append_query!(transaction.name, resource_class)
49
+ transaction.append!(transaction.name, resource_class)
44
50
  end
45
51
  end
46
-
52
+
47
53
  # For this exact address record, return an IP address
48
- match("dev.mydomain.org", IN::A) do |transaction|
49
- transaction.respond!("10.0.0.80")
54
+ match('dev.mydomain.org', IN::A) do |transaction|
55
+ transaction.respond!('10.0.0.80')
50
56
  end
51
57
 
52
- match("80.0.0.10.in-addr.arpa", IN::PTR) do |transaction|
53
- transaction.respond!(Name.create("dev.mydomain.org."))
58
+ match('80.0.0.10.in-addr.arpa', IN::PTR) do |transaction|
59
+ transaction.respond!(Name.create('dev.mydomain.org.'))
54
60
  end
55
61
 
56
- match("dev.mydomain.org", IN::MX) do |transaction|
57
- transaction.respond!(10, Name.create("mail.mydomain.org."))
62
+ match('dev.mydomain.org', IN::MX) do |transaction|
63
+ transaction.respond!(10, Name.create('mail.mydomain.org.'))
58
64
  end
59
-
65
+
60
66
  match(/^test([0-9]+).mydomain.org$/, IN::A) do |transaction, match_data|
61
67
  offset = match_data[1].to_i
62
-
68
+
63
69
  if offset > 0 && offset < 10
64
- logger.info "Responding with address #{"10.0.0." + (90 + offset).to_s}..."
65
- transaction.respond!("10.0.0." + (90 + offset).to_s)
70
+ logger.info "Responding with address #{'10.0.0.' + (90 + offset).to_s}..."
71
+ transaction.respond!('10.0.0.' + (90 + offset).to_s)
66
72
  else
67
73
  logger.info "Address out of range: #{offset}!"
68
74
  false
@@ -71,7 +77,7 @@ RubyDNS::run_server do
71
77
 
72
78
  # Default DNS handler
73
79
  otherwise do |transaction|
74
- logger.info "Passing DNS request upstream..."
80
+ logger.info 'Passing DNS request upstream...'
75
81
  transaction.passthrough!(R)
76
- end
82
+ end
77
83
  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,10 +20,8 @@
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'
24
- require 'rexec'
25
- require 'rexec/daemon'
26
23
  require 'rubydns'
24
+ require 'process-daemon'
27
25
 
28
26
  # To run this command, use the standard daemon syntax as root
29
27
  # ./daemon2.rb start
@@ -37,39 +35,39 @@ require 'rubydns'
37
35
  # dig +tcp @localhost test.mydomain.org
38
36
 
39
37
  # You might need to change the user name "daemon". This can be a user name or a user id.
40
- RUN_AS = "daemon"
38
+ RUN_AS = 'daemon'
41
39
 
42
40
  INTERFACES = [
43
- [:udp, "0.0.0.0", 53],
44
- [:tcp, "0.0.0.0", 53]
41
+ [:udp, '0.0.0.0', 53],
42
+ [:tcp, '0.0.0.0', 53]
45
43
  ]
46
44
 
47
45
  # We need to be root in order to bind to privileged port
48
- if RExec.current_user != "root"
49
- $stderr.puts "Sorry, this command needs to be run as root!"
50
- exit 1
46
+ if RExec.current_user != 'root'
47
+ $stderr.puts 'Sorry, this command needs to be run as root!'
48
+ exit 1
51
49
  end
52
50
 
53
51
  # The Daemon itself
54
- class Server < RExec::Daemon::Base
52
+ class Server < Process::Daemon
55
53
  Name = Resolv::DNS::Name
56
54
  IN = Resolv::DNS::Resource::IN
57
55
 
58
56
  # Use upstream DNS for name resolution.
59
- UPSTREAM = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
57
+ UPSTREAM = RubyDNS::Resolver.new([[:udp, '8.8.8.8', 53], [:tcp, '8.8.8.8', 53]])
60
58
 
61
- def self.run
59
+ def startup
62
60
  # Don't buffer output (for debug purposes)
63
61
  $stderr.sync = true
64
-
62
+
65
63
  # Start the RubyDNS server
66
- RubyDNS::run_server(:listen => INTERFACES) do
64
+ RubyDNS.run_server(INTERFACES) do
67
65
  on(:start) do
68
66
  RExec.change_user(RUN_AS)
69
67
  end
70
68
 
71
- match("test.mydomain.org", IN::A) do |transaction|
72
- transaction.respond!("10.0.0.80")
69
+ match('test.mydomain.org', IN::A) do |transaction|
70
+ transaction.respond!('10.0.0.80')
73
71
  end
74
72
 
75
73
  # Default DNS handler
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ # Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+
24
+ require 'rubydns'
25
+
26
+ require 'process/daemon'
27
+ require 'process/daemon/privileges'
28
+
29
+ require 'cgi'
30
+ require 'nokogiri'
31
+ require 'json'
32
+
33
+ require 'digest/md5'
34
+
35
+ require 'http'
36
+
37
+ # You might need to change the user name "daemon". This can be a user name
38
+ # or a user id.
39
+ RUN_AS = 'daemon'
40
+
41
+ if Process::Daemon::Privileges.current_user != 'root'
42
+ $stderr.puts 'Sorry, this command needs to be run as root!'
43
+ exit 1
44
+ end
45
+
46
+ # Encapsulates the logic for fetching information from Wikipedia.
47
+ module Wikipedia
48
+ def self.summary_url(title)
49
+ "http://en.wikipedia.org/w/api.php?action=parse&page=#{CGI.escape title}&prop=text&section=0&format=json"
50
+ end
51
+
52
+ def self.extract_summary(json_text)
53
+ document = JSON.parse(json_text)
54
+ return Nokogiri::HTML(document['parse']['text']['*']).css('p')[0].text
55
+ rescue
56
+ return 'Invalid Article.'
57
+ end
58
+ end
59
+
60
+ # A DNS server that queries Wikipedia and returns summaries for
61
+ # specifically crafted queries.
62
+ class WikipediaDNS < Process::Daemon
63
+ Name = Resolv::DNS::Name
64
+ IN = Resolv::DNS::Resource::IN
65
+
66
+ def startup
67
+ # Don't buffer output (for debug purposes)
68
+ $stderr.sync = true
69
+
70
+ stats = { requested: 0 }
71
+
72
+ # Start the RubyDNS server
73
+ RubyDNS.run_server do
74
+ on(:start) do
75
+ Process::Daemon::Privileges.change_user(RUN_AS)
76
+ if ARGV.include?('--debug')
77
+ @logger.level = Logger::DEBUG
78
+ else
79
+ @logger.level = Logger::WARN
80
+ end
81
+ end
82
+
83
+ match(/stats\.wikipedia/, IN::TXT) do |transaction|
84
+ transaction.respond!(*stats.inspect.chunked)
85
+ end
86
+
87
+ match(/(.+)\.wikipedia/, IN::TXT) do |transaction, match_data|
88
+ title = match_data[1]
89
+ stats[:requested] += 1
90
+
91
+ url = Wikipedia.summary_url(title)
92
+ response = HTTP.get(url) # socket_class: ... is not yet supported.
93
+
94
+ summary =
95
+ Wikipedia.extract_summary(response).force_encoding('ASCII-8BIT')
96
+ transaction.respond!(*summary.chunked)
97
+ end
98
+
99
+ # Default DNS handler
100
+ otherwise do |transaction|
101
+ transaction.fail!(:NXDomain)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ WikipediaDNS.daemonize