From cfc4303326d6ad4cd382e46f23ee7b141e5d7501 Mon Sep 17 00:00:00 2001 From: short <> Date: Thu, 14 Oct 2004 08:14:48 +0000 Subject: [PATCH 1/1] Initial version. --- netdnsspoof | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100755 netdnsspoof diff --git a/netdnsspoof b/netdnsspoof new file mode 100755 index 0000000..65b3b83 --- /dev/null +++ b/netdnsspoof @@ -0,0 +1,174 @@ +#! /usr/bin/perl +# +# $Id$ + +use strict; +use warnings; +use Getopt::Long; +require IO::Socket::INET; +require Net::DNS::Packet; +use Fcntl; +use Carp qw(cluck confess); +use Socket; + + +my $D; +my $opt_addr; +my $opt_port=5353; +my $opt_forward_addr="localhost"; +my $opt_forward_port=53; +my $opt_spoof_ip="192.196.60.1"; +die if !GetOptions( + "a|addr=s",\$opt_addr, + "p|port=s",\$opt_port, + "forward-addr=s",\$opt_forward_addr, + "forward-port=s",\$opt_forward_port, + "i|spoof-ip=s",\$opt_spoof_ip, + "d|debug+",\$D, + ); +my $forward_host=gethostbyname($opt_forward_addr) or die "resolving $opt_forward_addr: $!"; +my $forward_addr=sockaddr_in($opt_forward_port,$forward_host) or die "assembling $opt_forward_addr:$opt_forward_port"; + +my $sock_udp=IO::Socket::INET->new( + LocalAddr=>$opt_addr, + LocalPort=>$opt_port, + Proto=>"udp", + ) or die "socket(): $!"; +my $sock_udp_priv=IO::Socket::INET->new( + Proto=>"udp", + ) or die "socket(): $!"; +###fcntl($sock_udp,F_SETFL,O_NONBLOCK) or die "fnctl(sock_udp,F_SETFL,O_NONBLOCK)"; + +sub id_next() +{ + our $id; + $id=int(rand(0x10000)) if !defined $id; + $id++; + $id&=0xFFFF; + return $id; +} + +my %sent; + +sub got_query($$) +{ +my($msg,$from_addr)=@_; + + my $query=Net::DNS::Packet->new(\$msg); + my $query_id_orig=$query->header()->id(); + my $query_id=id_next(); + $query->header()->id($query_id); + my $msg_forward=$query->data(); + $query->header()->id($query_id_orig); # fix-up back + $sent{$query_id}={ + "msg"=>$msg, + "from_addr"=>$from_addr, + "query"=>$query, + }; + warn "registered pending query id $query_id..." if $D; + warn "sending forwarded DNS query (mapped id $query_id_orig -> $query_id)..." if $D; + send $sock_udp_priv,$msg_forward,0,$forward_addr or cluck "send(): $!"; +} + +sub check_spoofable($$) +{ +my($reply,$query)=@_; + + warn "checking packet spoofability from the reply... (non-spoofable if silent)" if $D; + my @questions=$query->question(); + return 0 if 1!=@questions; + my $question=$questions[0]; + return 0 if $question->qtype() ne "A" + && $question->qtype() ne "ANY"; + return 0 if $reply->header()->rcode() ne "NXDOMAIN"; + warn "packet considered as spoofable." if $D; + return 1; +} + +sub reply_spoof($$) +{ +my($query_from_addr,$query)=@_; + + warn "assembling spoof reply..." if $D; + my $question=($query->question())[0]; + my $spoof=Net::DNS::Packet->new($question->qname(),$question->qclass(),$question->qtype()); + $spoof->push("answer",Net::DNS::RR->new(join(" ", + $question->qname(), + 3600, # ttl + $question->qclass(), + "A", # qtype + $opt_spoof_ip, # rdata + ))); + $spoof->header()->rcode("NOERROR"); + $spoof->header()->aa(0); + $spoof->header()->qr(1); + $spoof->header()->ra(1); + $spoof->header()->rd($query->header()->rd()); + $spoof->header()->id($query->header()->id()); + my $msg_spoof=$spoof->data(); + warn "sending spoof reply..." if $D; + send $sock_udp,$msg_spoof,0,$query_from_addr or cluck "send(): $!"; +} + +sub got_forward_reply($) +{ +my($msg)=@_; + + warn "parsing reply from our forwarded DNS..." if $D; + my $reply=Net::DNS::Packet->new(\$msg); + my $reply_id=$reply->header()->id(); + my($query_msg,$query_from_addr,$query); + if (my $hash=$sent{$reply_id}) { + delete $sent{$reply_id}; + $query_msg =$hash->{"msg"}; + $query_from_addr=$hash->{"from_addr"}; + $query =$hash->{"query"}; + warn "deleted pending record with id $reply_id." if $D; + } + else { + warn "Got DNS reply for unknown packet id $reply_id"; + return; + } + if (check_spoofable($reply,$query)) { + reply_spoof $query_from_addr,$query; + return; + } + $reply->header()->id($query->header()->id()); + my $reply_back=$reply->data(); + warn "parsing reply back to the original query host..." if $D; + send $sock_udp,$reply_back,0,$query_from_addr or cluck "send(): $!"; +} + +for (;;) { + my $rfds=""; + vec($rfds,fileno($sock_udp),1)=1; + vec($rfds,fileno($sock_udp_priv),1)=1; + warn "select(2)..." if $D; + my $got=select $rfds,undef(),undef(),undef(); + warn "got from select." if $D; + die "Invalid select(2): ".Dumper($got) if !defined $got || $got<0; + + for my $sock ($sock_udp,$sock_udp_priv) { + my $msg; + defined(my $from_addr=recv $sock,$msg,0x1000,0) or do { cluck "recv(): $!"; next; }; + warn "got packet." if $D; + my($from_addr_port,$from_addr_host)=sockaddr_in($from_addr); + + if ($from_addr_host eq $forward_host && $from_addr_port eq $opt_forward_port) { + warn "packet returned from forwarding DNS..." if $D; + if ($sock eq $sock_udp) { + warn "packet returned from forwarding DNS forbidden from the main listening socket"; + next; + } + got_forward_reply $msg; + } + else { + warn "original query..." if $D; + if ($sock eq $sock_udp_priv) { + warn "original query forbidden from the private forwarding socket"; + next; + } + got_query $msg,$from_addr; + } + } + } -- 1.8.3.1