Как вручную разрешить узлы в HTTP (ах) соединениях в PHP

#php #https

#php #https

Вопрос:

Curl имеет функцию для ручного указания, на какой IP-адрес разрешить хост. Например:

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

Это особенно полезно при использовании HTTPS. Если бы это был просто HTTP-запрос, я мог бы добиться того же, указав IP-адрес напрямую и добавив заголовок host. Но в HTTPS это приведет к разрыву соединения, поскольку хост SSL-сертификата будет сравниваться с IP-адресом, а не с заголовком хоста.

Мой вопрос в том, как я могу сделать то же самое в PHP?

Комментарии:

1. Возможно CURLOPT_IPRESOLVE …!?

2. Из php.net//manual/en/function.curl-setopt.php : «Позволяет приложению выбирать, какие IP-адреса использовать при разрешении имен хостов. Это интересно только при использовании имен хостов, которые разрешают адреса, используя более одной версии IP, возможными значениями являются CURL_IPRESOLVE_WHATEVER, CURL_IPRESOLVE_V4, CURL_IPRESOLVE_V6, по умолчанию CURL_IPRESOLVE_WHATEVER.». Это не то, что я хочу. Я хочу указать полный IP-адрес.

3. Вы правы, мой плохой.

4. bugs.php.net/bug.php?id=63488

Ответ №1:

Хотя ответ @deceze верен, живой пример может быть полезен. Мне нужно CURLOPT_RESOLVE было, потому что я пытался подключиться напрямую к IP-адресу с дополнительным Host: www.example.com заголовком, но поскольку сервер использовал SNI, это не сработало.

Раньше я CURLOPT_RESOLVE решал свою проблему. Этот код позволяет мне подключаться к серверу SNI по выбранному мной IP-адресу:

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

Ответ №2:

Согласно списку изменений, поддержка CURLOPT_RESOLVE была добавлена в версии 5.5.0.
Обратите внимание, что на момент написания статьи это еще даже не задокументировано, но, согласно этому сообщению об ошибке, в качестве аргумента принимается массив.

Ответ №3:

Несмотря на то, что это старый вопрос, стоит заметить, что при использовании CURL на нескольких серверах с использованием этой CURLOPT_RESOLVE опции существует кэш DNS, который необходимо очистить перед повторным использованием CURL, иначе 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