如何在 PHP 中将 fsockopen(或兼容)与 SOCKS 代理一起使用?
How to use fsockopen (or compatible) with SOCKS proxies in PHP?
我使用 fsockopen
和相关函数在 PHP 中编写了一个非邪恶、非垃圾邮件的 IRC 机器人。有用。但是,问题是我需要支持代理(最好是 SOCKS5,但如果更简单的话 HTTP 也可以,我对此表示怀疑)。 fsockopen
.
不支持此功能
我已经浏览了 "PHP fsockopen proxy" 和相关查询的所有搜索结果。我知道所有不起作用的东西,所以请不要link其中之一。
fsockopen 的 PHP 手册页提到函数 stream_socket_client()
为
similar but provides a richer set of options, including non-blocking connection and the ability to provide a stream context.
起初这听起来很有希望,据说允许我用 stream_socket_client
替换 fsockopen
调用并指定代理,也许通过 "stream context"... 但它没有吨。或者是吗?我对 manual.
很困惑
请注意,必须是PHP代码解决方案;我无法支付 "Proxifier" 或使用任何其他外部软件来 "wrap around" 这个。
我尝试过的所有事情似乎总是导致我从服务器得到一堆空输出,然后套接字被强行关闭。请注意,当我使用具有相同网络的 HexChat(普通 IRC 客户端)时,我正在尝试使用的代理可以正常工作,因此不是代理本身有问题。
据我所知,没有为 fsockopen
或 stream_socket_client
设置 SOCKS 或 HTTP 代理的默认选项(我们可以创建上下文并在 HTTP 选项中设置代理,但是不适用于 stream_socket_client
)。不过我们可以手动建立连接。
连接到 HTTP 代理非常简单:
- 客户端连接到代理服务器并提交CONNECT请求。
- 如果请求被接受,服务器响应 200。
- 然后服务器代理客户端和目标主机之间的所有请求。
function connect_to_http_proxy($host, $port, $destination) {
$fp = fsockopen($host, $port, $errno, $errstr);
if ($errno == 0) {
$connect = "CONNECT $destination HTTP/1.1\r\n\r\n";
fwrite($fp, $connect);
$rsp = fread($fp, 1024);
if (preg_match('/^HTTP\/\d\.\d 200/', $rsp) == 1) {
return $fp;
}
echo "Request denied, $rsp\n";
return false;
}
echo "Connection failed, $errno, $errstr\n";
return false;
}
此函数returns如果连接成功则为文件指针资源,否则为FALSE。我们可以使用该资源与目标主机通信。
$proxy = "138.204.48.233";
$port = 8080;
$destination = "api.ipify.org:80";
$fp = connect_to_http_proxy($proxy, $port, $destination);
if ($fp) {
fwrite($fp, "GET /?format=json HTTP/1.1\r\nHost: $destination\r\n\r\n");
echo fread($fp, 1024);
fclose($fp);
}
SOCKS5 代理的通信协议稍微复杂一些:
- 客户端连接到代理服务器并发送(至少)三个字节:第一个字节是SOCKS版本,第二个是认证方法的数量,接下来的字节是认证方法(s) ).
- 服务器用两个字节响应,即 SOCKS 版本和所选的身份验证方法。
- 客户端请求连接到目标主机。该请求包含 SOCKS 版本,后跟命令(在本例中为 CONNECT),后跟一个空字节。第四个字节指定地址类型,后面是地址和端口。
- 服务器最终发送十个字节(或七个或 twenty-two,具体取决于目标地址类型)。第二个字节包含状态,如果请求成功,它应该为零。
- 服务器代理所有请求。
更多详情:SOCKS Protocol Version 5.
function connect_to_socks5_proxy($host, $port, $destination) {
$fp = fsockopen($host, $port, $errno, $errstr);
if ($errno == 0) {
fwrite($fp, "[=12=]");
$rsp = fread($fp, 2);
if ($rsp === "[=12=]" ) {
list($host, $port) = explode(":", $destination);
$host = gethostbyname($host); //not required if $host is an IP
$req = "[=12=]" . inet_pton($host) . pack("n", $port);
fwrite($fp, $req);
$rsp = fread($fp, 10);
if ($rsp[1] === "[=12=]") {
return $fp;
}
echo "Request denied, status: " . ord($rsp[1]) . "\n";
return false;
}
echo "Request denied\n";
return false;
}
echo "Connection failed, $errno, $errstr\n";
return false;
}
此函数的工作方式与 connect_to_http_proxy
相同。虽然这两个功能都经过测试,但最好使用库;该代码主要用于教育目的。
SSL 支持和身份验证。
我们无法使用 ssl:// 或 tls:// 协议与 fsockopen
创建 SSL 连接,因为那样会尝试与代理服务器而非目标主机创建 SSL 连接.但是在与代理服务器建立连接后,可以使用 stream_socket_enable_crypto
启用 SSL 并创建与目的地的安全通信通道。这需要禁用对等验证,这可以通过 stream_socket_client
使用自定义上下文来完成。请注意,禁用对等验证可能是一个安全问题。
对于 HTTP 代理,我们可以使用 Proxy-Authenticate
header 添加身份验证。这个header的值是认证类型,后面是用户名和密码,base64编码(基本认证)。
对于 SOCKS5 代理,身份验证过程再次变得更加复杂。看来我们必须将身份验证代码从 0x00
(无需身份验证)更改为 0x02
(USERNAME/PASSWORD 身份验证)。我不清楚如何使用身份验证值创建请求,因此我无法提供示例。
function connect_to_http_proxy($host, $port, $destination, $creds=null) {
$context = stream_context_create(
['ssl'=> ['verify_peer'=> false, 'verify_peer_name'=> false]]
);
$soc = stream_socket_client(
"tcp://$host:$port", $errno, $errstr, 20,
STREAM_CLIENT_CONNECT, $context
);
if ($errno == 0) {
$auth = $creds ? "Proxy-Authorization: Basic ".base64_encode($creds)."\r\n": "";
$connect = "CONNECT $destination HTTP/1.1\r\n$auth\r\n";
fwrite($soc, $connect);
$rsp = fread($soc, 1024);
if (preg_match('/^HTTP\/\d\.\d 200/', $rsp) == 1) {
return $soc;
}
echo "Request denied, $rsp\n";
return false;
}
echo "Connection failed, $errno, $errstr\n";
return false;
}
$host = "proxy IP";
$port = "proxy port";
$destination = "chat.freenode.net:6697";
$credentials = "user:pass";
$soc = connect_to_http_proxy($host, $port, $destination, $credentials);
if ($soc) {
stream_socket_enable_crypto($soc, true, STREAM_CRYPTO_METHOD_ANY_CLIENT);
fwrite($soc,"USER test\nNICK test\n");
echo fread($soc, 1024);
fclose($soc);
}
我使用 fsockopen
和相关函数在 PHP 中编写了一个非邪恶、非垃圾邮件的 IRC 机器人。有用。但是,问题是我需要支持代理(最好是 SOCKS5,但如果更简单的话 HTTP 也可以,我对此表示怀疑)。 fsockopen
.
我已经浏览了 "PHP fsockopen proxy" 和相关查询的所有搜索结果。我知道所有不起作用的东西,所以请不要link其中之一。
fsockopen 的 PHP 手册页提到函数 stream_socket_client()
为
similar but provides a richer set of options, including non-blocking connection and the ability to provide a stream context.
起初这听起来很有希望,据说允许我用 stream_socket_client
替换 fsockopen
调用并指定代理,也许通过 "stream context"... 但它没有吨。或者是吗?我对 manual.
请注意,必须是PHP代码解决方案;我无法支付 "Proxifier" 或使用任何其他外部软件来 "wrap around" 这个。
我尝试过的所有事情似乎总是导致我从服务器得到一堆空输出,然后套接字被强行关闭。请注意,当我使用具有相同网络的 HexChat(普通 IRC 客户端)时,我正在尝试使用的代理可以正常工作,因此不是代理本身有问题。
据我所知,没有为 fsockopen
或 stream_socket_client
设置 SOCKS 或 HTTP 代理的默认选项(我们可以创建上下文并在 HTTP 选项中设置代理,但是不适用于 stream_socket_client
)。不过我们可以手动建立连接。
连接到 HTTP 代理非常简单:
- 客户端连接到代理服务器并提交CONNECT请求。
- 如果请求被接受,服务器响应 200。
- 然后服务器代理客户端和目标主机之间的所有请求。
function connect_to_http_proxy($host, $port, $destination) {
$fp = fsockopen($host, $port, $errno, $errstr);
if ($errno == 0) {
$connect = "CONNECT $destination HTTP/1.1\r\n\r\n";
fwrite($fp, $connect);
$rsp = fread($fp, 1024);
if (preg_match('/^HTTP\/\d\.\d 200/', $rsp) == 1) {
return $fp;
}
echo "Request denied, $rsp\n";
return false;
}
echo "Connection failed, $errno, $errstr\n";
return false;
}
此函数returns如果连接成功则为文件指针资源,否则为FALSE。我们可以使用该资源与目标主机通信。
$proxy = "138.204.48.233";
$port = 8080;
$destination = "api.ipify.org:80";
$fp = connect_to_http_proxy($proxy, $port, $destination);
if ($fp) {
fwrite($fp, "GET /?format=json HTTP/1.1\r\nHost: $destination\r\n\r\n");
echo fread($fp, 1024);
fclose($fp);
}
SOCKS5 代理的通信协议稍微复杂一些:
- 客户端连接到代理服务器并发送(至少)三个字节:第一个字节是SOCKS版本,第二个是认证方法的数量,接下来的字节是认证方法(s) ).
- 服务器用两个字节响应,即 SOCKS 版本和所选的身份验证方法。
- 客户端请求连接到目标主机。该请求包含 SOCKS 版本,后跟命令(在本例中为 CONNECT),后跟一个空字节。第四个字节指定地址类型,后面是地址和端口。
- 服务器最终发送十个字节(或七个或 twenty-two,具体取决于目标地址类型)。第二个字节包含状态,如果请求成功,它应该为零。
- 服务器代理所有请求。
更多详情:SOCKS Protocol Version 5.
function connect_to_socks5_proxy($host, $port, $destination) {
$fp = fsockopen($host, $port, $errno, $errstr);
if ($errno == 0) {
fwrite($fp, "[=12=]");
$rsp = fread($fp, 2);
if ($rsp === "[=12=]" ) {
list($host, $port) = explode(":", $destination);
$host = gethostbyname($host); //not required if $host is an IP
$req = "[=12=]" . inet_pton($host) . pack("n", $port);
fwrite($fp, $req);
$rsp = fread($fp, 10);
if ($rsp[1] === "[=12=]") {
return $fp;
}
echo "Request denied, status: " . ord($rsp[1]) . "\n";
return false;
}
echo "Request denied\n";
return false;
}
echo "Connection failed, $errno, $errstr\n";
return false;
}
此函数的工作方式与 connect_to_http_proxy
相同。虽然这两个功能都经过测试,但最好使用库;该代码主要用于教育目的。
SSL 支持和身份验证。
我们无法使用 ssl:// 或 tls:// 协议与 fsockopen
创建 SSL 连接,因为那样会尝试与代理服务器而非目标主机创建 SSL 连接.但是在与代理服务器建立连接后,可以使用 stream_socket_enable_crypto
启用 SSL 并创建与目的地的安全通信通道。这需要禁用对等验证,这可以通过 stream_socket_client
使用自定义上下文来完成。请注意,禁用对等验证可能是一个安全问题。
对于 HTTP 代理,我们可以使用 Proxy-Authenticate
header 添加身份验证。这个header的值是认证类型,后面是用户名和密码,base64编码(基本认证)。
对于 SOCKS5 代理,身份验证过程再次变得更加复杂。看来我们必须将身份验证代码从 0x00
(无需身份验证)更改为 0x02
(USERNAME/PASSWORD 身份验证)。我不清楚如何使用身份验证值创建请求,因此我无法提供示例。
function connect_to_http_proxy($host, $port, $destination, $creds=null) {
$context = stream_context_create(
['ssl'=> ['verify_peer'=> false, 'verify_peer_name'=> false]]
);
$soc = stream_socket_client(
"tcp://$host:$port", $errno, $errstr, 20,
STREAM_CLIENT_CONNECT, $context
);
if ($errno == 0) {
$auth = $creds ? "Proxy-Authorization: Basic ".base64_encode($creds)."\r\n": "";
$connect = "CONNECT $destination HTTP/1.1\r\n$auth\r\n";
fwrite($soc, $connect);
$rsp = fread($soc, 1024);
if (preg_match('/^HTTP\/\d\.\d 200/', $rsp) == 1) {
return $soc;
}
echo "Request denied, $rsp\n";
return false;
}
echo "Connection failed, $errno, $errstr\n";
return false;
}
$host = "proxy IP";
$port = "proxy port";
$destination = "chat.freenode.net:6697";
$credentials = "user:pass";
$soc = connect_to_http_proxy($host, $port, $destination, $credentials);
if ($soc) {
stream_socket_enable_crypto($soc, true, STREAM_CRYPTO_METHOD_ANY_CLIENT);
fwrite($soc,"USER test\nNICK test\n");
echo fread($soc, 1024);
fclose($soc);
}