如何在PHP中手动解析HTTP(S)连接中的主机

时间:2014-06-22 12:19:53

标签: php https

Curl具有手动指定要解析主机的IP的功能。例如:

curl https://www.google.com --resolve "www.google.com:443:173.194.72.112"

这在使用HTTPS时特别有用。如果它只是一个HTTP请求,我可以通过直接指定IP地址并添加主机头来实现相同的目的。但是在HTTPS中会破坏连接,因为SSL证书主机将与IP地址而不是主机头进行比较。

我的问题是,我如何在PHP中做同样的事情?

3 个答案:

答案 0 :(得分:5)

虽然@ deceze的答案是正确的,但实际的例子可能会有用。我需要CURLOPT_RESOLVE,因为我尝试使用额外的Host: www.example.com标头直接连接到IP地址,但由于服务器使用的是SNI,因此无效。

我使用CURLOPT_RESOLVE来解决我的问题。此代码允许我使用我选择的IP地址连接到SNI服务器:

$resolve = array(sprintf(
    "%s:%d:%s", 
    $hostname,
    $port,
    $host_ip
));

$ch = curl_init($url); 
curl_setopt($ch, CURLOPT_RESOLVE, $resolve);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
$result = curl_exec($ch); 
curl_close($ch);

答案 1 :(得分:4)

根据the changelog,在5.5.0中添加了对CURLOPT_RESOLVE的支持 请注意,在撰写本文时,它甚至尚未记录,但根据this bug report,它需要一个数组作为参数。

答案 2 :(得分:0)

即使是一个老问题,值得注意的是,当使用CURLOPT_RESOLVE选项在多个服务器上使用CURL时,在再次使用CURL之前,需要清除DNS缓存,否则CURL将指向不论curl_setopt($ch, CURLOPT_RESOLVE, $resolve);设置如何,都是第一台服务器。

完成此工作的唯一方法是将一个解析字符串添加到$resolve数组中,最后一个使用的服务器前缀为'-':

$servers = [ '192.0.2.1', '192.0.2.2', '192.0.2.3' ];

foreach ($servers as $idx => $server) {

    $resolve = [];

    // Remove the last server used from the DNS cache
    if($idx){
        $last_server = $server[$idx-1];
        $resolve[] = "-example.com:443:{$last_server}";
    }

    // resolve the new server
    $resolve[] = "example.com:443:{$server}";

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, 0);
    curl_setopt($ch, CURLOPT_RESOLVE, $resolve);
    curl_setopt($ch, CURLOPT_URL, "https://example.com/some/path");
    curl_setopt($ch, CURLOPT_VERBOSE, 1);

    $result = curl_exec($ch);
    $info = curl_getinfo($ch);

    echo $info['primary_ip']."\n";
    curl_close($ch);
}

如此处所指出:https://bugs.php.net/bug.php?id=74135