5

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?

Stefano Sanfilippo
  • 32,265
  • 7
  • 79
  • 80
GreatFire
  • 427
  • 1
  • 5
  • 11
  • Probably `CURLOPT_IPRESOLVE`...!? – deceze Jun 22 '14 at 12:28
  • From http://www.php.net//manual/en/function.curl-setopt.php: "Allows an application to select what kind of IP addresses to use when resolving host names. This is only interesting when using host names that resolve addresses using more than one version of IP, possible values are CURL_IPRESOLVE_WHATEVER, CURL_IPRESOLVE_V4, CURL_IPRESOLVE_V6, by default CURL_IPRESOLVE_WHATEVER.". That's not what I want. I want to specify the full IP address. – GreatFire Jun 22 '14 at 12:30
  • You're right, my bad. – deceze Jun 22 '14 at 12:31
  • https://bugs.php.net/bug.php?id=63488 – deceze Jun 22 '14 at 12:37

3 Answers3

8

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

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.

deceze
  • 510,633
  • 85
  • 743
  • 889
1

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

Phoenix
  • 1,256
  • 2
  • 17
  • 25