Net_DNS
[ class tree: Net_DNS ] [ index: Net_DNS ] [ all elements ]

Source for file Packet.php

Documentation is available at Packet.php

  1. <?php
  2. /*
  3.  *  License Information:
  4.  *
  5.  *    Net_DNS:  A resolver library for PHP
  6.  *    Copyright (c) 2002-2003 Eric Kilfoil eric@ypass.net
  7.  *
  8.  *    This library is free software; you can redistribute it and/or
  9.  *    modify it under the terms of the GNU Lesser General Public
  10.  *    License as published by the Free Software Foundation; either
  11.  *    version 2.1 of the License, or (at your option) any later version.
  12.  *
  13.  *    This library is distributed in the hope that it will be useful,
  14.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16.  *    Lesser General Public License for more details.
  17.  *
  18.  *    You should have received a copy of the GNU Lesser General Public
  19.  *    License along with this library; if not, write to the Free Software
  20.  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21.  */
  22.  
  23. /* Net_DNS_Packet object definition {{{ */
  24. /**
  25.  * A object represation of a DNS packet (RFC1035)
  26.  *
  27.  * This object is used to manage a DNS packet.  It contains methods for
  28.  * DNS packet compression as defined in RFC1035, as well as parsing  a DNS
  29.  * packet response from a DNS server, or building a DNS packet from  the
  30.  * instance variables contained in the class.
  31.  *
  32.  * @package Net_DNS
  33.  */
  34. {
  35.     /* class variable definitions {{{ */
  36.     /**
  37.      * debugging flag
  38.      *
  39.      * If set to TRUE (non-zero), debugging code will be displayed as the
  40.      * packet is parsed.
  41.      *
  42.      * @var boolean $debug 
  43.      * @access  public
  44.      */
  45.     var $debug;
  46.     /**
  47.      * A packet Header object.
  48.      *
  49.      * An object of type Net_DNS_Header which contains the header
  50.      * information  of the packet.
  51.      *
  52.      * @var object Net_DNS_Header $header 
  53.      * @access  public
  54.      */
  55.     var $header;
  56.     /**
  57.      * A hash of compressed labels
  58.      *
  59.      * A list of all labels which have been compressed in the DNS packet
  60.      * and  the location offset of the label within the packet.
  61.      *
  62.      * @var array   $compnames 
  63.      */
  64.     var $compnames;
  65.     /**
  66.      * The origin of the packet, if the packet is a server response.
  67.      *
  68.      * This contains a string containing the IP address of the name server
  69.      * from which the answer was given.
  70.      *
  71.      * @var string  $answerfrom 
  72.      * @access  public
  73.      */
  74.     var $answerfrom;
  75.     /**
  76.      * The size of the answer packet, if the packet is a server response.
  77.      *
  78.      * This contains a integer containing the size of the DNS packet the
  79.      * server responded with if this packet was received by a DNS server
  80.      * using the query() method.
  81.      *
  82.      * @var string  $answersize 
  83.      * @access  public
  84.      */
  85.     var $answersize;
  86.     /**
  87.      * An array of Net_DNS_Question objects
  88.      *
  89.      * Contains all of the questions within the packet.  Each question is
  90.      * stored as an object of type Net_DNS_Question.
  91.      *
  92.      * @var array   $question 
  93.      * @access  public
  94.      */
  95.     var $question;
  96.     /**
  97.      * An array of Net_DNS_RR ANSWER objects
  98.      *
  99.      * Contains all of the answer RRs within the packet.  Each answer is
  100.      * stored as an object of type Net_DNS_RR.
  101.      *
  102.      * @var array   $answer 
  103.      * @access  public
  104.      */
  105.     var $answer;
  106.     /**
  107.      * An array of Net_DNS_RR AUTHORITY objects
  108.      *
  109.      * Contains all of the authority RRs within the packet.  Each authority is
  110.      * stored as an object of type Net_DNS_RR.
  111.      *
  112.      * @var array   $authority 
  113.      * @access  public
  114.      */
  115.     var $authority;
  116.     /**
  117.      * An array of Net_DNS_RR ADDITIONAL objects
  118.      *
  119.      * Contains all of the additional RRs within the packet.  Each additional is
  120.      * stored as an object of type Net_DNS_RR.
  121.      *
  122.      * @var array   $additional 
  123.      * @access  public
  124.      */
  125.     var $additional;
  126.  
  127.     /* }}} */
  128.     /* class constructor - Net_DNS_Packet($debug = FALSE) {{{ */
  129.     /*
  130.      * unfortunately (or fortunately), we can't follow the same
  131.      * silly method for determining if name is a hostname or a packet
  132.      * stream in PHP, since there is no ref() function.  So we're going
  133.      * to define a new method called parse to deal with this
  134.      * circumstance and another method called buildQuestion to build a question.
  135.      * I like it better that way anyway.
  136.      */
  137.     /**
  138.      * Initalizes a Net_DNS_Packet object
  139.      *
  140.      * @param boolean $debug Turns debugging on or off
  141.      */
  142.     function Net_DNS_Packet($debug = FALSE)
  143.     {
  144.         $this->debug = $debug;
  145.         $this->compnames = array();
  146.     }
  147.  
  148.     /* }}} */
  149.     /* Net_DNS_Packet::buildQuestion($name, $type = "A", $class = "IN") {{{ */
  150.     /**
  151.      * Adds a DNS question to the DNS packet
  152.      *
  153.      * @param   string $name    The name of the record to query
  154.      * @param   string $type    The type of record to query
  155.      * @param   string $class   The class of record to query
  156.      * @see Net_DNS::typesbyname(), Net_DNS::classesbyname()
  157.      */
  158.     function buildQuestion($name$type 'A'$class 'IN')
  159.     {
  160.         $this->header = new Net_DNS_Header();
  161.         $this->header->qdcount = 1;
  162.         $this->question[0= new Net_DNS_Question($name$type$class);
  163.         $this->answer = NULL;
  164.         $this->authority = NULL;
  165.         $this->additional = NULL;
  166.         /* Do not print question packet
  167.         if ($this->debug) {
  168.             $this->display();
  169.         }
  170.         */
  171.     }
  172.  
  173.     /* }}} */
  174.     /* Net_DNS_Packet::parse($data) {{{ */
  175.     /**
  176.      * Parses a DNS packet returned by a DNS server
  177.      *
  178.      * Parses a complete DNS packet and builds an object hierarchy
  179.      * containing all of the parts of the packet:
  180.      * <ul>
  181.      *   <li>HEADER
  182.      *   <li>QUESTION
  183.      *   <li>ANSWER || PREREQUISITE
  184.      *   <li>ADDITIONAL || UPDATE
  185.      *   <li>AUTHORITY
  186.      * </ul>
  187.      *
  188.      * @param string $data  A binary string containing a DNS packet
  189.      * @return boolean TRUE on success, NULL on parser error
  190.      */
  191.     function parse($data)
  192.     {
  193.         if ($this->debug{
  194.             echo ';; HEADER SECTION' "\n";
  195.         }
  196.  
  197.         $this->header = new Net_DNS_Header($data);
  198.  
  199.         if ($this->debug{
  200.             $this->header->display();
  201.         }
  202.  
  203.         /*
  204.          *  Print and parse the QUESTION section of the packet
  205.          */
  206.         if ($this->debug{
  207.             echo "\n";
  208.             $section ($this->header->opcode  == 'UPDATE''ZONE' 'QUESTION';
  209.             echo ";; $section SECTION (" . $this->header->qdcount . ' record' .
  210.                 ($this->header->qdcount == 1 ? '' 's'")\n";
  211.         }
  212.  
  213.         $offset = 12;
  214.  
  215.         $this->question = array();
  216.         for ($ctr = 0; $ctr $this->header->qdcount$ctr++{
  217.             list($qobj$offset$this->parse_question($data$offset);
  218.             if (is_null($qobj)) {
  219.                 return(NULL);
  220.             }
  221.  
  222.             $this->question[count($this->question)$qobj;
  223.             if ($this->debug{
  224.                 echo ";;\n;";
  225.                 $qobj->display();
  226.             }
  227.         }
  228.  
  229.         /*
  230.          *  Print and parse the PREREQUISITE or ANSWER  section of the packet
  231.          */
  232.         if ($this->debug{
  233.             echo "\n";
  234.             $section ($this->header->opcode == 'UPDATE''PREREQUISITE' :'ANSWER';
  235.             echo ";; $section SECTION (" .
  236.                 $this->header->ancount . ' record' .
  237.                 (($this->header->ancount == 1'' 's'.
  238.                 ")\n";
  239.         }
  240.  
  241.         $this->answer = array();
  242.         for ($ctr = 0; $ctr $this->header->ancount$ctr++{
  243.             list($rrobj$offset$this->parse_rr($data$offset);
  244.  
  245.             if (is_null($rrobj)) {
  246.                 return(NULL);
  247.             }
  248.             array_push($this->answer$rrobj);
  249.             if ($this->debug{
  250.                 $rrobj->display();
  251.             }
  252.         }
  253.  
  254.         /*
  255.          *  Print and parse the UPDATE or AUTHORITY section of the packet
  256.          */
  257.         if ($this->debug{
  258.             echo "\n";
  259.             $section ($this->header->opcode == 'UPDATE''UPDATE' 'AUTHORITY';
  260.             echo ";; $section SECTION (" .
  261.                 $this->header->nscount . ' record' .
  262.                 (($this->header->nscount == 1'' 's'.
  263.                 ")\n";
  264.         }
  265.  
  266.         $this->authority = array();
  267.         for ($ctr = 0; $ctr $this->header->nscount$ctr++{
  268.             list($rrobj$offset$this->parse_rr($data$offset);
  269.  
  270.             if (is_null($rrobj)) {
  271.                 return(NULL);
  272.             }
  273.             array_push($this->authority$rrobj);
  274.             if ($this->debug{
  275.                 $rrobj->display();
  276.             }
  277.         }
  278.  
  279.         /*
  280.          *  Print and parse the ADDITIONAL section of the packet
  281.          */
  282.         if ($this->debug{
  283.             echo "\n";
  284.             echo ';; ADDITIONAL SECTION (' .
  285.                 $this->header->arcount . ' record' .
  286.                 (($this->header->arcount == 1'' 's'.
  287.                 ")\n";
  288.         }
  289.  
  290.         $this->additional = array();
  291.         for ($ctr = 0; $ctr $this->header->arcount$ctr++{
  292.             list($rrobj$offset$this->parse_rr($data$offset);
  293.  
  294.             if (is_null($rrobj)) {
  295.                 return(NULL);
  296.             }
  297.             array_push($this->additional$rrobj);
  298.             if ($this->debug{
  299.                 $rrobj->display();
  300.             }
  301.         }
  302.  
  303.         return(TRUE);
  304.     }
  305.  
  306.     /* }}} */
  307.     /* Net_DNS_Packet::data() {{{*/
  308.     /**
  309.      * Build a packet from a Packet object hierarchy
  310.      *
  311.      * Builds a valid DNS packet suitable for sending to a DNS server or
  312.      * resolver client containing all of the data in the packet hierarchy.
  313.      *
  314.      * @return string A binary string containing a DNS Packet
  315.      */
  316.     function data()
  317.     {
  318.         $data $this->header->data();
  319.  
  320.         for ($ctr = 0; $ctr $this->header->qdcount$ctr++{
  321.             $data .= $this->question[$ctr]->data($thisstrlen($data));
  322.         }
  323.  
  324.         for ($ctr = 0; $ctr $this->header->ancount$ctr++{
  325.             $data .= $this->answer[$ctr]->data($thisstrlen($data));
  326.         }
  327.  
  328.         for ($ctr = 0; $ctr $this->header->nscount$ctr++{
  329.             $data .= $this->authority[$ctr]->data($thisstrlen($data));
  330.         }
  331.  
  332.         for ($ctr = 0; $ctr $this->header->arcount$ctr++{
  333.             $data .= $this->additional[$ctr]->data($thisstrlen($data));
  334.         }
  335.  
  336.         return($data);
  337.     }
  338.  
  339.     /*}}}*/
  340.     /* Net_DNS_Packet::dn_comp($name, $offset) {{{*/
  341.     /**
  342.      * DNS packet compression method
  343.      *
  344.      * Returns a domain name compressed for a particular packet object, to
  345.      * be stored beginning at the given offset within the packet data.  The
  346.      * name will be added to a running list of compressed domain names for
  347.      * future use.
  348.      *
  349.      * @param string    $name       The name of the label to compress
  350.      * @param integer   $offset     The location offset in the packet to where
  351.      *                               the label will be stored.
  352.      * @return string   $compname   A binary string containing the compressed
  353.      *                               label.
  354.      * @see Net_DNS_Packet::dn_expand()
  355.      */
  356.     function dn_comp($name$offset)
  357.     {
  358.         $names explode('.'$name);
  359.         $compname '';
  360.         while (count($names)) {
  361.             $dname join('.'$names);
  362.             if (isset($this->compnames[$dname])) {
  363.                 $compname .= pack('n'0xc000 | $this->compnames[$dname]);
  364.                 break;
  365.             }
  366.  
  367.             $this->compnames[$dname$offset;
  368.             $first array_shift($names);
  369.             $length strlen($first);
  370.             $compname .= pack('Ca*'$length$first);
  371.             $offset += $length + 1;
  372.         }
  373.         if (count($names)) {
  374.             $compname .= pack('C'0);
  375.         }
  376.         return($compname);
  377.     }
  378.  
  379.     /*}}}*/
  380.     /* Net_DNS_Packet::dn_expand($packet, $offset) {{{ */
  381.     /**
  382.      * DNS packet decompression method
  383.      *
  384.      * Expands the domain name stored at a particular location in a DNS
  385.      * packet.  The first argument is a variable containing  the packet
  386.      * data.  The second argument is the offset within the  packet where
  387.      * the (possibly) compressed domain name is stored.
  388.      *
  389.      * @param   string  $packet The packet data
  390.      * @param   integer $offset The location offset in the packet of the
  391.      *                           label to decompress.
  392.      * @return  array   Returns a list of type array($name, $offset) where
  393.      *                   $name is the name of the label which was decompressed
  394.      *                   and $offset is the offset of the next field in the
  395.      *                   packet.  Returns array(NULL, NULL) on error
  396.      */
  397.     function dn_expand($packet$offset)
  398.     {
  399.         $packetlen strlen($packet);
  400.         $int16sz = 2;
  401.         $name '';
  402.         while (1{
  403.             if ($packetlen ($offset + 1)) {
  404.                 return(array(NULLNULL));
  405.             }
  406.  
  407.             $a unpack("@$offset/Cchar"$packet);
  408.             $len $a['char'];
  409.  
  410.             if ($len == 0{
  411.                 $offset++;
  412.                 break;
  413.             else if (($len 0xc0== 0xc0{
  414.                 if ($packetlen ($offset $int16sz)) {
  415.                     return(array(NULLNULL));
  416.                 }
  417.                 $ptr unpack("@$offset/ni"$packet);
  418.                 $ptr $ptr['i'];
  419.                 $ptr $ptr 0x3fff;
  420.                 $name2 Net_DNS_Packet::dn_expand($packet$ptr);
  421.  
  422.                 if (is_null($name2[0])) {
  423.                     return(array(NULLNULL));
  424.                 }
  425.                 $name .= $name2[0];
  426.                 $offset += $int16sz;
  427.                 break;
  428.             else {
  429.                 $offset++;
  430.  
  431.                 if ($packetlen ($offset $len)) {
  432.                     return(array(NULLNULL));
  433.                 }
  434.  
  435.                 $elem substr($packet$offset$len);
  436.                 $name .= $elem '.';
  437.                 $offset += $len;
  438.             }
  439.         }
  440.         $name ereg_replace('\.$'''$name);
  441.         return(array($name$offset));
  442.     }
  443.  
  444.     /*}}}*/
  445.     /* Net_DNS_Packet::label_extract($packet, $offset) {{{ */
  446.     /**
  447.      * DNS packet decompression method
  448.      *
  449.      * Extracts the label stored at a particular location in a DNS
  450.      * packet.  The first argument is a variable containing  the packet
  451.      * data.  The second argument is the offset within the  packet where
  452.      * the (possibly) compressed domain name is stored.
  453.      *
  454.      * @param   string  $packet The packet data
  455.      * @param   integer $offset The location offset in the packet of the
  456.      *                           label to extract.
  457.      * @return  array   Returns a list of type array($name, $offset) where
  458.      *                   $name is the name of the label which was decompressed
  459.      *                   and $offset is the offset of the next field in the
  460.      *                   packet.  Returns array(NULL, NULL) on error
  461.      */
  462.     function label_extract($packet$offset)
  463.     {
  464.         $packetlen strlen($packet);
  465.         $name '';
  466.         if ($packetlen ($offset + 1)) {
  467.             return(array(NULLNULL));
  468.         }
  469.  
  470.         $a unpack("@$offset/Cchar"$packet);
  471.         $len $a['char'];
  472.         $offset++;
  473.  
  474.         if ($len $offset $packetlen{
  475.             $name substr($packet$offset);
  476.             $offset $packetlen;
  477.         else {
  478.             $name substr($packet$offset$len);
  479.             $offset += $len;
  480.         }
  481.         return(array($name$offset));
  482.     }
  483.  
  484.     /*}}}*/
  485.     /* Net_DNS_Packet::parse_question($data, $offset) {{{ */
  486.     /**
  487.      * Parses the question section of a packet
  488.      *
  489.      * Examines a DNS packet at the specified offset and parses the data
  490.      * of the QUESTION section.
  491.      *
  492.      * @param   string  $data   The packet data returned from the server
  493.      * @param   integer $offset The location offset of the start of the
  494.      *                           question section.
  495.      * @return  array   An array of type array($q, $offset) where $q
  496.      *                   is a Net_DNS_Question object and $offset is the
  497.      *                   location of the next section of the packet which
  498.      *                   needs to be parsed.
  499.      */
  500.     function parse_question($data$offset)
  501.     {
  502.         list($qname$offset$this->dn_expand($data$offset);
  503.         if (is_null($qname)) {
  504.             return(array(NULLNULL));
  505.         }
  506.  
  507.         if (strlen($data($offset + 2 * 2)) {
  508.             return(array(NULLNULL));
  509.         }
  510.  
  511.         $q unpack("@$offset/n2int"$data);
  512.         $qtype $q['int1'];
  513.         $qclass $q['int2'];
  514.         $offset += 2 * 2;
  515.  
  516.         $qtype Net_DNS::typesbyval($qtype);
  517.         $qclass Net_DNS::classesbyval($qclass);
  518.  
  519.         $q = new Net_DNS_Question($qname$qtype$qclass);
  520.         return(array($q$offset));
  521.     }
  522.  
  523.     /*}}}*/
  524.     /* Net_DNS_Packet::parse_rr($data, $offset) {{{ */
  525.     /**
  526.      * Parses a resource record section of a packet
  527.      *
  528.      * Examines a DNS packet at the specified offset and parses the data
  529.      * of a section which contains RRs (ANSWER, AUTHORITY, ADDITIONAL).
  530.      *
  531.      * @param string    $data   The packet data returned from the server
  532.      * @param integer   $offset The location offset of the start of the resource
  533.      *                           record section.
  534.      * @return  array   An array of type array($rr, $offset) where $rr
  535.      *                   is a Net_DNS_RR object and $offset is the
  536.      *                   location of the next section of the packet which
  537.      *                   needs to be parsed.
  538.      */
  539.     function parse_rr($data$offset)
  540.     {
  541.         list($name$offset$this->dn_expand($data$offset);
  542.         if ($name === NULL{
  543.             return(array(NULLNULL));
  544.         }
  545.  
  546.         if (strlen($data($offset + 10)) {
  547.             return(array(NULLNULL));
  548.         }
  549.  
  550.         $a unpack("@$offset/n2tc/Nttl/nrdlength"$data);
  551.         $type $a['tc1'];
  552.         $class $a['tc2'];
  553.         $ttl $a['ttl'];
  554.         $rdlength $a['rdlength'];
  555.  
  556.         $type Net_DNS::typesbyval($type);
  557.         $class Net_DNS::classesbyval($class);
  558.  
  559.         $offset += 10;
  560.         if (strlen($data($offset $rdlength)) {
  561.             return(array(NULLNULL));
  562.         }
  563.  
  564.         $rrobj &Net_DNS_RR::factory(array($name,
  565.                     $type,
  566.                     $class,
  567.                     $ttl,
  568.                     $rdlength,
  569.                     $data,
  570.                     $offset));
  571.  
  572.         if (is_null($rrobj)) {
  573.             return(array(NULLNULL));
  574.         }
  575.  
  576.         $offset += $rdlength;
  577.  
  578.         return(array($rrobj$offset));
  579.     }
  580.  
  581.     /* }}} */
  582.     /* Net_DNS_Packet::display() {{{ */
  583.     /**
  584.      * Prints out the packet in a human readable formatted string
  585.      */
  586.     function display()
  587.     {
  588.         echo $this->string();
  589.     }
  590.  
  591.     /*}}}*/
  592.     /* Net_DNS_Packet::string() {{{ */
  593.     /**
  594.      * Builds a human readable formatted string representing a packet
  595.      */
  596.     function string()
  597.     {
  598.         $retval '';
  599.         if ($this->answerfrom{
  600.             $retval .= ';; Answer received from ' $this->answerfrom . '(' .
  601.                 $this->answersize . " bytes)\n;;\n";
  602.         }
  603.  
  604.         $retval .= ";; HEADER SECTION\n";
  605.         $retval .= $this->header->string();
  606.         $retval .= "\n";
  607.  
  608.         $section ($this->header->opcode == 'UPDATE''ZONE' 'QUESTION';
  609.         $retval .= ";; $section SECTION (" . $this->header->qdcount     .
  610.             ' record' ($this->header->qdcount == 1 ? '' 's'.
  611.             ")\n";
  612.  
  613.         foreach ($this->question as $qr{
  614.             $retval .= ';; ' $qr->string("\n";
  615.         }
  616.  
  617.         $section ($this->header->opcode == 'UPDATE''PREREQUISITE' 'ANSWER';
  618.         $retval .= "\n;; $section SECTION (" . $this->header->ancount     .
  619.             ' record' ($this->header->ancount == 1 ? '' 's'.
  620.             ")\n";
  621.  
  622.         if (is_array($this->answer)) {
  623.             foreach ($this->answer as $ans{
  624.                 $retval .= ';; ' $ans->string("\n";
  625.             }
  626.         }
  627.  
  628.         $section ($this->header->opcode == 'UPDATE''UPDATE' 'AUTHORITY';
  629.         $retval .= "\n;; $section SECTION (" . $this->header->nscount     .
  630.             ' record' ($this->header->nscount == 1 ? '' 's'.
  631.             ")\n";
  632.  
  633.         if (is_array($this->authority)) {
  634.             foreach ($this->authority as $auth{
  635.                 $retval .= ';; ' $auth->string("\n";
  636.             }
  637.         }
  638.  
  639.         $retval .= "\n;; ADDITIONAL SECTION (" $this->header->arcount     .
  640.             ' record' ($this->header->arcount == 1 ? '' 's'.
  641.             ")\n";
  642.  
  643.         if (is_array($this->additional)) {
  644.             foreach ($this->additional as $addl{
  645.                 $retval .= ';; ' $addl->string("\n";
  646.             }
  647.         }
  648.  
  649.         $retval .= "\n\n";
  650.         return($retval);
  651.     }
  652.  
  653.     /*}}}*/
  654. }
  655. /* }}} */
  656. /* VIM settings {{{
  657.  * Local variables:
  658.  * tab-width: 4
  659.  * c-basic-offset: 4
  660.  * soft-stop-width: 4
  661.  * c indent on
  662.  * End:
  663.  * vim600: sw=4 ts=4 sts=4 cindent fdm=marker et
  664.  * vim<600: sw=4 ts=4
  665.  * }}} */
  666. ?>

Documentation generated on Mon, 11 Mar 2019 14:31:30 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.