DNS client を自作する

可能な限り CPAN mobule に依存する + rawsocket という条件付きで DNS client を Perl で書いてみた。

#!/usr/local/bin/perl

use strict;
use warnings;
use IO::Interface::Simple;
use IO::Socket::INET;
use Net::DNS::Packet;
use Net::RawIP qw(:pcap);
use App::Options(
    values => \%ARGV,
    option => {
        name => {
            type     => 'string',
            required => 1,
        },
        ns => {
            type     => 'string',
            required => 1,
        },
        type => {
            type    => 'string',
            default => 'A',
        },
        localport => {
            type    => 'int',
            default => 53,
        },
    },
);

# get local address from interface
my $saddr;
my @interfaces = IO::Interface::Simple->interfaces;
for my $if (@interfaces) {
    if ( not $if->is_loopback and $if->is_running and $if->is_broadcast ) {
        $saddr = $if->address;
        last;
    }
}

my $server = IO::Socket::INET->new(
    Proto     => 'udp',
    LocalPort => $ARGV{localport},
);
my $dns_query = Net::DNS::Packet->new( $ARGV{name}, $ARGV{type}, 'IN' );
my $udp = Net::RawIP->new( { udp => {} } );
$udp->set(
    {
        ip => {
            saddr => $saddr,
            daddr => $ARGV{ns},
        },
        udp => {
            source => $ARGV{localport},
            dest   => 53,
            data   => $dns_query->data,
        },
    }
);
$udp->send;

$server->recv( my $msg, 65535 ) or die "recv failed : $!\n";
my $dns_response = Net::DNS::Packet->new( \$msg );
print $dns_response->string;

実行するとこんな感じ

# ./dns_client.pl --name=d.hatena.ne.jp --ns=ns0.future-s.com
;; HEADER SECTION
;; id = 45073
;; qr = 1    opcode = QUERY    aa = 1    tc = 0    rd = 1
;; ra = 1    ad = 0    cd = 0    rcode  = NOERROR
;; qdcount = 1  ancount = 1  nscount = 2  arcount = 2

;; QUESTION SECTION (1 record)
;; d.hatena.ne.jp.      IN      A

;; ANSWER SECTION (1 record)
d.hatena.ne.jp. 86400   IN      A       59.106.108.77

;; AUTHORITY SECTION (2 records)
hatena.ne.jp.   86400   IN      NS      ns0.future-s.com.
hatena.ne.jp.   86400   IN      NS      ns1.future-s.com.

;; ADDITIONAL SECTION (2 records)
ns0.future-s.com.       600     IN      A       219.99.160.180
ns1.future-s.com.       600     IN      A       219.99.160.181