deewanagan has asked for the wisdom of the Perl Monks concerning the following question:
any one's help is appreciated, thank u$hostname=$ARGV[0]; $counter=0; ##formate of each label. for (split(/\./,$hostname)){ $label[$counter++] = length; $label[$counter++] = $_; $lformat .= "C a* "; } print "\n@label\n"; $question = pack($lformat."C n2",@labels,0,1,1);
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: How to create DNS packet
by gone2015 (Deacon) on Nov 18, 2008 at 01:06 UTC | |
So, a DNS message takes the form: +---------------------+ | Header | +---------------------+ | Question | the question for the name server +---------------------+ | Answer | RRs answering the question +---------------------+ | Authority | RRs pointing toward an authority +---------------------+ | Additional | RRs holding additional information +---------------------+where the header is: 1 1 1 1 1 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ID | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QDCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ANCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | NSCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ARCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+which is 6 network order 16 bit values (the pack/unpack code for those is 'n'). You're constructing a query, so you'll have at least one Question Section, which takes the form: 1 1 1 1 1 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | / QNAME / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QTYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QCLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+where the name is a sequence of labels, each of which is a length byte followed by the label characters, terminated by a zero length byte. You'll want to look at the pack/unpack 'C/a*' and a plain 'C' to terminate.
You appear to almost have that -- where you append "C a* " to your $lformat you want "C/a*". Or you could try:
For some reason the DNS RFC numbers the bits with '0' being the MS bit (!). Unpacking the responses is moderately straightforward, once you get over the name handling. Watch out for the message compression stuff -- the length byte in front of a label can only have a value in the range 0x00..0x3F, any length byte >= 0xC0 is the first byte of a two byte offset, pointing somewhere else in the packet for the rest of the name -- note that this is a "jump" not a "call". It's a lot of fun ! | [reply] [d/l] [select] |
Re: How to create DNS packet
by almut (Canon) on Nov 18, 2008 at 01:03 UTC | |
Can't tell for sure... but your problem might just be that @label and @labels are two different variables. (Had you been using strictures, you'd probably have caught that typo yourself...)
| [reply] [d/l] [select] |
Re: How to create DNS packet
by deewanagan (Novice) on Nov 18, 2008 at 18:24 UTC | |
or used this one:
| [reply] [d/l] [select] |
by broomduster (Priest) on Nov 18, 2008 at 20:19 UTC | |
| [reply] |
by almut (Canon) on Nov 18, 2008 at 19:35 UTC | |
Use "C a*" when supplying length/label pairs in @labels (as in the first snippet), or "C/a*" when supplying labels only (as in the second snippet). In the latter case, the length bytes will automatically be generated. (Note that the length bytes (when < 32) might map to non-printable control characters if you try to print them as ASCII... so you probably want to visualize the data in hex representation.) I'm no expert in DNS packet format nitty gritties, but the latter snippet looks okay to me... What exactly doesn't work; any error messages? | [reply] [d/l] [select] |
by gone2015 (Deacon) on Nov 18, 2008 at 19:59 UTC | |
in wireshark, it shows the Query name like "3.www.6.google.3.com" I think wireshark is being helpful, and showing the label lengths in human readable form, using '.' as a separator (it won't, after all, appear in any label !) This is a little crude: but gives: "\x04much\x03ado\x05about\x03not\x04alot\x03org\x00\x00\x01\x00\x01"which looks right to me. | [reply] [d/l] |
Re: How to create DNS packet
by deewanagan (Novice) on Nov 18, 2008 at 12:18 UTC | |
| [reply] |
Re: How to create DNS packet
by deewanagan (Novice) on Nov 19, 2008 at 03:04 UTC | |
thank u! | [reply] [d/l] |
by gone2015 (Deacon) on Nov 19, 2008 at 11:51 UTC | |
Dealing with the responses is where it gets interesting. You need a way to handle the response packet as an array of bytes/8-bit-characters. Inter alia, the name compression requires this. You could split the response packet into an array @resp = map { ord($_) } split(//, $response) ; and proceed in a C-like fashion to process that as an array of unsigned 8-bit integers -- reconstructing 16-bit integers and character strings. Or you can process the string containing response packet directly: using either substr($response, $p, $n) or unpack("\@$p ....", $response) -- where $p is your current "pointer" into the packet. One way of using unpack to extract a name is to step along it and count the labels, so you can then unpack it much as you've suggested. This: sets $n to the number of labels, $s to the start of the name and advances $p past the name in the response packet $response. Then join('.', unpack("\@$s (C/a*)$n", $response)) will do the business. (Compressed names require a bit more code than this, of course.) As you have found, you need to process the response piece-meal, because you cannot: So a number of unpack operations with '@' are required, each using your $p to start processing in the right place in the response. I'd knock up a subroutine to extract a name from a given position in the current resoonse packet. You'll want it to return two things, the name and the position just after the name. Since you're new to Perl, you'll need to learn either that a subroutine can return a list (in List Context, of course), so: or that you can do "call by reference" type things: Enjoy ! | [reply] [d/l] [select] |
by tokhi (Initiate) on Nov 27, 2008 at 21:05 UTC | |
---------------------------------- So a number of unpack operations with '@' are required, each using your $p to start processing in the right place in the response. I'd knock up a subroutine to extract a name from a given position in the current resoonse packet. You'll want it to return two things, the name and the position just after the name. Since you're new to Perl, you'll need to learn either that a subroutine can return a list (in List Context, of course), so: or that you can do "call by reference" type things: ------------------------------ | [reply] [d/l] [select] |