The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Net::DNS::Dynamic::Proxyserver - A dynamic DNS proxy-server

DESCRIPTION

This proxy-server is able to resolve incoming DNS queries by asking the /etc/hosts file and/or a SQL database. You could run it as a simple proxy-server which just loops through the DNS question/answer to other nameservers. However, it could also be used to build your own dynamic DNS service, share one /etc/hosts file with all PC's in your local network or to deliver different answers for hosts at different locations.

For example, if you have a home or office server behind NAT with port-forwarding and want to connect to this server always with the same hostname based on your notebooks location, you could run this dns-proxy to answer to your servers hostname with it's internal RFC1918 IP when you are at home or in the office. When you're at a different location and use an external nameserver, the hostname of your server will be resolved with the "external" IP address (of your router).

If you like to build your own dynamic DNS service, you need to write your dynamic IP addresses into a SQL databases and let your DNS proxy-server answer queries from it.

SYNOPSIS

 my $proxy = Net::DNS::Dynamic::Proxyserver->new(

     debug => 1,

     host => '*',
     port => 53,

     uid => 65534,
     gid => 65534,
     
     nameservers      => [ '127.0.0.1', '192.168.1.110' ],
     nameservers_port => 53,

     ask_etc_hosts => { ttl => 3600 },

     ask_sql => {
         
         ttl => 60, 

         dsn => 'DBI:mysql:database=my_database;host=localhost;port=3306',
         user => 'my_user',
         pass => 'my_password',

         statement => "SELECT ip FROM hosts WHERE hostname='{qname}' AND type='{qtype}'"
     },
 );

 $proxy->run();

WORKFLOW

At startup, the file /etc/resolv.conf will be read and parsed. All defined nameservers will be used to proxy through queries that can not be answered locally. If you define the 'ask_etc_hosts' argument, then also the file /etc/hosts will be read at startup and will be used as the first resource to answer DNS questions. If you make changes to /etc/hosts, you can send a kernel signal HUP to your script, which will trigger a re-read of this file at run-time. The hosts-file will only answer queries for type 'A' (name to IP) and 'PTR' (IP to name).

If you specify the 'ask_sql' argument, the SQL database will be asked in second order, right after a look into the hosts file. The SQL statement will be parsed for every query with the given query name and type. Your statement should return the IP address as the first column in the result-set. Right now, only "forward lookups" are supported (PTR records can not be resolved yet because we'd need a second, different SQL statement for that).

Then, if the query could not be answered from the hosts-file and/or the database, the question will be handed over to the nameserves from your /etc/resolv.conf and the answer will be looped trough to the caller.

Arguments to new()

The following options may be passed over when creating a new object:

debug Int

When the debug option is set to 1 or higher (1-3), this module will print out some helpful debug informations to STDOUT. If you like, redirect the output to a log-file, like so

./my-dns-proxy.pl >>/var/log/my_dns_proxy.log

A debug value of 1 prints out some basic action logging. A value of 2 and higher turns on nameserver verbosity, a value of 3 and higher turns on resolver debug output.

host String

You can specify the IP address to bind to with this option. If not defined, the server binds to all interfaces.

Examples:

 my $proxy = Net::DNS::Dynamic::Proxyserver->new( host => '127.0.0.1' );

 my $proxy = Net::DNS::Dynamic::Proxyserver->new( host => '192.168.1.1' );

 my $proxy = Net::DNS::Dynamic::Proxyserver->new( host => '*' );

port Int

The tcp & udp port to run the DNS server under. Default is port 53, which means that you need to start your script as user root (all ports below 1000 need root rights).

  my $proxy = Net::DNS::Dynamic::Proxyserver->new( port => 5353 );

uid Int

The user id to switch to, after the socket has been created. Could be set to the uid of 'nobody' (65534 on some systems).

  my $proxy = Net::DNS::Dynamic::Proxyserver->new( uid => 65534 );

gid Int

The group id to switch to, after the socket has been created. Could be set to the gid of 'nogroup' (65534 on some systems).

  my $proxy = Net::DNS::Dynamic::Proxyserver->new( gid => 65534 );

nameservers ArrayRef

This argument allows to defined one or more nameservers to forward any DNS question which can not be locally answered. Must be an Arrayref of IP addresses.

If you do not specify nameservers this way, the file /etc/resolv.conf will be read instead and any nameserver defined there will be used.

 my $proxy = Net::DNS::Dynamic::Proxyserver->new( nameservers => [ '127.0.0.1', '192.168.1.110' ] );

nameservers_port Int

Specify the port of the remote nameservers. By default, this is set to 53 (the standard port), but you can ovewrite it if you run a nameserver on a different port. This port will be used for every nameserver - due to a limitation of Net::DNS::Resolver which cant deal with ports for each individual nameserver.

 my $proxy = Net::DNS::Dynamic::Proxyserver->new( nameservers_port => 5353 );

ask_etc_hosts HashRef

If you'd like to anwer DNS queries from entries in your /etc/hosts file, then define this argument like so:

 my $proxy = Net::DNS::Dynamic::Proxyserver->new( ask_etc_hosts => { ttl => 3600 } );

The only argument that can be passed to 'ask_etc_hosts' is the TTL (time to life) for the response.

If 'ask_etc_hosts' is not defined, no queries to /etc/hosts will be made.

If you make changes to your /etc/hosts file, you can send your script a signal HUP and it will re-read the file on the fly.

ask_sql HashRef

If you'd like to answer DNS queries from entries in your SQL database, then define this argument like so:

  my $proxy = Net::DNS::Dynamic::Proxyserver->new( ask_sql => {
        
        ttl => 60, 
        dsn => 'DBI:mysql:database=db_name;host=localhost;port=3306',
        user => 'my_user',
        pass => 'my_password',
        statement => "SELECT ip FROM hosts WHERE hostname='{qname}' AND type='{qtype}'"
  } );

The 'ttl' specifies the TTL (time to life) for the DNS response. Setting this to a low value will tell the client to ask you again after the TTL time has passed by; which also means some higher load for your dns-proxy-server.

The 'dsn' is the 'data source name' for the DBI module. This information is used to connect to your SQL database. You can use every flavour of SQL database that is supported by DBI and a DBD::* module, like MySQL, PostgreSQL, SQLite, Oracle, etc... Please have a look at the manual page of DBI and DBD::* to see how a dsn looks like and which options it could contain.

The 'user' and 'pass' is the username and password for the connection to the database. If you use SQLite, just leave the values empty (user => '', pass => ''). Also make sure, the SQLite database file can be accessed (read/write) with the defined uid/gid!

The 'statement' is a SELECT statement, which must return the IP address for the given query name (qname) and query type (qtype, like 'A' or 'MX'). The placeholders {qname} and {qtype} will be replaced by the actual query name and type. Your statement must return the IP address as the first column in the result.

If 'ask_sql' is not defined, no queries to a database will be made.

AUTHOR

Marc Sebastian Jakobs <maja@cpan.org>

COPYRIGHT AND LICENSE

Copyright 2009 by Marc Sebastian Jakobs

This library is free software, you can redistribute it and/or modify it under the same terms as Perl itself.