GithubHelp home page GithubHelp logo

powerdns_pipe's Introduction

Ruby PowerDNS Pipe Backend Abstraction

powerdns_pipe is a Ruby library for developing PowerDNS pipe backend resolvers.

More information on the PowerDNS pipe backend can be found here.

Installation

sudo gem install powerdns_pipe

Documentation

PowerDNS::Pipe works by handling all the communication with PowerDNS for you. It handles the HELO and PING and stuff, but calls a block when a query or axfr request is made. The block has access to a question object which has information about the query from PowerDNS, and the answer method, which provides responses to PowerDNS.

PowerDNS::Pipe.new takes a hash of options, all are optional and the defaults are usually all you need:

:input

The IO object to read requests from PowerDNS. Defaults to STDIN

:output

The IO object to write responses to PowerDNS. Defaults to STDOUT

:err

The IO object to write debugging information. Defaults to STDERR, but is rarely used.

:version_range

A Range instance representing the Pipe protocol versions this backend supports. Defaults to 1..2

:banner

A string used in response to HELO requests from PowerDNS, is logged to the PowerDNS logs. Defaults to “Ruby PowerDNS::Pipe”

question object

The question object provides information about the query from the server:

name

The record being requested, e.g: www.example.com

qtype

The request type, such as A or MX. PowerDNS often uses ANY, to which you should return all valid records for the name and PowerDNS worries about returning the right one to the client. You must support this type.

qclass

The request class, this is always IN.

remote_ip_address

The IP address of the host making the dns request. You could use this to return different records for different geographic regions.

local_ip_address

The server IP address the request came into. Useful if your PowerDNS server is listening on multiple IPs and you want to consider that in your answers.

id

The id of the last answer to this question provided to PowerDNS by this backend for this. This might be useful to you to speed up subsequent lookups. -1 by default and can be ignored.

query?

Returns true if this is a normal Q query.

axfr?

Returns true if this is an axfr query.

answer method

The answer method is used to return records to PowerDNS. It can be called multiple times to return multiple records. Any exceptions are caught for you so garbage is not returned to PowerDNS. If you have nothing to return, just don’t call answer at all.

It takes the following options:

:name

The record name, e.g: www.example.com. Can usually just be set to question.name

:ttl

Time to Live in seconds. Defaults to 3600

:content

The content of the response, so an IP address string for A answers, or arbitrary text for TXT answers. For records with a priority (like MX records) put the priority first and then a tab and then the content, e.g: 10\t mail.example.com

:id

An integer id for this answer. PowerDNS will remember this and pass it back for subsequent requests for the same record. You might use this to pass around a primary key or something to speed up subsequent lookups. Defaults to -1 and can be ignored.

:class

The class of this answer, defaults to IN and shouldn’t be changed.

Basic example

Return an A record of 1.2.3.4 for all queries:

require 'powerdns_pipe'
PowerDNS::Pipe.new.run! do
  answer :name => question.name, :type => 'A', :ttl => 60, :content => '1.2.3.4'
end

Advanced example

Return the HTTP Server header as a TXT record for the host requested.

Example usage:

$ host -t txt www.ubuntu.com.example.com
ubuntu.com.example.com descriptive text "Apache/2.2.8 (Ubuntu) mod_python/3.3.1"

Code:

require 'powerdns_pipe'
require 'net/http'
re = Regexp.new("^(.+)\.example\.com$")
pipe = PowerDNS::Pipe.new :banner => 'HTTP Server Header TXT Pipe'
pipe.run! do
  if m = re.match(question.name)
    domain = m[1]
    case question.qtype
    when "TXT", "ANY"
      res = Net::HTTP.get_response(URI.parse("http://" + domain))
      answer :name => question.name , :type => 'TXT', :ttl => 3600, 
        :content => res['Server']
    end
  end
end

More Info

Author

John Leach ([email protected])

Copyright

Copyright © 2010-2018 John Leach

License

MIT

Github

github.com/johnl/powerdns_pipe/tree/master

See also the ruby-pdns library which does things differently, with some limitations.

powerdns_pipe's People

Contributors

johnl avatar

Stargazers

Jenechka avatar Andrew Eberbach avatar xdsubei avatar Angus H. avatar Bao Nguyen avatar  avatar Dāvis avatar Sokratis Galiatsis avatar Renaud Chaput avatar Max avatar Max Trense avatar Keith Larrimore avatar Nikolay Bochev avatar Aaron Daniel avatar  avatar fannar avatar

