mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-10-20 11:29:35 +00:00
Answer queued TCP queries after a stale answer
When `tcp_request()` answers from stale cache data, it closes the connection right away, so that the child process can refresh the name and exit. A query the client has already sent on that connection is never read, and its answer is lost. glibc runs into this: after a truncated UDP reply, it sends the A and AAAA queries together on one TCP connection, gets EOF after the first answer and fails the lookup without retrying the second query. In a test with both answers expired, 7 of 20 such lookups lost the second answer. Close the connection only if the client has not sent anything else yet. Otherwise, refresh the name and go on answering the queries on the connection, as for any other forwarded query. Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
1 parent
03fd0c19ef
commit
bc80b93cd1
1 file changed
+23
-7
+23
-7
@@ -2406,7 +2406,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
unsigned char *pheader;
|
||||
unsigned int mark = 0;
|
||||
int have_mark = 0;
|
||||
int first, last, filtered, do_stale = 0;
|
||||
int first, last, filtered, do_stale = 0, closed = 0;
|
||||
struct iovec out_iov[2];
|
||||
|
||||
bigbuff->iov_base = NULL;
|
||||
@@ -2742,8 +2742,16 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
}
|
||||
}
|
||||
|
||||
/* Fresh data is in the cache now. Go on with the queries the client
|
||||
had already sent, if the connection is still open. */
|
||||
if (do_stale)
|
||||
break;
|
||||
{
|
||||
if (closed)
|
||||
break;
|
||||
do_stale = 0;
|
||||
daemon->log_source_addr = &peer_addr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* In case of local answer or no connections made. */
|
||||
if (m == 0)
|
||||
@@ -2792,19 +2800,27 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
/* If we answered with stale data, this process will now try and get fresh data into
|
||||
the cache and cannot therefore accept new queries. Close the incoming
|
||||
connection to signal that to the client. Then set do_stale and loop round
|
||||
once more to try and get fresh data, after which we exit. */
|
||||
once more to try and get fresh data, after which we exit. A client which
|
||||
has already sent its next query is waiting for the answer and would lose
|
||||
it, so keep the connection open then and answer it after the refresh. */
|
||||
if (stale)
|
||||
{
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
char c;
|
||||
|
||||
if (recv(confd, &c, 1, MSG_PEEK | MSG_DONTWAIT) != 1)
|
||||
{
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
closed = 1;
|
||||
}
|
||||
do_stale = 1;
|
||||
/* Don't mark the query with the source when refreshing stale data. */
|
||||
daemon->log_source_addr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we ran once to get fresh data, confd is already closed. */
|
||||
if (!do_stale)
|
||||
/* If we ran once to get fresh data, confd may be closed already. */
|
||||
if (!closed)
|
||||
{
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
|
||||
Reference in new issue
Block a user