em-powerdns 0.0.1

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.
@@ -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
+