坦克/reactphp-socks

异步 SOCKS 代理连接客户端和服务器实现,通过 SOCKS5 或 SOCKS4(a) 代理服务器隧道任何基于 TCP/IP 的协议,建立在 ReactPHP 之上。

1.x-dev 2023-01-12 14:29 UTC

This package is auto-updated.

Last update: 2024-09-12 17:49:22 UTC


README

CI status installs on Packagist

异步 SOCKS 代理连接客户端和服务器实现,通过 SOCKS5 或 SOCKS4(a) 代理服务器隧道任何基于 TCP/IP 的协议,建立在 ReactPHP 之上。

SOCKS 代理协议家族(SOCKS5、SOCKS4 和 SOCKS4a)通常用于通过中间代理(“代理”)隧道 HTTP(S) 流量,以隐藏原始地址(匿名性)或绕过地址阻止(地理封锁)。虽然许多(公共)SOCKS 代理服务器通常仅限于 HTTP(S) 端口 80443,但技术上可以使用它来隧道任何基于 TCP/IP 的协议(HTTP、SMTP、IMAP 等)。此库提供了一种简单的 API 来为您创建这些隧道连接。因为它实现了 ReactPHP 的标准 ConnectorInterface,它可以直接用作普通连接器。这使得在几乎任何现有的高级协议实现中添加 SOCKS 代理支持变得相当简单。除了客户端,它还提供了一个简单的 SOCKS 服务器实现,允许您使用自定义业务逻辑构建自己的 SOCKS 代理服务器。

  • 异步执行连接 - 并行发送任意数量的 SOCKS 请求,并在结果到来时立即处理它们的响应。基于 Promise 的设计提供了处理无序响应和可能的连接错误的合理接口。
  • 标准接口 - 通过实现 ReactPHP 的标准 ConnectorInterface,允许轻松集成现有高级组件。
  • 轻量级、SOLID 设计 - 提供了一个薄的抽象层,这是 正好足够好 的,并且不会妨碍您。建立在经过良好测试的组件和已确立的概念之上,而不是重新发明轮子。
  • 良好的测试覆盖率 - 包含自动测试套件,并定期针对实际野外代理服务器进行测试。

目录

支持我们

我们在开发、维护和更新我们出色的开源项目上投入了大量的时间。您可以通过 在 GitHub 上成为赞助商 来帮助我们保持工作的高质量。赞助商将获得许多回报,有关详细信息,请参阅我们的 赞助页面

让我们共同将这些项目提升到下一个层次!🚀

快速入门示例

安装后,您可以使用以下代码通过本地 SOCKS 代理服务器发送安全的 HTTPS 请求到 google.com

<?php

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

$proxy = new Clue\React\Socks\Client('127.0.0.1:1080');

$connector = new React\Socket\Connector(array(
    'tcp' => $proxy,
    'dns' => false
));

$browser = new React\Http\Browser($connector);

