| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 |
- <?php
- /*
- * License Information:
- *
- * Net_DNS: A resolver library for PHP
- * Copyright (c) 2002-2003 Eric Kilfoil eric@ypass.net
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- /* Net_DNS_Packet object definition {{{ */
- /**
- * A object represation of a DNS packet (RFC1035)
- *
- * This object is used to manage a DNS packet. It contains methods for
- * DNS packet compression as defined in RFC1035, as well as parsing a DNS
- * packet response from a DNS server, or building a DNS packet from the
- * instance variables contained in the class.
- *
- * @package Net_DNS
- */
- class Net_DNS_Packet
- {
- /* class variable definitions {{{ */
- /**
- * debugging flag
- *
- * If set to TRUE (non-zero), debugging code will be displayed as the
- * packet is parsed.
- *
- * @var boolean $debug
- * @access public
- */
- var $debug;
- /**
- * A packet Header object.
- *
- * An object of type Net_DNS_Header which contains the header
- * information of the packet.
- *
- * @var object Net_DNS_Header $header
- * @access public
- */
- var $header;
- /**
- * A hash of compressed labels
- *
- * A list of all labels which have been compressed in the DNS packet
- * and the location offset of the label within the packet.
- *
- * @var array $compnames
- */
- var $compnames;
- /**
- * The origin of the packet, if the packet is a server response.
- *
- * This contains a string containing the IP address of the name server
- * from which the answer was given.
- *
- * @var string $answerfrom
- * @access public
- */
- var $answerfrom;
- /**
- * The size of the answer packet, if the packet is a server response.
- *
- * This contains a integer containing the size of the DNS packet the
- * server responded with if this packet was received by a DNS server
- * using the query() method.
- *
- * @var string $answersize
- * @access public
- */
- var $answersize;
- /**
- * An array of Net_DNS_Question objects
- *
- * Contains all of the questions within the packet. Each question is
- * stored as an object of type Net_DNS_Question.
- *
- * @var array $question
- * @access public
- */
- var $question;
- /**
- * An array of Net_DNS_RR ANSWER objects
- *
- * Contains all of the answer RRs within the packet. Each answer is
- * stored as an object of type Net_DNS_RR.
- *
- * @var array $answer
- * @access public
- */
- var $answer;
- /**
- * An array of Net_DNS_RR AUTHORITY objects
- *
- * Contains all of the authority RRs within the packet. Each authority is
- * stored as an object of type Net_DNS_RR.
- *
- * @var array $authority
- * @access public
- */
- var $authority;
- /**
- * An array of Net_DNS_RR ADDITIONAL objects
- *
- * Contains all of the additional RRs within the packet. Each additional is
- * stored as an object of type Net_DNS_RR.
- *
- * @var array $additional
- * @access public
- */
- var $additional;
- /* }}} */
- /* class constructor - Net_DNS_Packet($debug = FALSE) {{{ */
- /*
- * unfortunately (or fortunately), we can't follow the same
- * silly method for determining if name is a hostname or a packet
- * stream in PHP, since there is no ref() function. So we're going
- * to define a new method called parse to deal with this
- * circumstance and another method called buildQuestion to build a question.
- * I like it better that way anyway.
- */
- /**
- * Initalizes a Net_DNS_Packet object
- *
- * @param boolean $debug Turns debugging on or off
- */
- function Net_DNS_Packet($debug = FALSE)
- {
- $this->debug = $debug;
- $this->compnames = array();
- }
- /* }}} */
- /* Net_DNS_Packet::buildQuestion($name, $type = "A", $class = "IN") {{{ */
- /**
- * Adds a DNS question to the DNS packet
- *
- * @param string $name The name of the record to query
- * @param string $type The type of record to query
- * @param string $class The class of record to query
- * @see Net_DNS::typesbyname(), Net_DNS::classesbyname()
- */
- function buildQuestion($name, $type = 'A', $class = 'IN')
- {
- $this->header = new Net_DNS_Header();
- $this->header->qdcount = 1;
- $this->question[0] = new Net_DNS_Question($name, $type, $class);
- $this->answer = NULL;
- $this->authority = NULL;
- $this->additional = NULL;
- if ($this->debug) {
- $this->display();
- }
- }
- /* }}} */
- /* Net_DNS_Packet::parse($data) {{{ */
- /**
- * Parses a DNS packet returned by a DNS server
- *
- * Parses a complete DNS packet and builds an object hierarchy
- * containing all of the parts of the packet:
- * <ul>
- * <li>HEADER
- * <li>QUESTION
- * <li>ANSWER || PREREQUISITE
- * <li>ADDITIONAL || UPDATE
- * <li>AUTHORITY
- * </ul>
- *
- * @param string $data A binary string containing a DNS packet
- * @return boolean TRUE on success, NULL on parser error
- */
- function parse($data)
- {
- if ($this->debug) {
- echo ';; HEADER SECTION' . "\n";
- }
- $this->header = new Net_DNS_Header($data);
- if ($this->debug) {
- $this->header->display();
- }
- /*
- * Print and parse the QUESTION section of the packet
- */
- if ($this->debug) {
- echo "\n";
- $section = ($this->header->opcode == 'UPDATE') ? 'ZONE' : 'QUESTION';
- echo ";; $section SECTION (" . $this->header->qdcount . ' record' .
- ($this->header->qdcount == 1 ? '' : 's') . ")\n";
- }
- $offset = 12;
- $this->question = array();
- for ($ctr = 0; $ctr < $this->header->qdcount; $ctr++) {
- list($qobj, $offset) = $this->parse_question($data, $offset);
- if (is_null($qobj)) {
- return(NULL);
- }
- $this->question[count($this->question)] = $qobj;
- if ($this->debug) {
- echo ";;\n;";
- $qobj->display();
- }
- }
- /*
- * Print and parse the PREREQUISITE or ANSWER section of the packet
- */
- if ($this->debug) {
- echo "\n";
- $section = ($this->header->opcode == 'UPDATE') ? 'PREREQUISITE' :'ANSWER';
- echo ";; $section SECTION (" .
- $this->header->ancount . ' record' .
- (($this->header->ancount == 1) ? '' : 's') .
- ")\n";
- }
- $this->answer = array();
- for ($ctr = 0; $ctr < $this->header->ancount; $ctr++) {
- list($rrobj, $offset) = $this->parse_rr($data, $offset);
- if (is_null($rrobj)) {
- return(NULL);
- }
- array_push($this->answer, $rrobj);
- if ($this->debug) {
- $rrobj->display();
- }
- }
- /*
- * Print and parse the UPDATE or AUTHORITY section of the packet
- */
- if ($this->debug) {
- echo "\n";
- $section = ($this->header->opcode == 'UPDATE') ? 'UPDATE' : 'AUTHORITY';
- echo ";; $section SECTION (" .
- $this->header->nscount . ' record' .
- (($this->header->nscount == 1) ? '' : 's') .
- ")\n";
- }
- $this->authority = array();
- for ($ctr = 0; $ctr < $this->header->nscount; $ctr++) {
- list($rrobj, $offset) = $this->parse_rr($data, $offset);
- if (is_null($rrobj)) {
- return(NULL);
- }
- array_push($this->authority, $rrobj);
- if ($this->debug) {
- $rrobj->display();
- }
- }
- /*
- * Print and parse the ADDITIONAL section of the packet
- */
- if ($this->debug) {
- echo "\n";
- echo ';; ADDITIONAL SECTION (' .
- $this->header->arcount . ' record' .
- (($this->header->arcount == 1) ? '' : 's') .
- ")\n";
- }
- $this->additional = array();
- for ($ctr = 0; $ctr < $this->header->arcount; $ctr++) {
- list($rrobj, $offset) = $this->parse_rr($data, $offset);
- if (is_null($rrobj)) {
- return(NULL);
- }
- array_push($this->additional, $rrobj);
- if ($this->debug) {
- $rrobj->display();
- }
- }
- return(TRUE);
- }
- /* }}} */
- /* Net_DNS_Packet::data() {{{*/
- /**
- * Build a packet from a Packet object hierarchy
- *
- * Builds a valid DNS packet suitable for sending to a DNS server or
- * resolver client containing all of the data in the packet hierarchy.
- *
- * @return string A binary string containing a DNS Packet
- */
- function data()
- {
- $data = $this->header->data();
- for ($ctr = 0; $ctr < $this->header->qdcount; $ctr++) {
- $data .= $this->question[$ctr]->data($this, strlen($data));
- }
- for ($ctr = 0; $ctr < $this->header->ancount; $ctr++) {
- $data .= $this->answer[$ctr]->data($this, strlen($data));
- }
- for ($ctr = 0; $ctr < $this->header->nscount; $ctr++) {
- $data .= $this->authority[$ctr]->data($this, strlen($data));
- }
- for ($ctr = 0; $ctr < $this->header->arcount; $ctr++) {
- $data .= $this->additional[$ctr]->data($this, strlen($data));
- }
- return($data);
- }
- /*}}}*/
- /* Net_DNS_Packet::dn_comp($name, $offset) {{{*/
- /**
- * DNS packet compression method
- *
- * Returns a domain name compressed for a particular packet object, to
- * be stored beginning at the given offset within the packet data. The
- * name will be added to a running list of compressed domain names for
- * future use.
- *
- * @param string $name The name of the label to compress
- * @param integer $offset The location offset in the packet to where
- * the label will be stored.
- * @return string $compname A binary string containing the compressed
- * label.
- * @see Net_DNS_Packet::dn_expand()
- */
- function dn_comp($name, $offset)
- {
- $names = explode('.', $name);
- $compname = '';
- while (count($names)) {
- $dname = join('.', $names);
- if (isset($this->compnames[$dname])) {
- $compname .= pack('n', 0xc000 | $this->compnames[$dname]);
- break;
- }
- $this->compnames[$dname] = $offset;
- $first = array_shift($names);
- $length = strlen($first);
- $compname .= pack('Ca*', $length, $first);
- $offset += $length + 1;
- }
- if (! count($names)) {
- $compname .= pack('C', 0);
- }
- return($compname);
- }
- /*}}}*/
- /* Net_DNS_Packet::dn_expand($packet, $offset) {{{ */
- /**
- * DNS packet decompression method
- *
- * Expands the domain name stored at a particular location in a DNS
- * packet. The first argument is a variable containing the packet
- * data. The second argument is the offset within the packet where
- * the (possibly) compressed domain name is stored.
- *
- * @param string $packet The packet data
- * @param integer $offset The location offset in the packet of the
- * label to decompress.
- * @return array Returns a list of type array($name, $offset) where
- * $name is the name of the label which was decompressed
- * and $offset is the offset of the next field in the
- * packet. Returns array(NULL, NULL) on error
- */
- function dn_expand($packet, $offset)
- {
- $packetlen = strlen($packet);
- $int16sz = 2;
- $name = '';
- while (1) {
- if ($packetlen < ($offset + 1)) {
- return(array(NULL, NULL));
- }
- $a = unpack("@$offset/Cchar", $packet);
- $len = $a['char'];
- if ($len == 0) {
- $offset++;
- break;
- } else if (($len & 0xc0) == 0xc0) {
- if ($packetlen < ($offset + $int16sz)) {
- return(array(NULL, NULL));
- }
- $ptr = unpack("@$offset/ni", $packet);
- $ptr = $ptr['i'];
- $ptr = $ptr & 0x3fff;
- $name2 = Net_DNS_Packet::dn_expand($packet, $ptr);
- if (is_null($name2[0])) {
- return(array(NULL, NULL));
- }
- $name .= $name2[0];
- $offset += $int16sz;
- break;
- } else {
- $offset++;
- if ($packetlen < ($offset + $len)) {
- return(array(NULL, NULL));
- }
- $elem = substr($packet, $offset, $len);
- $name .= $elem . '.';
- $offset += $len;
- }
- }
- $name = ereg_replace('\.$', '', $name);
- return(array($name, $offset));
- }
- /*}}}*/
- /* Net_DNS_Packet::label_extract($packet, $offset) {{{ */
- /**
- * DNS packet decompression method
- *
- * Extracts the label stored at a particular location in a DNS
- * packet. The first argument is a variable containing the packet
- * data. The second argument is the offset within the packet where
- * the (possibly) compressed domain name is stored.
- *
- * @param string $packet The packet data
- * @param integer $offset The location offset in the packet of the
- * label to extract.
- * @return array Returns a list of type array($name, $offset) where
- * $name is the name of the label which was decompressed
- * and $offset is the offset of the next field in the
- * packet. Returns array(NULL, NULL) on error
- */
- function label_extract($packet, $offset)
- {
- $packetlen = strlen($packet);
- $name = '';
- if ($packetlen < ($offset + 1)) {
- return(array(NULL, NULL));
- }
- $a = unpack("@$offset/Cchar", $packet);
- $len = $a['char'];
- $offset++;
- if ($len + $offset > $packetlen) {
- $name = substr($packet, $offset);
- $offset = $packetlen;
- } else {
- $name = substr($packet, $offset, $len);
- $offset += $len;
- }
- return(array($name, $offset));
- }
- /*}}}*/
- /* Net_DNS_Packet::parse_question($data, $offset) {{{ */
- /**
- * Parses the question section of a packet
- *
- * Examines a DNS packet at the specified offset and parses the data
- * of the QUESTION section.
- *
- * @param string $data The packet data returned from the server
- * @param integer $offset The location offset of the start of the
- * question section.
- * @return array An array of type array($q, $offset) where $q
- * is a Net_DNS_Question object and $offset is the
- * location of the next section of the packet which
- * needs to be parsed.
- */
- function parse_question($data, $offset)
- {
- list($qname, $offset) = $this->dn_expand($data, $offset);
- if (is_null($qname)) {
- return(array(NULL, NULL));
- }
- if (strlen($data) < ($offset + 2 * 2)) {
- return(array(NULL, NULL));
- }
- $q = unpack("@$offset/n2int", $data);
- $qtype = $q['int1'];
- $qclass = $q['int2'];
- $offset += 2 * 2;
- $qtype = Net_DNS::typesbyval($qtype);
- $qclass = Net_DNS::classesbyval($qclass);
- $q = new Net_DNS_Question($qname, $qtype, $qclass);
- return(array($q, $offset));
- }
- /*}}}*/
- /* Net_DNS_Packet::parse_rr($data, $offset) {{{ */
- /**
- * Parses a resource record section of a packet
- *
- * Examines a DNS packet at the specified offset and parses the data
- * of a section which contains RRs (ANSWER, AUTHORITY, ADDITIONAL).
- *
- * @param string $data The packet data returned from the server
- * @param integer $offset The location offset of the start of the resource
- * record section.
- * @return array An array of type array($rr, $offset) where $rr
- * is a Net_DNS_RR object and $offset is the
- * location of the next section of the packet which
- * needs to be parsed.
- */
- function parse_rr($data, $offset)
- {
- list($name, $offset) = $this->dn_expand($data, $offset);
- if (! strlen($name)) {
- return(array(NULL, NULL));
- }
- if (strlen($data) < ($offset + 10)) {
- return(array(NULL, NULL));
- }
- $a = unpack("@$offset/n2tc/Nttl/nrdlength", $data);
- $type = $a['tc1'];
- $class = $a['tc2'];
- $ttl = $a['ttl'];
- $rdlength = $a['rdlength'];
- $type = Net_DNS::typesbyval($type);
- $class = Net_DNS::classesbyval($class);
- $offset += 10;
- if (strlen($data) < ($offset + $rdlength)) {
- return(array(NULL, NULL));
- }
- $rrobj = new Net_DNS_RR(array($name,
- $type,
- $class,
- $ttl,
- $rdlength,
- $data,
- $offset));
- if (is_null($rrobj)) {
- return(array(NULL, NULL));
- }
- $offset += $rdlength;
- return(array($rrobj, $offset));
- }
- /* }}} */
- /* Net_DNS_Packet::display() {{{ */
- /**
- * Prints out the packet in a human readable formatted string
- */
- function display()
- {
- echo $this->string();
- }
- /*}}}*/
- /* Net_DNS_Packet::string() {{{ */
- /**
- * Builds a human readable formatted string representing a packet
- */
- function string()
- {
- $retval = '';
- if ($this->answerfrom) {
- $retval .= ';; Answer received from ' . $this->answerfrom . '(' .
- $this->answersize . " bytes)\n;;\n";
- }
- $retval .= ";; HEADER SECTION\n";
- $retval .= $this->header->string();
- $retval .= "\n";
- $section = ($this->header->opcode == 'UPDATE') ? 'ZONE' : 'QUESTION';
- $retval .= ";; $section SECTION (" . $this->header->qdcount .
- ' record' . ($this->header->qdcount == 1 ? '' : 's') .
- ")\n";
- foreach ($this->question as $qr) {
- $retval .= ';; ' . $qr->string() . "\n";
- }
- $section = ($this->header->opcode == 'UPDATE') ? 'PREREQUISITE' : 'ANSWER';
- $retval .= "\n;; $section SECTION (" . $this->header->ancount .
- ' record' . ($this->header->ancount == 1 ? '' : 's') .
- ")\n";
- if (is_array($this->answer)) {
- foreach ($this->answer as $ans) {
- $retval .= ';; ' . $ans->string() . "\n";
- }
- }
- $section = ($this->header->opcode == 'UPDATE') ? 'UPDATE' : 'AUTHORITY';
- $retval .= "\n;; $section SECTION (" . $this->header->nscount .
- ' record' . ($this->header->nscount == 1 ? '' : 's') .
- ")\n";
- if (is_array($this->authority)) {
- foreach ($this->authority as $auth) {
- $retval .= ';; ' . $auth->string() . "\n";
- }
- }
- $retval .= "\n;; ADDITIONAL SECTION (" . $this->header->arcount .
- ' record' . ($this->header->arcount == 1 ? '' : 's') .
- ")\n";
- if (is_array($this->additional)) {
- foreach ($this->additional as $addl) {
- $retval .= ';; ' . $addl->string() . "\n";
- }
- }
- $retval .= "\n\n";
- return($retval);
- }
- /*}}}*/
- }
- /* }}} */
- /* VIM settings {{{
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * soft-stop-width: 4
- * c indent on
- * End:
- * vim600: sw=4 ts=4 sts=4 cindent fdm=marker et
- * vim<600: sw=4 ts=4
- * }}} */
- ?>
|