Skip to content

Pass connector into Client instead of loop, remove unneeded deps #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 21 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ to google.com via a local SOCKS proxy server:

```php
$loop = React\EventLoop\Factory::create();
$client = new Client('127.0.0.1:9050', $loop);
$client = new Client('127.0.0.1:9050', new TcpConnector($loop));

$client->create('www.google.com', 80)->then(function (Stream $stream) {
$stream->write("GET / HTTP/1.0\r\n\r\n");
Expand Down Expand Up @@ -90,18 +90,21 @@ $connector->create('google.com', 443)->then(
### Client

The `Client` is responsible for communication with your SOCKS server instance.
It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage).
It accepts a SOCKS server URI like this:
Its constructor simply accepts an SOCKS proxy URI and a connector used to
connect to the SOCKS proxy server address.

In its most simple form, you can simply pass React's
[`TcpConnector`](https://github.com/reactphp/socket-client#tcpconnector)
like this:

```php
$loop = \React\EventLoop\Factory::create();
$client = new Client('127.0.0.1:1080', $loop);
```
$connector = new React\SocketClient\TcpConnector($loop);
$client = new Client('127.0.0.1:1080', $connector);

You can omit the port if you're using the default SOCKS port 1080:

```php
$client = new Client('127.0.0.1', $loop);
$client = new Client('127.0.0.1', $connector);
```

If you need custom connector settings (DNS resolution, timeouts etc.), you can explicitly pass a
Expand All @@ -119,7 +122,7 @@ $connector = new DnsConnector(
$resolver
);

$client = new Client('my-socks-server.local:1080', $loop, $connector);
$client = new Client('my-socks-server.local:1080', $connector);
```

This is the main class in this package.
Expand All @@ -130,7 +133,7 @@ higher-level component:

```diff
- $client = new SomeClient($connector);
+ $proxy = new Client('127.0.0.1:9050', $loop);
+ $proxy = new Client('127.0.0.1:9050', $connector);
+ $client = new SomeClient($proxy);
```

Expand Down Expand Up @@ -283,7 +286,7 @@ If want to explicitly set the protocol version, use the supported values URI
schemes `socks4`, `socks4a` or `socks5` as part of the SOCKS URI:

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

As seen above, both SOCKS5 and SOCKS4a support remote and local DNS resolution.
Expand Down Expand Up @@ -316,7 +319,7 @@ using SOCKS4), you can use the following code:

