Skip to content

Commit

Permalink
Merge pull request #43 from clue-labs/uri
Browse files Browse the repository at this point in the history
Apply protocol version and username/password auth from SOCKS URI
  • Loading branch information
clue authored Nov 25, 2016
2 parents dcaf78c + 51f3e3b commit 7922540
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 10 deletions.
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,14 @@ In general this library automatically switches to higher protocol versions
when needed, but tries to keep things simple otherwise and sticks to lower
protocol versions when possible.

If want to explicitly set the protocol version, use the supported values `4`, `4a` or `5`:
If want to explicitly set the protocol version, use the supported values `4`, `4a` or `5`
as part of the SOCKS URI:

```php
$client = new Client('socks4a://127.0.0.1', $loop);
```

You can also explicitly set the protocol version later:

```PHP
$client->setProtocolVersion('4a');
Expand Down Expand Up @@ -266,7 +273,28 @@ Authentication is only supported by protocol version 5 (SOCKS5),
so setting authentication on the `Client` enforces communication with protocol
version 5 and complains if you have explicitly set anything else.

```PHP
You can simply pass the authentication information as part of the SOCKS URI:

```php
$client = new Client('username:password@127.0.0.1', $loop);
```

Note that both the username and password must be percent-encoded if they contain
special characters:

```php
$user = 'he:llo';
$pass = 'p@ss';

$client = new Client(
rawurlencode($user) . ':' . rawurlencode($pass) . '@127.0.0.1',
$loop
);
```

You can also explicitly set the authentication information later:

```php
$client->setAuth('username', 'password');
```

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"react/promise": "^2.1 || ^1.2"
},
"require-dev": {
"clue/socks-server": "^0.5",
"clue/socks-server": "^0.5.1",
"clue/block-react": "^1.1"
}
}
2 changes: 0 additions & 2 deletions examples/01-http.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
$client = new Client('127.0.0.1:' . $port, $loop);
$client->setTimeout(3.0);
$client->setResolveLocal(false);
// $client->setProtocolVersion(5);
// $client->setAuth('test','test');

echo 'Demo SOCKS client connecting to SOCKS server 127.0.0.1:' . $port . PHP_EOL;
echo 'Not already running a SOCKS server? Try this: ssh -D ' . $port . ' localhost' . PHP_EOL;
Expand Down
2 changes: 0 additions & 2 deletions examples/02-https.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
$client = new Client('127.0.0.1:' . $port, $loop);
$client->setTimeout(3.0);
$client->setResolveLocal(false);
// $client->setProtocolVersion(5);
// $client->setAuth('test','test');

echo 'Demo SOCKS client connecting to SOCKS server 127.0.0.1:' . $port . PHP_EOL;
echo 'Not already running a SOCKS server? Try this: ssh -D ' . $port . ' localhost' . PHP_EOL;
Expand Down
9 changes: 9 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ public function __construct($socksUri, LoopInterface $loop, ConnectorInterface $
$connector = new DnsConnector(new TcpConnector($loop), $resolver);
}

if ($parts['scheme'] !== 'socks') {
$this->setProtocolVersion(substr($parts['scheme'], 5));
}

if (isset($parts['user']) || isset($parts['pass'])) {
$parts += array('user' => '', 'pass' => '');
$this->setAuth(rawurldecode($parts['user']), rawurldecode($parts['pass']));
}

$this->loop = $loop;
$this->socksHost = $parts['host'];
$this->socksPort = $parts['port'];
Expand Down
13 changes: 13 additions & 0 deletions tests/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ public function testValidAuthVersion()
$this->assertNull($this->client->setProtocolVersion(5));
}

public function testValidAuthAndVersionFromUri()
{
$this->client = new Client('socks5://username:password@127.0.0.1:9050', $this->loop);
}

/**
* @expectedException UnexpectedValueException
*/
Expand All @@ -77,6 +82,14 @@ public function testInvalidCanNotSetAuthenticationForSocks4()
$this->client->setAuth('username', 'password');
}

/**
* @expectedException UnexpectedValueException
*/
public function testInvalidCanNotSetAuthenticationForSocks4Uri()
{
$this->client = new Client('socks4://username:password@127.0.0.1:9050', $this->loop);
}

public function testUnsetAuth()
{
// unset auth even if it's not set is valid
Expand Down
34 changes: 31 additions & 3 deletions tests/FunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ class FunctionalTest extends TestCase
private $loop;
private $client;
private $server;
private $port;

public function setUp()
{
$this->loop = React\EventLoop\Factory::create();

$socket = $this->createSocketServer();
$port = $socket->getPort();
$this->assertNotEquals(0, $port);
$this->port = $socket->getPort();
$this->assertNotEquals(0, $this->port);

$this->server = new Server($this->loop, $socket);
$this->client = new Client('127.0.0.1:' . $port, $this->loop);
$this->client = new Client('127.0.0.1:' . $this->port, $this->loop);
}

public function testConnection()
Expand Down Expand Up @@ -68,6 +69,33 @@ public function testConnectionAuthentication()
$this->assertResolveStream($this->client->createConnection('www.google.com', 80));
}

public function testConnectionAuthenticationFromUri()
{
$this->server->setAuthArray(array('name' => 'pass'));

$this->client = new Client('name:pass@127.0.0.1:' . $this->port, $this->loop);

$this->assertResolveStream($this->client->createConnection('www.google.com', 80));
}

public function testConnectionAuthenticationFromUriEncoded()
{
$this->server->setAuthArray(array('name' => 'p@ss:w0rd'));

$this->client = new Client(rawurlencode('name') . ':' . rawurlencode('p@ss:w0rd') . '@127.0.0.1:' . $this->port, $this->loop);

$this->assertResolveStream($this->client->createConnection('www.google.com', 80));
}

public function testConnectionAuthenticationFromUriWithOnlyUserAndNoPassword()
{
$this->server->setAuthArray(array('empty' => ''));

$this->client = new Client('empty@127.0.0.1:' . $this->port, $this->loop);

$this->assertResolveStream($this->client->createConnection('www.google.com', 80));
}

public function testConnectionAuthenticationUnused()
{
$this->client->setAuth('name', 'pass');
Expand Down

0 comments on commit 7922540

Please sign in to comment.