You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Socket.php 21KB

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