PageRenderTime 104ms CodeModel.GetById 40ms app.highlight 10ms RepoModel.GetById 50ms app.codeStats 0ms

/htdocs/includes/net/dns2/socket/streams.php

https://bitbucket.org/speedealing/speedealing
PHP | 393 lines | 165 code | 66 blank | 162 comment | 35 complexity | 7b76db59d8b32cec288ac46a8bee7ae6 MD5 | raw file
  1<?php
  2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3
  4/**
  5 * DNS Library for handling lookups and updates. 
  6 *
  7 * PHP Version 5
  8 *
  9 * Copyright (c) 2010, Mike Pultz <mike@mikepultz.com>.
 10 * All rights reserved.
 11 *
 12 * Redistribution and use in source and binary forms, with or without
 13 * modification, are permitted provided that the following conditions
 14 * are met:
 15 *
 16 *   * Redistributions of source code must retain the above copyright
 17 *     notice, this list of conditions and the following disclaimer.
 18 *
 19 *   * Redistributions in binary form must reproduce the above copyright
 20 *     notice, this list of conditions and the following disclaimer in
 21 *     the documentation and/or other materials provided with the
 22 *     distribution.
 23 *
 24 *   * Neither the name of Mike Pultz nor the names of his contributors 
 25 *     may be used to endorse or promote products derived from this 
 26 *     software without specific prior written permission.
 27 *
 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 32 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 33 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 34 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 35 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 36 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC
 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 38 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 39 * POSSIBILITY OF SUCH DAMAGE.
 40 *
 41 * @category  Networking
 42 * @package   Net_DNS2
 43 * @author    Mike Pultz <mike@mikepultz.com>
 44 * @copyright 2010 Mike Pultz <mike@mikepultz.com>
 45 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
 46 * @version   SVN: $Id: Streams.php 168 2012-09-13 02:01:29Z mike.pultz $
 47 * @link      http://pear.php.net/package/Net_DNS2
 48 * @since     File available since Release 0.6.0
 49 *
 50 */
 51
 52/**
 53 * Socket handling class using the PHP Streams
 54 *
 55 * The sockets extension is faster than the stream functions in PHP, but it's
 56 * not standard. So if the extension is loaded, then the Net_DNS_Socket_Sockets
 57 * class it used, otherwise, this class it used.
 58 *   
 59 * @category Networking
 60 * @package  Net_DNS2
 61 * @author   Mike Pultz <mike@mikepultz.com>
 62 * @license  http://www.opensource.org/licenses/bsd-license.php  BSD License
 63 * @link     http://pear.php.net/package/Net_DNS2
 64 * @see      Net_DNS2_Socket
 65 *
 66 */
 67class Net_DNS2_Socket_Streams extends Net_DNS2_Socket
 68{
 69    private $_context;
 70
 71    /**
 72     * opens a socket connection to the DNS server
 73     *
 74     * @return boolean
 75     * @access public
 76     *
 77     */
 78    public function open()
 79    {
 80        //
 81        // create a list of options for the context 
 82        //
 83        $opts = array('socket' => array());
 84        
 85        //
 86        // bind to a local IP/port if it's set
 87        //
 88        if (strlen($this->local_host) > 0) {
 89
 90            $opts['socket']['bindto'] = $this->local_host;
 91            if ($this->local_port > 0) {
 92
 93                $opts['socket']['bindto'] .= ':' . $this->local_port;
 94            }
 95        }
 96
 97        //
 98        // create the context
 99        //
100        $this->_context = @stream_context_create($opts);
101
102        //
103        // create socket
104        //
105        $errno;
106        $errstr;
107
108        switch($this->type) {
109        case Net_DNS2_Socket::SOCK_STREAM:
110
111            if (Net_DNS2::isIPv4($this->host) == true) {
112
113                $this->sock = @stream_socket_client(
114                    'tcp://' . $this->host . ':' . $this->port, 
115                    $errno, $errstr, $this->timeout, 
116                    STREAM_CLIENT_CONNECT, $this->_context
117                );
118            } else if (Net_DNS2::isIPv6($this->host) == true) {
119
120                $this->sock = @stream_socket_client(
121                    'tcp://[' . $this->host . ']:' . $this->port, 
122                    $errno, $errstr, $this->timeout, 
123                    STREAM_CLIENT_CONNECT, $this->_context
124                );
125            } else {
126
127                $this->last_error = 'invalid address type: ' . $this->host;
128                return false;
129            }
130
131            break;
132        
133        case Net_DNS2_Socket::SOCK_DGRAM:
134
135            if (Net_DNS2::isIPv4($this->host) == true) {
136
137                $this->sock = @stream_socket_client(
138                    'udp://' . $this->host . ':' . $this->port, 
139                    $errno, $errstr, $this->timeout, 
140                    STREAM_CLIENT_CONNECT, $this->_context
141                );
142            } else if (Net_DNS2::isIPv6($this->host) == true) {
143
144                $this->sock = @stream_socket_client(
145                    'udp://[' . $this->host . ']:' . $this->port, 
146                    $errno, $errstr, $this->timeout, 
147                    STREAM_CLIENT_CONNECT, $this->_context
148                );
149            } else {
150
151                $this->last_error = 'invalid address type: ' . $this->host;
152                return false;
153            }
154
155            break;
156            
157        default:
158            $this->last_error = 'Invalid socket type: ' . $this->type;
159            return false;
160        }
161
162        if ($this->sock === false) {
163
164            $this->last_error = $errstr;
165            return false;
166        }
167
168        //
169        // set it to non-blocking and set the timeout
170        //
171        @stream_set_blocking($this->sock, 0);
172        @stream_set_timeout($this->sock, $this->timeout);
173
174        return true;
175    }
176
177    /**
178     * closes a socket connection to the DNS server
179     *
180     * @return boolean
181     * @access public
182     *
183     */
184    public function close()
185    {
186        if (is_resource($this->sock) === true) {
187
188            @fclose($this->sock);
189        }
190        return true;
191    }
192
193    /**
194     * writes the given string to the DNS server socket
195     *
196     * @param string $data a binary packed DNS packet
197     *
198     * @return boolean
199     * @access public
200     *
201     */
202    public function write($data)
203    {
204        $length = strlen($data);
205        if ($length == 0) {
206
207            $this->last_error = 'empty data on write()';
208            return false;
209        }
210
211        $read   = null;
212        $write  = array($this->sock);
213        $except = null;
214
215        //
216        // select on write
217        //
218        switch(@stream_select($read, $write, $except, $this->timeout)) {
219        case false:
220            $this->last_error = 'failed on stream_select()';
221            return false;
222            break;
223       
224        case 0:
225            return false;
226            break;
227            
228        default:
229            ;
230        }
231
232        //
233        // if it's a TCP socket, then we need to packet and send the length of the
234        // data as the first 16bit of data.
235        //        
236        if ($this->type == Net_DNS2_Socket::SOCK_STREAM) {
237
238            $s = chr($length >> 8) . chr($length);
239
240            if (@fwrite($this->sock, $s) === false) {
241
242                $this->last_error = 'failed to fwrite() 16bit length';
243                return false;
244            }
245        }
246
247        //
248        // write the data to the socket
249        //
250        $size = @fwrite($this->sock, $data);
251        if ( ($size === false) || ($size != $length) ) {
252        
253            $this->last_error = 'failed to fwrite() packet';
254            return false;
255        }
256
257        return true;
258    }
259
260    /**
261     * reads a response from a DNS server
262     *
263     * @param integer &$size the size of the DNS packet read is passed back
264     *
265     * @return mixed         returns the data on success and false on error
266     * @access public
267     *
268     */
269    public function read(&$size)
270    {
271        $read   = array($this->sock);
272        $write  = null;
273        $except = null;
274
275        //
276        // make sure our socket is non-blocking
277        //
278        @stream_set_blocking($this->sock, 0);
279
280        //
281        // select on read
282        //
283        switch(stream_select($read, $write, $except, $this->timeout)) {
284        case false:
285            $this->last_error = 'error on stream_select()';
286            return false;
287            break;
288            
289        case 0:
290            return false;
291            break;
292            
293        default:
294            ;
295        }
296
297        $data = '';
298        $length = Net_DNS2_Lookups::DNS_MAX_UDP_SIZE;
299
300        //
301        // if it's a TCP socket, then the first two bytes is the length of the DNS
302        // packet- we need to read that off first, then use that value for the    
303        // packet read.
304        //
305        if ($this->type == Net_DNS2_Socket::SOCK_STREAM) {
306    
307            if (($data = fread($this->sock, 2)) === false) {
308                
309                $this->last_error = 'failed on fread() for data length';
310                return false;
311            }
312        
313            $length = ord($data[0]) << 8 | ord($data[1]);
314            if ($length < Net_DNS2_Lookups::DNS_HEADER_SIZE) {
315
316                return false;
317            }
318        }
319
320        //
321        // at this point, we know that there is data on the socket to be read,
322        // because we've already extracted the length from the first two bytes.
323        //
324        // so the easiest thing to do, is just turn off socket blocking, and
325        // wait for the data.
326        //
327        @stream_set_blocking($this->sock, 1);
328
329        //
330        // read the data from the socket
331        //
332        $data = '';
333
334        //
335        // the streams socket is weird for TCP sockets; it doesn't seem to always
336        // return all the data properly; but the looping code I added broke UDP
337        // packets- my fault- 
338        //
339        // the sockets library works much better.
340        //
341        if ($this->type == Net_DNS2_Socket::SOCK_STREAM) {
342
343            $chunk = '';
344            $chunk_size = $length;
345
346            //
347            // loop so we make sure we read all the data
348            //
349            while (1) {
350
351                $chunk = fread($this->sock, $chunk_size);
352                if ($chunk === false) {
353            
354                    $this->last_error = 'failed on fread() for data';
355                    return false;
356                }
357
358                $data .= $chunk;
359                $chunk_size -= strlen($chunk);
360
361                if (strlen($data) >= $length) {
362                    break;
363                }
364            }
365
366        //
367        // if it's UDP, ti's a single fixed-size frame, and the streams library
368        // doesn't seem to have a problem reading it.
369        //
370        } else {
371
372            $data = fread($this->sock, $length);
373            if ($length === false) {
374            
375                $this->last_error = 'failed on fread() for data';
376                return false;
377            }
378        }
379        
380        $size = strlen($data);
381
382        return $data;
383    }
384}
385
386/*
387 * Local variables:
388 * tab-width: 4
389 * c-basic-offset: 4
390 * c-hanging-comment-ender-p: nil
391 * End:
392 */
393?>