【问题标题】:How to use fsockopen (or compatible) with SOCKS proxies in PHP?如何在 PHP 中将 fsockopen(或兼容)与 SOCKS 代理一起使用?
【发布时间】:2019-07-18 17:01:23
【问题描述】:

我使用 fsockopen 和相关函数在 PHP 中编写了一个非恶意、非垃圾邮件的 IRC 机器人。有用。但是,问题是我需要支持代理(最好是 SOCKS5,但如果这更容易,HTTP 也可以,我对此表示怀疑)。 fsockopen 不支持此功能。

我浏览了“PHP fsockopen 代理”和相关查询的所有搜索结果。我知道所有不起作用的东西,所以请不要链接到其中之一。

fsockopen 的 PHP 手册页提到函数 stream_socket_client()

类似,但提供了更丰富的选项集,包括非阻塞连接和提供流上下文的能力。

一开始这听起来很有希望,据说可以让我将 fsockopen 调用替换为 stream_socket_client 并指定一个代理,也许是通过“流上下文”......但事实并非如此。或者是吗?我对manual 感到很困惑。

请注意必须是PHP代码解决方案;我不能为“Proxifier”付费或使用任何其他外部软件来“环绕”这个。

我尝试过的所有事情似乎总是导致我从服务器获得一堆空输出,然后套接字被强制关闭。请注意,当我在同一网络中使用 HexChat(普通 IRC 客户端)时,我尝试使用的代理可以正常工作,因此问题不是代理本身。

【问题讨论】:

  • 我有一种强烈的感觉,这个问题以前也有过——这是怎么回事?
  • 有权访问它的人:这被莫名其妙地删除了stackoverflow.com/questions/54502266/…
  • 我在邮件中解释了原因,但被“karel”删除了。
  • 那么,您认为删除旧问题并发布新问题会有所帮助吗?
  • 重点是:删除它并发布一个新问题,您将失去知名度——您不会获得任何收益。让问题保持开放

标签: php windows proxy irc fsockopen


【解决方案1】:

据我所知,没有默认选项可以为 fsockopenstream_socket_client 设置 SOCKS 或 HTTP 代理(我们可以创建上下文并在 HTTP 选项中设置代理,但这不适用于 @ 987654324@)。但是我们可以手动建立连接。

连接到 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;
}

如果连接成功,此函数返回文件指针资源,否则返回 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 版本,第二个是身份验证方法的数量,下一个字节是身份验证方法。
  • 服务器响应两个字节,即 SOCKS 版本和选择的身份验证方法。
  • 客户端请求连接到目标主机。该请求包含 SOCKS 版本,后跟命令(在本例中为 CONNECT),后跟空字节。第四个字节指定地址类型,后面是地址和端口。
  • 服务器最终发送 10 个字节(或 7 个或 22 个字节,具体取决于目标地址类型)。第二个字节包含状态,如果请求成功,它应该为零。
  • 服务器代理所有请求。

更多详情:SOCKS Protocol Version 5

function connect_to_socks5_proxy($host, $port, $destination) {
    $fp = fsockopen($host, $port, $errno, $errstr);
    if ($errno == 0) {
        fwrite($fp, "\05\01\00");
        $rsp = fread($fp, 2);
        if ($rsp === "\05\00" ) {
            list($host, $port) = explode(":", $destination);
            $host = gethostbyname($host); //not required if $host is an IP
            $req = "\05\01\00\01" . inet_pton($host) . pack("n", $port);
            fwrite($fp, $req);
            $rsp = fread($fp, 10);
            if ($rsp[1] === "\00") {
                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 标头添加身份验证。该头的值为认证类型,后跟用户名和密码,base64编码(Basic Authentication)。

对于 SOCKS5 代理,身份验证过程再次变得更加复杂。看来我们必须将身份验证代码从0x00(不需要身份验证)更改为0x02(用户名/密码身份验证)。我不清楚如何使用身份验证值创建请求,因此我无法提供示例。

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);
}

【讨论】:

  • 感谢您的回复。我真的很高兴,因为我认为这是一个失败的事业。但是,我无法让您的功能正常工作。使用 HTTP 代理功能,它立即输出:“Request denied, HTTP/1.1 200 Connection established Connection failed, 0” 使用 SOCKS 1,它稍等片刻,然后输出: > USER > NICK
  • 我将您的“200 OK”部分更改为“200 Connection established”,因为这显然是它的响应(不确定这本身是否是代理的一些错误/非标准行为?) ,然后它“工作”......从某种意义上说,它输出一个空行并关闭连接,这次没有错误......但它不起作用。
  • 很高兴听到它成功了!恐怕SSl不像使用“ssl://”那么简单,我们想要一个与IRC服务器的SSL连接,而不是代理服务器。我正在尝试stream_socket_enable_crypto,如果可以的话,我会更新我的答案。
  • 是的,我想 FALSE 在失败的情况下会更合适。我们可以使用 Proxy-Authenticate 标头为 HTTP 代理添加基本身份验证。对于 SOCKS5 代理,在身份验证方法中 USERNAME/PASSWORD 代码似乎是 0x02,但文章没有提到如何发送 USERNAME/PASSWORD 值。
  • 添加了一个带有 SSL 和基本身份验证的 HTTP 代理示例。我设法从 chat.freenode.net:6697 获得了新代码的回复。不知道如何在 SOCKS5 中使用身份验证,但 SSL 通信的代码与 HTTP 相同:使用 stream_socket_client 创建连接,然后使用 stream_socket_enable_crypto 启用 SSL。
猜你喜欢
  • 2012-06-08
  • 2014-05-19
  • 2021-05-22
  • 2012-10-18
  • 1970-01-01
  • 2012-11-06
  • 1970-01-01
  • 2015-07-22
  • 2015-05-05
相关资源
最近更新 更多