mpapet has asked for the wisdom of the Perl Monks concerning the following question:

Greeting Wise Ones,

I'm working on a program that captures DNS packets via an iptables queue, then I'd like to rewrite the answer portion of the packet for undesirable URL's. (Basically, advert blocking at the firewall+kernel level)

Net::DNS::Packet doesn't work. It parses the header okay, but can't find the answer.

Net::ncap and Net::pcap listen on interfaces. I get my packets from the firewall queue.

NetPacket::UDP appears to get me the binary payload. In the payload is the Question as a variable length string and one or more answers.

I have the format of the packet in a pretty picture, http://www.firewall.cx/dns-query-format.php.

DNS RFC is here: http://www.faqs.org/rfcs/rfc1035.html

I think my question is how do I unpack the payload, then turn the RR into a string?

Replies are listed 'Best First'.
Re: Disassembling DNS Packet
by NetWallah (Canon) on May 13, 2010 at 04:41 UTC
    Net::DNS::Packet doesn't work. It parses the header okay, but can't find the answer.
    Since it parses the header, it probably getting you 80% of the way there - You should be able to subclass the part that does not work.

    You did not define "does not work" - does it give you the right length ? Right number of RR's ? It is just the RR parsing that is broken ?

    Have you tried to run it in debug mode to see where it breaks ?

         Syntactic sugar causes cancer of the semicolon.        --Alan Perlis

      Thanks for taking a stab at a vague question.

      You did not define "does not work"

      It does not return any RR's or the Question. It parses the UDP header. I have that with NetPacket::UDP.

      I've got a more specific question that will probably get me started in the right direction. The transaction ID is the first two bytes in the payload. The following code should return the transaction ID, but I'm not doing it right

      my $udp_packet = NetPacket::UDP->decode($A_raw_IP_Packet); my $payload = $udp_obj->{data}; #unpack "v" might return the two-bytes as an integer? my $transID = unpack(v, substr($payload,0,2); print "there's a transID of $transID \n";

      Running my script, I get

      Theres a transid 14964. In a separate terminal, dig returns an ID 7751

      Thanks for taking a stab at a vague question.

      You did not define "does not work"

      It does not return any RR's or the Question. It parses the UDP header. I have that with NetPacket::UDP.

      I've got a more specific question that will probably get me started in the right direction. The transaction ID is the first two bytes in the payload. The following code should return the transaction ID, but I'm not doing it right

      my $udp_packet = NetPacket::UDP->decode($A_raw_IP_Packet); my $payload = $udp_obj->{data}; #unpack "v" might return the two-bytes as an integer? my $transID = unpack(v, substr($payload,0,2)); print "there's a transID of $transID \n";

      Running my script, I get

      Theres a transid 14964. In a separate terminal, dig returns an ID 7751

        I figured the question about getting the ID from the UDP payload with everyone's help. My problem was the implementation of NetPacket::IP and then NetPacket::UDP. The following code gets the ID out of the DNS packet.

        my $ip_obj = NetPacket::IP::strip($Raw_IP_Packet); my $udp_obj = NetPacket::UDP->decode($ip_obj); if ($udp_obj->{len}) { my $payload = $udp_obj->{data}; my $transid1 = unpack('n', substr($payload,0,2)); print "The transID is $transid1 \n"; }

        My big mistake was the first line was wrong. " ...NetPacket::IP->decode($Raw_IP_packet);" This doesn't get the packet set up right. As the documentation for NetPacket::IP says, it happily parses garbage.

        Net::DNS::Packet works, but I didn't see the errors of my ways until I got the ID working. the following code works in my situation.

        use Net::DNS::Packet; use Data::Dumper; use NetPacket::UDP; use NetPacket::IP; my $ip_obj = NetPacket::IP::strip($Raw_IP_Packet); my $udp_obj = NetPacket::UDP->decode($ip_obj); if ($udp_obj->{len}) { my $payload2 = $udp_obj->{data}; my $test = Net::DNS::Packet->new(\$payload2); if ($test) { my @answer = $test->answer; print Dumper(@answer); } else { print "no Net::DNS::Packet \n";} } }

        I get a complete @answer back when doing "dig slashdot.org" in another terminal.

        A big thank you to everyone who gave me a push in the right direction despite my vague question.

        unpack "v"? Are you sure? Little-endian? For a network protocol?

        As JavaFan has said that should be 'n', not 'v'. If IDs in script and terminal are different, then how do you know that you captured the right packet? Why do you assigning decoded UDP packet to $udp_packet variable, but getting $payload from $udp_obj? Are you using strict and warnings? Could you post packet data and actual script that we could run?

        See also How do I post a question effectively?

      Weird....

      Are you sure you are using the methods correctly. Also. Does the header question count indicate there is a question section, same for the answer section?

      e.g:

      my $packet = $res->send($data->{'name'}, $data->{'type'}, 'IN'); ok($packet, "Got an answer for $data->{name} IN $data->{type}"); is($packet->header->qdcount, 1, 'Only one question'); is($packet->header->ancount, 1, 'Got single answer'); my $question = ($packet->question)[0]; my $answer = ($packet->answer)[0];

      If you did all those things. You could consider sending a bug report via https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Net-DNS and make sure you include some sample packet data.

      --Olaf (Net::DNS maintainer)

Re: Disassembling DNS Packet
by JavaFan (Canon) on May 12, 2010 at 22:58 UTC
    I think my question is how do I unpack the payload,
    Use unpack()?
Re: Disassembling DNS Packet
by mpapet (Novice) on May 13, 2010 at 18:50 UTC
    I figured the question about getting the ID from the UDP payload with everyone's help. My problem was the implementation of NetPacket::IP and then NetPacket::UDP. The following code gets the ID out of the DNS packet.

    my $ip_obj = NetPacket::IP::strip($Raw_IP_Packet); my $udp_obj = NetPacket::UDP->decode($ip_obj); if ($udp_obj->{len}) { my $payload = $udp_obj->{data}; my $transid1 = unpack('n', substr($payload,0,2)); print "The transID is $transid1 \n"; }

    My big mistake was the first line was wrong. " ...NetPacket::IP->decode($Raw_IP_packet);" This doesn't get the packet set up right. As the documentation for NetPacket::IP says, it happily parses garbage.

    Net::DNS::Packet works, but I didn't see the errors of my ways until I got the ID working. the following code works in my situation.

    use Net::DNS::Packet; use Data::Dumper; use NetPacket::UDP; use NetPacket::IP; my $ip_obj = NetPacket::IP::strip($Raw_IP_Packet); my $udp_obj = NetPacket::UDP->decode($ip_obj); if ($udp_obj->{len}) { my $payload2 = $udp_obj->{data}; my $test = Net::DNS::Packet->new(\$payload2); if ($test) { my @answer = $test->answer; print Dumper(@answer); } else { print "no Net::DNS::Packet \n";} } }

    I get a complete @answer back when doing "dig slashdot.org" in another terminal.

    A big thank you to everyone who gave me a push in the right direction despite my vague question.