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: Sockets.php 198 2013-05-26 05:05:22Z 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 sockets extension 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 this class is used, if 57 * it's not, then the Net_DNS2_Socket_Streams class is 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_Sockets extends Net_DNS2_Socket 68{ 69 /** 70 * opens a socket connection to the DNS server 71 * 72 * @return boolean 73 * @access public 74 * 75 */ 76 public function open() 77 { 78 // 79 // create the socket 80 // 81 if (Net_DNS2::isIPv4($this->host) == true) { 82 83 $this->sock = @socket_create( 84 AF_INET, $this->type, 85 ($this->type == Net_DNS2_Socket::SOCK_STREAM) ? SOL_TCP : SOL_UDP 86 ); 87 88 } else if (Net_DNS2::isIPv6($this->host) == true) { 89 90 $this->sock = @socket_create( 91 AF_INET6, $this->type, 92 ($this->type == Net_DNS2_Socket::SOCK_STREAM) ? SOL_TCP : SOL_UDP 93 ); 94 95 } else { 96 97 $this->last_error = 'invalid address type: ' . $this->host; 98 return false; 99 } 100 101 if ($this->sock === false) { 102 103 $this->last_error = socket_strerror(socket_last_error()); 104 return false; 105 } 106 107 @socket_set_option($this->sock, SOL_SOCKET, SO_REUSEADDR, 1); 108 109 // 110 // bind to a local IP/port if it's set 111 // 112 if (strlen($this->local_host) > 0) { 113 114 $result = @socket_bind( 115 $this->sock, $this->local_host, 116 ($this->local_port > 0) ? $this->local_port : null 117 ); 118 if ($result === false) { 119 120 $this->last_error = socket_strerror(socket_last_error()); 121 return false; 122 } 123 } 124 125 // 126 // mark the socket as non-blocking 127 // 128 if (@socket_set_nonblock($this->sock) === false) { 129 130 $this->last_error = socket_strerror(socket_last_error()); 131 return false; 132 } 133 134 // 135 // connect to the socket; don't check for status here, we'll check it on the 136 // socket_select() call so we can handle timeouts properly 137 // 138 @socket_connect($this->sock, $this->host, $this->port); 139 140 $read = null; 141 $write = array($this->sock); 142 $except = null; 143 144 // 145 // select on write to check if the call to connect worked 146 // 147 switch(@socket_select($read, $write, $except, $this->timeout)) { 148 case false: 149 $this->last_error = socket_strerror(socket_last_error()); 150 return false; 151 break; 152 153 case 0: 154 return false; 155 break; 156 157 default: 158 ; 159 } 160 161 return true; 162 } 163 164 /** 165 * closes a socket connection to the DNS server 166 * 167 * @return boolean 168 * @access public 169 * 170 */ 171 public function close() 172 { 173 if (is_resource($this->sock) === true) { 174 175 @socket_close($this->sock); 176 } 177 return true; 178 } 179 180 /** 181 * writes the given string to the DNS server socket 182 * 183 * @param string $data a binary packed DNS packet 184 * 185 * @return boolean 186 * @access public 187 * 188 */ 189 public function write($data) 190 { 191 $length = strlen($data); 192 if ($length == 0) { 193 194 $this->last_error = 'empty data on write()'; 195 return false; 196 } 197 198 $read = null; 199 $write = array($this->sock); 200 $except = null; 201 202 // 203 // select on write 204 // 205 switch(@socket_select($read, $write, $except, $this->timeout)) { 206 case false: 207 $this->last_error = socket_strerror(socket_last_error()); 208 return false; 209 break; 210 211 case 0: 212 return false; 213 break; 214 215 default: 216 ; 217 } 218 219 // 220 // if it's a TCP socket, then we need to packet and send the length of the 221 // data as the first 16bit of data. 222 // 223 if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { 224 225 $s = chr($length >> 8) . chr($length); 226 227 if (@socket_write($this->sock, $s) === false) { 228 229 $this->last_error = socket_strerror(socket_last_error()); 230 return false; 231 } 232 } 233 234 // 235 // write the data to the socket 236 // 237 $size = @socket_write($this->sock, $data); 238 if ( ($size === false) || ($size != $length) ) { 239 240 $this->last_error = socket_strerror(socket_last_error()); 241 return false; 242 } 243 244 return true; 245 } 246 247 /** 248 * reads a response from a DNS server 249 * 250 * @param integer &$size the size of the DNS packet read is passed back 251 * 252 * @return mixed returns the data on success and false on error 253 * @access public 254 * 255 */ 256 public function read(&$size) 257 { 258 $read = array($this->sock); 259 $write = null; 260 $except = null; 261 262 // 263 // make sure our socket is non-blocking 264 // 265 if (@socket_set_nonblock($this->sock) === false) { 266 267 $this->last_error = socket_strerror(socket_last_error()); 268 return false; 269 } 270 271 // 272 // select on read 273 // 274 switch(@socket_select($read, $write, $except, $this->timeout)) { 275 case false: 276 $this->last_error = socket_strerror(socket_last_error()); 277 return false; 278 break; 279 280 case 0: 281 return false; 282 break; 283 284 default: 285 ; 286 } 287 288 $data = ''; 289 $length = Net_DNS2_Lookups::DNS_MAX_UDP_SIZE; 290 291 // 292 // if it's a TCP socket, then the first two bytes is the length of the DNS 293 // packet- we need to read that off first, then use that value for the 294 // packet read. 295 // 296 if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { 297 298 if (($size = @socket_recv($this->sock, $data, 2, 0)) === false) { 299 300 $this->last_error = socket_strerror(socket_last_error()); 301 return false; 302 } 303 304 $length = ord($data[0]) << 8 | ord($data[1]); 305 if ($length < Net_DNS2_Lookups::DNS_HEADER_SIZE) { 306 307 return false; 308 } 309 } 310 311 // 312 // at this point, we know that there is data on the socket to be read, 313 // because we've already extracted the length from the first two bytes. 314 // 315 // so the easiest thing to do, is just turn off socket blocking, and 316 // wait for the data. 317 // 318 if (@socket_set_block($this->sock) === false) { 319 320 $this->last_error = socket_strerror(socket_last_error()); 321 return false; 322 } 323 324 // 325 // read the data from the socket 326 // 327 // loop while reading since some OS's (specifically Win < 2003) don't support 328 // MSG_WAITALL properly, so they may return with less data than is available. 329 // 330 // According to M$, XP and below don't support MSG_WAITALL at all; and there 331 // also seems to be some issue in 2003 and 2008 where the MSG_WAITALL is 332 // defined as 0, but if you actually pass 8 (which is the correct defined 333 // value), it works as it's supposed to- so in these cases, it's just the 334 // define that's incorrect- this is likely a PHP issue. 335 // 336 $data = ''; 337 $size = 0; 338 339 while (1) { 340 341 $chunk_size = @socket_recv($this->sock, $chunk, $length, MSG_WAITALL); 342 if ($chunk_size === false) { 343 344 $size = $chunk_size; 345 $this->last_error = socket_strerror(socket_last_error()); 346 347 return false; 348 } 349 350 $data .= $chunk; 351 $size += $chunk_size; 352 353 $length -= $chunk_size; 354 if ( ($length <= 0) || ($this->type == Net_DNS2_Socket::SOCK_DGRAM) ) { 355 break; 356 } 357 } 358 359 return $data; 360 } 361} 362 363/* 364 * Local variables: 365 * tab-width: 4 366 * c-basic-offset: 4 367 * c-hanging-comment-ender-p: nil 368 * End: 369 */ 370?> 371