Skip to content

Commit d537361

Browse files
committed
Validate target URI so hostname can not contain excessive URI components
1 parent f176425 commit d537361

2 files changed

Lines changed: 56 additions & 4 deletions

File tree

src/Server.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,12 @@
77
use React\Promise;
88
use React\Promise\Deferred;
99
use React\Promise\PromiseInterface;
10-
use React\Promise\CancellablePromiseInterface;
1110
use React\Socket\ConnectorInterface;
1211
use React\Socket\Connector;
1312
use React\Socket\ConnectionInterface;
1413
use React\EventLoop\LoopInterface;
1514
use \UnexpectedValueException;
1615
use \InvalidArgumentException;
17-
use \RuntimeException;
1816
use \Exception;
1917

2018
class Server extends EventEmitter
@@ -326,6 +324,12 @@ public function connectTarget(ConnectionInterface $stream, array $target)
326324
}
327325
$uri = $uri . ':' . $target[1];
328326

327+
// validate URI so a string hostname can not pass excessive URI parts
328+
$parts = parse_url('tcp://' . $uri);
329+
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || count($parts) !== 3) {
330+
return Promise\reject(new InvalidArgumentException('Invalid target URI given'));
331+
}
332+
329333
$stream->emit('target', $target);
330334
$that = $this;
331335
$connecting = $this->connector->connect($uri);

tests/ServerTest.php

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public function testHandleSocksConnectionWillEndOnInvalidData()
152152
$connection->emit('data', array('asdasdasdasdasd'));
153153
}
154154

155-
public function testHandleSocks4ConnectionWillEstablishOutgoingConnection()
155+
public function testHandleSocks4ConnectionWithIpv4WillEstablishOutgoingConnection()
156156
{
157157
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock();
158158

@@ -165,7 +165,31 @@ public function testHandleSocks4ConnectionWillEstablishOutgoingConnection()
165165
$connection->emit('data', array("\x04\x01" . "\x00\x50" . pack('N', ip2long('127.0.0.1')) . "\x00"));
166166
}
167167

168-
public function testHandleSocks5ConnectionWillEstablishOutgoingConnection()
168+
public function testHandleSocks4aConnectionWithHostnameWillEstablishOutgoingConnection()
169+
{
170+
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock();
171+
172+
$promise = new Promise(function () { });
173+
174+
$this->connector->expects($this->once())->method('connect')->with('example.com:80')->willReturn($promise);
175+
176+
$this->server->onConnection($connection);
177+
178+
$connection->emit('data', array("\x04\x01" . "\x00\x50" . "\x00\x00\x00\x01" . "\x00" . "example.com" . "\x00"));
179+
}
180+
181+
public function testHandleSocks4aConnectionWithInvalidHostnameWillNotEstablishOutgoingConnection()
182+
{
183+
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock();
184+
185+
$this->connector->expects($this->never())->method('connect');
186+
187+
$this->server->onConnection($connection);
188+
189+
$connection->emit('data', array("\x04\x01" . "\x00\x50" . "\x00\x00\x00\x01" . "\x00" . "tls://example.com:80?" . "\x00"));
190+
}
191+
192+
public function testHandleSocks5ConnectionWithIpv4WillEstablishOutgoingConnection()
169193
{
170194
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock();
171195

@@ -191,6 +215,30 @@ public function testHandleSocks5ConnectionWithIpv6WillEstablishOutgoingConnectio
191215
$connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x04" . inet_pton('::1') . "\x00\x50"));
192216
}
193217

218+
public function testHandleSocks5ConnectionWithHostnameWillEstablishOutgoingConnection()
219+
{
220+
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock();
221+
222+
$promise = new Promise(function () { });
223+
224+
$this->connector->expects($this->once())->method('connect')->with('example.com:80')->willReturn($promise);
225+
226+
$this->server->onConnection($connection);
227+
228+
$connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x03\x0B" . "example.com" . "\x00\x50"));
229+
}
230+
231+
public function testHandleSocks5ConnectionWithInvalidHostnameWillNotEstablishOutgoingConnection()
232+
{
233+
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end', 'write'))->getMock();
234+
235+
$this->connector->expects($this->never())->method('connect');
236+
237+
$this->server->onConnection($connection);
238+
239+
$connection->emit('data', array("\x05\x01\x00" . "\x05\x01\x00\x03\x15" . "tls://example.com:80?" . "\x00\x50"));
240+
}
241+
194242
public function testHandleSocksConnectionWillCancelOutputConnectionIfIncomingCloses()
195243
{
196244
$connection = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('pause', 'end'))->getMock();

0 commit comments

Comments
 (0)