NAME Net::DNS::ToolKit - tools for working with DNS packets SYNOPSIS use Net::DNS::ToolKit qw( get1char get16 get32 put1char put16 put32 getIPv4 putIPv4 getstring putstring dn_comp dn_expand parse_char gethead newhead getflags get_qdcount get_ancount get_nscount get_arcount put_qdcount put_ancount put_nscount put_arcount inet_aton inet_ntoa sec2time ttlAlpha2Num collapse strip get_ns gettimeofday ); $char = get1char(\$buffer,$offset); ($int, $newoff) = get16(\$buffer,$offset); ($long, $newoff) = get32(\$buffer,$offset); $newoff = put1char(\$buffer,$offset,$u_char); $newoff = put16(\$buffer,$offset,$int); $newoff = put32(\$buffer,$offset,$long); $flags = getflags(\$buffer); $int = get_qdcount(\$buffer); $int = get_ancount(\$buffer); $int = get_nscount(\$buffer); $int = get_arcount(\$buffer); $newoff = put_qdcount(\$buffer,$int); $newoff = put_ancount(\$buffer,$int); $newoff = put_nscount(\$buffer,$int); $newoff = put_arcount(\$buffer,$int); ($netaddr,$newoff)=getIPv4(\$buffer,$offset); $newoff = putIPv4(\$buffer,$offset,$netaddr); ($offset, $id,$qr,$opcode,$aa,$tc,$rd,$ra,$mbz,$ad,$cd,$rcode, $qdcount,$ancount,$nscount,$arcount) = gethead(\$buffer); $newoff = newhead(\$buffer,$id,$flags, $qdcount,$ancount,$nscount,$arcount); ($b,$h,$d,$a)=parse_char($buffer,$offset); ($newoff,$name) = dn_expand(\$buffer,$offset); ($newoff,@dnptrs)=dn_comp(\$buffer,$offset,\$name,\@dnptrs); $dotquad = inet_ntoa($netaddr); $netaddr = inet_aton($dotquad); $timetxt = sec2time($seconds); $seconds = ttlAlpha2Num($timetext); $shorthost = collapse($zonename,$longhost); $tag = strip($P_tag); @nameservers = get_ns(); ($secs,$usecs) = gettimeofday(); DESCRIPTION Routines to pick apart, examine and put together DNS packets. They can be used for diagnostic purposes or as building blocks for DNS applications such as DNS servers and clients or to allow user applications to interact directly with remote DNS servers. See: Net::DNS::ToolKit for individual Resource Record methods. These functions return a value and offset in list context and first value only in scalar context. ($int,$newoff) = get16(... ($long,$newoff) = get32(... ($netaddr,$newoff) = getIPv4(... ($string,$newoff) = getstring(... ($newoff,$name) = dn_expand(... ($secs,$usecs) = gettimeofday(... These functions return only a value or an offset. $newoff = put1char(... $newoff = put16(... $newoff = put32(... $newoff = put_qdcount(... $newoff = put_ancount(... $newoff = put_nscount(... $newoff = put_arcount(... $newoff = putIPv4(... $newoff = putstring(... $newoff = newhead(... $flags = getflags(... $int = get_qdcount(... $int = get_ancount(... $int = get_nscount(... $int = get_arcount(... $char = get1char(... $dotquad = inet_ntoa(... $netaddr = inet_aton(... $timetxt = sec2time(... $seconds = ttlAlpha2Num(... $tag = strip(... $shorthost = collapse(... This function always return list context prefixed by a new offset. ($newoff,@dnptrs) = dn_comp(... ($offset,@list) = gethead(... These functions always return list context. @list = parse_char(... @nameservers = get_ns(... * $char = get1char(\$buffer,$offset); Get a single character from the buffer at $offset input: pointer to buffer, offset into buffer output: the "character" or undef if the pointer is outside the buffer * ($int, $newoff) = get16(\$buffer,$offset); Get a 16 bit integer from the buffer at $offset. Return the value and a new offset pointing at the next character. Returns and empty array on error. input: pointer to buffer, offset into buffer returns: 16 bit integer, offset + size of int In SCALAR context, returns just the value. * $newoff = put1char(\$buffer,$offset,$u_char); Put an unsigned 8 bit value into the buffer at $offset. Return the value of the new offset pointer to the next char (usually end of buffer). * $newoff = put16(\$buffer,$offset,$int); Put a 16 bit integer into the buffer at $offset. Return the value of the new offset pointer to the the next char (usually end of buffer). input: pointer to buffer, offset into buffer, 16 bit integer returns: offset + size of int * ($long, $newoff) = get32(\$buffer,$offset); Get a 32 bit long from the buffer at $offset. Return the long and a new offset pointing at the next character. Returns and empty array on error. input: pointer to buffer, offset into buffer returns: 32 bit long, offset + size long In SCALAR context, returns just the value. * $newoff = put32(\$buffer,$offset,$long); Put a 32 bit long into the buffer at $offset. Return the value of the new offset pointer to the the next char (usually end of buffer). input: pointer to buffer, offset into buffer, 32 bit long returns: offset + size of int * $flags = getflags(\$buffer); Get the flag bits from the header input: pointer to buffer, returns: flag bits * $int = get_qdcount(\$buffer); Get the contents of the qdcount. input: pointer to buffer, returns: 16 bit integer, * $int = get_ancount(\$buffer); Get the contents of the ancount. input: pointer to buffer, returns: 16 bit integer, * $int = get_nscount(\$buffer); Get the contents of the nscount. input: pointer to buffer, returns: 16 bit integer, * $int = get_arcount(\$buffer); Get the contents of the arcount. input: pointer to buffer, returns: 16 bit integer, * $newoff = put_qdcount(\$buffer,$int); Put a 16 bit integer into qdcount. Return an offset to ancount. input: pointer to buffer, 16 bit integer, returns: offset to ancount * $newoff = put_ancount(\$buffer,$int); Put a 16 bit integer into ancount. Return an offset to nscount. input: pointer to buffer, 16 bit integer, returns: offset to nscount * $newoff = put_nscount(\$buffer,$int); Put a 16 bit chunk into nscount. Return an offset to arcount. input: pointer to buffer, 16 bit integer, returns: offset to arcount * $newoff = put_arcount(\$buffer,$int); Put a 16 bit integer into arcount. Return an offset to answer section. input: pointer to buffer, 16 bit integer, returns: offset to question section * ($netaddr,$newoff)=getIPv4(\$buffer,$offset); Get an IPv4 network address from the buffer at $offset. Return the netaddr and a new offset pointing at the next character beyond. Returns and empty array on error. input: pointer to buffer, offset into buffer returns: netaddr, offset + size of ipaddr In SCALAR context, returns just netaddr. * $newoff = putIPv4(\$buffer,$offset,$netaddr); Put a netaddr into the buffer. Return the value of the new offset pointer to the the next char (usually end of buffer). input: pointer to buffer, offset into buffer, packed IPv4 net address returns: pointer to end of buffer * ($string,$newoff) = getstring(\$buffer,$offset,$length); Return a string of $length from the buffer. input: pointer to buffer, offset, length of string returns: string, new offset to end off string in buffer * $newoff = putstring(\$buffer,$offset,\$string); Append a string to $buffer at $offset. input: pointer to buffer, offset into buffer, pointer to string returns: new offset to end of buffer * ($offset,@headitems) = gethead(\$buffer); ($offset, $id,$qr,$opcode,$aa,$tc,$rd,$ra,$mbz,$ad,$cd,$rcode, $qdcount,$ancount,$nscount,$arcount) = gethead(\$buffer); Get the numeric codes for header variables 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ------------------------------------------------- 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ID | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QDCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ANCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | NSCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ARCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ The length of this header is NS_HFIXEDSZ input: pointer to message buffer returns: offset to question section, array of variables * $newoff=newhead(\$buffer, $id,$flags,$qdcount,$ancount,$nscount,$arcount); Creat a new header and return the offset to question input: \$buffer $id, $flags, $qdcount, $ancount, $nscount, $arcount returns: offset to question = NS_HFIXEDSZ or undefined on error If qdcount, ancount, nscount, arcount are not present, then they will be set to zero. example dump script: use lib qw(blib/lib blib/arch); use Net::DNS::Codes qw(:all); use Net::DNS::ToolKit::Debug qw( print_head print_buf ); use Net::DNS::ToolKit qw( get1char parse_char newhead ); my $buffer = ''; newhead(\$buffer, 1234, # ID QR | BITS_QUERY | RD, 1, # questions 5, # answers 2, # ns authority 3, # glue records ); print_head(\$buffer); print_buf(\$buffer); Will produce the following output: ID => 1234 QR => 1 OPCODE => QUERY AA => 0 TC => 0 RD => 1 RA => 0 Z => 0 AD => 0 CD => 0 RCODE => NOERROR QDCOUNT => 1 ANCOUNT => 5 NSCOUNT => 2 ARCOUNT => 3 0 : 0000_0100 0x04 4 1 : 1101_0010 0xD2 210 2 : 1000_0001 0x81 129 3 : 0000_0000 0x00 0 4 : 0000_0000 0x00 0 5 : 0000_0001 0x01 1 6 : 0000_0000 0x00 0 7 : 0000_0101 0x05 5 8 : 0000_0000 0x00 0 9 : 0000_0010 0x02 2 10 : 0000_0000 0x00 0 11 : 0000_0011 0x03 3 * ($b,$h,$d,$a) = parse_char($char); return strings for the character in: binary hex decimal ascii 0011_1001 0x39 57 9 as appropriate. Ascii is only returned if printable. A simple script using this routine can provide a view into a DNS packet to examine the bits and byte. Very useful while writing DNS client and server routines. See the example below. * ($name,$newoff) = dn_expand(\$buffer,$offset); Expands a compressed domain name into a full domain name. input: pointer to buffer, offset into buffer returns: expanded name, pointer to next RR * ($newoff,@dnptrs)=dn_comp(\$buffer,$offset,\$name,\@dnptrs); Compress a domain name and append it to the buffer. input: pointer to buffer, offset to insertion point, (usually end of buffer) pointer to name, pointer to array of offsets of previously compressed names, returns: new offset to end of buffer, updated array of offsets to previous compressed names, NOTES: 1) When the first domain name is compressed, the \@dnptrs array is ommited. dn_comp will return an initialized array that can then be used for subsequent calls. i.e. initial call ($newoff,@dnptrs)=dn_comp(\$name,\$buffer,$offset); 2) compression can be suppressed for test purposes if the pointer to $name is stored in a glob reference rather than a scalar. i.e. $name = 'hostname.com'; local *glob = \$name; ($newoff,@dnptrs)=dn_comp(\$buffer,$offset,\*glob); [use a pointer to *glob] * $dotquad = inet_ntoa($netaddr); Convert a packed IPv4 network address to a dot-quad IP address. input: packed network address returns: IP address i.e. 10.4.12.123 * $netaddr = inet_aton($dotquad); Convert a dot-quad IP address into an IPv4 packed network address. input: IP address i.e. 192.5.16.32 returns: packed network address * $timetxt = sec2time($seconds); Convert numeric seconds into a string of the form NNw NNd NNh NNm NNs for weeks, days, hours, minutes, seconds respectively. input: seconds returns: elapsed time text * $seconds = ttlAlpha2Num($timetext); Convert a string of time text of the form NNw NNd NNh NNm NNs into seconds. Upper case is OK. input: ttl in form numeric or alpha numeric returns: seconds * $shorthost = collapse($zonename,$longhost); Remove the zone portion of a fully qualified domain name and return the host portion. input: zone name, fqdn returns: short host name i.e. zone = bar.com fqdn = foo.bar.com foo = collapse(zone,fqdn); Testing is not case sensitive. If the fqdn does not end in the zone name then the fqdn is returned. * $tag = strip($P_tag); Remove the leading character(s) from a type/class label. input: label # like T_MX or C_IN returns: tag # MX, IN * @nameservers = get_ns(); Return a list of name server addresses in packed network form for use by this host. * ($secs,$usecs) = gettimeofday(); Returns a time value that is accurate to the nearest microsecond but also has a range of years. input: none returns: seconds since epoch, microseconds (of current sec) INSTALLATION To install this module, type: perl Makefile.PL make make test make install DEPENDENCIES perl 5.00503 Net::DNS::Codes 0.06 EXPORT None EXPORT_OK get1char get16 get32 put1char put16 put32 getIPv4 putIPv4 getstring putstring dn_comp dn_expand parse_char gethead newhead getflags get_qdcount get_ancount get_nscount get_arcount put_qdcount put_ancount put_nscount put_arcount inet_aton inet_ntoa sec2time ttlAlpha2Num collapse strip get_ns gettimeofday BUGS There have been some reports of the "C" library function for "int res_init(void); not properly returning the local resolver nameserver configuration information for certain Perl 5.6 -> 5.8 hosts. This is for the ToolKit function "get_ns()". I have been unable to duplicate this on any of the ix86 Linux or Sun-Sparc systems that I have. If you have a system that exhibits this problem and can provide a user account, I'd appreciate it if you would contact me so I can fix it. AUTHOR Michael Robinton ACKNOWLEDGEMENTS The following functions are used in whole or in part as include files to ToolKit.xs. The copyrights are include in the respective files. file: functions: dn_expand.inc dn_expand miniSocket.inc inet_aton, inet_ntoa dn_expand is from Michael Fuhr's Net::DNS package (DNS.pm), copyright (c) 1997-2002. Thank you Michael. inet_aton, inet_ntoa are from the perl-5.8.0 release by Larry Wall, copyright 1989-2002. Thank you Larry for making PERL possible for all of us. COPYRIGHT Copyright 2003, Michael Robinton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. See also: Net::DNS::Codes(3), Net::DNS::ToolKit::RR(3), Net::DNS::ToolKit::Debug(3)