Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 3 namespace Horde\Socket; 4 5 /** 6 * Copyright 2013-2017 Horde LLC (http://www.horde.org/) 7 * 8 * See the enclosed file LICENSE for license information (LGPL). If you 9 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 10 * 11 * @category Horde 12 * @copyright 2013-2017 Horde LLC 13 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 14 * @package Socket_Client 15 */ 16 17 /** 18 * Utility interface for establishing a stream socket client. 19 * 20 * @author Michael Slusarz <slusarz@horde.org> 21 * @author Jan Schneider <jan@horde.org> 22 * @category Horde 23 * @copyright 2013-2017 Horde LLC 24 * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 25 * @package Socket_Client 26 * 27 * @property-read boolean $connected Is there an active connection? 28 * @property-read boolean $secure Is the active connection secure? 29 */ 30 class Client 31 { 32 /** 33 * Is there an active connection? 34 * 35 * @var boolean 36 */ 37 protected $_connected = false; 38 39 /** 40 * Configuration parameters. 41 * 42 * @var array 43 */ 44 protected $_params; 45 46 /** 47 * Is the connection secure? 48 * 49 * @var boolean 50 */ 51 protected $_secure = false; 52 53 /** 54 * The actual socket. 55 * 56 * @var resource 57 */ 58 protected $_stream; 59 60 /** 61 * Constructor. 62 * 63 * @param string $host Hostname of remote server (can contain 64 * protocol prefx). 65 * @param integer $port Port number of remote server. 66 * @param integer $timeout Connection timeout (in seconds). 67 * @param mixed $secure Security layer requested. One of: 68 * - false: (No encryption) [DEFAULT] 69 * - 'ssl': (Auto-detect SSL version) 70 * - 'sslv2': (Force SSL version 3) 71 * - 'sslv3': (Force SSL version 2) 72 * - 'tls': (TLS; started via protocol-level negotation over unencrypted 73 * channel) 74 * - 'tlsv1': (TLS version 1.x connection) 75 * - true: (TLS if available/necessary) 76 * @param array $context Any context parameters passed to 77 * stream_create_context(). 78 * @param array $params Additional options. 79 * 80 * @throws Horde\Socket\Client\Exception 81 */ 82 public function __construct( 83 $host, $port = null, $timeout = 30, $secure = false, 84 $context = array(), array $params = array() 85 ) 86 { 87 if ($secure && !extension_loaded('openssl')) { 88 if ($secure !== true) { 89 throw new \InvalidArgumentException('Secure connections require the PHP openssl extension.'); 90 } 91 $secure = false; 92 } 93 94 $context = array_merge_recursive( 95 array( 96 'ssl' => array( 97 'verify_peer' => false, 98 'verify_peer_name' => false 99 ) 100 ), 101 $context 102 ); 103 104 $this->_params = $params; 105 106 $this->_connect($host, $port, $timeout, $secure, $context); 107 } 108 109 /** 110 */ 111 public function __get($name) 112 { 113 switch ($name) { 114 case 'connected': 115 return $this->_connected; 116 117 case 'secure': 118 return $this->_secure; 119 } 120 } 121 122 /** 123 * This object can not be cloned. 124 */ 125 public function __clone() 126 { 127 throw new \LogicException('Object cannot be cloned.'); 128 } 129 130 /** 131 * This object can not be serialized. 132 */ 133 public function __sleep() 134 { 135 throw new \LogicException('Object can not be serialized.'); 136 } 137 138 /** 139 * Start a TLS connection. 140 * 141 * @return boolean Whether TLS was successfully started. 142 */ 143 public function startTls() 144 { 145 if ($this->connected && !$this->secure) { 146 if (defined('STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT')) { 147 $mode = STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT 148 | STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT 149 | STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; 150 } else { 151 $mode = STREAM_CRYPTO_METHOD_TLS_CLIENT; 152 } 153 if (@stream_socket_enable_crypto($this->_stream, true, $mode) === true) { 154 $this->_secure = true; 155 return true; 156 } 157 } 158 159 return false; 160 } 161 162 /** 163 * Close the connection. 164 */ 165 public function close() 166 { 167 if ($this->connected) { 168 @fclose($this->_stream); 169 $this->_connected = $this->_secure = false; 170 $this->_stream = null; 171 } 172 } 173 174 /** 175 * Returns information about the connection. 176 * 177 * Currently returns four entries in the result array: 178 * - timed_out (bool): The socket timed out waiting for data 179 * - blocked (bool): The socket was blocked 180 * - eof (bool): Indicates EOF event 181 * - unread_bytes (int): Number of bytes left in the socket buffer 182 * 183 * @throws Horde\Socket\Client\Exception 184 * @return array Information about existing socket resource. 185 */ 186 public function getStatus() 187 { 188 $this->_checkStream(); 189 return stream_get_meta_data($this->_stream); 190 } 191 192 /** 193 * Returns a line of data. 194 * 195 * @param int $size Reading ends when $size - 1 bytes have been read, 196 * or a newline or an EOF (whichever comes first). 197 * 198 * @throws Horde\Socket\Client\Exception 199 * @return string $size bytes of data from the socket 200 */ 201 public function gets($size) 202 { 203 $this->_checkStream(); 204 $data = @fgets($this->_stream, $size); 205 if ($data === false) { 206 throw new Client\Exception('Error reading data from socket'); 207 } 208 return $data; 209 } 210 211 /** 212 * Returns a specified amount of data. 213 * 214 * @param integer $size The number of bytes to read from the socket. 215 * 216 * @throws Horde\Socket\Client\Exception 217 * @return string $size bytes of data from the socket. 218 */ 219 public function read($size) 220 { 221 $this->_checkStream(); 222 $data = @fread($this->_stream, $size); 223 if ($data === false) { 224 throw new Client\Exception('Error reading data from socket'); 225 } 226 return $data; 227 } 228 229 /** 230 * Writes data to the stream. 231 * 232 * @param string $data Data to write. 233 * 234 * @throws Horde\Socket\Client\Exception 235 */ 236 public function write($data) 237 { 238 $this->_checkStream(); 239 if (!@fwrite($this->_stream, $data)) { 240 $meta_data = $this->getStatus(); 241 if (!empty($meta_data['timed_out'])) { 242 throw new Client\Exception('Timed out writing data to socket'); 243 } 244 throw new Client\Exception('Error writing data to socket'); 245 } 246 } 247 248 /* Internal methods. */ 249 250 /** 251 * Connect to the remote server. 252 * 253 * @see __construct() 254 * 255 * @throws Horde\Socket\Client\Exception 256 */ 257 protected function _connect( 258 $host, $port, $timeout, $secure, $context, $retries = 0 259 ) 260 { 261 $conn = ''; 262 if (!strpos($host, '://')) { 263 switch (strval($secure)) { 264 case 'ssl': 265 case 'sslv2': 266 case 'sslv3': 267 $conn = $secure . '://'; 268 $this->_secure = true; 269 break; 270 271 case 'tlsv1': 272 $conn = 'tls://'; 273 $this->_secure = true; 274 break; 275 276 case 'tls': 277 default: 278 $conn = 'tcp://'; 279 break; 280 } 281 } 282 $conn .= $host; 283 if ($port) { 284 $conn .= ':' . $port; 285 } 286 287 $this->_stream = @stream_socket_client( 288 $conn, 289 $error_number, 290 $error_string, 291 $timeout, 292 STREAM_CLIENT_CONNECT, 293 stream_context_create($context) 294 ); 295 296 if ($this->_stream === false) { 297 /* From stream_socket_client() page: a function return of false, 298 * with an error code of 0, indicates a "problem initializing the 299 * socket". These kind of issues are seen on the same server 300 * (and even the same user account) as sucessful connections, so 301 * these are likely transient issues. Retry up to 3 times in these 302 * instances. */ 303 if (!$error_number && ($retries < 3)) { 304 return $this->_connect($host, $port, $timeout, $secure, $context, ++$retries); 305 } 306 307 $e = new Client\Exception( 308 'Error connecting to server.' 309 ); 310 $e->details = sprintf("[%u] %s", $error_number, $error_string); 311 throw $e; 312 } 313 314 stream_set_timeout($this->_stream, $timeout); 315 316 if (function_exists('stream_set_read_buffer')) { 317 stream_set_read_buffer($this->_stream, 0); 318 } 319 stream_set_write_buffer($this->_stream, 0); 320 321 $this->_connected = true; 322 } 323 324 /** 325 * Throws an exception is the stream is not a resource. 326 * 327 * @throws Horde\Socket\Client\Exception 328 */ 329 protected function _checkStream() 330 { 331 if (!is_resource($this->_stream)) { 332 throw new Client\Exception('Not connected'); 333 } 334 } 335 336 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body