powerdns_pipe 1.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 (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
+