libcurl - прямое соединение с IP

Подскажите пожалуйста, вот пытаюсь сделать запрос на сервер с libcurl не через доменное - ru.stackoverflow.com, а через указание прямого IP-шника: 151.101.65.69

В принципе беру чисто пример из документации и подставляю туда свои данные для ru.stackoverflow.com:

struct curl_slist* host = NULL;
host = curl_slist_append(NULL, "mysite.com:777:151.101.65.69"); //IP - ru.stackoverflow.com
curl_easy_setopt(CURL_p, CURLOPT_RESOLVE, host);
curl_easy_setopt(CURL_p, CURLOPT_URL, "mysite.com");

Однако - CURLOPT_RESOLVE - или работает некорректно или я не правильно понял, как оно должно работать.

Я предполагал, что опция CURLOPT_RESOLVE - должна, как бы виртуально создать "запись в DNS" о указанном Хосте и соответствующим указном IP-адресе.

И когда, я вызываю уже CURLOPT_URL, то URL указанный, как "mysite.com" будет искаться среди "виртуального DNS", однако - этого не происходит и идет реальный resolve URL через DNS и подключение к IP-адресу именно mysite.com.

PS:Разобрался, для меня не сразу было очевидно:

CURLOPT_RESOLVE - создает некий "виртуальный либкуровский кеш DNS", с помощью curl_slist_append я добавляю туда запись к примеру"mysite.com:777:151.101.65.69".

Далее я взываю CURLOPT_URL, после чего данный указанный URL сначала ищется в этом "виртуальном DNS`е", если находится соответствие, то запрос идет именно на "mysite.com:777:151.101.65.69", если не находится, то делается реальный запрос к реальному DNS.

Но тут оказался нюанс, который подсказал Kotomi и который я не сразу понял: если я в CURLOPT_URL - указал "mysite.com", то это означает, mysite.com:80, если бы указал https:\\mysite.com, то означало бы естественно mysite.com:443, в итоге получилось, что при вызове CURLOPT_URL - запись mysite:80.com искалась среди "виртуального DNS" среди записи mysite.com:777 и в итоге соответствие не находилось, и libcurl делал реальный запрос к DNS на ресолв mysite.com, и оказалось, что такой сайт действительно есть, и DNS возвращало реальный IP-шник и соответвенно страницу я получал именно этого сайта.


Ответы (1 шт):

Автор решения: KoVadim

Когда то в далекие-далекие времена на одном сервере был один сайт. И если обратится по айпи, то сервер с легкостью мог отдать содержимое этого сайта. Но пришли другие времена и иногда одному сайту нужно десяток серверов, а иногда на одном сервере может лежать (лежать:) ) десяток сайтов. И вот в этом втором случае возникает вопрос, а как серверу узнать, какой именно сайт хочет клиент. Ведь домен был преобразован в айпи и к нему уже пришли с айпи.

Умные дядьки подумали и решили добавить прям в заголовки доменное имя, добавив для этого заголовок Host. Сервер (то есть apache/nginx или подобное) теперь мог заглянуть внутрь (а он это и так делает обычно, для разных целей), посмотреть желаемый хост и перенаправить/поправить запрос. Все, проблема решена.

Некоторые сайты (такие как stackoverflow) пошли ещё дальше, они одновременно на нескольких серверах и несколько доменов. Это достаточно удобно.

Вернемся к нашим баранам. Как же сделать запрос по айпи и при этом получить страницу. Вот Вам минимальный пример с моими комментариями.

#include <curl/curl.h>

int main(int argc, char *argv[])
{
  CURLcode ret;
  CURL *hnd;
  struct curl_slist *slist1;

  slist1 = NULL;
  // вот оно, самое важное!!!
  slist1 = curl_slist_append(slist1, "Host: ru.stackoverflow.com");

  // инициализируем по классике
  hnd = curl_easy_init();
  curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L);
  curl_easy_setopt(hnd, CURLOPT_URL, "https://151.101.65.69/");
  curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
  // добавим наш хедер
  curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, slist1);
  curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/7.74.0");
  curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
  // у меня не хотело, просило сертификаты
  curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS);
  curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
  curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
  // весь код взят с другого примера, возможно тут есть лишнее
  curl_easy_setopt(hnd, CURLOPT_FTP_SKIP_PASV_IP, 1L);
  curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);

  ret = curl_easy_perform(hnd);

  curl_easy_cleanup(hnd);
  hnd = NULL;
  curl_slist_free_all(slist1);
  slist1 = NULL;

  return (int)ret;
}

Если же хочется использовать CUROLOPT_RESOLVE, то можно просто серединку поменять на такое

slist1 = NULL;
slist1 = curl_slist_append(slist1, "ru.stackoverflow.com:443:151.101.65.69:443");

hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L);
curl_easy_setopt(hnd, CURLOPT_URL, "https://ru.stackoverflow.com/");
→ Ссылка