Watchers

 avatar Dāvis avatar James Cloos avatar xdsubei avatar

Forkers

amdtech federate

powerdns_pipe's Issues

[PIPEBackend] coprocess returned emtpy line in query for test_host

I'm seeing this:

# host test_host 127.0.0.1
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases: 

Host test_host not found: 2(SERVFAIL)

This is what's in the logs:

Oct 28 17:06:03 zanview pdns[19954]: [PIPEBackend] coprocess returned incomplete or empty line in data section for query for test_host
Oct 28 17:06:03 zanview pdns[19954]: Database module reported condition which prevented lookup (Format error communicating with coprocess in data section) sending out servfail

My code is:

#!/usr/bin/ruby
require 'powerdns_pipe'
PowerDNS::Pipe.new.run! do
  $ip = '89.207.135.250'
  ipp_file = open("/etc/openvpn/log/ipp.txt", "r")
  while (line = ipp_file.gets)
    hostname, ip = line.split(',')
    $ip = ip if hostname == question.name
  end
  answer name: question.name, type: 'A', ttl: 60, content: $ip
end

Is it the opening and reading from a file that's causing a problem? - Any ideas why this is failing?

If I however do:

# host test_host.moo.com 127.0.0.1
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases: 

test_host.moo.com has address 89.207.135.250

With the moo.com, it works, but with just host, it does not. Any ideas?

how to cleanly answer that a request cannot be resolved?

If I do something like:

answer name: question.name, type: 'A', ttl: 3600, content: -1

I see this in syslog:

Mar 7 00:15:02 server862 pdns[27846]: Exception building answer packet (Parsing record content: while parsing IP address, expected digits at position 0 in '-1') sending out servfail

Is there a cleaner way to tell powerdns know this request cannot be resolved?

AXFR is not handled correctly

According to PDNS doc, AXFR queries only contain an "id" field.

In your code, that id is parsed as name (first attribute of Question):

#<struct PowerDNS::Pipe::Question tag="AXFR", name="-1", qclass=nil, qtype=nil, id=nil, remote_ip_address=nil, local_ip_address=nil>

Also, the AXFR query spec is wrong:

it "should answer an axfr" do
  @input.string = "AXFR\texample.com\tIN\tANY\t-1\t8.8.8.8\t127.0.0.1"

I'll try to submit a patch for this.

Should handle bad data being given to it

In issue #3, the content was being sent with a \n on the end which resulted in an empty line sent to powerdns (which it doesn't like and).

I've also seen a situation where we answered with a nil for content, which sent powerdns nothing for content and it didn't like that either.

I think powerdns_pipe should do what it can to prevent bad data getting sent to powerdns, and log it instead (I think the best thing to do is issue a LOG and a FAIL)

So any data given with a tab or a \n should be rejected.

We should perhaps turn nils and empty strings into a space character, so it's explicitly an empty content - need to test how powerdns acts with that (is returning an A record with no content a valid and/or useful thing to be able to do?)

`readline': Interrupt

I'm using the "basic" example in the readme and I have this in my powerdns config:

 launch=pipe
 pipe-command=/etc/openvpn/dns_pipe.rb

When it tries to resolve, I get this error:

 /usr/local/rvm/gems/ruby-1.9.3-p194/gems/powerdns_pipe-1.0.1/lib/powerdns_pipe.rb:62:in `readline': Interrupt
 from /usr/local/rvm/gems/ruby-1.9.3-p194/gems/powerdns_pipe-1.0.1/lib/powerdns_pipe.rb:62:in `run!'
 from dns_pipe.rb:4:in `<main>'

Any ideas?

it should be difficult to pass bad data back to powerdns

If you pass bad data back to powerdns it kills the process and logs lots of messy stuff to the ogs.

it'd be nice if powerdns_pipe did some basic validation on the data before returning it to powerdns, to avoid this somewhat.

Looking at pipebackend.cc, should have 7 tokens for abi versions 1 to 2, and 9 tokens for abi version 3.

and mx/srv lines have 1 extra token.

and it seems like if the content token is empty, then it doesn't get "counted", so an error is given.

so at it's most basic, we should probably ensure the data field is never an empty string.

not quit sure what to do though - raising an exception and exiting isn't much better. probably either explicitly FAIL or just LOG an error and return whatever other records we can.