$browser->get('https://google.com/')->then(function (Psr\Http\Message\ResponseInterface $response) {
    var_dump($response->getHeaders(), (string) $response->getBody());
}, function (Exception $e) {
    echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

如果您尚未运行任何其他SOCKS代理服务器,可以使用以下代码创建一个监听127.0.0.1:1080连接的SOCKS代理服务器。

<?php

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

// start a new SOCKS proxy server
$socks = new Clue\React\Socks\Server();

// listen on 127.0.0.1:1080
$socket = new React\Socket\SocketServer('127.0.0.1:1080');
$socks->listen($socket);

请参阅示例

用法

客户端

Client类负责与您的SOCKS服务器实例进行通信。

其构造函数仅接受一个包含SOCKS代理服务器地址的SOCKS代理URI。

$proxy = new Clue\React\Socks\Client('127.0.0.1:1080');

如果您使用的是默认的SOCKS端口1080,可以省略端口号。

$proxy = new Clue\React\Socks\Client('127.0.0.1');

如果您需要自定义连接器设置(DNS解析、TLS参数、超时、代理服务器等),可以显式传递一个自定义的ConnectorInterface实例。

$connector = new React\Socket\Connector(array(
    'dns' => '127.0.0.1',
    'tcp' => array(
        'bindto' => '192.168.10.1:0'
    )
));

$proxy = new Clue\React\Socks\Client('my-socks-server.local:1080', $connector);

这是本包中的两个主要类之一。因为它实现了ReactPHP的标准ConnectorInterface,所以可以简单地替换为一个普通连接器。因此,它只提供了一个公共方法,即connect()方法。使用connect(string $uri): PromiseInterface<ConnectionInterface, Exception>方法可以建立流式连接。它返回一个Promise,在成功时满足ConnectionInterface,在错误时拒绝。

这使得将SOCKS代理支持添加到几乎所有高级组件变得相当简单。

- $acme = new AcmeApi($connector);
+ $proxy = new Clue\React\Socks\Client('127.0.0.1:1080', $connector);
+ $acme = new AcmeApi($proxy);

普通 TCP 连接

SOCKS代理通常用于向目标发送HTTP(S)请求。然而,这实际上是在更高协议层上进行的,并且此连接器实际上是一个通用的纯TCP/IP连接器。如上所述,您可以简单地调用其connect()方法以建立流式纯TCP/IP连接,并使用任何高级协议,如下所示:

$proxy = new Clue\React\Socks\Client('127.0.0.1:1080');

$proxy->connect('tcp://www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
    echo 'connected to www.google.com:80';
    $connection->write("GET / HTTP/1.0\r\n\r\n");

    $connection->on('data', function ($chunk) {
        echo $chunk;
    });
});

您可以直接使用Client,或者可能希望将此连接器包装在ReactPHP的Connector中。

$proxy = new Clue\React\Socks\Client('127.0.0.1:1080');

$connector = new React\Socket\Connector(array(
    'tcp' => $proxy,
    'dns' => false
));

$connector->connect('tcp://www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
    echo 'connected to www.google.com:80';
    $connection->write("GET / HTTP/1.0\r\n\r\n");

    $connection->on('data', function ($chunk) {
        echo $chunk;
    });
});

请参阅第一个示例

也可以省略tcp://方案。传递任何其他方案将拒绝Promise。

可以通过取消其挂起的Promise来取消挂起的连接尝试,如下所示:

$promise = $connector->connect($uri);

$promise->cancel();

在挂起的Promise上调用cancel()将取消到SOCKS服务器的底层TCP/IP连接以及/或SOCKS协议协商,并拒绝结果Promise。

安全的 TLS 连接

如果想要在您和目标之间建立安全的TLS连接(以前称为SSL),例如使用到目标网站的HTTPS进行安全通信,也可以使用此类。您只需将此连接器包装在ReactPHP的Connector中即可。

$proxy = new Clue\React\Socks\Client('127.0.0.1:1080');

$connector = new React\Socket\Connector(array(
    'tcp' => $proxy,
    'dns' => false
));

$connector->connect('tls://www.google.com:443')->then(function (React\Socket\ConnectionInterface $connection) {
    // proceed with just the plain text data
    // everything is encrypted/decrypted automatically
    echo 'connected to SSL encrypted www.google.com';
    $connection->write("GET / HTTP/1.0\r\n\r\n");

    $connection->on('data', function ($chunk) {
        echo $chunk;
    });
});

请参阅第二个示例

通常可以通过取消其挂起的Promise来取消挂起的连接尝试。

注意,安全的TLS连接实际上是在此SOCKS客户端实现之外完全处理的。

您可以选择以这种方式将额外的SSL上下文选项传递给构造函数:

$connector = new React\Socket\Connector(array(
    'tcp' => $proxy,
    'tls' => array(
        'verify_peer' => false,
        'verify_peer_name' => false
    ),
    'dns' => false
));

HTTP 请求

此库还允许您通过SOCKS代理服务器发送HTTP请求

