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 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 $result = @socket_select($read, $write, $except, $this->timeout); 148 if ($result === false) { 149 150 $this->last_error = socket_strerror(socket_last_error()); 151 return false; 152 153 } else if ($result == 0) { 154 155 $this->last_error = 'timeout on write select for connect()'; 156 return false; 157 } 158 159 return true; 160 } 161 162 /** 163 * closes a socket connection to the DNS server 164 * 165 * @return boolean 166 * @access public 167 * 168 */ 169 public function close() 170 { 171 if (is_resource($this->sock) === true) { 172 173 @socket_close($this->sock); 174 } 175 return true; 176 } 177 178 /** 179 * writes the given string to the DNS server socket 180 * 181 * @param string $data a binary packed DNS packet 182 * 183 * @return boolean 184 * @access public 185 * 186 */ 187 public function write($data) 188 { 189 $length = strlen($data); 190 if ($length == 0) { 191 192 $this->last_error = 'empty data on write()'; 193 return false; 194 } 195 196 $read = null; 197 $write = array($this->sock); 198 $except = null; 199 200 // 201 // select on write 202 // 203 $result = @socket_select($read, $write, $except, $this->timeout); 204 if ($result === false) { 205 206 $this->last_error = socket_strerror(socket_last_error()); 207 return false; 208 209 } else if ($result == 0) { 210 211 $this->last_error = 'timeout on write select()'; 212 return false; 213 } 214 215 // 216 // if it's a TCP socket, then we need to packet and send the length of the 217 // data as the first 16bit of data. 218 // 219 if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { 220 221 $s = chr($length >> 8) . chr($length); 222 223 if (@socket_write($this->sock, $s) === false) { 224 225 $this->last_error = socket_strerror(socket_last_error()); 226 return false; 227 } 228 } 229 230 // 231 // write the data to the socket 232 // 233 $size = @socket_write($this->sock, $data); 234 if ( ($size === false) || ($size != $length) ) { 235 236 $this->last_error = socket_strerror(socket_last_error()); 237 return false; 238 } 239 240 return true; 241 } 242 243 /** 244 * reads a response from a DNS server 245 * 246 * @param integer &$size the size of the DNS packet read is passed back 247 * 248 * @return mixed returns the data on success and false on error 249 * @access public 250 * 251 */ 252 public function read(&$size, $max_size) 253 { 254 $read = array($this->sock); 255 $write = null; 256 $except = null; 257 258 // 259 // make sure our socket is non-blocking 260 // 261 if (@socket_set_nonblock($this->sock) === false) { 262 263 $this->last_error = socket_strerror(socket_last_error()); 264 return false; 265 } 266 267 // 268 // select on read 269 // 270 $result = @socket_select($read, $write, $except, $this->timeout); 271 if ($result === false) { 272 273 $this->last_error = socket_strerror(socket_last_error()); 274 return false; 275 276 } else if ($result == 0) { 277 278 $this->last_error = 'timeout on read select()'; 279 return false; 280 } 281 282 $data = ''; 283 $length = $max_size; 284 285 // 286 // if it's a TCP socket, then the first two bytes is the length of the DNS 287 // packet- we need to read that off first, then use that value for the 288 // packet read. 289 // 290 if ($this->type == Net_DNS2_Socket::SOCK_STREAM) { 291 292 if (($size = @socket_recv($this->sock, $data, 2, 0)) === false) { 293 294 $this->last_error = socket_strerror(socket_last_error()); 295 return false; 296 } 297 298 $length = ord($data[0]) << 8 | ord($data[1]); 299 if ($length < Net_DNS2_Lookups::DNS_HEADER_SIZE) { 300 301 return false; 302 } 303 } 304 305 // 306 // at this point, we know that there is data on the socket to be read, 307 // because we've already extracted the length from the first two bytes. 308 // 309 // so the easiest thing to do, is just turn off socket blocking, and 310 // wait for the data. 311 // 312 if (@socket_set_block($this->sock) === false) { 313 314 $this->last_error = socket_strerror(socket_last_error()); 315 return false; 316 } 317 318 // 319 // read the data from the socket 320 // 321 // loop while reading since some OS's (specifically Win < 2003) don't support 322 // MSG_WAITALL properly, so they may return with less data than is available. 323 // 324 // According to M$, XP and below don't support MSG_WAITALL at all; and there 325 // also seems to be some issue in 2003 and 2008 where the MSG_WAITALL is 326 // defined as 0, but if you actually pass 8 (which is the correct defined 327 // value), it works as it's supposed to- so in these cases, it's just the 328 // define that's incorrect- this is likely a PHP issue. 329 // 330 $data = ''; 331 $size = 0; 332 333 while (1) { 334 335 $chunk_size = @socket_recv($this->sock, $chunk, $length, MSG_WAITALL); 336 if ($chunk_size === false) { 337 338 $size = $chunk_size; 339 $this->last_error = socket_strerror(socket_last_error()); 340 341 return false; 342 } 343 344 $data .= $chunk; 345 $size += $chunk_size; 346 347 $length -= $chunk_size; 348 if ( ($length <= 0) || ($this->type == Net_DNS2_Socket::SOCK_DGRAM) ) { 349 break; 350 } 351 } 352 353 return $data; 354 } 355} 356 357/* 358 * Local variables: 359 * tab-width: 4 360 * c-basic-offset: 4 361 * c-hanging-comment-ender-p: nil 362 * End: 363 */ 364?> 365