Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manually resolve hosts in HTTP(S) connections in PHP

Tags:

php

https

Curl has a feature for manually specifying which IP to resolve a host to. For example:

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

This is especially useful when using HTTPS. If it was just a HTTP request, I could have achieved the same by specifying the IP address directly, and adding a host header. But in HTTPS that would break the connection since the SSL certificate host would be compared to the IP address and not the host header.

My question is, how can I do the same thing in PHP?

like image 400
GreatFire Avatar asked Jun 22 '14 12:06

GreatFire


3 Answers

Although @deceze's answer is correct, a live example might be useful. I needed CURLOPT_RESOLVE because I was trying to connect directly to the IP address with an additional Host: www.example.com header, but since the server was using SNI, this didn't work.

I used CURLOPT_RESOLVE to solve my problem. This code allows me to connect to the SNI server on a IP address of my choosing:

$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);
like image 79
Luc van Donkersgoed Avatar answered Sep 28 '22 03:09

Luc van Donkersgoed


According to the changelog, support for CURLOPT_RESOLVE was added in 5.5.0.
Note that at the time of writing it's not even documented yet, but according to this bug report it takes an array as argument.

like image 35
deceze Avatar answered Sep 28 '22 03:09

deceze


Even though it's an old question is worth noticing that when using CURL to multiple servers using the CURLOPT_RESOLVE option, there is a DNS cache which needs to be cleared before using CURL again, otherwise the CURL will point to the first server regardless of the curl_setopt($ch, CURLOPT_RESOLVE, $resolve); setting.

The only way to make this work is to add to the $resolve array a resolve string with the last sever used prefixed with a '-':

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

As pointed here: https://bugs.php.net/bug.php?id=74135

like image 34
Phoenix Avatar answered Sep 28 '22 02:09

Phoenix