powerdns_pipe 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.rdoc +149 -0
  2. data/lib/powerdns_pipe.rb +127 -0
  3. metadata +71 -0
data/README.rdoc ADDED
@@ -0,0 +1,149 @@
1
+ = Ruby PowerDNS Pipe Backend Abstraction
2
+
3
+ powerdns_pipe is a Ruby library for developing
4
+ {PowerDNS}[http://www.powerdns.com] pipe backend resolvers.
5
+
6
+ More information on the PowerDNS pipe backend {can be found
7
+ here}[http://doc.powerdns.com/backends-detail.html].
8
+
9
+ == Installation
10
+
11
+ sudo gem install powerdns_pipe
12
+
13
+ == Documentation
14
+
15
+ PowerDNS::Pipe works by handling all the communication with PowerDNS
16
+ for you. It handles the <tt>HELO</tt> and <tt>PING</tt> and stuff,
17
+ but calls a block when a query or axfr request is made. The block has
18
+ access to a <tt>question</tt> object which has information about the
19
+ query from PowerDNS, and the <tt>answer</tt> method, which provides
20
+ responses to PowerDNS.
21
+
22
+ <tt>PowerDNS::Pipe.new</tt> takes a hash of options, all are optional
23
+ and the defaults are usually all you need:
24
+
25
+ [:input] The IO object to read requests from PowerDNS. Defaults to
26
+ STDIN
27
+
28
+ [:output] The IO object to write responses to PowerDNS. Defaults to
29
+ STDOUT
30
+
31
+ [:err] The IO object to write debugging information. Defaults to
32
+ STDERR, but is rarely used.
33
+
34
+ [:version_range] A Range instance representing the Pipe protocol
35
+ versions this backend supports. Defaults to 1..2
36
+
37
+ [:banner] A string used in response to HELO requests from PowerDNS, is
38
+ logged to the PowerDNS logs. Defaults to "Ruby PowerDNS::Pipe"
39
+
40
+ === question object
41
+
42
+ The <tt>question</tt> object provides information about the query from
43
+ the server:
44
+
45
+ [<tt>name</tt>] The record being requested, e.g: <tt>www.example.com</tt>
46
+
47
+ [<tt>qtype</tt>] The request type, such as <tt>A</tt> or
48
+ <tt>MX</tt>. PowerDNS often uses <tt>ANY</tt>, to
49
+ which you should return all valid records for the
50
+ name and PowerDNS worries about returning the right
51
+ one to the client. You <em>must</em> support this
52
+ type.
53
+
54
+ [<tt>qclass</tt>] The request class, this is always <tt>IN</tt>.
55
+
56
+ [<tt>remote_ip_address</tt>] The IP address of the host making the
57
+ dns request. You could use this to
58
+ return different records for different
59
+ geographic regions.
60
+
61
+ [<tt>local_ip_address</tt>] The server IP address the request came
62
+ into. Useful if your PowerDNS server is
63
+ listening on multiple IPs and you want to
64
+ consider that in your answers.
65
+
66
+ [<tt>id</tt>] The id of the last answer to this question provided to
67
+ PowerDNS by this backend for this. This might be
68
+ useful to you to speed up subsequent lookups. -1 by
69
+ default and can be ignored.
70
+
71
+ [<tt>query?</tt>] Returns true if this is a normal Q query.
72
+
73
+ [<tt>axfr?</tt>] Returns true if this is an axfr query.
74
+
75
+ === answer method
76
+
77
+ The <tt>answer</tt> method is used to return records to PowerDNS. It
78
+ can be called multiple times to return multiple records. Any
79
+ exceptions are caught for you so garbage is not returned to PowerDNS.
80
+ If you have nothing to return, just don't call answer at all.
81
+
82
+ It takes the following options:
83
+
84
+ [<tt>:name</tt>] The record name, e.g: <tt>www.example.com</tt>. Can
85
+ usually just be set to <tt>question.name</tt>
86
+
87
+ [<tt>:ttl</tt>] Time to Live in seconds. Defaults to 3600
88
+
89
+ [<tt>:content</tt>] The content of the response, so an IP address
90
+ string for <tt>A</tt> answers, or arbitrary text
91
+ for <tt>TXT</tt> answers. For records with a
92
+ priority (like MX records) put the priority first
93
+ and then a space and then the content, e.g: <tt>10
94
+ mail.example.com</tt>
95
+
96
+ [<tt>:id</tt>] An integer id for this answer. PowerDNS will remember
97
+ this and pass it back for subsequent requests for the
98
+ same record. You might use this to pass around a
99
+ primary key or something to speed up subsequent
100
+ lookups. Defaults to -1 and can be ignored.
101
+
102
+ [<tt>:class</tt>] The class of this answer, defaults to <tt>IN</tt>
103
+ and shouldn't be changed.
104
+
105
+ == Basic example
106
+
107
+ Return an <tt>A</tt> record of <tt>1.2.3.4</tt> for all queries:
108
+
109
+ require 'powerdns_pipe'
110
+ PowerDNS::Pipe.new.run! do
111
+ answer :name => question.name, :type => 'A', :ttl => 60, :content => '1.2.3.4'
112
+ end
113
+
114
+ == Advanced example
115
+
116
+ Return the HTTP Server header as a TXT record for the host requested.
117
+
118
+ Example usage:
119
+
120
+ $ host -t txt www.ubuntu.com.example.com
121
+ ubuntu.com.example.com descriptive text "Apache/2.2.8 (Ubuntu) mod_python/3.3.1"
122
+
123
+ Code:
124
+
125
+ require 'powerdns_pipe'
126
+ require 'net/http'
127
+ re = Regexp.new("^(.+)\.example\.com$")
128
+ pipe = PowerDNS::Pipe.new :banner => 'HTTP Server Header TXT Pipe'
129
+ pipe.run! do
130
+ if m = re.match(question.name)
131
+ domain = m[1]
132
+ case question.qtype
133
+ when "TXT", "ANY"
134
+ res = Net::HTTP.get_response(URI.parse("http://" + domain))
135
+ answer :name => question.name , :type => 'TXT', :ttl => 3600,
136
+ :content => res['Server']
137
+ end
138
+ end
139
+ end
140
+
141
+ = More Info
142
+
143
+ Author:: John Leach (mailto:john@johnleach.co.uk)
144
+ Copyright:: Copyright (c) 2010 John Leach
145
+ License:: MIT
146
+ Github:: http://github.com/johnl/powerdns_pipe/tree/master
147
+
148
+ See also the {ruby-pdns library}[http://code.google.com/p/ruby-pdns/]
149
+ which does things differently, with some limitations.
@@ -0,0 +1,127 @@
1
+ module PowerDNS
2
+
3
+ # DnsPipe is an abstraction of the Powerdns pipe backend protocol.
4
+ # http://doc.powerdns.com/backends-detail.html
5
+ #
6
+ # It's dead simple to use, see the README for examples
7
+ #
8
+ # Written by John Leach <john@johnleach.co.uk>
9
+ #
10
+ class Pipe
11
+ attr_reader :input, :output, :err, :version_range, :banner
12
+
13
+ # Answer object is just used to wrap the block that handles answering
14
+ # queries
15
+ class Answer
16
+ attr_reader :question
17
+
18
+ def initialize(pipe, question)
19
+ @pipe = pipe
20
+ @question = question
21
+ end
22
+
23
+ def answer(*args)
24
+ @pipe.answer *args
25
+ end
26
+
27
+ end
28
+
29
+ # Question wraps any type of question line from the server
30
+ class Question < Struct.new(:tag, :name, :qclass, :qtype, :id, :remote_ip_address, :local_ip_address)
31
+
32
+ def to_s
33
+ [tag, name, qclass, qtype, id, remote_ip_address, local_ip_address].join("\t")
34
+ end
35
+
36
+ def query?
37
+ tag == "Q"
38
+ end
39
+
40
+ def axfr?
41
+ tag == "AXFR"
42
+ end
43
+ end
44
+
45
+ def initialize(options = {})
46
+ options = {
47
+ :input => STDIN,
48
+ :output => STDOUT,
49
+ :err => STDERR,
50
+ :version_range => 1..2,
51
+ :banner => "Ruby PowerDNS::Pipe"
52
+ }.merge options
53
+
54
+ @input = options[:input]
55
+ @output = options[:output]
56
+ @err = options[:err]
57
+ @version_range = options[:version_range]
58
+ @banner = options[:banner]
59
+ end
60
+
61
+ def run!(&query_processor)
62
+ while (line = input.readline) do
63
+ process_line line, query_processor
64
+ end
65
+ rescue EOFError
66
+ err.write "EOF, terminating loop\n"
67
+ end
68
+
69
+ def answer(options = {})
70
+ options = {
71
+ :ttl => 3600,
72
+ :id => -1,
73
+ :class => 'IN'
74
+ }.merge options
75
+
76
+ respond "DATA", options[:name], options[:class], options[:type], options[:ttl], options[:content]
77
+ end
78
+
79
+ private
80
+
81
+ def process_line(line, query_processor)
82
+ q = Question.new *line.chomp.split("\t")
83
+ qtypes = {
84
+ "HELO" => :process_helo,
85
+ "Q" => :process_query,
86
+ "AXFR" => :process_query,
87
+ "PING" => :process_ping
88
+ }
89
+
90
+ self.send(qtypes.fetch(q.tag, :process_unknown), q, query_processor)
91
+ end
92
+
93
+ def process_helo(q, query_processor)
94
+ if version_range === (q.name.to_i rescue -1)
95
+ respond "OK", banner
96
+ else
97
+ respond "FAIL", banner
98
+ end
99
+ end
100
+
101
+ def process_query(q, query_processor)
102
+ answer = Answer.new(self, q)
103
+ begin
104
+ answer.instance_eval &query_processor
105
+ respond "END"
106
+ rescue StandardError => e
107
+ respond "LOG", "Error: #{e.class}: #{e.message}"
108
+ respond "FAIL"
109
+ end
110
+
111
+ end
112
+
113
+ def process_unknown(q, query_processor)
114
+ respond "LOG", "Unknown Question received: #{q}"
115
+ respond "FAIL"
116
+ end
117
+
118
+ def process_ping(q, query_processor)
119
+ respond "END"
120
+ end
121
+
122
+ def respond(*args)
123
+ output.write(args.join("\t") + "\n")
124
+ output.flush
125
+ end
126
+ end
127
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: powerdns_pipe
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ version: "1.0"
10
+ platform: ruby
11
+ authors:
12
+ - John Leach
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-08-16 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: A library to allow easy development of powerdns pipe backend resolvers in Ruby
22
+ email: john@johnleach.co.uk
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.rdoc
29
+ files:
30
+ - lib/powerdns_pipe.rb
31
+ - README.rdoc
32
+ has_rdoc: true
33
+ homepage: http://github.com/johnl/powerdns_pipe/tree/master
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --title
39
+ - PowerDNS Pipe
40
+ - --main
41
+ - README.rdoc
42
+ - --line-numbers
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ hash: 3
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.7
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: A Ruby abstraction of the PowerDNS pipe backend protocol
70
+ test_files: []
71
+