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
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.