em-powerdns 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+ .bundle
21
+ vendor
22
+
23
+ ## PROJECT::SPECIFIC
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source :gemcutter
2
+
3
+ group :runtime do
4
+ gem 'eventmachine'
5
+ end
6
+
7
+ group :release do
8
+ gem 'jeweler'
9
+ gem 'rake'
10
+ gem 'bundler', '~> 0.9.11'
11
+ end
12
+
13
+ group :test do
14
+ gem 'rspec', '>= 1.2.9', :require => 'spec'
15
+ gem 'reek'
16
+ gem 'roodi'
17
+ gem 'yard'
18
+ gem 'ruby-debug'
19
+ gem 'sys-uname'
20
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Martin Emde
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,36 @@
1
+ = em-powerdns
2
+
3
+ You can use this plus the unix program PowerDNS to make a custom DNS server
4
+ with the lookup done by a ruby script you write.
5
+
6
+ Even though the actual PowerDNS pipe backend is synchronous, using this will
7
+ allow a script to start up other side tasks, logging or stats for example,
8
+ without interrupting the scripts ability to serve more requests.
9
+
10
+ Include EM::P::PowerDNS in a module or class to use it as a PowerDNS backend.
11
+
12
+ == Example
13
+
14
+ module RedisBackend
15
+ include EM::P::PowerDNS
16
+
17
+ def receive_query(query)
18
+ # make some logic here to resolve the DNS
19
+ ip = redis[query.qname]
20
+ data query.qname, query.qclass, :A, 3600, 1, ip
21
+ super
22
+ end
23
+
24
+ def redis
25
+ @redis ||= Redis.new
26
+ end
27
+ end
28
+
29
+ EM.run { EM.open_keyboard(RedisBackend) }
30
+
31
+ PowerDNS uses stdin to send all it's queries so EM.open_keyboard does exactly
32
+ what we need.
33
+
34
+ == Copyright
35
+
36
+ Copyright (c) 2010 Martin Emde. See LICENSE for details.
@@ -0,0 +1,86 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+ Bundler.require
4
+
5
+ load 'tasks/powerdns.rake'
6
+
7
+ begin
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |gem|
10
+ gem.name = "em-powerdns"
11
+ gem.summary = %Q{Ruby EventMachine PowerDNS Protocol for PDNS custom pipe backend}
12
+ gem.description = gem.summary
13
+ gem.email = "martin.emde@gmail.com"
14
+ gem.homepage = "http://github.com/martinemde/em-powerdns"
15
+ gem.authors = ["Martin Emde"]
16
+
17
+ bundle = Bundler::Definition.from_gemfile('Gemfile')
18
+ bundle.dependencies.each do |dep|
19
+ gem.add_dependency(dep.name, dep.requirement.to_s) if dep.groups.include?(:runtime)
20
+ end
21
+ end
22
+ Jeweler::GemcutterTasks.new
23
+ rescue LoadError
24
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
25
+ end
26
+
27
+ require 'spec/rake/spectask'
28
+ Spec::Rake::SpecTask.new(:spec) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.spec_files = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
34
+ spec.libs << 'lib' << 'spec'
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ begin
40
+ require 'cucumber/rake/task'
41
+ Cucumber::Rake::Task.new(:features) do |t|
42
+ t.cucumber_opts = %w{--guess}
43
+ end
44
+
45
+ task :features => :check_dependencies
46
+ rescue LoadError
47
+ task :features do
48
+ abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
49
+ end
50
+ end
51
+
52
+ task :default => %w[spec features]
53
+
54
+ begin
55
+ require 'reek/adapters/rake_task'
56
+ Reek::RakeTask.new do |t|
57
+ t.fail_on_error = true
58
+ t.verbose = false
59
+ t.source_files = 'lib/**/*.rb'
60
+ end
61
+ rescue LoadError
62
+ task :reek do
63
+ abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
64
+ end
65
+ end
66
+
67
+ begin
68
+ require 'roodi'
69
+ require 'roodi_task'
70
+ RoodiTask.new do |t|
71
+ t.verbose = false
72
+ end
73
+ rescue LoadError
74
+ task :roodi do
75
+ abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
76
+ end
77
+ end
78
+
79
+ begin
80
+ require 'yard'
81
+ YARD::Rake::YardocTask.new
82
+ rescue LoadError
83
+ task :yardoc do
84
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
85
+ end
86
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,139 @@
1
+ require 'eventmachine'
2
+
3
+ # Instances of this class wrap the PowerDNS I/O stream with a nice API
4
+ module EventMachine::Protocols
5
+ module PowerDNS
6
+ class Error < RuntimeError
7
+ attr_reader :original_exception
8
+
9
+ def initialize(data, exception)
10
+ @original_exception = exception
11
+ super "Unexpected error in the EM::P::PowerDNS:\nPowerDNS Data: #{data.inspect}\nError: #{exception}\n#{exception.backtrace}"
12
+ end
13
+ end
14
+
15
+ include EM::Protocols::LineText2
16
+ include Output
17
+
18
+ SEPARATOR = "\t"
19
+ EVENT_MAP = Hash.new(:receive_garbage)
20
+ EVENT_MAP.update(
21
+ 'HELO' => :receive_raw_handshake,
22
+ 'Q' => :receive_raw_query,
23
+ 'AXFR' => :receive_raw_axfr,
24
+ 'PING' => :receive_raw_ping
25
+ )
26
+
27
+ def self.start
28
+ EM.open_keyboard(self)
29
+ end
30
+
31
+ def initialize
32
+ $stdin.sync = true
33
+ $stdout.sync = true
34
+ end
35
+
36
+ def receive_line(data)
37
+ parts = data.split SEPARATOR
38
+ type = parts.shift
39
+ send EVENT_MAP[type], *parts
40
+ rescue => e
41
+ receive_error Error.new(data, e)
42
+ end
43
+
44
+ def receive_raw_handshake(*parts)
45
+ @version = parts.first.to_i
46
+ if [1,2].include?(@version)
47
+ receive_handshake(@version)
48
+ else
49
+ fail "Received unexpected handshake: #{parts.inspect}"
50
+ end
51
+ end
52
+
53
+ def receive_handshake(version)
54
+ ok "Backend starting version #{version}."
55
+ end
56
+
57
+ class Query < Struct.new(:qname, :qclass, :qtype, :id, :remote_ip, :local_ip)
58
+ end
59
+
60
+ def receive_raw_query(*parts)
61
+ if parts.size != parts_size_for_version
62
+ fail "Received unexpected format for Q: #{parts.inspect}"
63
+ else
64
+ query = Query.new(*parts)
65
+ receive_query(query)
66
+ end
67
+ end
68
+
69
+ def parts_size_for_version
70
+ @version == 1 ? 5 : 6 # already chopped off the 'Q'
71
+ end
72
+
73
+ # Stub method for users implementing this protocol
74
+ def receive_query(query)
75
+ done
76
+ end
77
+
78
+ def receive_raw_axfr(*parts)
79
+ if parts.size != 1
80
+ fail "Received unexpected format for AXFR: #{parts.inspect}"
81
+ else
82
+ receive_axfr(parts.first)
83
+ end
84
+ end
85
+
86
+ # Stub method for users implementing this protocol
87
+ def receive_axfr(soa_resource)
88
+ done
89
+ end
90
+
91
+ def receive_raw_ping(*parts)
92
+ receive_ping
93
+ end
94
+
95
+ # Stub method for users implementing this protocol
96
+ def receive_ping
97
+ done
98
+ end
99
+
100
+ def receive_garbage(line)
101
+ fail "Unknown Question: #{line.inspect}"
102
+ end
103
+
104
+ def receive_error(error)
105
+ fail "An unexpected error occurred in backend: #{error.original_exception.message}"
106
+ raise error
107
+ end
108
+ module Output
109
+ def ok(line)
110
+ send_line "OK", line
111
+ end
112
+
113
+ def data(*parts)
114
+ send_line "DATA", *parts
115
+ end
116
+
117
+ def log(line)
118
+ send_line "LOG", line
119
+ end
120
+
121
+ def fail(line = nil)
122
+ log(line) if line
123
+ send_line "FAIL"
124
+ end
125
+
126
+ def done(line = nil)
127
+ log(line) if line
128
+ send_line "END"
129
+ end
130
+
131
+ def send_line(*parts)
132
+ send_data parts.join(PowerDNS::Connection::SEPARATOR)
133
+ end
134
+
135
+ def send_data(data)
136
+ $stdout.puts data
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,219 @@
1
+ # Inspired by powerdns, which was inspired by rabbitmq.rake the Redbox project at http://github.com/rick/redbox/tree/master
2
+ require 'rake'
3
+ require 'fileutils'
4
+ require 'pathname'
5
+ require 'sys/uname'
6
+
7
+ class PDNS
8
+ SOURCE_URI = "http://downloads.powerdns.com/releases/pdns-2.9.22.tar.gz"
9
+ BOOST_URI = "http://downloads.sourceforge.net/project/boost/boost/1.42.0/boost_1_42_0.tar.gz?use_mirror=superb-sea2"
10
+ DTACH_URI = "http://downloads.sourceforge.net/project/dtach/dtach/0.8/dtach-0.8.tar.gz"
11
+ ROOT = (Pathname.new(File.dirname(__FILE__)) + '../powerdns').cleanpath
12
+ PDNS_SERVER = 'pdns_server'
13
+ BACKEND = 'backend'
14
+
15
+ def self.bin() ROOT + 'bin' end
16
+ def self.etc() ROOT + 'etc' end
17
+ def self.src() ROOT + 'src' end
18
+ def self.tmp() ROOT + 'tmp' end
19
+
20
+ def self.source_path() src + 'powerdns' end
21
+ def self.compiled_path() source_path + 'pdns' end
22
+ def self.boost_path() src + 'boost' end
23
+
24
+ def self.server_command() bin + PDNS_SERVER end
25
+ def self.pipe_command() bin + BACKEND end
26
+ def self.config() etc + 'pdns.conf' end
27
+
28
+ def self.dtach_socket
29
+ tmp.mkpath
30
+ tmp + 'powerdns.dtach'
31
+ end
32
+
33
+ # Just check for existance of dtach socket
34
+ def self.running?
35
+ File.exists? dtach_socket
36
+ end
37
+
38
+ def self.start
39
+ puts 'Detach with Ctrl+\ Re-attach with rake powerdns:attach'
40
+ sleep 3
41
+ exec "dtach -A #{dtach_socket} #{server_command}"
42
+ end
43
+
44
+ def self.start_detached
45
+ system "dtach -n #{dtach_socket} #{server_command}"
46
+ end
47
+
48
+ def self.attach
49
+ exec "dtach -a #{dtach_socket}"
50
+ end
51
+
52
+ def self.stop
53
+ system 'echo "SHUTDOWN" | nc localhost 6379'
54
+ end
55
+
56
+ end
57
+
58
+ namespace :powerdns do
59
+
60
+ desc 'About powerdns'
61
+ task :about do
62
+ puts "\nSee http://www.powerdns.com/ for information about PowerDNS.\n\n"
63
+ end
64
+
65
+ desc 'Start powerdns'
66
+ task :start do
67
+ PDNS.start
68
+ end
69
+
70
+ desc 'Stop powerdns'
71
+ task :stop do
72
+ PDNS.stop
73
+ end
74
+
75
+ desc 'Restart powerdns'
76
+ task :restart do
77
+ PDNS.stop
78
+ PDNS.start
79
+ end
80
+
81
+ desc 'Attach to powerdns dtach socket'
82
+ task :attach do
83
+ PDNS.attach
84
+ end
85
+
86
+ desc 'Install PowerDNS server and configuration file'
87
+ task :install => %w[about install:server install:backend install:config]
88
+
89
+ namespace :install do
90
+ desc 'Install PowerDNS server'
91
+ task :server => [:paths] do
92
+ script = PDNS.server_command
93
+ script.delete if script.exist?
94
+ script.open('w') do |f|
95
+ f.puts <<-SCRIPT
96
+ #!/bin/bash
97
+ sudo #{PDNS.compiled_path.realpath + PDNS::PDNS_SERVER} --config-dir=#{PDNS.etc}
98
+ SCRIPT
99
+ end
100
+ script.chmod(0755)
101
+ puts "Installed #{script}."
102
+ end
103
+
104
+ desc 'Install PowerDNS config'
105
+ task :config => [:backend, :paths] do
106
+ config = PDNS.config
107
+ config.delete if config.exist?
108
+ config.open('w') do |file|
109
+ file.puts <<-CONFIG
110
+ launch=pipe
111
+ pipe-command=#{PDNS.pipe_command}
112
+ pipebackend-abi-version=2
113
+ CONFIG
114
+ end
115
+ `cat #{PDNS.compiled_path.realpath + 'pdns.conf-dist'} >> #{config}`
116
+ puts "Installed #{config} and configured it for testing.\n You should double check this file."
117
+ end
118
+
119
+ desc 'Install backend wrapper'
120
+ task :backend => [:paths] do
121
+ gem_root = File.expand_path('../..', __FILE__)
122
+ pipe = PDNS.pipe_command
123
+ pipe.delete if pipe.exist?
124
+ pipe.open('w') do |f|
125
+ f.puts <<-SCRIPT
126
+ #!/bin/bash
127
+ cd #{gem_root}
128
+ bundle exec bin/backend
129
+ SCRIPT
130
+ end
131
+ pipe.chmod(0755)
132
+ puts "Linked #{pipe} to #{executable}."
133
+ end
134
+ end
135
+
136
+ desc "Configure and make PowerDNS"
137
+ task :make => [:paths, :download, :boost] do
138
+ cflags = "-I#{PDNS.boost_path.realpath}"
139
+ cflags << " -DDARWIN" if Sys::Uname.sysname =~ /Darwin/i
140
+
141
+ system <<-COMMAND
142
+ cd #{PDNS.source_path}
143
+
144
+ echo "Configuring PowerDNS for pipe backend ..."
145
+ CXXFLAGS="#{cflags}" ./configure --with-modules=pipe --without-pgsql --without-mysql --without-sqlite --without-sqlite3
146
+
147
+ echo "make clean ..."
148
+ make clean
149
+
150
+ echo "make ..."
151
+ make
152
+ COMMAND
153
+ end
154
+
155
+ desc "Download PowerDNS source"
156
+ task :download => [:paths, :boost] do
157
+ source_path = PDNS.source_path
158
+ unless source_path.exist?
159
+ source_path.mkpath
160
+ puts "Downloading PowerDNS source..."
161
+ system "curl -L #{PDNS::SOURCE_URI} | tar zx --strip-components 1 -C #{source_path}"
162
+ end
163
+ end
164
+
165
+ desc "PowerDNS requires boost libs at compile time"
166
+ task :boost => :paths do
167
+ boost_path = PDNS.boost_path
168
+ unless boost_path.exist?
169
+ boost_path.mkpath
170
+ puts "Downloading Boost source..."
171
+ system "curl -L #{PDNS::BOOST_URI} | tar zx --strip-components 1 -C #{boost_path}"
172
+ end
173
+ end
174
+
175
+ task :paths do
176
+ [PDNS.bin, PDNS.etc, PDNS.src].each { |path| path.mkpath }
177
+ end
178
+
179
+ namespace :purge do
180
+ def purge(*paths)
181
+ paths.each do |path|
182
+ path.rmtree if path.exist?
183
+ path.mkpath
184
+ end
185
+ end
186
+
187
+ task :installs do
188
+ purge(PDNS.bin, PDNS.etc)
189
+ end
190
+
191
+ task :src do
192
+ purge(PDNS.src)
193
+ end
194
+ end
195
+ end
196
+
197
+ namespace :dtach do
198
+ desc 'About dtach'
199
+ task :about do
200
+ puts <<-ABOUT
201
+ dtach allows background proccess that are not attached to a term.
202
+ See http://dtach.sourceforge.net/ for more information about dtach.
203
+
204
+ ABOUT
205
+ end
206
+
207
+ desc 'Install dtach 0.8 from source'
208
+ task :install => [:about] do
209
+ dtach_path = PDNS.source_path + 'dtach'
210
+
211
+ unless dtach.exist?
212
+ dtach.mkpath
213
+ system "curl -L #{PDNS::DTACH_URI} | tar zx --strip-components 1 -C #{dtach_path}"
214
+ end
215
+
216
+ system "cd #{dtach_path} && ./configure && make && ln -s dtach #{PDNS.bin + 'dtach'}"
217
+ puts "dtach installed to #{PDNS.bin}."
218
+ end
219
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-powerdns
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Martin Emde
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-10 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: eventmachine
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ description: Ruby EventMachine PowerDNS Protocol for PDNS custom pipe backend
33
+ email: martin.emde@gmail.com
34
+ executables: []
35
+
36
+ extensions: []
37
+
38
+ extra_rdoc_files:
39
+ - LICENSE
40
+ - README.rdoc
41
+ files:
42
+ - .gitignore
43
+ - Gemfile
44
+ - LICENSE
45
+ - README.rdoc
46
+ - Rakefile
47
+ - VERSION
48
+ - lib/em-powerdns.rb
49
+ - tasks/powerdns.rake
50
+ has_rdoc: true
51
+ homepage: http://github.com/martinemde/em-powerdns
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --charset=UTF-8
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.6
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Ruby EventMachine PowerDNS Protocol for PDNS custom pipe backend
80
+ test_files: []
81
+