为了发送HTTP请求,您首先需要为ReactPHP的异步HTTP客户端添加依赖。这允许您发送普通HTTP和TLS加密的HTTPS请求,如下所示:

$proxy = new Clue\React\Socks\Client('127.0.0.1:1080');

$connector = new React\Socket\Connector(array(
    'tcp' => $proxy,
    'dns' => false
));

$browser = new React\Http\Browser($connector);

$browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseInterface $response) {
    var_dump($response->getHeaders(), (string) $response->getBody());
}, function (Exception $e) {
    echo 'Error: ' . $e->getMessage() . PHP_EOL;
});

有关更多信息,请参阅ReactPHP的HTTP客户端任何示例

协议版本

此库支持SOCKS5和SOCKS4(a)协议版本。它侧重于通过SOCKS代理服务器连接到目标主机的最常用核心功能。在此模式下,SOCKS代理服务器充当通用代理,允许高级应用程序协议通过它工作。

默认情况下,Client通过SOCKS5与SOCKS服务器通信。这是因为SOCKS5是SOCKS协议家族的最新版本,通常在其他供应商中得到最佳支持。您也可以省略默认的socks:// URI方案。同样,socks5:// URI方案充当默认的socks:// URI方案的别名。

// all three forms are equivalent
$proxy = new Clue\React\Socks\Client('127.0.0.1:1080');
$proxy = new Clue\React\Socks\Client('socks://127.0.0.1:1080');
$proxy = new Clue\React\Socks\Client('socks5://127.0.0.1:1080');

如果您想明确设置协议版本为SOCKS4(a),您可以在SOCKS URI中使用URI方案socks4://

$proxy = new Clue\React\Socks\Client('socks4://127.0.0.1:1080');

DNS 解析

默认情况下,Client根本不执行任何DNS解析,而只是将您尝试连接到的任何主机名转发到SOCKS服务器。因此,远程SOCKS服务器负责通过DNS查找任何主机名(这种默认模式因此称为远程DNS解析)。如上所示,此模式由SOCKS5和SOCKS4a协议支持,但不支持原始的SOCKS4协议,因为该协议缺乏通信主机名的方式。

另一方面,所有SOCKS协议版本都支持将目标IP地址发送到SOCKS服务器。在此模式下,您必须坚持仅使用IP(这通常不可行)或在本地执行任何DNS查找,然后仅传输解析后的目标IP(这种模式因此称为本地DNS解析)。

默认的远程DNS解析在您的本地Client无法解析目标主机名(因为它没有直接访问互联网)或其出站DNS流量可能被拦截(特别是当使用Tor网络)时非常有用。

如上所述,Client默认使用远程DNS解析。但是,将Client包装在ReactPHP的Connector中实际上执行本地DNS解析,除非明确指定否则。鉴于远程DNS解析假定是首选模式,所有其他示例都明确禁用DNS解析,如下所示:

$proxy = new Clue\React\Socks\Client('127.0.0.1:1080');

$connector = new React\Socket\Connector(array(
    'tcp' => $proxy,
    'dns' => false
));

如果您想明确使用本地DNS解析(例如,在明确使用SOCKS4时),您可以使用以下代码:

$proxy = new Clue\React\Socks\Client('127.0.0.1:1080');

// set up Connector which uses Google's public DNS (8.8.8.8)
$connector = new React\Socket\Connector(array(
    'tcp' => $proxy,
    'dns' => '8.8.8.8'
));

请参阅第四个示例

可以通过取消其挂起的承诺来取消挂起的连接尝试。

请注意,本地DNS解析实际上完全由此SOCKS客户端实现之外处理。

身份验证

此库支持SOCKS5服务器上的用户名/密码认证,如RFC 1929中定义。

在客户端,只需传递您的用户名和密码用于身份验证(见下文)。对于每个后续连接,客户端将仅向服务器发送一个标志,指示身份验证信息可用。只有当服务器在初始握手期间请求身份验证时,实际的认证凭据才会传输到服务器。

请注意,密码以明文形式传输到SOCKS代理服务器,因此这种方法不应在网络中使用,您需要担心窃听。