```php
// usual client setup
$client = new Client($uri, $loop);
$client = new Client($uri, $connector);

// set up DNS server to use (Google's public DNS here)
$factory = new React\Dns\Resolver\Factory();
Expand Down Expand Up @@ -365,7 +368,7 @@ so this methods should not be used on a network where you have to worry about ea
You can simply pass the authentication information as part of the SOCKS URI:

```php
$client = new Client('username:[email protected]', $loop);
$client = new Client('username:[email protected]', $connector);
```

Note that both the username and password must be percent-encoded if they contain
Expand All @@ -377,7 +380,7 @@ $pass = 'p@ss';

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

Expand All @@ -387,7 +390,7 @@ version 5 and complains if you have explicitly set anything else:

```php
// throws InvalidArgumentException
new Client('socks4://user:[email protected]', $loop);
new Client('socks4://user:[email protected]', $connector);
```

#### Proxy chaining
Expand Down Expand Up @@ -416,8 +419,8 @@ SOCKS connector from another SOCKS client like this:
// which in turn then uses MiddlemanSocksServer.
// this creates a TCP/IP connection to MiddlemanSocksServer, which then connects
// to TargetSocksServer, which then connects to the TargetHost
$middle = new Client($addressMiddle, $loop, new TcpConnector($loop));
$target = new Client($addressTarget, $loop, $middle);
$middle = new Client($addressMiddle, new TcpConnector($loop));
$target = new Client($addressTarget, $middle);

$ssl = new React\SocketClient\SecureConnector($target, $loop);

Expand Down Expand Up @@ -477,24 +480,6 @@ as usual.
> Also note how connection timeout is in fact entirely handled outside of this
SOCKS client implementation.

### Connector

The `Connector` instance can be used to establish TCP connections to remote hosts.
Each instance can be used to establish any number of TCP connections.

It implements React's `ConnectorInterface` which only provides a single
`create()` method.

The `create($host, $port)` method can be used to establish a TCP
connection to the given target host and port.

It functions as an [adapter](https://en.wikipedia.org/wiki/Adapter_pattern):
Many higher-level networking protocols build on top of TCP. It you're dealing
with one such client implementation, it probably uses/accepts an instance
implementing React's `ConnectorInterface` (and usually its default `Connector`
instance). In this case you can also pass this `Connector` instance instead
to make this client implementation SOCKS-aware. That's it.

## Servers

### Using a PHP SOCKS server
Expand All @@ -514,7 +499,7 @@ a `local "dynamic" application-level port forwarding`) by issuing:
`$ ssh -D 9050 ssh-server`

```PHP
$client = new Client('127.0.0.1:9050', $loop);
$client = new Client('127.0.0.1:9050', $connector);
```

### Using the Tor (anonymity network) to tunnel SOCKS connections
Expand All @@ -529,8 +514,7 @@ Also, Tor provides hidden services through an `.onion` pseudo top-level domain
which have to be resolved by Tor.

```PHP
$client = new Client('127.0.0.1:9050', $loop);
$client->setResolveLocal(false);
$client = new Client('127.0.0.1:9050', $connector);
```

## Install
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
},
"require": {
"php": ">=5.3",
"react/event-loop": "0.3.*|0.4.*",
"react/socket-client": "^0.5.1",
"react/dns": "0.3.*|0.4.*",
"react/stream": "0.3.*|0.4.*",
"react/promise": "^2.1 || ^1.2"
},
"require-dev": {
"react/event-loop": "0.3.*|0.4.*",
"react/dns": "0.3.*|0.4.*",
"clue/socks-server": "^0.5.1",
"clue/block-react": "^1.1"
}
Expand Down
3 changes: 2 additions & 1 deletion examples/01-http.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use React\Stream\Stream;
use Clue\React\Socks\Client;
use React\SocketClient\TcpConnector;
use React\SocketClient\TimeoutConnector;

include_once __DIR__.'/../vendor/autoload.php';
Expand All @@ -10,7 +11,7 @@

$loop = React\EventLoop\Factory::create();

$client = new Client('127.0.0.1:' . $port, $loop);
$client = new Client('127.0.0.1:' . $port, new TcpConnector($loop));

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
5 changes: 3 additions & 2 deletions examples/02-https.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

use React\Stream\Stream;
use Clue\React\Socks\Client;
use React\SocketClient\TimeoutConnector;
use React\SocketClient\TcpConnector;
use React\SocketClient\SecureConnector;
use React\SocketClient\TimeoutConnector;

include_once __DIR__.'/../vendor/autoload.php';

$port = isset($argv[1]) ? $argv[1] : 9050;

$loop = React\EventLoop\Factory::create();

$client = new Client('127.0.0.1:' . $port, $loop);
$client = new Client('127.0.0.1:' . $port, new TcpConnector($loop));

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
4 changes: 2 additions & 2 deletions examples/03-proxy-chaining.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
// https via the proxy chain "foo -> bar -> target"
// please note how the client uses bar (not foo!), which in turn then uses foo
// this creates a TCP/IP connection to foo, which then connects to bar, which then connects to the target
$foo = new Client('127.0.0.1:' . $first, $loop, new TcpConnector($loop));
$bar = new Client('127.0.0.1:' . $second, $loop, $foo);
$foo = new Client('127.0.0.1:' . $first, new TcpConnector($loop));
$bar = new Client('127.0.0.1:' . $second, $foo);

$ssl = new SecureConnector($bar, $loop);

Expand Down
3 changes: 2 additions & 1 deletion examples/04-local-dns.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use React\Stream\Stream;
use Clue\React\Socks\Client;
use React\Dns\Resolver\Factory;
use React\SocketClient\TcpConnector;
use React\SocketClient\DnsConnector;
use React\SocketClient\SecureConnector;
use React\SocketClient\TimeoutConnector;
Expand All @@ -13,7 +14,7 @@

$loop = React\EventLoop\Factory::create();

$client = new Client('127.0.0.1:' . $port, $loop);
$client = new Client('127.0.0.1:' . $port, new TcpConnector($loop));

// set up DNS server to use (Google's public DNS)
$factory = new Factory();
Expand Down
29 changes: 7 additions & 22 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,14 @@
namespace Clue\React\Socks;

use React\Promise;
use React\Promise\CancellablePromiseInterface;
use React\Promise\PromiseInterface;
use React\Promise\Deferred;
use React\Dns\Resolver\Factory as DnsFactory;
use React\Dns\Resolver\Resolver;
use React\Stream\Stream;
use React\EventLoop\LoopInterface;
use React\SocketClient\ConnectorInterface;
use React\SocketClient\DnsConnector;
use React\SocketClient\TcpConnector;
use Clue\React\Socks\Connector;
use \Exception;
use \InvalidArgumentException;
use \UnexpectedValueException;
use RuntimeException;
use React\Promise\CancellablePromiseInterface;
use React\Promise\PromiseInterface;

class Client implements ConnectorInterface
{
Expand All @@ -35,7 +28,7 @@ class Client implements ConnectorInterface

private $auth = null;

public function __construct($socksUri, LoopInterface $loop, ConnectorInterface $connector = null, Resolver $resolver = null)
public function __construct($socksUri, ConnectorInterface $connector)
{
// assume default scheme if none is given
if (strpos($socksUri, '://') === false) {
Expand All @@ -53,15 +46,6 @@ public function __construct($socksUri, LoopInterface $loop, ConnectorInterface $
$parts['port'] = 1080;
}

if ($resolver === null) {
// default to using Google's public DNS server
$dnsResolverFactory = new DnsFactory();
$resolver = $dnsResolverFactory->createCached('8.8.8.8', $loop);
}
if ($connector === null) {
$connector = new DnsConnector(new TcpConnector($loop), $resolver);
}

// user or password in URI => SOCKS5 authentication
if (isset($parts['user']) || isset($parts['pass'])) {
if ($parts['scheme'] === 'socks') {
Expand Down Expand Up @@ -130,7 +114,7 @@ public function create($host, $port)
return Promise\reject(new InvalidArgumentException('Requires an IPv4 address for SOCKS4'));
}

if (strlen($host) > 255 || $port > 65535 || $port < 0) {
if (strlen($host) > 255 || $port > 65535 || $port < 0 || (string)$port !== (string)(int)$port) {
return Promise\reject(new InvalidArgumentException('Invalid target specified'));
}

Expand Down Expand Up @@ -181,6 +165,7 @@ function ($_, $reject) use ($promise) {
* @param string $host
* @param int $port
* @return Promise Promise<stream, Exception>
* @internal
*/
public function handleConnectedSocks(Stream $stream, $host, $port)
{
Expand Down Expand Up @@ -224,7 +209,7 @@ function ($error) use ($stream) {
return $deferred->promise();
}

protected function handleSocks4(Stream $stream, $host, $port, StreamReader $reader)
private function handleSocks4(Stream $stream, $host, $port, StreamReader $reader)
{
// do not resolve hostname. only try to convert to IP
$ip = ip2long($host);
Expand All @@ -251,7 +236,7 @@ protected function handleSocks4(Stream $stream, $host, $port, StreamReader $read
});
}

protected function handleSocks5(Stream $stream, $host, $port, StreamReader $reader)
private function handleSocks5(Stream $stream, $host, $port, StreamReader $reader)
{
// protocol version 5
$data = pack('C', 0x05);
Expand Down
Loading