From 679e42967bf43f9ed0fab67a7a950f2a98f2c1a1 Mon Sep 17 00:00:00 2001 From: Andreas Wolf Date: Mon, 18 May 2026 10:35:07 +0200 Subject: [PATCH 1/2] Issue 5676: Introduced useTcpForFallbackDnsResolving --- .../dns/AddressResolverOptionsConverter.java | 6 +++++ .../core/dns/AddressResolverOptions.java | 26 +++++++++++++++++++ .../dns/impl/DnsAddressResolverProvider.java | 3 ++- .../io/vertx/tests/dns/NameResolverTest.java | 5 ++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/vertx-core/src/main/generated/io/vertx/core/dns/AddressResolverOptionsConverter.java b/vertx-core/src/main/generated/io/vertx/core/dns/AddressResolverOptionsConverter.java index d397c670732..4d945062ba5 100644 --- a/vertx-core/src/main/generated/io/vertx/core/dns/AddressResolverOptionsConverter.java +++ b/vertx-core/src/main/generated/io/vertx/core/dns/AddressResolverOptionsConverter.java @@ -102,6 +102,11 @@ static void fromJson(Iterable> json, Address obj.setRoundRobinInetAddress((Boolean)member.getValue()); } break; + case "useTcpForFallbackDnsResolving": + if (member.getValue() instanceof Boolean) { + obj.setUseTcpForFallbackDnsResolving((Boolean)member.getValue()); + } + break; } } } @@ -141,5 +146,6 @@ static void toJson(AddressResolverOptions obj, java.util.Map jso json.put("ndots", obj.getNdots()); json.put("rotateServers", obj.isRotateServers()); json.put("roundRobinInetAddress", obj.isRoundRobinInetAddress()); + json.put("useTcpForFallbackDnsResolving", obj.isUseTcpForFallbackDnsResolving()); } } diff --git a/vertx-core/src/main/java/io/vertx/core/dns/AddressResolverOptions.java b/vertx-core/src/main/java/io/vertx/core/dns/AddressResolverOptions.java index fa5c2a2534d..da7747d14c7 100644 --- a/vertx-core/src/main/java/io/vertx/core/dns/AddressResolverOptions.java +++ b/vertx-core/src/main/java/io/vertx/core/dns/AddressResolverOptions.java @@ -116,6 +116,11 @@ public class AddressResolverOptions { */ public static final boolean DEFAULT_ROUND_ROBIN_INET_ADDRESS = false; + /** + * The default whether to use TCP for DNS resolving if resolving via UDP times out = false + */ + public static final boolean DEFAULT_USE_TCP_FOR_FALLBACK_DNS_RESOLVING = false; + private String hostsPath; private Buffer hostsValue; private TimeUnit hostsRefreshPeriodUnit; @@ -132,6 +137,7 @@ public class AddressResolverOptions { private int ndots; private boolean rotateServers; private boolean roundRobinInetAddress; + private boolean useTcpForFallbackDnsResolving; public AddressResolverOptions() { servers = DEFAULT_SERVERS; @@ -148,6 +154,7 @@ public AddressResolverOptions() { roundRobinInetAddress = DEFAULT_ROUND_ROBIN_INET_ADDRESS; hostsRefreshPeriodUnit = DEFAULT_HOSTS_REFRESH_PERIOD_UNIT; hostsRefreshPeriod = DEFAULT_HOSTS_REFRESH_PERIOD; + useTcpForFallbackDnsResolving = DEFAULT_USE_TCP_FOR_FALLBACK_DNS_RESOLVING; } public AddressResolverOptions(AddressResolverOptions other) { @@ -167,6 +174,7 @@ public AddressResolverOptions(AddressResolverOptions other) { this.ndots = other.ndots; this.rotateServers = other.rotateServers; this.roundRobinInetAddress = other.roundRobinInetAddress; + this.useTcpForFallbackDnsResolving = other.useTcpForFallbackDnsResolving; } public AddressResolverOptions(JsonObject json) { @@ -530,6 +538,24 @@ public AddressResolverOptions setRoundRobinInetAddress(boolean roundRobinInetAdd return this; } + /** + * @return the value {@code true} when using TCP for DNS resolving as a fallback is enabled when resolving via + * UDP times out. + */ + public boolean isUseTcpForFallbackDnsResolving() { + return useTcpForFallbackDnsResolving; + } + + /** + * Set to {@code true} to enable using TCP as a fallback for DNS resolving when using UDP times out. + * + * @return a reference to this, so the API can be used fluently + */ + public AddressResolverOptions setUseTcpForFallbackDnsResolving(boolean useTcpForFallbackDnsResolving) { + this.useTcpForFallbackDnsResolving = useTcpForFallbackDnsResolving; + return this; + } + public JsonObject toJson() { JsonObject json = new JsonObject(); AddressResolverOptionsConverter.toJson(this, json); diff --git a/vertx-core/src/main/java/io/vertx/core/dns/impl/DnsAddressResolverProvider.java b/vertx-core/src/main/java/io/vertx/core/dns/impl/DnsAddressResolverProvider.java index 3f4dfa78067..bc187fb83dd 100644 --- a/vertx-core/src/main/java/io/vertx/core/dns/impl/DnsAddressResolverProvider.java +++ b/vertx-core/src/main/java/io/vertx/core/dns/impl/DnsAddressResolverProvider.java @@ -107,7 +107,8 @@ private DnsAddressResolverProvider(VertxInternal vertx, AddressResolverOptions o DnsNameResolverBuilder builder = new DnsNameResolverBuilder(); builder.hostsFileEntriesResolver(this); builder.datagramChannelFactory(vertx.transport().datagramChannelFactory()); - builder.socketChannelFactory(() -> (SocketChannel) vertx.transport().channelFactory(false).newChannel()); + builder.socketChannelFactory(() -> (SocketChannel) vertx.transport().channelFactory(false).newChannel(), + options.isUseTcpForFallbackDnsResolving()); builder.nameServerProvider(nameServerAddressProvider); builder.queryServerAddressStream(new ThreadLocalNameServerAddressStream(nameServerAddressProvider, "")); builder.optResourceEnabled(options.isOptResourceEnabled()); diff --git a/vertx-core/src/test/java/io/vertx/tests/dns/NameResolverTest.java b/vertx-core/src/test/java/io/vertx/tests/dns/NameResolverTest.java index 54a8b82c157..d2243c2c8bc 100644 --- a/vertx-core/src/test/java/io/vertx/tests/dns/NameResolverTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/dns/NameResolverTest.java @@ -196,6 +196,7 @@ public void testOptions() { assertEquals(AddressResolverOptions.DEFAULT_RD_FLAG, options.getRdFlag()); assertEquals(AddressResolverOptions.DEFAULT_NDOTS, options.getNdots()); assertEquals(AddressResolverOptions.DEFAULT_SEARCH_DOMAINS, options.getSearchDomains()); + assertEquals(AddressResolverOptions.DEFAULT_USE_TCP_FOR_FALLBACK_DNS_RESOLVING, options.isUseTcpForFallbackDnsResolving()); boolean optResourceEnabled = TestUtils.randomBoolean(); List servers = Arrays.asList("1.2.3.4", "5.6.7.8"); @@ -210,11 +211,13 @@ public void testOptions() { for (int i = 0; i < 2; i++) { searchDomains.add(TestUtils.randomAlphaString(15)); } + boolean useTcpForFallbackDnsResolving = true; assertSame(options, options.setOptResourceEnabled(optResourceEnabled)); assertSame(options, options.setServers(new ArrayList<>(servers))); assertSame(options, options.setCacheMinTimeToLive(0)); assertSame(options, options.setCacheMinTimeToLive(minTTL)); + assertSame(options, options.setUseTcpForFallbackDnsResolving(useTcpForFallbackDnsResolving)); try { options.setCacheMinTimeToLive(-1); fail("Should throw exception"); @@ -308,6 +311,7 @@ public void testOptions() { assertEquals(rdFlag, jsonCopy.getRdFlag()); assertEquals(ndots, jsonCopy.getNdots()); assertEquals(searchDomains, jsonCopy.getSearchDomains()); + assertEquals(useTcpForFallbackDnsResolving, jsonCopy.isUseTcpForFallbackDnsResolving()); } @Test @@ -323,6 +327,7 @@ public void testDefaultJsonOptions() { assertEquals(AddressResolverOptions.DEFAULT_RD_FLAG, options.getRdFlag()); assertEquals(AddressResolverOptions.DEFAULT_SEARCH_DOMAINS, options.getSearchDomains()); assertEquals(AddressResolverOptions.DEFAULT_NDOTS, options.getNdots()); + assertEquals(AddressResolverOptions.DEFAULT_USE_TCP_FOR_FALLBACK_DNS_RESOLVING, options.isUseTcpForFallbackDnsResolving()); } @Test From 383802b8e15fbc8c6b1bbe57ecef57a895c24e0d Mon Sep 17 00:00:00 2001 From: Andreas Wolf Date: Mon, 18 May 2026 10:35:07 +0200 Subject: [PATCH 2/2] Issue 5676: Added test --- .../io/vertx/tests/dns/NameResolverTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/vertx-core/src/test/java/io/vertx/tests/dns/NameResolverTest.java b/vertx-core/src/test/java/io/vertx/tests/dns/NameResolverTest.java index d2243c2c8bc..42d2f6d6acf 100644 --- a/vertx-core/src/test/java/io/vertx/tests/dns/NameResolverTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/dns/NameResolverTest.java @@ -795,6 +795,41 @@ public void testNetSearchDomain() throws Exception { testNet("host1"); } + @Test + public void testTcpFallbackWhenUdpFails() throws Exception { + // Verify normal UDP resolution works + AddressResolverOptions optionsWithoutFallback = getAddressResolverOptions() + .setUseTcpForFallbackDnsResolving(false) + .setQueryTimeout(5000); + + VertxInternal vertxWithoutFallback = (VertxInternal) vertx(new VertxOptions().setAddressResolverOptions(optionsWithoutFallback)); + + CountDownLatch noFallbackLatch = new CountDownLatch(1); + resolve(vertxWithoutFallback, "vertx.io").onComplete(onSuccess(resolved -> { + assertEquals("127.0.0.1", resolved.getHostAddress()); + noFallbackLatch.countDown(); + })); + + assertTrue("Normal UDP resolution should succeed", noFallbackLatch.await(2, TimeUnit.SECONDS)); + + // Verify TCP fallback works by using extremely short timeout + // This forces UDP to timeout quickly, but TCP fallback should still succeed + AddressResolverOptions tcpFallbackOptions = getAddressResolverOptions() + .setUseTcpForFallbackDnsResolving(true) + .setQueryTimeout(1); // Extremely short timeout to force UDP timeout + + VertxInternal vertxWithFallback = (VertxInternal) vertx(new VertxOptions().setAddressResolverOptions(tcpFallbackOptions)); + + CountDownLatch fallbackLatch = new CountDownLatch(1); + resolve(vertxWithFallback, "vertx.io").onComplete(onSuccess(resolved -> { + assertEquals("127.0.0.1", resolved.getHostAddress()); + fallbackLatch.countDown(); + })); + + assertTrue("TCP fallback resolution should succeed within timeout", fallbackLatch.await(5, TimeUnit.SECONDS)); + testComplete(); + } + @Test public void testParseResolvConf() { assertEquals(-1, NameResolver.parseLinux("options").ndots());