1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of Dedipanel project |
5
|
|
|
* |
6
|
|
|
* (c) 2010-2015 Dedipanel <http://www.dedicated-panel.net> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace DP\GameServer\SteamServerBundle\SteamQuery; |
13
|
|
|
|
14
|
|
|
use DP\GameServer\GameServerBundle\Socket\Socket; |
15
|
|
|
use DP\GameServer\GameServerBundle\Socket\Packet; |
16
|
|
|
use DP\GameServer\GameServerBundle\Socket\PacketCollection; |
17
|
|
|
use DP\GameServer\GameServerBundle\Socket\Exception\ConnectionFailedException; |
18
|
|
|
use DP\GameServer\GameServerBundle\Socket\Exception\NotConnectedException; |
19
|
|
|
use DP\GameServer\GameServerBundle\Socket\Exception\RecvTimeoutException; |
20
|
|
|
use DP\GameServer\SteamServerBundle\SteamQuery\Exception\ServerTimeoutException; |
21
|
|
|
use DP\GameServer\GameServerBundle\Query\RconInterface; |
22
|
|
|
|
23
|
|
|
class SourceRcon implements RconInterface |
24
|
|
|
{ |
25
|
|
|
protected $socket; |
26
|
|
|
protected $packetFactory; |
27
|
|
|
protected $rconPassword; |
28
|
|
|
protected $authenticated = null; |
29
|
|
|
protected $fullyConstructed = false; |
30
|
|
|
|
31
|
|
View Code Duplication |
public function __construct($container, $host, $port, $rconPassword) |
|
|
|
|
32
|
|
|
{ |
33
|
|
|
/*$callback = function(Packet $packet, Socket $socket) { |
34
|
|
|
if (is_null($packet) || $packet->isEmpty()) return false; |
35
|
|
|
|
36
|
|
|
$remaining = $packet->getLong(false); |
37
|
|
|
$packet->setPos(4); |
38
|
|
|
$id = $packet->getLong(false); |
39
|
|
|
|
40
|
|
|
if ($remaining > 0) { |
41
|
|
|
$splittedPackets = new PacketCollection(); |
42
|
|
|
$respId = null; |
43
|
|
|
|
44
|
|
|
do { |
45
|
|
|
if (!$respId) { |
46
|
|
|
$respId = $id; |
47
|
|
|
} |
48
|
|
|
elseif ($respId != $id) { |
49
|
|
|
$packet = $socket->recv(false); |
50
|
|
|
continue; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
$splittedPackets->add($packet->rewind()); |
54
|
|
|
$packet = $socket->recv(false, $remaining); |
55
|
|
|
$remaining -= $packet->getLength(); |
56
|
|
|
} while ($remaining > 0); |
57
|
|
|
|
58
|
|
|
return $splittedPackets->reassemble( |
59
|
|
|
function(Packet $bigPacket, Packet $packet) { |
60
|
|
|
if ($bigPacket->isEmpty()) { |
61
|
|
|
$bigPacket->addContent($packet->getContent()); |
62
|
|
|
$bigPacket->setPos($packet->getLength()-2); |
63
|
|
|
} |
64
|
|
|
else { |
65
|
|
|
$bigPacket->addContent($packet); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
return $bigPacket; |
69
|
|
|
} |
70
|
|
|
)->rewind(); |
71
|
|
|
} |
72
|
|
|
else { |
73
|
|
|
return $packet; |
74
|
|
|
} |
75
|
|
|
};*/ |
76
|
|
|
$callbacks = array(); |
77
|
|
|
// Permet de déterminé s'il s'agit d'une réponse multi-paquet |
78
|
|
|
$callbacks[Socket::MULTI_DETECTOR] = function(Packet $packet) { |
79
|
|
|
if (is_null($packet) || $packet->isEmpty()) return false; |
80
|
|
|
|
81
|
|
|
// Récupération de la longueur de la réponse |
82
|
|
|
$len = $packet->getLong(false); |
83
|
|
|
// Il faut rajouter 4 puisque la taille récupéré correspondant |
84
|
|
|
// A la taille du paquet sans l'entier contenant la taille |
85
|
|
|
$remaining = $len - $packet->getLength() + 4; |
86
|
|
|
|
87
|
|
|
if ($remaining > 0) { |
88
|
|
|
return true; |
89
|
|
|
} |
90
|
|
|
else { |
91
|
|
|
return false; |
92
|
|
|
} |
93
|
|
|
}; |
94
|
|
|
// Permet de récupérer les différents paquets qui composent une réponse |
95
|
|
|
$callbacks[Socket::MULTI_RECEIVER] = function(Packet $packet, Socket $socket) { |
96
|
|
|
if (is_null($packet) || $packet->isEmpty()) return false; |
97
|
|
|
|
98
|
|
|
// Récupération de la longueur de la réponse |
99
|
|
|
$len = $packet->getLong(false); |
100
|
|
|
// Il faut rajouter 4 puisque la taille récupéré correspondant |
101
|
|
|
// A la taille du paquet sans l'entier contenant la taille |
102
|
|
|
$remaining = $len - $packet->getLength() + 4; |
103
|
|
|
|
104
|
|
|
while ($remaining > 0) { |
105
|
|
|
// Récupération du prochain paquet et calcul de la taille restante à récupérer |
106
|
|
|
$newPacket = $socket->recv(false, $remaining); |
107
|
|
|
$remaining = $remaining - $newPacket->getLength(); |
108
|
|
|
|
109
|
|
|
// Ajout du paquet au paquet originel |
110
|
|
|
$packet->setPos($packet->getLength() - 2); |
111
|
|
|
$packet->addContent($newPacket->getContent()); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
return $packet; |
115
|
|
|
}; |
116
|
|
|
|
117
|
|
|
$this->rconPassword = $rconPassword; |
118
|
|
|
$this->socket = $container->get('socket')->getTCPSocket($host, $port, $callbacks); |
119
|
|
|
$this->packetFactory = $container->get('packet.factory.rcon.source'); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Permet de ne finaliser la création du rcon qu'en cas d'utilisation de celui-ci |
124
|
|
|
* Permet ainsi à la classe d'être instancié un certains nombre de fois sans pour autant être utilisé |
125
|
|
|
* (utile pour le QueryInjector) |
126
|
|
|
*/ |
127
|
|
|
protected function fullConstruct() |
128
|
|
|
{ |
129
|
|
|
$this->fullyConstructed = true; |
130
|
|
|
|
131
|
|
|
try { |
132
|
|
|
$this->socket->connect(); |
133
|
|
|
$this->auth(); |
134
|
|
|
} |
135
|
|
|
catch (ConnectionFailedException $e) {} |
136
|
|
|
} |
137
|
|
|
|
138
|
|
View Code Duplication |
private function auth() |
|
|
|
|
139
|
|
|
{ |
140
|
|
|
if (!$this->fullyConstructed) { |
141
|
|
|
$this->fullConstruct(); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
if ($this->authenticated == null) { |
145
|
|
|
$id = null; |
146
|
|
|
$packet = $this->packetFactory->getAuthPacket($id, $this->rconPassword); |
147
|
|
|
|
148
|
|
|
$this->socket->send($packet); |
149
|
|
|
$resp = $this->socket->recv(false); |
150
|
|
|
|
151
|
|
|
if ($resp->isEmpty()) { |
152
|
|
|
$this->authenticated = false; |
153
|
|
|
return; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
$infos = $resp->extract(array( |
157
|
|
|
'size' => 'long', |
158
|
|
|
'id' => 'long', |
159
|
|
|
'type' => 'long', |
160
|
|
|
's1' => 'string', |
161
|
|
|
's2' => 'string' |
162
|
|
|
)); |
163
|
|
|
|
164
|
|
|
if ($infos['type'] == $this->packetFactory->SERVER_RESPONSE_VALUE) { |
165
|
|
|
$resp = $this->socket->recv(false); |
166
|
|
|
$infos = $resp->extract(array( |
167
|
|
|
'size' => 'long', |
168
|
|
|
'id' => 'long', |
169
|
|
|
'type' => 'long', |
170
|
|
|
's1' => 'string', |
171
|
|
|
's2' => 'string' |
172
|
|
|
)); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
if ($infos['id'] == $id && |
176
|
|
|
$infos['type'] == $this->packetFactory->SERVER_AUTH_RESPONSE) { |
177
|
|
|
$this->authenticated = true; |
178
|
|
|
} |
179
|
|
|
else { |
180
|
|
|
$this->authenticated = false; |
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
public function sendCmd($cmd) |
186
|
|
|
{ |
187
|
|
|
if (!$this->fullyConstructed) { |
188
|
|
|
$this->fullConstruct(); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
if ($this->authenticated) { |
192
|
|
|
$id = null; |
193
|
|
|
$packet = $this->packetFactory->getCmdPacket($id, $cmd); |
194
|
|
|
|
195
|
|
|
$this->socket->send($packet); |
196
|
|
|
$resp = $this->recv(); |
197
|
|
|
|
198
|
|
|
if ($resp == null) { |
199
|
|
|
return false; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
$resp = $resp->rewind()->extract(array( |
203
|
|
|
'size' => 'long', |
204
|
|
|
'id' => 'long', |
205
|
|
|
'type' => 'long', |
206
|
|
|
'body' => 'string', |
207
|
|
|
)); |
208
|
|
|
|
209
|
|
View Code Duplication |
if ($resp['id'] != $id || $resp['type'] != $this->packetFactory->SERVER_RESPONSE_VALUE) { |
|
|
|
|
210
|
|
|
return false; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
return $resp['body']; |
214
|
|
|
} |
215
|
|
|
else { |
216
|
|
|
return false; |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Get mulitple packets from socket recv method |
222
|
|
|
* Return reassembled packets if there is reponses |
223
|
|
|
* Or return null if there is a RecvTimeoutException catched and any response recovered |
224
|
|
|
* before any content has been received. |
225
|
|
|
* |
226
|
|
|
* @return \DP\GameServer\GameServerBundle\Socket\Packet|null |
227
|
|
|
*/ |
228
|
|
View Code Duplication |
private function recv() |
|
|
|
|
229
|
|
|
{ |
230
|
|
|
$packets = new PacketCollection(); |
231
|
|
|
|
232
|
|
|
do { |
233
|
|
|
try { |
234
|
|
|
$resp = $this->socket->recv(); |
235
|
|
|
$packets->add($resp->rewind()); |
236
|
|
|
} |
237
|
|
|
catch (RecvTimeoutException $e) { |
238
|
|
|
$resp = null; |
239
|
|
|
} |
240
|
|
|
} while ($resp != null); |
241
|
|
|
|
242
|
|
|
// Verif que tous les paquets ont bien été reçus |
243
|
|
|
// Si c'est le cas on renvoie les données reçus, sinon on renvoie rien |
244
|
|
|
if ($packets->count() > 0 && $this->verifyAllPacketsReceived()) { |
245
|
|
|
$packetFactory = $this->packetFactory; |
246
|
|
|
|
247
|
|
|
return $packets->reassemble(function (Packet $bigPacket, Packet $packet) use ($packetFactory) { |
248
|
|
|
if ($bigPacket->isEmpty()) { |
249
|
|
|
$bigPacket->addContent($packet); |
250
|
|
|
} |
251
|
|
|
else { |
252
|
|
|
$bigPacket->rewind(); |
253
|
|
|
|
254
|
|
|
// Ajout de la taille du packet au bigPacket |
255
|
|
|
$packetSize = $packet->getLong(false); |
256
|
|
|
$bigPacketSize = $bigPacket->getLong(); |
257
|
|
|
$newSize = $packetSize + $bigPacketSize; |
258
|
|
|
|
259
|
|
|
$bigPacket->rewind()->addContent( |
260
|
|
|
$packetFactory->transformLong($newSize) |
261
|
|
|
); |
262
|
|
|
|
263
|
|
|
// Ajout du contenu au bigPacket |
264
|
|
|
$bigPacket->setPos($bigPacket->getLength()-4)->addContent( |
265
|
|
|
substr($packet->setPos(12)->getString(), 0, -4) |
266
|
|
|
); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
return $bigPacket; |
270
|
|
|
}); |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
return null; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
public function verifyAllPacketsReceived() |
277
|
|
|
{ |
278
|
|
|
$id = null; |
279
|
|
|
$packet = $this->packetFactory->getEmptyResponsePacket($id); |
280
|
|
|
|
281
|
|
|
$this->socket->send($packet); |
282
|
|
|
$resp = $this->socket->recv(false); |
283
|
|
|
|
284
|
|
|
if ($resp == $packet) { |
285
|
|
|
$resp = $this->socket->recv(false); |
286
|
|
|
$resp->setPos(13); |
287
|
|
|
|
288
|
|
|
if ($resp->getByte() == 1) { |
289
|
|
|
return true; |
290
|
|
|
} |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
return false; |
294
|
|
|
|
295
|
|
|
} |
296
|
|
|
} |
297
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.