1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Thruster\Component\SocketClient; |
4
|
|
|
|
5
|
|
|
use Thruster\Component\EventLoop\EventLoopInterface; |
6
|
|
|
use Thruster\Component\Promise\Deferred; |
7
|
|
|
use Thruster\Component\Stream\Stream; |
8
|
|
|
use UnexpectedValueException; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* Class StreamEncryption |
12
|
|
|
* |
13
|
|
|
* @package Thruster\Component\SocketClient |
14
|
|
|
* @author Aurimas Niekis <[email protected]> |
15
|
|
|
*/ |
16
|
|
|
class StreamEncryption |
17
|
|
|
{ |
18
|
|
|
/** |
19
|
|
|
* @var EventLoopInterface |
20
|
|
|
*/ |
21
|
|
|
private $loop; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @var int |
25
|
|
|
*/ |
26
|
|
|
private $method; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var string |
30
|
|
|
*/ |
31
|
|
|
private $errStr; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var int |
35
|
|
|
*/ |
36
|
|
|
private $errno; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var bool |
40
|
|
|
*/ |
41
|
|
|
private $wrapSecure; |
42
|
|
|
|
43
|
1 |
|
public function __construct(EventLoopInterface $loop) |
44
|
|
|
{ |
45
|
1 |
|
$this->method = STREAM_CRYPTO_METHOD_TLS_CLIENT; |
46
|
1 |
|
$this->wrapSecure = false; |
47
|
|
|
|
48
|
1 |
|
$this->loop = $loop; |
49
|
|
|
|
50
|
1 |
|
if (defined('STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT')) { |
51
|
1 |
|
$this->method |= STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT; |
52
|
|
|
} |
53
|
1 |
|
if (defined('STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT')) { |
54
|
1 |
|
$this->method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; |
55
|
|
|
} |
56
|
1 |
|
if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { |
57
|
1 |
|
$this->method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; |
58
|
|
|
} |
59
|
1 |
|
} |
60
|
|
|
|
61
|
1 |
|
public function enable(Stream $stream) |
62
|
|
|
{ |
63
|
1 |
|
return $this->toggle($stream, true); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
public function disable(Stream $stream) |
67
|
|
|
{ |
68
|
|
|
return $this->toggle($stream, false); |
69
|
|
|
} |
70
|
|
|
|
71
|
1 |
|
public function toggle(Stream $stream, $toggle) |
72
|
|
|
{ |
73
|
1 |
|
if ($stream instanceof SecureStream) { |
74
|
|
|
$stream = $stream->getDecorating(); |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
// pause actual stream instance to continue operation on raw stream socket |
78
|
1 |
|
$stream->pause(); |
79
|
|
|
|
80
|
|
|
// TODO: add write() event to make sure we're not sending any excessive data |
81
|
|
|
|
82
|
1 |
|
$deferred = new Deferred(); |
83
|
|
|
|
84
|
|
|
// get actual stream socket from stream instance |
85
|
1 |
|
$socket = $stream->getStream(); |
86
|
|
|
|
87
|
|
|
$toggleCrypto = function () use ($socket, $deferred, $toggle) { |
88
|
1 |
|
$this->toggleCrypto($socket, $deferred, $toggle); |
89
|
1 |
|
}; |
90
|
|
|
|
91
|
1 |
|
$this->loop->addReadStream($socket, $toggleCrypto); |
92
|
1 |
|
$toggleCrypto(); |
93
|
|
|
|
94
|
|
|
return $deferred->promise()->then(function () use ($stream, $toggle) { |
95
|
1 |
|
if ($toggle && $this->wrapSecure) { |
96
|
|
|
return new SecureStream($stream, $this->loop); |
97
|
|
|
} |
98
|
|
|
|
99
|
1 |
|
$stream->resume(); |
100
|
|
|
|
101
|
1 |
|
return $stream; |
102
|
1 |
|
}, function ($error) use ($stream) { |
103
|
|
|
$stream->resume(); |
104
|
|
|
throw $error; |
105
|
1 |
|
}); |
106
|
|
|
} |
107
|
|
|
|
108
|
1 |
|
public function toggleCrypto($socket, Deferred $deferred, $toggle) |
109
|
|
|
{ |
110
|
|
|
//set_error_handler([$this, 'handleError']); |
111
|
1 |
|
$result = stream_socket_enable_crypto($socket, $toggle, $this->method); |
112
|
|
|
//restore_error_handler(); |
113
|
|
|
|
114
|
1 |
|
if (true === $result) { |
115
|
1 |
|
$this->loop->removeReadStream($socket); |
116
|
|
|
|
117
|
1 |
|
$deferred->resolve(); |
118
|
1 |
|
} elseif (false === $result) { |
119
|
|
|
$this->loop->removeReadStream($socket); |
120
|
|
|
|
121
|
|
|
$deferred->reject(new UnexpectedValueException( |
122
|
|
|
sprintf('Unable to complete SSL/TLS handshake: %s', $this->errStr), |
123
|
|
|
$this->errno |
124
|
|
|
)); |
125
|
|
|
} else { |
126
|
|
|
// need more data, will retry |
127
|
|
|
} |
128
|
1 |
|
} |
129
|
|
|
|
130
|
|
|
public function handleError($errNo, $errStr) |
131
|
|
|
{ |
132
|
|
|
$this->errstr = str_replace(["\r", "\n"], ' ', $errStr); |
|
|
|
|
133
|
|
|
$this->errno = $errNo; |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.