Search code examples
c++x86visual-studio-2022libcurlschannel

libcurl curl_easy_perform reports "Couldn't resolve host name" (code 6)


I am getting this error deterministically and immediately every time I run the code below.

I am trying to use libcurl that I have built from source using nmap and using the recommended procedure and interestingly enough, I am able to send the same GET request using the built curl binary successfully. I am also able to send this query successfully using Postman.

This is the curl command that I am using (I am using <my.website> in place of the actual host and <token> in place of the actual correct token):

PS> cd C:\curl-8.8.0\builds\libcurl-vc-x86-release-static-ipv6-sspi-schannel\bin
PS> ./curl --header "Authorization: Bearer <token>" https://<my.website>/api/test

... and I am getting back the expected:

Hello back

However, when I try the same in my C++ x86 project in Visual Studio 2022 like this:

#include <curl/curl.h>
#include <iostream>

void test(CURL* curl) {

    // setup
    const char* url = "https://<my.website>/api/test";
    curl_easy_setopt(curl, CURLOPT_URL, url);
    struct curl_slist* headers = NULL;
    headers = curl_slist_append(headers, "Authorization: Bearer <token>");
    setres = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);

    // request
    CURLcode res;
    res = curl_easy_perform(curl);
    std::cerr << "RESULT: " << res << std::endl;

    // request result checking
    if (res != CURLE_OK) {
        std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
    } else {
        std::cout << "POST request sent successfully!" << std::endl;
    }

    // cleanup
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
}


int main() {

    // global initialization
    CURL * curl;
    curl_global_init(CURL_GLOBAL_SSL);

    curl = curl_easy_init();
    if (curl) {
        test(curl);
    } else {
        std::cerr << "INIT FAILED" << std::endl;
    }

    // global cleanup
    curl_global_cleanup();
}

... I get the following output:

* getaddrinfo() thread failed to start
* Could not resolve host: <my.website>
* Closing connection
RESULT: 6
curl_easy_perform() failed: Couldn't resolve host name

(the <my.website> is again just a placeholder; the real output contains the actual website, of course)

This problem appears immediately with basically zero waiting time (non-zero waiting times may indicate DNS problems acccording to some answers I found).

I have built libcurl "8.8.0 Schannel WinIDN" on my Windows 11 in a x86 mode (using Developer Command Prompt for VS 2022). This created the C:\curl-8.8.0\builds\libcurl-vc-x86-release-static-ipv6-sspi-schannel\ with the include, lib and bin subdirectories.

My project is constructed as a x86 project with the following options in properties:

C/C++ / General / Additional Include Directories: C:\curl-8.8.0\builds\libcurl-vc-x86-release-static-ipv6-sspi-schannel\include
C/C++ / Preprocessor / Preprocessor Definitions: CURL_STATICLIB;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
Linker / General / Additional Library Directories: C:\curl-8.8.0\builds\libcurl-vc-x86-release-static-ipv6-sspi-schannel\lib
Linker / Input / Additional Dependencies: libcurl_a.lib;Ws2_32.lib;Wldap32.lib;Crypt32.lib;Normaliz.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)

While trying to figure out what's wrong, I have read this post getaddrinfo thread failed to start #6495 which recommended to execute also the following:

    curl_version_info_data* ver = curl_version_info(CURLVERSION_NOW);
    std::cerr << "VERSION: " << curl_version() << std::endl;
    if (!(ver->features & CURL_VERSION_ASYNCHDNS)) {
        std::cerr << "SYNCHRONOUS" << std::endl;
    } else if (!ver->age || ver->ares_num) {
        std::cerr << "ARES" << std::endl;
    } else {
        std::cerr << "THREADED" << std::endl;
    }

... which outputs:

VERSION: libcurl/8.8.0 Schannel WinIDN
THREADED

Which is what I would expect.


EDIT:

After painstakingly stepping through the libcurl calls, I have found that it ultimately fails on this code:

  listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(listener == CURL_SOCKET_BAD)
    return -1;

I cannot step into the socket function to debug what's happening there.


EDIT 2:

I figured that the socket() method is actually a Windows method so I have created a separate project where I have only added this library:

Linker / Input / Additional Dependencies: Ws2_32.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)

... then even just the trivial code:

#include <iostream>
#include <winsock.h>

int main() {

    // Create a socket (IPv4, TCP)
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == -1) {
        std::cerr << "Failed to create socket" << std::endl;
        return 1;
    }
    std::cerr << "OK" << std::endl;

}

... results in:

Failed to create socket

Can someone please tell me what I am doing wrong?


Solution

  • This line is the issue:

    curl_global_init(CURL_GLOBAL_SSL);
    

    The curl_global_init function is responsible for initializing a number of subsystems. Normally you call it with CURL_GLOBAL_ALL or CURL_GLOBAL_DEFAULT, which include the CURL_GLOBAL_WIN32 flag.

    That flag initializes Winsock for you:

    Initialize the Win32 socket libraries.

    The implication here is that if this bit is not set, the initialization of winsock has to be done by the application or you risk getting undefined behaviors. This option exists for when the initialization is handled outside of libcurl so there is no need for libcurl to do it again.