您可以将身份验证信息作为SOCKS URI的一部分直接传递。

$proxy = new Clue\React\Socks\Client('alice:password@127.0.0.1:1080');

请注意,如果用户名和密码包含特殊字符,它们必须进行百分号编码。

$user = 'he:llo';
$pass = 'p@ss';
$url = rawurlencode($user) . ':' . rawurlencode($pass) . '@127.0.0.1:1080';

$proxy = new Clue\React\Socks\Client($url);

只有当SOCKS代理服务器需要用户名/密码认证时,认证详情才会以明文形式传输到SOCKS代理服务器。如果认证详情缺失或远程SOCKS代理服务器不接受,则它应拒绝每个连接尝试,并返回异常错误码SOCKET_EACCES(13)。

只有协议版本5(SOCKS5)支持身份验证,因此将身份验证传递给Client强制使用协议版本5,并会抱怨如果您已明确设置其他任何内容。

// throws InvalidArgumentException
new Clue\React\Socks\Client('socks4://alice:password@127.0.0.1:1080');

代理链

Client负责创建到SOCKS服务器的连接,然后该服务器连接到目标主机。

Client -> SocksServer -> TargetHost

有时可能需要通过另一个SOCKS服务器建立出站连接。例如,如果您想隐藏您的原始地址,这可能很有用。

Client -> MiddlemanSocksServer -> TargetSocksServer -> TargetHost

Client使用任何ConnectorInterface实例来建立出站连接。为了通过另一个SOCKS服务器连接,您可以使用另一个SOCKS客户端中的SOCKS连接器,如下所示

// https via the proxy chain  "MiddlemanSocksServer -> TargetSocksServer -> TargetHost"
// please note how the client uses TargetSocksServer (not MiddlemanSocksServer!),
// 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 Clue\React\Socks\Client('127.0.0.1:1080');
$target = new Clue\React\Socks\Client('example.com:1080', $middle);

$connector = new React\Socket\Connector(array(
    'tcp' => $target,
    'dns' => false
));

$connector->connect('tls://www.google.com:443')->then(function (React\Socket\ConnectionInterface $connection) {
    // …
});

请参阅第三个示例

可以通过取消其挂起的承诺来取消挂起的连接尝试,就像通常那样。

代理链可以在服务器端和/或客户端上发生。

  • 如果您要求客户端通过多个代理进行链式连接,那么每个代理服务器实际上根本不知道有关链式连接的任何事情。这意味着这是一个客户端独有的属性。

  • 如果您要求服务器通过另一个代理进行链式连接,那么客户端实际上根本不知道有关链式连接的任何事情。这意味着这是一个服务器独有的属性,不属于此类。例如,您可以在下面的Server类中找到它,或者在您使用Tor网络时找到类似的功能。

连接超时

默认情况下,Client不实现任何超时来建立远程连接。您的底层操作系统可能会对挂起和/或空闲的TCP/IP连接施加限制,范围从几分钟到几小时不等。

许多用例需要更多对超时的控制,并且可能值较小,通常只有几秒钟。

您可以使用ReactPHP的Connector装饰任何给定的ConnectorInterface实例。它提供了相同的connect()方法,但如果底层连接尝试花费太长时间,则会自动拒绝。

$proxy = new Clue\React\Socks\Client('127.0.0.1:1080');

$connector = new React\Socket\Connector(array(
    'tcp' => $proxy,
    'dns' => false,
    'timeout' => 3.0
));

$connector->connect('tcp://google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
    // connection succeeded within 3.0 seconds
});

请参阅示例

可以通过取消其挂起的承诺来取消挂起的连接尝试。

请注意,连接超时实际上完全由本SOCKS客户端实现之外的部分处理。

SOCKS over TLS

所有SOCKS协议版本都支持转发基于TCP/IP的连接和高级协议。这意味着您也可以使用安全的TLS连接通过SOCKS代理服务器传输敏感数据。这意味着任何监听者或代理服务器都无法解密您的数据。

