0

I need to regularly and randomly test with Linux/C++/libcurl the responses of several servers that are available through a single DNS name, such as

$ host example.com
n1.example.com 1.2.3.4
n2.example.com 1.2.3.5
n3.example.com 1.2.3.6

The list changes. When I try https://example.com libcurl always uses the same IP for the span of the TTL, and I cannot switch to the next host. There is CURLOPT_DNS_CACHE_TIMEOUT setopt, but setting it to zero does not help - even if I fully recreate easycurl object I still get the same IP. Therefore, this does not help: curl - How to set up TTL for dns cache & How to clear the curl cache

I can of course manually resolve DNS names and iterate, but are there any options? Polling randomly is okay. I see curl uses c-ares. Is there a way to clean up the cache there and will it help?

  • If the DNS returns the persistent order of IP addresses, curl uses that order starting from the first entry. – 273K Jul 14 '21 at 13:29
  • Yeah, exactly.. There are more interesting details I have found out. Appears, I can do what I need with curl binary, but not with libcurl, which is strange. I will update my text – Aviaconstructor Jul 14 '21 at 18:21

1 Answers1

0

I cannot do exactly what I need with curl without doing a resolve by myself, but there are findings for the others to share with:

First of all, as a well-written TCP client, curl will try the hosts from the DNS list from top to bottom until a successful connection is made. Since then it will use that host even if it returns some higher level error (such as SSL error or HTTP 500). This is good for all major cases.

Curl command line of newer curl versions has --retry and --retry-all-errors - but there are no such things in libcurl, unfortunately. The feature is being enhanced right now, and there is no release yet as of 2021-07-14 that will enumerate all DNS hosts until there is one that returns HTTP 200. Instead, the released curl versions (I tried 7.76 and 7.77) will always do retries with the same host. But the nightly build (2021-07-14) does enumerate all DNS hosts. Here is how it behaves for two retries and three inexisting hosts (note, the retries will happen if any host returns HTTP 5xx):

$ ./src/curl http://nohost.sureno --trace - --retry 2 --retry-all-errors
    == Info:   Trying 192.168.1.112:80...
    == Info: connect to 192.168.1.112 port 80 failed: No route to host
    == Info:   Trying 192.168.1.113:80...
    == Info: connect to 192.168.1.113 port 80 failed: No route to host
    == Info:   Trying 192.168.1.114:80...
    == Info: connect to 192.168.1.114 port 80 failed: No route to host
    == Info: Failed to connect to nohost.sureno port 80 after 9210 ms: No route to host
    == Info: Closing connection 0
    curl: (7) Failed to connect to nohost.sureno port 80 after 9210 ms: No route to host
    Warning: Problem (retrying all errors). Will retry in 1 seconds. 2 retries
    Warning: left.
    == Info: Hostname nohost.sureno was found in DNS cache
    == Info:   Trying 192.168.1.112:80...
    == Info: connect to 192.168.1.112 port 80 failed: No route to host
    == Info:   Trying 192.168.1.113:80...
    == Info: connect to 192.168.1.113 port 80 failed: No route to host
    == Info:   Trying 192.168.1.114:80...
    == Info: connect to 192.168.1.114 port 80 failed: No route to host
    == Info: Failed to connect to nohost.sureno port 80 after 9206 ms: No route to host
    == Info: Closing connection 1
    curl: (7) Failed to connect to nohost.sureno port 80 after 9206 ms: No route to host
    Warning: Problem (retrying all errors). Will retry in 2 seconds. 1 retries

This behavior can be very helpful for the users of libcurl, but unfortunately, these retry flags presently have no mapping to curl_easy_setopt. And as a result, if you give --libcurl to the command line you will not see any retry-related code

  • Each retry is just a for-loop calling curl_easy_performe(). – 273K Jul 14 '21 at 19:01
  • Yeah, this is how curl 7.76 behavior can be implemented easily with `libcurl`. What many would prefer is how the current nightly build does the retries going from one host to the next - reproducing it in libcurl requires doing a resolve manually and feeding in the IP address to `CURLOPT_RESOLVE` - one at a time of retry. I see you are contributing to curl - cudos! – Aviaconstructor Jul 15 '21 at 07:33