clue/socks-react

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

v1.4.0 2022-08-31 14:39 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代理服务器向google.com发送安全的HTTPS请求

<?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,在错误时拒绝为Exception

这使得向几乎任何高级组件添加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客户端实现之外处理。

身份验证

此库支持按照RFC 1929定义的SOCKS5服务器的用户名/密码认证。

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

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

您可以直接将认证信息作为SOCKS URI的一部分传递

$proxy = new Clue\React\Socks\Client('alice:[email protected]: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:[email protected]: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通信之前,先建立一个安全的TLS连接到您的SOCKS代理。这意味着没有窃听者能够看到您想连接的目标地址或您的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:[email protected]: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仅用于客户端与代理服务器之间的连接,路径实际上不会通过协议传递。这意味着这也不会支持代理链式连接

服务器

Server负责接受来自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构造函数接受可选参数来明确配置要使用的连接器以及要求认证。有关更多详细信息,请继续阅读...

服务器连接器

服务器使用 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 实例作为连接器,有关更多详细信息,请参阅 服务器代理链

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

服务器认证

默认情况下,服务器不需要从客户端进行任何认证。您可以通过启用认证支持来确保客户端在转发任何连接之前必须通过有效的用户名和密码。

在服务器上设置认证强制要求每个进一步连接的客户端使用协议版本 5(SOCKS5)。如果客户端尝试使用任何其他协议版本、不发送认证信息或认证信息无法验证,则连接将被拒绝。

如果您只想接受静态认证信息,可以简单地将包含您的认证信息的附加关联数组传递给服务器,如下所示

$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:[email protected]:1234
    // or sockss://alice:[email protected]: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;
    });
});

服务器代理链

服务器负责创建到目标主机的连接。

Client -> SocksServer -> TargetHost

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

Client -> MiddlemanSocksServer -> TargetSocksServer -> TargetHost

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

// set next SOCKS server example.com:1080 as target
$proxy = new Clue\React\Socks\Client('alice:[email protected]: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 <端口>选项启动本地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