然而,客户端和代理之间的初始SOCKS通信通常是通过一个未加密的纯TCP/IP连接进行的。这意味着监听者可能能够看到您连接的地方,也可能能够看到您SOCKS认证的明文细节。

作为替代方案,您可以在开始初始SOCKS通信之前建立与SOCKS代理的安全TLS连接。这意味着任何监听者都无法看到您想要连接的目标地址或您的SOCKS认证细节。

您可以使用sockss:// URI方案,或使用以下方式的显式SOCKS协议版本

$proxy = new Clue\React\Socks\Client('sockss://127.0.0.1:1080');

$proxy = new Clue\React\Socks\Client('socks4s://127.0.0.1:1080');

另请参阅示例32

类似地,您也可以这样结合认证

$proxy = new Clue\React\Socks\Client('sockss://alice:password@127.0.0.1:1080');

请注意,对于大多数使用场景,应使用安全的TLS连接。SOCKS over TLS被认为是高级用法,在实践中很少使用。特别是,SOCKS服务器必须接受安全的TLS连接,有关更多详细信息,请参阅服务器SOCKS over TLS。此外,PHP不支持在单个连接上执行“双重加密”。这意味着在通过SOCKS over TLS打开的通信通道上启用安全的TLS连接可能不受支持。

请注意,SOCKS协议不支持TLS的概念。上述方案之所以运行良好,是因为TLS仅用于客户端和代理服务器之间的连接,而SOCKS协议数据本身保持不变。这意味着这也可能只有限地支持代理链式连接

Unix 域套接字

所有SOCKS协议版本都支持转发基于TCP/IP的连接和高级协议。在某些高级情况下,让您的SOCKS服务器监听Unix域套接字(UDS)路径而不是IP:端口组合可能很有用。例如,这允许您依赖于文件系统权限,而不是必须依赖显式的认证

您可以使用socks+unix:// URI方案,或使用以下方式的显式SOCKS协议版本

$proxy = new Clue\React\Socks\Client('socks+unix:///tmp/proxy.sock');

$proxy = new Clue\React\Socks\Client('socks4+unix:///tmp/proxy.sock');

类似地,您也可以这样结合认证

$proxy = new Clue\React\Socks\Client('socks+unix://alice:password@/tmp/proxy.sock');

请注意,Unix域套接字(UDS)被认为是高级用法,PHP对此仅提供有限支持。特别是,启用安全的TLS可能不受支持。

请注意,SOCKS协议不支持UDS路径的概念。上述方案之所以运行良好,是因为UDS仅用于客户端和代理服务器之间的连接,路径实际上不会通过协议传递。这意味着这也不会支持代理链式连接

服务器

服务器负责接收来自SOCKS客户端的通信并将请求的连接转发到目标主机。默认支持SOCKS5和SOCKS4(a)协议版本。您可以使用如下方式在底层的TCP/IP套接字服务器上开始监听:

$socks = new Clue\React\Socks\Server();

// listen on 127.0.0.1:1080
$socket = new React\Socket\SocketServer('127.0.0.1:1080');
$socks->listen($socket);

此类接受一个可选的LoopInterface|null $loop参数,可以用来将事件循环实例传递给此对象。您可以使用null值来使用默认循环。除非您确定要显式使用给定的事件循环实例,否则不应提供此值。

此外,Server构造函数接受可选参数来显式配置要使用的连接器以及要求身份验证。更多详情,请继续阅读...

服务器连接器

Server使用ReactPHP的ConnectorInterface实例为每个传入的连接请求建立出站连接。

如果您需要自定义连接器设置(DNS解析、TLS参数、超时、代理服务器等),可以显式传递一个自定义的ConnectorInterface实例。

$connector = new React\Socket\Connector(array(
    'dns' => '127.0.0.1',
    'tcp' => array(
        'bindto' => '192.168.10.1:0'
    )
));

$socks = new Clue\React\Socks\Server(null, $connector);

