1<?php 2/** 3 * Net_Socket 4 * 5 * PHP Version 4 6 * 7 * Copyright (c) 1997-2013 The PHP Group 8 * 9 * This source file is subject to version 2.0 of the PHP license, 10 * that is bundled with this package in the file LICENSE, and is 11 * available at through the world-wide-web at 12 * http://www.php.net/license/2_02.txt. 13 * If you did not receive a copy of the PHP license and are unable to 14 * obtain it through the world-wide-web, please send a note to 15 * license@php.net so we can mail you a copy immediately. 16 * 17 * Authors: Stig Bakken <ssb@php.net> 18 * Chuck Hagenbuch <chuck@horde.org> 19 * 20 * @category Net 21 * @package Net_Socket 22 * @author Stig Bakken <ssb@php.net> 23 * @author Chuck Hagenbuch <chuck@horde.org> 24 * @copyright 1997-2003 The PHP Group 25 * @license http://www.php.net/license/2_02.txt PHP 2.02 26 * @link http://pear.php.net/packages/Net_Socket 27 */ 28 29require_once 'PEAR.php'; 30 31define('NET_SOCKET_READ', 1); 32define('NET_SOCKET_WRITE', 2); 33define('NET_SOCKET_ERROR', 4); 34 35/** 36 * Generalized Socket class. 37 * 38 * @category Net 39 * @package Net_Socket 40 * @author Stig Bakken <ssb@php.net> 41 * @author Chuck Hagenbuch <chuck@horde.org> 42 * @copyright 1997-2003 The PHP Group 43 * @license http://www.php.net/license/2_02.txt PHP 2.02 44 * @link http://pear.php.net/packages/Net_Socket 45 */ 46class Net_Socket extends PEAR 47{ 48 /** 49 * Socket file pointer. 50 * @var resource $fp 51 */ 52 var $fp = null; 53 54 /** 55 * Whether the socket is blocking. Defaults to true. 56 * @var boolean $blocking 57 */ 58 var $blocking = true; 59 60 /** 61 * Whether the socket is persistent. Defaults to false. 62 * @var boolean $persistent 63 */ 64 var $persistent = false; 65 66 /** 67 * The IP address to connect to. 68 * @var string $addr 69 */ 70 var $addr = ''; 71 72 /** 73 * The port number to connect to. 74 * @var integer $port 75 */ 76 var $port = 0; 77 78 /** 79 * Number of seconds to wait on socket operations before assuming 80 * there's no more data. Defaults to no timeout. 81 * @var integer|float $timeout 82 */ 83 var $timeout = null; 84 85 /** 86 * Number of bytes to read at a time in readLine() and 87 * readAll(). Defaults to 2048. 88 * @var integer $lineLength 89 */ 90 var $lineLength = 2048; 91 92 /** 93 * The string to use as a newline terminator. Usually "\r\n" or "\n". 94 * @var string $newline 95 */ 96 var $newline = "\r\n"; 97 98 /** 99 * Connect to the specified port. If called when the socket is 100 * already connected, it disconnects and connects again. 101 * 102 * @param string $addr IP address or host name (may be with protocol prefix). 103 * @param integer $port TCP port number. 104 * @param boolean $persistent (optional) Whether the connection is 105 * persistent (kept open between requests 106 * by the web server). 107 * @param integer $timeout (optional) Connection socket timeout. 108 * @param array $options See options for stream_context_create. 109 * 110 * @access public 111 * 112 * @return boolean|PEAR_Error True on success or a PEAR_Error on failure. 113 */ 114 function connect($addr, $port = 0, $persistent = null, 115 $timeout = null, $options = null) 116 { 117 if (is_resource($this->fp)) { 118 @fclose($this->fp); 119 $this->fp = null; 120 } 121 122 if (!$addr) { 123 return $this->raiseError('$addr cannot be empty'); 124 } else if (strspn($addr, ':.0123456789') == strlen($addr)) { 125 $this->addr = strpos($addr, ':') !== false ? '['.$addr.']' : $addr; 126 } else { 127 $this->addr = $addr; 128 } 129 130 $this->port = $port % 65536; 131 132 if ($persistent !== null) { 133 $this->persistent = $persistent; 134 } 135 136 $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen'; 137 $errno = 0; 138 $errstr = ''; 139 140 $old_track_errors = @ini_set('track_errors', 1); 141 142 if ($timeout <= 0) { 143 $timeout = @ini_get('default_socket_timeout'); 144 } 145 146 if ($options && function_exists('stream_context_create')) { 147 $context = stream_context_create($options); 148 149 // Since PHP 5 fsockopen doesn't allow context specification 150 if (function_exists('stream_socket_client')) { 151 $flags = STREAM_CLIENT_CONNECT; 152 153 if ($this->persistent) { 154 $flags = STREAM_CLIENT_PERSISTENT; 155 } 156 157 $addr = $this->addr . ':' . $this->port; 158 $fp = stream_socket_client($addr, $errno, $errstr, 159 $timeout, $flags, $context); 160 } else { 161 $fp = @$openfunc($this->addr, $this->port, $errno, 162 $errstr, $timeout, $context); 163 } 164 } else { 165 $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout); 166 } 167 168 if (!$fp) { 169 if ($errno == 0 && !strlen($errstr) && isset($php_errormsg)) { 170 $errstr = $php_errormsg; 171 } 172 @ini_set('track_errors', $old_track_errors); 173 return $this->raiseError($errstr, $errno); 174 } 175 176 @ini_set('track_errors', $old_track_errors); 177 $this->fp = $fp; 178 $this->setTimeout(); 179 return $this->setBlocking($this->blocking); 180 } 181 182 /** 183 * Disconnects from the peer, closes the socket. 184 * 185 * @access public 186 * @return mixed true on success or a PEAR_Error instance otherwise 187 */ 188 function disconnect() 189 { 190 if (!is_resource($this->fp)) { 191 return $this->raiseError('not connected'); 192 } 193 194 @fclose($this->fp); 195 $this->fp = null; 196 return true; 197 } 198 199 /** 200 * Set the newline character/sequence to use. 201 * 202 * @param string $newline Newline character(s) 203 * @return boolean True 204 */ 205 function setNewline($newline) 206 { 207 $this->newline = $newline; 208 return true; 209 } 210 211 /** 212 * Find out if the socket is in blocking mode. 213 * 214 * @access public 215 * @return boolean The current blocking mode. 216 */ 217 function isBlocking() 218 { 219 return $this->blocking; 220 } 221 222 /** 223 * Sets whether the socket connection should be blocking or 224 * not. A read call to a non-blocking socket will return immediately 225 * if there is no data available, whereas it will block until there 226 * is data for blocking sockets. 227 * 228 * @param boolean $mode True for blocking sockets, false for nonblocking. 229 * 230 * @access public 231 * @return mixed true on success or a PEAR_Error instance otherwise 232 */ 233 function setBlocking($mode) 234 { 235 if (!is_resource($this->fp)) { 236 return $this->raiseError('not connected'); 237 } 238 239 $this->blocking = $mode; 240 stream_set_blocking($this->fp, (int)$this->blocking); 241 return true; 242 } 243 244 /** 245 * Sets the timeout value on socket descriptor, 246 * expressed in the sum of seconds and microseconds 247 * 248 * @param integer $seconds Seconds. 249 * @param integer $microseconds Microseconds, optional. 250 * 251 * @access public 252 * @return mixed True on success or false on failure or 253 * a PEAR_Error instance when not connected 254 */ 255 function setTimeout($seconds = null, $microseconds = null) 256 { 257 if (!is_resource($this->fp)) { 258 return $this->raiseError('not connected'); 259 } 260 261 if ($seconds === null && $microseconds === null) { 262 $seconds = (int) $this->timeout; 263 $microseconds = (int) (($this->timeout - $seconds) * 1000000); 264 } else { 265 $this->timeout = $seconds + $microseconds/1000000; 266 } 267 268 if ($this->timeout > 0) { 269 return stream_set_timeout($this->fp, (int) $seconds, (int) $microseconds); 270 } 271 else { 272 return false; 273 } 274 } 275 276 /** 277 * Sets the file buffering size on the stream. 278 * See php's stream_set_write_buffer for more information. 279 * 280 * @param integer $size Write buffer size. 281 * 282 * @access public 283 * @return mixed on success or an PEAR_Error object otherwise 284 */ 285 function setWriteBuffer($size) 286 { 287 if (!is_resource($this->fp)) { 288 return $this->raiseError('not connected'); 289 } 290 291 $returned = stream_set_write_buffer($this->fp, $size); 292 if ($returned == 0) { 293 return true; 294 } 295 return $this->raiseError('Cannot set write buffer.'); 296 } 297 298 /** 299 * Returns information about an existing socket resource. 300 * Currently returns four entries in the result array: 301 * 302 * <p> 303 * timed_out (bool) - The socket timed out waiting for data<br> 304 * blocked (bool) - The socket was blocked<br> 305 * eof (bool) - Indicates EOF event<br> 306 * unread_bytes (int) - Number of bytes left in the socket buffer<br> 307 * </p> 308 * 309 * @access public 310 * @return mixed Array containing information about existing socket 311 * resource or a PEAR_Error instance otherwise 312 */ 313 function getStatus() 314 { 315 if (!is_resource($this->fp)) { 316 return $this->raiseError('not connected'); 317 } 318 319 return stream_get_meta_data($this->fp); 320 } 321 322 /** 323 * Get a specified line of data 324 * 325 * @param int $size Reading ends when size - 1 bytes have been read, 326 * or a newline or an EOF (whichever comes first). 327 * If no size is specified, it will keep reading from 328 * the stream until it reaches the end of the line. 329 * 330 * @access public 331 * @return mixed $size bytes of data from the socket, or a PEAR_Error if 332 * not connected. If an error occurs, FALSE is returned. 333 */ 334 function gets($size = null) 335 { 336 if (!is_resource($this->fp)) { 337 return $this->raiseError('not connected'); 338 } 339 340 if (is_null($size)) { 341 return @fgets($this->fp); 342 } else { 343 return @fgets($this->fp, $size); 344 } 345 } 346 347 /** 348 * Read a specified amount of data. This is guaranteed to return, 349 * and has the added benefit of getting everything in one fread() 350 * chunk; if you know the size of the data you're getting 351 * beforehand, this is definitely the way to go. 352 * 353 * @param integer $size The number of bytes to read from the socket. 354 * 355 * @access public 356 * @return $size bytes of data from the socket, or a PEAR_Error if 357 * not connected. 358 */ 359 function read($size) 360 { 361 if (!is_resource($this->fp)) { 362 return $this->raiseError('not connected'); 363 } 364 365 return @fread($this->fp, $size); 366 } 367 368 /** 369 * Write a specified amount of data. 370 * 371 * @param string $data Data to write. 372 * @param integer $blocksize Amount of data to write at once. 373 * NULL means all at once. 374 * 375 * @access public 376 * @return mixed If the socket is not connected, returns an instance of 377 * PEAR_Error. 378 * If the write succeeds, returns the number of bytes written. 379 * If the write fails, returns false. 380 * If the socket times out, returns an instance of PEAR_Error. 381 */ 382 function write($data, $blocksize = null) 383 { 384 if (!is_resource($this->fp)) { 385 return $this->raiseError('not connected'); 386 } 387 388 if (is_null($blocksize) && !OS_WINDOWS) { 389 $written = @fwrite($this->fp, $data); 390 391 // Check for timeout or lost connection 392 if ($written===false) { 393 $meta_data = $this->getStatus(); 394 395 if (!is_array($meta_data)) { 396 return $meta_data; // PEAR_Error 397 } 398 399 if (!empty($meta_data['timed_out'])) { 400 return $this->raiseError('timed out'); 401 } 402 } 403 404 return $written; 405 } else { 406 if (is_null($blocksize)) { 407 $blocksize = 1024; 408 } 409 410 $pos = 0; 411 $size = strlen($data); 412 while ($pos < $size) { 413 $written = @fwrite($this->fp, substr($data, $pos, $blocksize)); 414 415 // Check for timeout or lost connection 416 if ($written===false) { 417 $meta_data = $this->getStatus(); 418 419 if (!is_array($meta_data)) { 420 return $meta_data; // PEAR_Error 421 } 422 423 if (!empty($meta_data['timed_out'])) { 424 return $this->raiseError('timed out'); 425 } 426 427 return $written; 428 } 429 430 $pos += $written; 431 } 432 433 return $pos; 434 } 435 } 436 437 /** 438 * Write a line of data to the socket, followed by a trailing newline. 439 * 440 * @param string $data Data to write 441 * 442 * @access public 443 * @return mixed fwrite() result, or PEAR_Error when not connected 444 */ 445 function writeLine($data) 446 { 447 if (!is_resource($this->fp)) { 448 return $this->raiseError('not connected'); 449 } 450 451 return fwrite($this->fp, $data . $this->newline); 452 } 453 454 /** 455 * Tests for end-of-file on a socket descriptor. 456 * 457 * Also returns true if the socket is disconnected. 458 * 459 * @access public 460 * @return bool 461 */ 462 function eof() 463 { 464 return (!is_resource($this->fp) || feof($this->fp)); 465 } 466 467 /** 468 * Reads a byte of data 469 * 470 * @access public 471 * @return 1 byte of data from the socket, or a PEAR_Error if 472 * not connected. 473 */ 474 function readByte() 475 { 476 if (!is_resource($this->fp)) { 477 return $this->raiseError('not connected'); 478 } 479 480 return ord(@fread($this->fp, 1)); 481 } 482 483 /** 484 * Reads a word of data 485 * 486 * @access public 487 * @return 1 word of data from the socket, or a PEAR_Error if 488 * not connected. 489 */ 490 function readWord() 491 { 492 if (!is_resource($this->fp)) { 493 return $this->raiseError('not connected'); 494 } 495 496 $buf = @fread($this->fp, 2); 497 return (ord($buf[0]) + (ord($buf[1]) << 8)); 498 } 499 500 /** 501 * Reads an int of data 502 * 503 * @access public 504 * @return integer 1 int of data from the socket, or a PEAR_Error if 505 * not connected. 506 */ 507 function readInt() 508 { 509 if (!is_resource($this->fp)) { 510 return $this->raiseError('not connected'); 511 } 512 513 $buf = @fread($this->fp, 4); 514 return (ord($buf[0]) + (ord($buf[1]) << 8) + 515 (ord($buf[2]) << 16) + (ord($buf[3]) << 24)); 516 } 517 518 /** 519 * Reads a zero-terminated string of data 520 * 521 * @access public 522 * @return string, or a PEAR_Error if 523 * not connected. 524 */ 525 function readString() 526 { 527 if (!is_resource($this->fp)) { 528 return $this->raiseError('not connected'); 529 } 530 531 $string = ''; 532 while (($char = @fread($this->fp, 1)) != "\x00") { 533 $string .= $char; 534 } 535 return $string; 536 } 537 538 /** 539 * Reads an IP Address and returns it in a dot formatted string 540 * 541 * @access public 542 * @return Dot formatted string, or a PEAR_Error if 543 * not connected. 544 */ 545 function readIPAddress() 546 { 547 if (!is_resource($this->fp)) { 548 return $this->raiseError('not connected'); 549 } 550 551 $buf = @fread($this->fp, 4); 552 return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]), 553 ord($buf[2]), ord($buf[3])); 554 } 555 556 /** 557 * Read until either the end of the socket or a newline, whichever 558 * comes first. Strips the trailing newline from the returned data. 559 * 560 * @access public 561 * @return All available data up to a newline, without that 562 * newline, or until the end of the socket, or a PEAR_Error if 563 * not connected. 564 */ 565 function readLine() 566 { 567 if (!is_resource($this->fp)) { 568 return $this->raiseError('not connected'); 569 } 570 571 $line = ''; 572 573 $timeout = time() + $this->timeout; 574 575 while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) { 576 $line .= @fgets($this->fp, $this->lineLength); 577 if (substr($line, -1) == "\n") { 578 return rtrim($line, $this->newline); 579 } 580 } 581 return $line; 582 } 583 584 /** 585 * Read until the socket closes, or until there is no more data in 586 * the inner PHP buffer. If the inner buffer is empty, in blocking 587 * mode we wait for at least 1 byte of data. Therefore, in 588 * blocking mode, if there is no data at all to be read, this 589 * function will never exit (unless the socket is closed on the 590 * remote end). 591 * 592 * @access public 593 * 594 * @return string All data until the socket closes, or a PEAR_Error if 595 * not connected. 596 */ 597 function readAll() 598 { 599 if (!is_resource($this->fp)) { 600 return $this->raiseError('not connected'); 601 } 602 603 $data = ''; 604 while (!feof($this->fp)) { 605 $data .= @fread($this->fp, $this->lineLength); 606 } 607 return $data; 608 } 609 610 /** 611 * Runs the equivalent of the select() system call on the socket 612 * with a timeout specified by tv_sec and tv_usec. 613 * 614 * @param integer $state Which of read/write/error to check for. 615 * @param integer $tv_sec Number of seconds for timeout. 616 * @param integer $tv_usec Number of microseconds for timeout. 617 * 618 * @access public 619 * @return False if select fails, integer describing which of read/write/error 620 * are ready, or PEAR_Error if not connected. 621 */ 622 function select($state, $tv_sec, $tv_usec = 0) 623 { 624 if (!is_resource($this->fp)) { 625 return $this->raiseError('not connected'); 626 } 627 628 $read = null; 629 $write = null; 630 $except = null; 631 if ($state & NET_SOCKET_READ) { 632 $read[] = $this->fp; 633 } 634 if ($state & NET_SOCKET_WRITE) { 635 $write[] = $this->fp; 636 } 637 if ($state & NET_SOCKET_ERROR) { 638 $except[] = $this->fp; 639 } 640 if (false === ($sr = stream_select($read, $write, $except, 641 $tv_sec, $tv_usec))) { 642 return false; 643 } 644 645 $result = 0; 646 if (count($read)) { 647 $result |= NET_SOCKET_READ; 648 } 649 if (count($write)) { 650 $result |= NET_SOCKET_WRITE; 651 } 652 if (count($except)) { 653 $result |= NET_SOCKET_ERROR; 654 } 655 return $result; 656 } 657 658 /** 659 * Turns encryption on/off on a connected socket. 660 * 661 * @param bool $enabled Set this parameter to true to enable encryption 662 * and false to disable encryption. 663 * @param integer $type Type of encryption. See stream_socket_enable_crypto() 664 * for values. 665 * 666 * @see http://se.php.net/manual/en/function.stream-socket-enable-crypto.php 667 * @access public 668 * @return false on error, true on success and 0 if there isn't enough data 669 * and the user should try again (non-blocking sockets only). 670 * A PEAR_Error object is returned if the socket is not 671 * connected 672 */ 673 function enableCrypto($enabled, $type) 674 { 675 if (version_compare(phpversion(), "5.1.0", ">=")) { 676 if (!is_resource($this->fp)) { 677 return $this->raiseError('not connected'); 678 } 679 return @stream_socket_enable_crypto($this->fp, $enabled, $type); 680 } else { 681 $msg = 'Net_Socket::enableCrypto() requires php version >= 5.1.0'; 682 return $this->raiseError($msg); 683 } 684 } 685 686} 687