Socket.php 21 KB

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