如果您想通过另一个SOCKS代理转发出站连接,也可以传递一个Client实例作为连接器,有关详细信息,请参阅服务器代理链

内部,Server使用ReactPHP的常规connect()方法,但它也将原始客户端IP作为?source={remote}参数传递。该source参数包含完整的远程URI,包括协议和任何认证信息,例如socks://alice:password@1.2.3.4:5678socks4://1.2.3.4:5678用于旧版SOCKS4(a)。您可以使用此参数进行日志记录或通过提供自定义的ConnectorInterface实现来限制特定客户端的连接请求。

服务器身份验证

默认情况下,Server不需要客户端进行任何身份验证。您可以通过启用身份验证支持来要求客户端在转发任何连接之前传递有效的用户名和密码。

Server上设置身份验证强制要求每个已连接的客户端使用协议版本5(SOCKS5)。如果客户端尝试使用任何其他协议版本、未发送身份验证信息或身份验证信息无法验证,则连接将被拒绝。

如果您只想接受静态身份验证信息,可以简单地向Server传递一个包含您的身份验证信息的附加关联数组,如下所示:

$socks = new Clue\React\Socks\Server(null, null, array(
    'alice' => 'password',
    'bob' => 's3cret!1'
));

另请参阅示例#12

如果您想对身份验证有更多控制,可以传递一个认证函数,该函数应返回一个bool值,如下同步示例所示:

$socks = new Clue\React\Socks\Server(null, null, function ($username, $password, $remote) {
    // $remote is a full URI à la socks://alice:password@192.168.1.1:1234
    // or sockss://alice:password@192.168.1.1:1234 for SOCKS over TLS
    // or may be null when remote is unknown (SOCKS over Unix Domain Sockets)
    // useful for logging or extracting parts, such as the remote IP
    $ip = parse_url($remote, PHP_URL_HOST);

    return ($username === 'root' && $password === 'secret' && $ip === '127.0.0.1');
});

因为您的认证机制可能需要一些时间来实际检查提供的认证凭据(如查询远程数据库或web服务),所以服务器还支持基于Promise的接口。虽然一开始这可能看起来更复杂,但实际上它提供了一种非常强大的方式来并发处理大量连接,而不会阻塞任何连接。您可以从认证函数中返回一个Promise,如下异步示例所示:

$socks = new Clue\React\Socks\Server(null, null, function ($username, $password) use ($db) {
    // pseudo-code: query database for given authentication details
    return $db->query(
        'SELECT 1 FROM users WHERE name = ? AND password = ?',
        array($username, $password)
    )->then(function (QueryResult $result) {
        // ensure we find exactly one match in the database
        return count($result->resultRows) === 1;
    });
});

服务器代理链

Server负责创建到目标主机的连接。

Client -> SocksServer -> TargetHost

有时可能需要通过另一个 SOCKS 服务器建立出站连接。例如,如果你的目标 SOCKS 服务器需要身份验证,但你的客户端不支持发送身份验证信息(例如,大多数网页浏览器),这将很有用。

Client -> MiddlemanSocksServer -> TargetSocksServer -> TargetHost

Server 使用 ConnectorInterface 的任何实例来建立出站连接。为了通过另一个 SOCKS 服务器连接,你可以简单地使用上面的 Client SOCKS 连接器。你可以这样创建一个 SOCKS Client 实例:

// set next SOCKS server example.com:1080 as target
$proxy = new Clue\React\Socks\Client('alice:password@example.com:1080');

// start a new server which forwards all connections to the other SOCKS server
$socks = new Clue\React\Socks\Server(null, $proxy);

// listen on 127.0.0.1:1080
$socket = new React\Socket\SocketServer('127.0.0.1:1080');
$socks->listen($socket);

参见示例 #21

代理链可以在服务器端和/或客户端上发生。

  • 如果你要求你的客户端通过多个代理进行链式连接,那么每个代理服务器实际上对链式连接一无所知。这意味着这是一个客户端属性,而不是此类的一部分。例如,你可以在上面的 Client 类中找到它。

  • 如果你要求你的服务器通过另一个代理进行链式连接,那么你的客户端实际上对链式连接一无所知。这意味着这是一个服务器属性,可以像上面那样实现。

服务器 SOCKS over TLS

SOCKS5 和 SOCKS4(a) 协议版本都支持转发基于 TCP/IP 的连接和高级协议。这意味着你还可以使用 安全 TLS 连接 在 SOCKS 代理服务器之间传输敏感数据。这意味着任何窃听者或代理服务器都无法解密你的数据。

然而,客户端和代理之间的初始 SOCKS 通信通常是通过未加密的普通 TCP/IP 连接进行的。这意味着窃听者可能能够看到客户端连接的位置,也可能能够看到 SOCKS 身份验证 的详细信息。

作为替代,你可以监听 SOCKS over TLS 连接,这样客户端在开始初始 SOCKS 通信之前必须与你 SOCKS 代理建立安全的 TLS 连接。这意味着任何窃听者都无法看到客户端想要连接的目标地址或他们的 SOCKS 身份验证 详细信息。

你可以这样简单地开始你的监听套接字,使用 tls:// URI 方案:

$socks = new Clue\React\Socks\Server();

// listen on tls://127.0.0.1:1080 with the given server certificate
$socket = new React\Socket\SocketServer('tls://127.0.0.1:1080', array(
    'tls' => array(
        'local_cert' => __DIR__ . '/localhost.pem',
    )
));
$socks->listen($socket);

参见示例 31

请注意,对于大多数用例,应使用 安全 TLS 连接。SOCKS over TLS 被认为是高级用法,在实践中很少使用。

请注意,SOCKS 协议不支持 TLS 的概念。上述内容之所以能合理工作,是因为 TLS 仅用于客户端和代理服务器之间的连接,而 SOCKS 协议数据在其余方面是相同的。这意味着它也不支持通过多个 TLS 路径的 代理链式连接

服务器 Unix 域套接字

SOCKS5 和 SOCKS4(a) 协议版本都支持转发基于 TCP/IP 的连接和高级协议。在某些高级情况下,可能有用的是让您的 SOCKS 服务器监听 Unix 域套接字(UDS)路径,而不是 IP:端口组合。例如,这允许您依赖文件系统权限,而不是必须依赖明确的 身份验证

你可以这样简单地开始你的监听套接字,使用 unix:// URI 方案:

$socks = new Clue\React\Socks\Server();

// listen on /tmp/proxy.sock
$socket = new React\Socket\SocketServer('unix:///tmp/proxy.sock');
$socks->listen($socket);

请注意,Unix域套接字(UDS)被视为高级用法,并且SOCKS协议不支持UDS路径的概念。上述方法工作得相当好,因为UDS仅用于客户端和代理服务器之间的连接,路径实际上并不会通过协议传递。这意味着这也不支持通过多个UDS路径进行代理链式连接

服务器

使用 PHP SOCKS 服务器

  • 如果您正在寻找面向最终用户的SOCKS服务器守护进程,您可能想使用LeProxyclue/psocksd
  • 如果您正在寻找SOCKS服务器实现,请考虑使用上述的Server类。

使用 SSH 作为 SOCKS 服务器

如果您已经设置了SSH服务器,您可以轻松地将其用作SOCKS隧道端点。在您的客户端上,只需启动SSH客户端,并使用-D <port>选项启动本地SOCKS服务器(根据手册页:这是一个本地“动态”应用层端口转发)。

如果您已经运行了SSH守护进程,您可以通过创建到本地系统的环回连接来启动本地SOCKS服务器。

ssh -D 1080 localhost

或者,您可以通过经过运行SSH守护进程的远程主机启动本地SOCKS服务器隧道。

ssh -D 1080 example.com

现在您可以直接像这样使用这个SSH SOCKS服务器

$proxy = new Clue\React\Socks\Client('127.0.0.1:1080');

$proxy->connect('tcp://www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
    $connection->write("GET / HTTP/1.0\r\n\r\n");

    $connection->on('data', function ($chunk) {
        echo $chunk;
    });
});

