From 84dd9dc9c03d85dd08fdaa67ce78c71511e7a01d Mon Sep 17 00:00:00 2001 From: Maria Ralli Date: Tue, 4 Feb 2020 10:48:13 +0200 Subject: [PATCH] Add host address to BindTransportException message (#51269) When bind fails, show the host address in addition to the port. This helps debugging cases with wrong "network.host" values. Closes #48001 --- .../resources/forbidden/jdk-signatures.txt | 2 +- .../Netty4HttpServerTransportTests.java | 11 +++- .../http/nio/NioHttpServerTransportTests.java | 11 +++- .../common/network/NetworkAddress.java | 57 +++++++++++++++++-- .../http/AbstractHttpServerTransport.java | 5 +- .../elasticsearch/transport/TcpTransport.java | 5 +- .../common/network/NetworkAddressTests.java | 25 ++++++++ .../AbstractSimpleTransportTestCase.java | 6 +- 8 files changed, 108 insertions(+), 14 deletions(-) diff --git a/buildSrc/src/main/resources/forbidden/jdk-signatures.txt b/buildSrc/src/main/resources/forbidden/jdk-signatures.txt index 3ae2e4b609427..b2fd479dce5ff 100644 --- a/buildSrc/src/main/resources/forbidden/jdk-signatures.txt +++ b/buildSrc/src/main/resources/forbidden/jdk-signatures.txt @@ -59,7 +59,7 @@ java.net.MulticastSocket#(int) java.net.ServerSocket#(int) java.net.ServerSocket#(int,int) -@defaultMessage use NetworkAddress format/formatAddress to print IP or IP+ports +@defaultMessage use NetworkAddress format() to print IP or IP+ports java.net.InetAddress#toString() java.net.InetAddress#getHostAddress() java.net.Inet4Address#getHostAddress() diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java index 7364c0365128f..22ce1dc3921f6 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java @@ -41,6 +41,7 @@ import io.netty.handler.codec.http.HttpVersion; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; @@ -195,11 +196,17 @@ public void testBindUnavailableAddress() { xContentRegistry(), new NullDispatcher())) { transport.start(); TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses()); - Settings settings = Settings.builder().put("http.port", remoteAddress.getPort()).build(); + Settings settings = Settings.builder() + .put("http.port", remoteAddress.getPort()) + .put("network.host", remoteAddress.getAddress()) + .build(); try (Netty4HttpServerTransport otherTransport = new Netty4HttpServerTransport(settings, networkService, bigArrays, threadPool, xContentRegistry(), new NullDispatcher())) { BindHttpException bindHttpException = expectThrows(BindHttpException.class, otherTransport::start); - assertEquals("Failed to bind to [" + remoteAddress.getPort() + "]", bindHttpException.getMessage()); + assertEquals( + "Failed to bind to " + NetworkAddress.format(remoteAddress.address()), + bindHttpException.getMessage() + ); } } } diff --git a/plugins/transport-nio/src/test/java/org/elasticsearch/http/nio/NioHttpServerTransportTests.java b/plugins/transport-nio/src/test/java/org/elasticsearch/http/nio/NioHttpServerTransportTests.java index 3a747bf12e352..c94d2cb1027b0 100644 --- a/plugins/transport-nio/src/test/java/org/elasticsearch/http/nio/NioHttpServerTransportTests.java +++ b/plugins/transport-nio/src/test/java/org/elasticsearch/http/nio/NioHttpServerTransportTests.java @@ -33,6 +33,7 @@ import io.netty.handler.codec.http.HttpVersion; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; @@ -190,11 +191,17 @@ public void testBindUnavailableAddress() { threadPool, xContentRegistry(), new NullDispatcher(), new NioGroupFactory(Settings.EMPTY, logger))) { transport.start(); TransportAddress remoteAddress = randomFrom(transport.boundAddress().boundAddresses()); - Settings settings = Settings.builder().put("http.port", remoteAddress.getPort()).build(); + Settings settings = Settings.builder() + .put("http.port", remoteAddress.getPort()) + .put("network.host", remoteAddress.getAddress()) + .build(); try (NioHttpServerTransport otherTransport = new NioHttpServerTransport(settings, networkService, bigArrays, pageRecycler, threadPool, xContentRegistry(), new NullDispatcher(), new NioGroupFactory(Settings.EMPTY, logger))) { BindHttpException bindHttpException = expectThrows(BindHttpException.class, () -> otherTransport.start()); - assertEquals("Failed to bind to [" + remoteAddress.getPort() + "]", bindHttpException.getMessage()); + assertEquals( + "Failed to bind to " + NetworkAddress.format(remoteAddress.address()), + bindHttpException.getMessage() + ); } } } diff --git a/server/src/main/java/org/elasticsearch/common/network/NetworkAddress.java b/server/src/main/java/org/elasticsearch/common/network/NetworkAddress.java index 62a08cad388c3..5431ca03faf09 100644 --- a/server/src/main/java/org/elasticsearch/common/network/NetworkAddress.java +++ b/server/src/main/java/org/elasticsearch/common/network/NetworkAddress.java @@ -19,6 +19,8 @@ package org.elasticsearch.common.network; +import org.elasticsearch.common.transport.PortsRange; + import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -66,7 +68,7 @@ private NetworkAddress() {} * @return formatted string */ public static String format(InetAddress address) { - return format(address, -1); + return format(address, new PortsRange("")); } /** @@ -88,21 +90,64 @@ public static String format(InetSocketAddress address) { return format(address.getAddress(), address.getPort()); } - // note, we don't validate port, because we only allow InetSocketAddress - static String format(InetAddress address, int port) { + /** + * Formats a network address and port for display purposes. + *

+ * This formats the address with {@link #format(InetAddress)} + * and appends the port number. IPv6 addresses will be bracketed. + * Any host information, if present is ignored. + *

+ * Example output: + *

    + *
  • IPv4: {@code 127.0.0.1:9300}
  • + *
  • IPv6: {@code [::1]:9300}
  • + *
+ * @param address IPv4 or IPv6 address + * @param port port + * @return formatted string + */ + public static String format(InetAddress address, int port) { + return format(address, new PortsRange(String.valueOf(port))); + } + + /** + * Formats a network address and port range for display purposes. + *

+ * This formats the address with {@link #format(InetAddress)} + * and appends the port range in brackets. In case there is only one + * port, the result is the same with {@link #format(InetAddress, int)}. + *

+ * Example output: + *

    + *
  • IPv4 no port: {@code 127.0.0.1}
  • + *
  • IPv4 single port: {@code 127.0.0.1:9300}
  • + *
  • IPv4 multiple ports: {@code 127.0.0.1:[9300-9400]}
  • + *
  • IPv6 multiple ports: {@code [::1]:[9300-9400]}
  • + *
+ * @param address IPv4 or IPv6 address + * @param portsRange range of ports + * @return formatted string + */ + public static String format(InetAddress address, PortsRange portsRange) { Objects.requireNonNull(address); StringBuilder builder = new StringBuilder(); - if (port != -1 && address instanceof Inet6Address) { + int numberOfPorts = portsRange.ports().length; + + if (numberOfPorts != 0 && address instanceof Inet6Address) { builder.append(InetAddresses.toUriString(address)); } else { builder.append(InetAddresses.toAddrString(address)); } - if (port != -1) { + if (numberOfPorts != 0) { builder.append(':'); - builder.append(port); + if (numberOfPorts == 1) { + builder.append(portsRange.getPortRangeString()); + } else { + builder.append("[").append(portsRange.getPortRangeString()).append("]"); + } } return builder.toString(); diff --git a/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java b/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java index aab98be6fb60e..ae9028d07e06d 100644 --- a/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java +++ b/server/src/main/java/org/elasticsearch/http/AbstractHttpServerTransport.java @@ -174,7 +174,10 @@ private TransportAddress bindAddress(final InetAddress hostAddress) { return true; }); if (!success) { - throw new BindHttpException("Failed to bind to [" + port.getPortRangeString() + "]", lastException.get()); + throw new BindHttpException( + "Failed to bind to " + NetworkAddress.format(hostAddress, port), + lastException.get() + ); } if (logger.isDebugEnabled()) { diff --git a/server/src/main/java/org/elasticsearch/transport/TcpTransport.java b/server/src/main/java/org/elasticsearch/transport/TcpTransport.java index f283da830de72..7b7f3120e9b26 100644 --- a/server/src/main/java/org/elasticsearch/transport/TcpTransport.java +++ b/server/src/main/java/org/elasticsearch/transport/TcpTransport.java @@ -378,7 +378,10 @@ private InetSocketAddress bindToPort(final String name, final InetAddress hostAd return true; }); if (!success) { - throw new BindTransportException("Failed to bind to [" + port + "]", lastException.get()); + throw new BindTransportException( + "Failed to bind to " + NetworkAddress.format(hostAddress, portsRange), + lastException.get() + ); } } finally { closeLock.writeLock().unlock(); diff --git a/server/src/test/java/org/elasticsearch/common/network/NetworkAddressTests.java b/server/src/test/java/org/elasticsearch/common/network/NetworkAddressTests.java index 4f08eee9b777d..3fdc869ab7262 100644 --- a/server/src/test/java/org/elasticsearch/common/network/NetworkAddressTests.java +++ b/server/src/test/java/org/elasticsearch/common/network/NetworkAddressTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.common.network; +import org.elasticsearch.common.transport.PortsRange; import org.elasticsearch.test.ESTestCase; import java.io.IOException; @@ -52,12 +53,36 @@ public void testFormatPortV6() throws Exception { assertEquals("[::1]:1234", NetworkAddress.format(new InetSocketAddress(forge(null, "::1"), 1234))); } + public void testFormatPortsRangeV4() throws Exception { + assertEquals("127.0.0.1", NetworkAddress.format(forge("localhost", "127.0.0.1"), new PortsRange(""))); + assertEquals("127.0.0.1", NetworkAddress.format(forge(null, "127.0.0.1"), new PortsRange(""))); + + assertEquals("127.0.0.1:9300", NetworkAddress.format(forge("localhost", "127.0.0.1"), new PortsRange("9300"))); + assertEquals("127.0.0.1:9300", NetworkAddress.format(forge(null, "127.0.0.1"), new PortsRange("9300"))); + + assertEquals("127.0.0.1:[9300-9400]", NetworkAddress.format(forge("localhost", "127.0.0.1"), new PortsRange("9300-9400"))); + assertEquals("127.0.0.1:[9300-9400]", NetworkAddress.format(forge(null, "127.0.0.1"), new PortsRange("9300-9400"))); + } + + public void testFormatPortsRangeV6() throws Exception { + assertEquals("::1", NetworkAddress.format(forge("localhost", "::1"), new PortsRange(""))); + assertEquals("::1", NetworkAddress.format(forge(null, "::1"), new PortsRange(""))); + + assertEquals("[::1]:9300", NetworkAddress.format(forge("localhost", "::1"), new PortsRange("9300"))); + assertEquals("[::1]:9300", NetworkAddress.format(forge(null, "::1"), new PortsRange("9300"))); + + assertEquals("[::1]:[9300-9400]", NetworkAddress.format(forge("localhost", "::1"), new PortsRange("9300-9400"))); + assertEquals("[::1]:[9300-9400]", NetworkAddress.format(forge(null, "::1"), new PortsRange("9300-9400"))); + } + public void testNoScopeID() throws Exception { assertEquals("::1", NetworkAddress.format(forgeScoped(null, "::1", 5))); assertEquals("::1", NetworkAddress.format(forgeScoped("localhost", "::1", 5))); + assertEquals("::1", NetworkAddress.format(forgeScoped("localhost", "::1", 5), new PortsRange(""))); assertEquals("[::1]:1234", NetworkAddress.format(new InetSocketAddress(forgeScoped(null, "::1", 5), 1234))); assertEquals("[::1]:1234", NetworkAddress.format(new InetSocketAddress(forgeScoped("localhost", "::1", 5), 1234))); + assertEquals("[::1]:[9300-9400]", NetworkAddress.format(forgeScoped("localhost", "::1", 5), new PortsRange("9300-9400"))); } /** Test that ipv4 address formatting round trips */ diff --git a/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java b/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java index 770cae55bcfc7..71bb3350a4d29 100644 --- a/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java @@ -39,6 +39,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.network.CloseableChannel; +import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.network.NetworkUtils; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Setting; @@ -2698,13 +2699,16 @@ public void testProfilesIncludesDefault() { public void testBindUnavailableAddress() { int port = serviceA.boundAddress().publishAddress().getPort(); + String address = serviceA.boundAddress().publishAddress().getAddress(); Settings settings = Settings.builder() .put(Node.NODE_NAME_SETTING.getKey(), "foobar") + .put(TransportSettings.HOST.getKey(), address) .put(TransportSettings.PORT.getKey(), port) .build(); BindTransportException bindTransportException = expectThrows(BindTransportException.class, () -> buildService("test", Version.CURRENT, settings)); - assertEquals("Failed to bind to ["+ port + "]", bindTransportException.getMessage()); + InetSocketAddress inetSocketAddress = serviceA.boundAddress().publishAddress().address(); + assertEquals("Failed to bind to " + NetworkAddress.format(inetSocketAddress), bindTransportException.getMessage()); } public void testChannelCloseWhileConnecting() {