diff options
author | Florian Pritz <bluewind@xssn.at> | 2010-08-08 22:46:49 +0200 |
---|---|---|
committer | Florian Pritz <bluewind@xssn.at> | 2010-08-08 22:46:49 +0200 |
commit | d2754512c34a19dad7e489b97fd80bf0bd634089 (patch) | |
tree | 263e282b4c012a09ca95c51d5513eaa5982bb355 /nomde.pl | |
parent | 0170ef29ce9748022bc3437e3b981dcdc60cfd9e (diff) | |
download | bin-d2754512c34a19dad7e489b97fd80bf0bd634089.tar.gz bin-d2754512c34a19dad7e489b97fd80bf0bd634089.tar.xz |
add some crap
Signed-off-by: Florian Pritz <bluewind@xssn.at>
Diffstat (limited to 'nomde.pl')
-rw-r--r-- | nomde.pl | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/nomde.pl b/nomde.pl new file mode 100644 index 0000000..5217afe --- /dev/null +++ b/nomde.pl @@ -0,0 +1,359 @@ +#!/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; + |