请注意,上述方法将允许本地系统上的所有用户在不进行身份验证的情况下连接到SOCKS服务器,这可能不是您需要的。作为替代方案,最近版本的OpenSSH客户端也支持Unix域套接字(UDS)路径,这样您就可以依赖Unix文件系统权限

ssh -D/tmp/proxy.sock example.com

现在您可以直接像这样使用这个SSH SOCKS服务器

$proxy = new Clue\React\Socks\Client('socks+unix:///tmp/proxy.sock');

$proxy->connect('tcp://www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
    $connection->write("GET / HTTP/1.0\r\n\r\n");

    $connection->on('data', function ($chunk) {
        echo $chunk;
    });
});

作为手动设置的替代方案,您还可以考虑使用clue/reactphp-ssh-proxy,它会自动为您创建此SSH隧道。它提供了相同的ConnectorInterface实现,因此支持任何代理协议应该相对简单。

使用 Tor(匿名网络)隧道 SOCKS 连接

默认情况下,Tor匿名网络客户端软件旨在加密您的流量,并通过多个节点网络进行路由以隐藏其来源。它通过TCP端口9050提供SOCKS5和SOCKS4(a)接口,允许您通过匿名网络隧道任何流量。

$proxy = new Clue\React\Socks\Client('127.0.0.1:9050');

$proxy->connect('tcp://www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) {
    $connection->write("GET / HTTP/1.0\r\n\r\n");

    $connection->on('data', function ($chunk) {
        echo $chunk;
    });
});

在大多数常见场景中,您可能希望坚持默认的远程DNS解析,并且不希望您的客户端解析目标主机名,因为您会向观察您本地流量的人泄露DNS信息。此外,Tor通过.onion伪顶级域名提供隐藏服务,这些服务必须由Tor解析。

安装

建议通过Composer安装此库。您是Composer的新手吗?新手?

此项目遵循SemVer。这将安装最新支持版本

composer require clue/socks-react:^1.4

有关版本升级的详细信息,请参阅CHANGELOG

此项目旨在在任何平台上运行,因此不需要任何PHP扩展,并支持在PHP 5.3(通过当前PHP 8+和HHVM)上运行。强烈建议使用此项目的最新支持PHP版本。

测试

要运行测试套件,您首先需要克隆此存储库,然后通过Composer安装所有依赖项

composer install

要运行测试套件,请前往项目根目录并执行

vendor/bin/phpunit

测试套件包含一些依赖于工作互联网连接的测试,您也可以选择以下方式运行它

vendor/bin/phpunit --exclude-group internet

许可

本项目采用宽松的MIT许可协议发布。

你知道吗?我提供定制开发服务,并开具发布赞助和贡献的发票。如有需要,请联系我(@clue)了解详情。

更多信息

  • 如果您想了解更多关于ConnectorInterface及其常见实现的信息,请参阅底层react/socket组件的文档。
  • 如果您想了解更多关于处理数据流的信息,请参阅底层react/stream组件的文档。
  • 作为SOCKS5 / SOCKS4(a)代理的替代方案,您可能还想考虑使用HTTP CONNECT代理。您可能想使用clue/reactphp-http-proxy,它也提供了相同的ConnectorInterface实现,因此支持任何代理协议应该相当简单。
  • 作为SOCKS5 / SOCKS4(a)代理的替代方案,您可能还想考虑使用SSH代理(SSH隧道)代替。您可能想使用clue/reactphp-ssh-proxy,它也提供了相同的ConnectorInterface实现,因此支持任何代理协议应该相当简单。
  • 如果您处理的是公共代理,您可能会遇到质量不一且不可靠的代理。您可能想考虑使用clue/reactphp-connection-manager-extra,它允许重试不可靠的代理,意味着连接超时、同时处理多个连接器等功能。
  • 如果您正在寻找面向最终用户的SOCKS服务器守护进程,您可能想使用LeProxyclue/psocksd