hostip: cache negative name resolves

Hold them for half the normal lifetime. Helps when told to transfer N
URLs in quick succession that all use the same non-resolving hostname.

Done by storing a DNS entry with a NULL pointer for 'addr'.

Previously an attempt was made in #12406 by Björn Stenberg that was
ultimately never merged.

Closes #18157
This commit is contained in:
Daniel Stenberg 2025-08-04 00:06:03 +02:00
parent 06c12cc08b
commit df2b4ccc22
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
11 changed files with 155 additions and 19 deletions

View file

@ -252,7 +252,7 @@ test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 \
test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \
test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \
test2088 test2089 \
test2100 test2101 test2102 test2103 \
test2100 test2101 test2102 test2103 test2104 \
\
test2200 test2201 test2202 test2203 test2204 test2205 \
\

49
tests/data/test2104 Normal file
View file

@ -0,0 +1,49 @@
<testcase>
<info>
<keywords>
DNS cache
</keywords>
</info>
#
# Server-side
<reply>
<dns>
</dns>
</reply>
#
# Client-side
<client>
<server>
dns
</server>
<features>
override-dns
</features>
<name>
Get three URLs with bad host name - cache
</name>
<setenv>
CURL_DNS_SERVER=127.0.0.1:%DNSPORT
</setenv>
<command>
http://examplehost.example/%TESTNUMBER http://examplehost.example/%TESTNUMBER http://examplehost.example/%TESTNUMBER
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
# curl: (6) Could not resolve host: examplehost.example
<errorcode>
6
</errorcode>
# Ignore HTTPS requests here
<dns host="examplehost.example QTYPE A">
QNAME examplehost.example QTYPE A
QNAME examplehost.example QTYPE AAAA
</dns>
</verify>
</testcase>

View file

@ -36,7 +36,7 @@ lib%TESTNUMBER
resolver start callback
</name>
<command>
http://%HOSTIP:%HTTPPORT/%TESTNUMBER
http://failthis/%TESTNUMBER http://%HOSTIP:%HTTPPORT/%TESTNUMBER
</command>
</client>

View file

@ -74,7 +74,7 @@ static CURLcode test_lib655(const char *URL)
goto test_cleanup;
}
/* First set the URL that is about to receive our request. */
/* Set the URL that is about to receive our first request. */
test_setopt(curl, CURLOPT_URL, URL);
test_setopt(curl, CURLOPT_RESOLVER_START_DATA, TEST_DATA_STRING);
@ -91,6 +91,9 @@ static CURLcode test_lib655(const char *URL)
goto test_cleanup;
}
/* Set the URL that receives our second request. */
test_setopt(curl, CURLOPT_URL, libtest_arg2);
test_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_pass);
/* this should succeed */

View file

@ -1674,6 +1674,29 @@ sub singletest_check {
}
}
my @dnsd = getpart("verify", "dns");
if(@dnsd) {
# we're supposed to verify a dynamically generated file!
my %hash = getpartattr("verify", "dns");
my $hostname=$hash{'host'};
# Verify the sent DNS requests
my @out = loadarray("$logdir/dnsd.input");
my @sverify = sort @dnsd;
my @sout = sort @out;
if($hostname) {
# when a hostname is set, we filter out requests to just this
# pattern
@sout = grep {/$hostname/} @sout;
}
$res = compare($runnerid, $testnum, $testname, "DNS", \@sout, \@sverify);
if($res) {
return -1;
}
}
# accept multiple comma-separated error codes
my @splerr = split(/ *, */, $errorcode);
my $errok;

View file

@ -64,6 +64,19 @@ static int qname(const unsigned char **pkt, size_t *size)
#define QTYPE_AAAA 28
#define QTYPE_HTTPS 0x41
static const char *type2string(unsigned short qtype)
{
switch(qtype) {
case QTYPE_A:
return "A";
case QTYPE_AAAA:
return "AAAA";
case QTYPE_HTTPS:
return "HTTPS";
}
return "<unknown>";
}
/*
* Handle initial connection protocol.
*
@ -125,8 +138,7 @@ static int store_incoming(const unsigned char *data, size_t size,
fprintf(server, "Z: %x\n", (id & 0x70) >> 4);
fprintf(server, "RCODE: %x\n", (id & 0x0f));
#endif
qd = get16bit(&data, &size);
fprintf(server, "QDCOUNT: %04x\n", qd);
(void) get16bit(&data, &size);
data += 6; /* skip ANCOUNT, NSCOUNT and ARCOUNT */
size -= 6;
@ -136,14 +148,13 @@ static int store_incoming(const unsigned char *data, size_t size,
qptr = data;
if(!qname(&data, &size)) {
fprintf(server, "QNAME: %s\n", name);
qd = get16bit(&data, &size);
fprintf(server, "QTYPE: %04x\n", qd);
fprintf(server, "QNAME %s QTYPE %s\n", name, type2string(qd));
*qtype = qd;
logmsg("Question for '%s' type %x", name, qd);
logmsg("Question for '%s' type %x / %s", name, qd,
type2string(qd));
qd = get16bit(&data, &size);
logmsg("QCLASS: %04x\n", qd);
(void) get16bit(&data, &size);
*qlen = qsize - size; /* total size of the query */
memcpy(qbuf, qptr, *qlen);
@ -618,7 +629,7 @@ static int test_dnsd(int argc, char **argv)
clear_advisor_read_lock(loglockfile);
}
logmsg("end of one transfer");
/* logmsg("end of one transfer"); */
}
dnsd_cleanup: