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$ 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_DNS2_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 $result = stream_select($read, $write, $except, $this->timeout); 219 if ($result === false) { 220 221 $this->last_error = 'failed on write select()'; 222 return false; 223 224 } else if ($result == 0) { 225 226 $this->last_error = 'timeout on write select()'; 227 return false; 228 } 229 230 // 231 // if it's a TCP socket, then we need to packet and send the length of the 232 // data as the first 16bit of data. 233 // 234 if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { 235 236 $s = chr($length >> 8) . chr($length); 237 238 if (@fwrite($this->sock, $s) === false) { 239 240 $this->last_error = 'failed to fwrite() 16bit length'; 241 return false; 242 } 243 } 244 245 // 246 // write the data to the socket 247 // 248 $size = @fwrite($this->sock, $data); 249 if ( ($size === false) || ($size != $length) ) { 250 251 $this->last_error = 'failed to fwrite() packet'; 252 return false; 253 } 254 255 return true; 256 } 257 258 /** 259 * reads a response from a DNS server 260 * 261 * @param integer &$size the size of the DNS packet read is passed back 262 * 263 * @return mixed returns the data on success and false on error 264 * @access public 265 * 266 */ 267 public function read(&$size, $max_size) 268 { 269 $read = array($this->sock); 270 $write = null; 271 $except = null; 272 273 // 274 // make sure our socket is non-blocking 275 // 276 @stream_set_blocking($this->sock, 0); 277 278 // 279 // select on read 280 // 281 $result = stream_select($read, $write, $except, $this->timeout); 282 if ($result === false) { 283 284 $this->last_error = 'error on read select()'; 285 return false; 286 287 } else if ($result == 0) { 288 289 $this->last_error = 'timeout on read select()'; 290 return false; 291 } 292 293 $data = ''; 294 $length = $max_size; 295 296 // 297 // if it's a TCP socket, then the first two bytes is the length of the DNS 298 // packet- we need to read that off first, then use that value for the 299 // packet read. 300 // 301 if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { 302 303 if (($data = fread($this->sock, 2)) === false) { 304 305 $this->last_error = 'failed on fread() for data length'; 306 return false; 307 } 308 309 $length = ord($data[0]) << 8 | ord($data[1]); 310 if ($length < Net_DNS2_Lookups::DNS_HEADER_SIZE) { 311 312 return false; 313 } 314 } 315 316 // 317 // at this point, we know that there is data on the socket to be read, 318 // because we've already extracted the length from the first two bytes. 319 // 320 // so the easiest thing to do, is just turn off socket blocking, and 321 // wait for the data. 322 // 323 @stream_set_blocking($this->sock, 1); 324 325 // 326 // read the data from the socket 327 // 328 $data = ''; 329 330 // 331 // the streams socket is weird for TCP sockets; it doesn't seem to always 332 // return all the data properly; but the looping code I added broke UDP 333 // packets- my fault- 334 // 335 // the sockets library works much better. 336 // 337 if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { 338 339 $chunk = ''; 340 $chunk_size = $length; 341 342 // 343 // loop so we make sure we read all the data 344 // 345 while (1) { 346 347 $chunk = fread($this->sock, $chunk_size); 348 if ($chunk === false) { 349 350 $this->last_error = 'failed on fread() for data'; 351 return false; 352 } 353 354 $data .= $chunk; 355 $chunk_size -= strlen($chunk); 356 357 if (strlen($data) >= $length) { 358 break; 359 } 360 } 361 362 } else { 363 364 // 365 // if it's UDP, it's a single fixed-size frame, and the streams library 366 // doesn't seem to have a problem reading it. 367 // 368 $data = fread($this->sock, $length); 369 if ($length === false) { 370 371 $this->last_error = 'failed on fread() for data'; 372 return false; 373 } 374 } 375 376 $size = strlen($data); 377 378 return $data; 379 } 380} 381 382/* 383 * Local variables: 384 * tab-width: 4 385 * c-basic-offset: 4 386 * c-hanging-comment-ender-p: nil 387 * End: 388 */ 389?> 390