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