Unknown Question received: Q test_host IN ANY -1 192.0.43.10

I'm not sure what happened, the pipe used to work and now I'm seeing this:

# ruby dns_pipe2.rb 
Q test_host IN ANY -1 192.0.43.10
LOG Unknown Question received: Q test_host IN ANY -1 192.0.43.10                        
FAIL
Q test_host.xanview.com IN ANY -1 192.0.43.10
LOG Unknown Question received: Q test_host.xanview.com IN ANY -1 192.0.43.10                        
FAIL

The script looks like this:

require 'powerdns_pipe'
PowerDNS::Pipe.new.run! do
  answer name: question.name, type: 'A', ttl: 120, content: "127.0.0.1"
end

Not quite sure what is happening, I'm also seeing this in the PDNS logs:

Dec 31 16:41:03 zanview pdns[30585]: [PIPEBackend] coprocess returned incomplete or empty line in data section for query for -1
Dec 31 16:41:03 zanview pdns[30585]: Database module reported condition which prevented lookup (Format error communicating with coprocess in data section) sending out servfail

Any ideas?

Ever since upgrading to 1.1, I'm getting undefined method `answer' for #<PowerDNS::Pipe:0x00005646732e8910>

The full exception is:

/etc/powerdns/dns_pipe.rb:96:in `answer'
/etc/powerdns/dns_pipe.rb:303:in `soa_record'
/etc/powerdns/dns_pipe.rb:78:in `process'
/etc/powerdns/dns_pipe.rb:348:in `block in <main>'
/var/lib/gems/2.5.0/gems/powerdns_pipe-1.1/lib/powerdns_pipe.rb:101:in `instance_eval'
/var/lib/gems/2.5.0/gems/powerdns_pipe-1.1/lib/powerdns_pipe.rb:101:in `process_query'
/var/lib/gems/2.5.0/gems/powerdns_pipe-1.1/lib/powerdns_pipe.rb:87:in `process_line'
/var/lib/gems/2.5.0/gems/powerdns_pipe-1.1/lib/powerdns_pipe.rb:69:in `run!'
/etc/powerdns/dns_pipe.rb:346:in `<main>'

Did the API change?

incomplete MX/SRV line in data section for query

I have this code:

PowerDNS::Pipe.new.run! do
  answer name: question.name, type: 'MX', ttl: 3565,
    content: "10 ASPMX.L.GOOGLE.com"
end

When I run on the command line I get this:

# echo -e "Q\ttimeline.is\tIN\tMX\t-1\t127.0.0.1\t0.0.0.0" | /etc/powerdns/dns_pipe.rb
DATA    timeline.is IN  MX  3565    -1  10 ASPMX.L.GOOGLE.com
END
EOF, terminating loop

However when I restart powerdns to use the new code, it is giving me this:

Aug  4 20:41:36 Timeline pdns[4285]: Backend launched with banner: OK#011Ruby PowerDNS::Pipe
Aug  4 20:41:40 Timeline pdns[4285]: [PIPEBackend] coprocess returned incomplete MX/SRV line in data section for query for timeline.is
Aug  4 20:41:40 Timeline pdns[4285]: Backend reported permanent error which prevented lookup (Format error communicating with coprocess in data section of MX/SRV record), aborting
Aug  4 20:41:40 Timeline pdns[4285]: Backend error: Format error communicating with coprocess in data section of MX/SRV record
Aug  4 20:41:40 Timeline pdns_server[4268]: Aug 04 20:41:40 [PIPEBackend] coprocess returned incomplete MX/SRV line in data section for query for timeline.is
Aug  4 20:41:40 Timeline pdns_server[4268]: Aug 04 20:41:40 Backend reported permanent error which prevented lookup (Format error communicating with coprocess in data section of MX/SRV record), aborting
Aug  4 20:41:40 Timeline pdns_server[4268]: Aug 04 20:41:40 Backend error: Format error communicating with coprocess in data section of MX/SRV record

Any ideas why? - other types like A, NS, TXT, SOA all work correctly, it is just the MX type that is giving these errors.

log api in query responder block

How do you feel about adding a log api in the query responder block context to allow for individual logging while processing the request?

Something like this:

module PowerDNS
  class Pipe
    class Answer
      def log(msg); @pipe.log msg; end
    end
    def log(msg); respond('LOG', msg); end
  end
end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.