Permalink
Browse files

Support proxy forwarding via Unix domain sockets (UDS) paths

  • Loading branch information...
clue committed Mar 10, 2018
1 parent 571e142 commit 8b7391e0611ab7272944d8917c36d633d7f0ebd4
Showing with 68 additions and 26 deletions.
  1. +2 −1 README.md
  2. +2 −2 composer.json
  3. +16 −16 composer.lock
  4. +2 −2 leproxy.php
  5. +26 −2 src/ConnectorFactory.php
  6. +15 −1 tests/ConnectorFactoryTest.php
  7. +5 −2 tests/acceptance.sh
View
@@ -157,7 +157,8 @@ $ php leproxy.php --proxy=socks://user:pass@127.0.0.1:8080
```
> The upstream proxy server URI MUST contain a hostname or IP and SHOULD include
a port unless the proxy happens to use default port `8080`.
a port unless the proxy happens to use default port `8080` (or you can use a
Unix domain socket path).
If no scheme is given, the `http://` scheme will be assumed.
If no port is given, port `8080` will be assumed regardless of scheme.
The `http://` and `socks[5]://` schemes support optional username/password
View
@@ -15,8 +15,8 @@
"php": ">=5.4",
"clue/commander": "^1.3",
"clue/connection-manager-extra": "^1.1",
"clue/http-proxy-react": "^1.2",
"clue/socks-react": "^0.8.5",
"clue/http-proxy-react": "^1.3",
"clue/socks-react": "^0.8.7",
"react/http": "^0.8.3",
"react/http-client": "^0.5.9",
"react/socket": "^1.0"
View

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View
@@ -94,8 +94,8 @@
forwarded to (proxy chaining).
Any number of upstream proxies can be given.
Each address consists of full URI which may contain a scheme, username
and password, host and port. Default scheme is `http://`, default port
is `8080` for all schemes.
and password, host and port (or Unix domain socket path). Default scheme
is `http://`, default port is `8080` for all schemes.
--no-log
By default, LeProxy logs all connection attempts to STDOUT for
View
@@ -26,6 +26,30 @@ public static function coerceProxyUri($uri)
if ($uri === '') {
throw new \InvalidArgumentException('Upstream proxy URI must not be empty');
}
// match Unix domain sockets (UDS) paths like "[user:pass@]/path" or
// "http+unix://[user:pass@]/path" or "socks[5|4|4a]+unix://[user:pass@]/path"
if (preg_match('/^(?:(?<scheme>socks(?:5|4|4a)?|http)\+unix:\/\/)?(?<auth>[^@]*@)?(?<path>.?.?\/.*)$/', $uri, $match)) {
// apply default scheme http+unix://
if ($match['scheme'] === '') {
$match['scheme'] = 'http';
}
if ($match['auth'] !== '') {
// explicitly replace socks:// with socks5://
if ($match['scheme'] === 'socks') {
$match['scheme'] = 'socks5';
}
// only http:// and socks5:// support authentication
if ($match['scheme'] !== 'http' && $match['scheme'] !== 'socks5') {
throw new \InvalidArgumentException('Upstream proxy scheme "' . $match['scheme'] . '+unix://" does not support username/password authentication');
}
}
return $match['scheme'] . '+unix://' . (isset($match['auth']) ? $match['auth'] : '') . $match['path'];
}
if (strpos($uri, '://') === false) {
$uri = 'http://' . $uri;
}
@@ -126,7 +150,7 @@ public static function isIpLocal($ip)
* Creates a new connector for the given proxy chain (list of proxy servers)
*
* The proxy chain may contain any number of proxy server URIs.
* Each proxy server URI may use `http://` or `socks[5|4a|4]://` URI scheme,
* Each proxy server URI may use `http[+unix]://` or `socks[5|4a|4][+unix]://` URI scheme,
* with `http://` being the default if none is given.
*
* The proxy chain may be empty, in which case the connection will be direct.
@@ -144,7 +168,7 @@ public static function createConnectorChain(array $path, LoopInterface $loop)
));
foreach ($path as $proxy) {
if (strpos($proxy, '://') === false || strpos($proxy, 'http://') === 0) {
if (strpos($proxy, '://') === false || strpos($proxy, 'http://') === 0 || strpos($proxy, 'http+unix://') === 0) {
$connector = new HttpClient($proxy, $connector);
} else {
$connector = new SocksClient($proxy, $connector);
@@ -13,6 +13,17 @@ public function testCoerceProxyUri()
'socks://user:pass@host' => 'socks5://user:pass@host:8080',
'user@host' => 'http://user:@host:8080',
'socks4a://10.20.30.40:5060' => 'socks4a://10.20.30.40:5060',
'./proxy.sock' => 'http+unix://./proxy.sock',
'/tmp/proxy.sock' => 'http+unix:///tmp/proxy.sock',
'user:pass@./proxy.sock' => 'http+unix://user:pass@./proxy.sock',
'http+unix://./proxy.sock' => 'http+unix://./proxy.sock',
'http+unix:///tmp/proxy.sock' => 'http+unix:///tmp/proxy.sock',
'http+unix://user:pass@./proxy.sock' => 'http+unix://user:pass@./proxy.sock',
'http+unix://user@./proxy.sock' => 'http+unix://user@./proxy.sock',
'socks+unix://./proxy.sock' => 'socks+unix://./proxy.sock',
'socks+unix://user:pass@./proxy.sock' => 'socks5+unix://user:pass@./proxy.sock',
'socks5+unix://user:pass@./proxy.sock' => 'socks5+unix://user:pass@./proxy.sock',
);
foreach ($uris as $in => $out) {
@@ -30,12 +41,15 @@ public function testCoerceProxyUriInvalidThrows()
'excessive path' => 'host/root',
'excessive query' => 'host?query',
'excessive fragment' => 'host#fragment',
'excessive dots' => '.../server.sock',
'auth for invalid socks4+unix' => 'socks4+unix://user:pass@./proxy.sock'
);
foreach ($uris as $uri) {
try {
ConnectorFactory::coerceProxyUri($uri);
$this->fail();
$this->fail($uri);
} catch (InvalidArgumentException $e) {
$this->assertTrue(true);
}
View
@@ -92,11 +92,14 @@ out=$(curl -v --head --silent --fail --proxy http://127.0.0.1:8180 https://googl
out=$(curl -v --head --silent --fail --proxy http://127.0.0.1:8180 https://maps.google.com 2>&1) && echo "FAIL: $out" && exit 1 || (echo "$out" | grep -q "403 Forbidden" && echo OK) || (echo "FAIL: $out" && exit 1) || exit 1
out=$(curl -v --head --silent --fail --proxy http://127.0.0.1:8180 https://google.de 2>&1) && echo OK || (echo "FAIL: $out" && exit 1) || exit 1
# restart LeProxy on Unix domain socket path
# restart LeProxy on Unix domain socket path and another LeProxy instance for chaining
killall php 2>&- 1>&- || true
php $bin ./leproxy.tmp.socket --no-log &
pid=$!
sleep 1
php $bin :8180 --proxy ./leproxy.tmp.socket --no-log &
sleep 2
out=$(curl -v --head --silent --fail --proxy http://127.0.0.1:8180 http://reactphp.org 2>&1) && echo OK || (echo "FAIL: $out" && exit 1) || exit 1
kill $pid && rm leproxy.tmp.socket && echo . || (echo "FAIL" && exit 1) || exit 1
# restart LeProxy with authentication required

0 comments on commit 8b7391e

Please sign in to comment.