--- /dev/null
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+from collections.abc import AsyncGenerator
+
+from isctest.asyncserver import (
+ AsyncDnsServer,
+ DnsProtocol,
+ DnsResponseSend,
+ QueryContext,
+ ResponseAction,
+ ResponseDrop,
+ ResponseHandler,
+)
+
+
+class TcpOnlyHandler(ResponseHandler):
+ async def get_responses(
+ self, qctx: QueryContext
+ ) -> AsyncGenerator[ResponseAction, None]:
+ if qctx.protocol == DnsProtocol.TCP:
+ yield DnsResponseSend(qctx.response)
+ else:
+ yield ResponseDrop()
+
+
+def main() -> None:
+ server = AsyncDnsServer()
+ server.install_response_handler(TcpOnlyHandler())
+ server.run()
+
+
+if __name__ == "__main__":
+ main()
--- /dev/null
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+@ 3600 SOA . . 1 1 1 1 1
+@ 3600 NS ns
+ns 3600 A 10.53.0.4
+foo 3600 A 127.0.0.1
ns.nil. 300 A 10.53.0.1
example. 300 NS ns.example.
ns.example. 300 A 10.53.0.2
+tcp-only. 300 NS ns.tcp-only.
+ns.tcp-only. 300 A 10.53.0.4
--- /dev/null
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+import dns.message
+import pytest
+
+import isctest
+
+pytestmark = pytest.mark.extra_artifacts(
+ [
+ "ans*/ans.run",
+ ]
+)
+
+
+def test_tcponly_not_resolved():
+ """
+ An authoritative server that only answers over TCP is unreachable
+ when its zone is queried over UDP: the resolver does not transparently
+ fall back to TCP after UDP timeouts. (This confirms the expected behavior
+ for this commit; TCP fallback will be restored in the next.)
+ """
+ msg = dns.message.make_query("foo.tcp-only.", "A")
+ res = isctest.query.udp(msg, "10.53.0.2", timeout=15)
+ isctest.check.servfail(res)
if (fctx->timeout && (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
isc_sockaddr_t *sockaddr = &query->addrinfo->sockaddr;
- struct tried *tried;
+ struct tried *tried = triededns(fctx, sockaddr);
/*
* If this is the first timeout for this server in this
* fetch context, try setting EDNS UDP buffer size to
* the largest UDP response size we have seen from this
* server so far.
- *
- * If this server has already timed out twice or more in
- * this fetch context, force TCP.
*/
- if ((tried = triededns(fctx, sockaddr)) != NULL) {
- if (tried->count == 1U) {
- hint = dns_adb_getudpsize(fctx->adb,
- query->addrinfo);
- } else if (tried->count >= 2U) {
- if ((query->options & DNS_FETCHOPT_TCP) == 0) {
- /*
- * Inform the ADB that we're ending a
- * UDP fetch, and turn the query into
- * a TCP query.
- */
- dns_adb_endudpfetch(fctx->adb,
- query->addrinfo);
- query->options |= DNS_FETCHOPT_TCP;
- }
- }
+ if (tried != NULL && tried->count == 1U) {
+ hint = dns_adb_getudpsize(fctx->adb, query->addrinfo);
}
}
fctx->timeout = false;