in reply to How to create DNS packet

hi guys, its me again, sorry that i ask too many questions, its because i am new in perl.So i have send the packet to the server, and got response,now i want to unpack it,regardless of its compressed,the problem i get is, after unpacking header, i unpack the question section, into an array,after that i can't go on,because the rest of the packet will also be unpacked in that array.if i don't unpack in array i have to unpack in variables, but problem with that is that i don't know the length of the name,the code.
my ($id,$qr_opcode_aa_tc_rd,$ra_z_Rcode,$qdcount,$ancount,$nscount,$ar +count,@arrayans,) = unpack("n,B8,B8,n4,(C/a*)$n C n2",$buf);
thank u!

Replies are listed 'Best First'.
Re^2: How to create DNS packet
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:

    my $n = 0 ; my $s = $p ; my $l ; do { if ($l = unpack("\@$p C", $response)) { $n++ ; } ; $p += $l + 1 ; } while ($l) ;
    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:

    my ( ...., @labels, undef, $type, $class, ...) = unpack(".... (C/a*) +$n C n n ....") ;
    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:

    ($name, $p) = extract_name($response, $p) ; .... sub extract_name { my ($response, $p) = @_ ; my $extracted ... .... return ($extracted, $p) ; } ;
    or that you can do "call by reference" type things:
    sub extract_name { my ($response, $p) = @_ ; my $extracted ... .... $_[1] = $p ; # $_[1] is implicitly a reference to the 2nd +actual argument return $extracted ; } ;
    Enjoy !

      Dear all, can some one please explain this part of the post a little bit more in detail? its from the upper post, I could not get the following part:
      ----------------------------------
      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:
      ($name, $p) = extract_name($response, $p) ; .... sub extract_name { my + ($response, $p) = @_ ; my $extracted ... .... return ($extracted, $p +) ; } ;
      or that you can do "call by reference" type things:
      sub extract_name { my ($response, $p) = @_ ; my $extracted ... .... $_[1] = $p ; # $_[1] is implicitly a reference to the 2nd +actual argument return $ +extracted ; } ;
      ------------------------------