diff options
Diffstat (limited to 'nomde.pl')
-rw-r--r-- | nomde.pl | 359 |
1 files changed, 0 insertions, 359 deletions
diff --git a/nomde.pl b/nomde.pl deleted file mode 100644 index 5217afe..0000000 --- a/nomde.pl +++ /dev/null @@ -1,359 +0,0 @@ -#!/usr/bin/perl - -$VERSION = 0.1; - -use Fcntl; -use Net::DNS; -use Net::DNS::Nameserver; -#use LWP::Simple; -use LWP::UserAgent; -use Time::HiRes qw ( usleep gettimeofday ); -use MIME::Base64; -use MIME::Base32 qw ( RFC ); -use IO::Socket; -use Class::Struct; -use threads; -use threads::shared; -use Thread::Queue; -use Getopt::Long; -use English; - - -my %opts; -my %mods; - -$opts{ptrname} = "127.0.0.1"; -$opts{filename} = "nomde.pl"; -$opts{forward} = "sshdns:127.0.0.1:22"; -GetOptions( - "ip=s" => \$opts{ip}, - "filename=s" => \$opts{file}, - "ptrname" => \$opts{ptrname}, - "Localforward"=> \$opts{forward} -); -if($ARGV[0]) { - $opts{localname} = $ARGV[0]; - my @tmp = split('\.', $opts{localname}); - $opts{nameoffset} = $#tmp; - undef @tmp; -} - -if(!length($opts{localname}) || !length($opts{ip})){ - - print STDERR << "EOD"; -nomde $VERSION: Experimental DNS Server -Component of: OzymanDNS Dan Kaminsky(dan\@doxpara.com) - Usage: nomde -l 10.0.1.11 servername.foo.com - Options: -i [ip address]: IP address to host for all A requests - -f [filename] : Filename to host in TXT records [b64] - -p [name] : Name/IP to return for reverse lookups[ptr] - -L [name:host:port]: Forward function to address, port - (Default: sshdns:127.0.0.1:22) -EOD - exit 1; -} - -struct ( dns_sock => { - sock => '$', - lasttime => '$', - reader => '$', - queue => '$' -}); - - -struct ( dns_sock_data => { - data => '$', - lasttime => '$' -}); - -# set STDIN to nonblock -$flags=''; -fcntl(STDIN, F_GETFL, $flags) or die "1\n"; -$flags |= O_NONBLOCK; -fcntl(STDIN, F_SETFL, $flags) or die "2\n"; - - -#use strict; -#use warnings; - -$dataclean; - -sub reply_handler { - my ($qname, $qclass, $qtype, $peerhost, $header, $packet) = @_; - my ($rcode, @ans, @auth, @add, $val); - - my @namelist = split(/\./, $qname); - my @reverselist = reverse(@namelist); - my $function = $reverselist[$opts{nameoffset}+1]; - my @args = (@reverselist[$opts{nameoffset}+2..$#reverselist]); - - $rcode = "NXDOMAIN"; - - if ($qtype eq "AAAA") { $rcode = "NOTIMPL"; goto end;}; - if ($qtype eq "SOA") { - my $now = gettimeofday(); # yeah that'll give unique serials - my $name = $opts{localname}; - my ($ttl, $rdata) = (3600, "ns.$name root.$name $now 28800 14400 3600000 0"); - push @ans, Net::DNS::RR->new("$qname $ttl $qclass $qtype $rdata"); - $rcode = "NOERROR"; - goto end; - } - if ($qtype eq "TYPE38") { $rcode = "NOTIMPL"; goto end;}; - if ($qtype eq "A" && $function eq "glance") { - my $addr = $args[0]; - chomp($addr); - $addr =~ s/_/\./g; - $addr =~ s/-/\//g; - $addr = "http://" . $addr; - - my $ua = LWP::UserAgent->new; - my $request = HTTP::Request->new(HEAD => "$addr"); - my $response= $ua->request($request); - my $modified_time = $response->last_modified; - my $expires = $response->expires; - my $date = $response->date; - - my $ttl = $expires - $date; - - my @array = unpack("C*", pack("V", $modified_time)); # will it work? - my $nsip = join(".", @array); - if ($ttl < (60*20)) {$ttl = 60*20;} - push @ans, Net::DNS::RR->new("$qname $ttl $qclass $qtype $nsip"); - $rcode = "NOERROR"; - goto end; - } - if ($function eq "echo"){ - - if ($qtype eq "A" && $namelist[0] eq "ns") - { - my $nsip = $args[0]; - $nsip =~ s/-/./g; - my ($ttl, $rdata) = (20, $nsip); - push @ans, Net::DNS::RR->new("$qname $ttl $qclass $qtype $rdata"); - $rcode = "NOERROR"; - goto end; - } - else { - my $nsip = $args[0]; - $nsip =~ s/-/./g; - my $ns = "ns." . "$qname"; - print "\n$ns\n"; - $type = "NS"; - if($qtype eq "NS") { - push @ans, Net::DNS::RR->new("$qname 20 $qclass NS $ns"); - } else { - push @auth, Net::DNS::RR->new("$qname 20 $qclass NS $ns"); - } - push @add, Net::DNS::RR->new("$ns 20 $qclass A $nsip"); - $rcode = "NOERROR"; - goto end; - } - } - - - if ($qtype eq "CNAME") { $rcode = "NOERROR"; goto end;}; - if ($qtype eq "PTR") { - my ($ttl, $rdata) = (0, $opts{ptrname}); - push @ans, Net::DNS::RR->new("$qname $ttl $qclass $qtype $rdata"); - $rcode = "NOERROR"; - } - if ($qtype eq "TXT" && $function eq "b64" && $opts{file}) { - my $val, $i; - seek($opts{file}, $args[0], 0); - read($opts{file}, $val, 57*2); - $val = encode_base64($val); - read($opts{file}, $val2, 57*2); - $val2 = encode_base64($val2); - my $ttl = 60; - push @ans, Net::DNS::RR->new("$qname $ttl $qclass $qtype \"$val\" \"$val2\"" ); - $rcode = "NOERROR"; - goto end; - } - - my ($servname, $remotehost, $remoteport) = split(":", $opts{forward}); - if ($function eq "$servname"){ - my $op = $args[0]; - my $id = $args[1]; - my ($offset, $nonce) = split("-", $args[2]); - - my $now = gettimeofday(); - if(!$sockclean || $now - $sockclean > 240){ - $sockclean = gettimeofday(); - foreach $scanid (keys %socklist){ - if($now - $socklist{$scanid}->lasttime > 10){ - $socklist{$scanid}->sock->shutdown(2); - delete $socklist{$scanid}; - } - } - } - my $scanid; - if(!$dataclean || $now - $dataclean > 20){ - $dataclean = gettimeofday(); - foreach $scanid (keys %sockdata) { - if(defined($sockdata{"$scanid"})&& - $now - $sockdata{"$scanid"}->lasttime > 60){ - delete $sockdata{$scanid}; - } - } - } - - if(!exists $socklist{$id}) { - $socklist{$id}=new dns_sock; - my $sock = IO::Socket::INET->new( - PeerAddr => "$remotehost", - PeerPort => "$remoteport", - Proto => "tcp", - Type => SOCK_STREAM, - Blocking => 1);# or die "couldn't spawn socket\n"; - $socklist{$id}->queue(Thread::Queue->new); - $socklist{$id}->sock($sock); - $socklist{$id}->reader(threads->new(\&reader, $socklist{$id}->sock, $socklist{$id}->queue)); - } - - $socklist{$id}->lasttime($now); - my $sock = $socklist{$id}->sock; - - my $sockdata_is_fresh=0; - if(!exists $sockdata{$op,$offset,$id}) { - $sockdata{$op,$offset,$id} = new dns_sock_data; - $sockdata{$op,$offset,$id}->lasttime($now); - $sockdata_is_fresh=1; - } - - my $data = $sockdata{$op,$offset,$id}->data; - if($op eq "up" && $qtype eq "A"){ - my $size=0; - $data = uc join("", reverse(@args[3..$#args])); - $data = MIME::Base32::decode($data); - - if($sockdata_is_fresh){ - while($size != length($data)) { - my $outdata; - $outdata = substr($data, $size); - $size += syswrite($sock, $outdata, length($data)-$size); - if($size != length($data)){usleep (100 * 1000);} - } - $data="1"; #for now, we don't store incoming data - } - $sockdata{$op,$offset,$id}->data($data); - my $reply = "$size.0.0.0"; - my $ttl = 0; - push @ans, Net::DNS::RR->new("$qname $ttl $qclass A $reply"); - $rcode = "NOERROR"; - - goto end; - } - - sub reader { - my @args = @_; - my $sock = @args[0]; - my $queue= @args[1]; - - while(1){ - if($queue->pending < 32) { - sysread($sock, $data, 220); - if(length($data)) {$queue->enqueue($data);} - usleep (50 * 1000 / 10); - } - } - } - - if($op eq "down"){ #intentionally not txt checking - if($sockdata_is_fresh || length($data)==0) { - $data = $socklist{$id}->queue->dequeue_nb; - #sysread($sock, $data, 220); - } - $sockdata{$op,$offset,$id}->data($data); - my $data1 = substr($data, 0, 110); - my $data2 = substr($data, 110, 110); - my $txt1 = encode_base64($data1); - my $txt2 = encode_base64($data2); - my $x = $socklist{$id}->queue->pending; - - push @ans, Net::DNS::RR->new("$qname $ttl $qclass $qtype \"$txt1\" \"$txt2\""); - push @add, Net::DNS::RR->new("pending.$qname $ttl $qclass A $x.0.0.0"); - $rcode = "NOERROR"; - goto end; - } - } - - if ($function eq "b32" && $args[0] eq "set") { - my $descrip = $args[1]; - my @data = @args[2..$#args]; - my $string = join(".", @data); - #my $data = MIME::Base32::decode($string); - $datastore{$descrip} = $qname; #$string; - push @ans, Net::DNS::RR->new("$qname $ttl $qclass A 1.0.0.0"); - #push @ans, Net::DNS::RR->new("$qname $ttl $qclass TXT \"ok: $descrip = $string\""); - $rcode = "NOERROR"; - goto end; - } - if ($function eq "b32" && $args[0] eq "get") { - my $descrip = $args[1]; - if(exists $datastore{$descrip}){ - push @ans, Net::DNS::RR->new("$qname $ttl $qclass CNAME $datastore{$descrip}"); - push @add, Net::DNS::RR->new("$datastore{descrip} $ttl $qclass A 1.0.0.0"); - $rcode = "NOERROR"; - } else { - $rcode = "NXDOMAIN"; - } - goto end; - } - - if ($qtype eq "TXT") { - $val = `/bin/date`; - chomp $val; - $val = "Hi: $val"; - my ($ttl, $rdata) = (0, $val); - push @ans, Net::DNS::RR->new("$qname $ttl $qclass $qtype \"$rdata\""); - $rcode = "NOERROR"; - goto end; - } - if ($qtype eq "NS") { - if($args[0] ne "ns"){ my ($ttl, $rdata) = (3600, "ns.$name");} - push @ans, Net::DNS::RR->new("$qname $ttl $qclass $qtype $rdata"); - push @add, Net::DNS::RR->new("$qname $ttl $qclass $qtype $opts{ip}"); - $rcode = "NOERROR"; - goto end; - } - - - if ($qtype eq "A") { - my ($ttl, $rdata) = (3600, $opts{ip}); - push @ans, Net::DNS::RR->new("$qname $ttl $qclass $qtype $rdata"); - $rcode = "NOERROR"; - goto end; - } - - end: - my $element, $name; - print "\n"; - foreach $element (@ans, @auth, @add) { - $name = $element->string; - chomp $name; - print $name, "\n"; - } - - # mark the answer as authoritive (by setting the 'aa' flag - my @response = ($rcode, \@ans, \@auth, \@add, { aa => 1 }); - return @response; -} -if(length($opts{filename})) {open FILE, "$opts{filename}" or die $!;} -binmode FILE; -$opts{file}=FILE; - -my %datastore; - -# note that this socket is blocking -- WAY harder to do nonblocking with -# callbacks (need fork/ipc). -my %socklist : shared; -%sockdata; - -my $ns = Net::DNS::Nameserver->new( - LocalPort => 53, - ReplyHandler => \&reply_handler, - Verbose => 2, -) || die "couldn't create nameserver object\n"; - -$ns->main_loop; - |