From 42260fd56b4a368d4adca8945442ce483be25840 Mon Sep 17 00:00:00 2001 From: Gabriel Camargo Fukushima Date: Wed, 2 Nov 2022 16:42:16 +1100 Subject: [PATCH] Add port conflict exception (#4565) Signed-off-by: Gabriel Fukushima --- .../org/hyperledger/besu/cli/BesuCommand.java | 27 ++++++++++++++- .../hyperledger/besu/cli/BesuCommandTest.java | 14 ++++++++ .../api/jsonrpc/JsonRpcHttpService.java | 1 + .../jsonrpc/websocket/WebSocketService.java | 1 - .../hyperledger/besu/util/NetworkUtility.java | 33 +++++++++++++++++++ .../besu/util/NetworkUtilityTest.java | 9 +++++ 6 files changed, 83 insertions(+), 2 deletions(-) diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index f9e3c1e97..7c008d4fc 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -180,6 +180,7 @@ import org.hyperledger.besu.services.RpcEndpointServiceImpl; import org.hyperledger.besu.services.SecurityModuleServiceImpl; import org.hyperledger.besu.services.StorageServiceImpl; import org.hyperledger.besu.services.kvstore.InMemoryStoragePlugin; +import org.hyperledger.besu.util.InvalidConfigurationException; import org.hyperledger.besu.util.Log4j2ConfiguratorUtil; import org.hyperledger.besu.util.NetworkUtility; import org.hyperledger.besu.util.PermissioningConfigurationValidator; @@ -1972,7 +1973,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { private void configure() throws Exception { checkPortClash(); - + checkIfRequiredPortsAreAvailable(); syncMode = getDefaultSyncModeIfNotSet(syncMode); ethNetworkConfig = updateNetworkConfig(network); @@ -3126,6 +3127,30 @@ public class BesuCommand implements DefaultCommandValues, Runnable { }); } + private void checkIfRequiredPortsAreAvailable() { + final List unavailablePorts = new ArrayList<>(); + getEffectivePorts().stream() + .filter(Objects::nonNull) + .filter(port -> port > 0) + .forEach( + port -> { + if (port.equals(p2PDiscoveryOptionGroup.p2pPort) + && !NetworkUtility.isPortAvailable(port)) { + unavailablePorts.add(port); + } + if (!port.equals(p2PDiscoveryOptionGroup.p2pPort) + && !NetworkUtility.isPortAvailableForTcp(port)) { + unavailablePorts.add(port); + } + }); + if (!unavailablePorts.isEmpty()) { + throw new InvalidConfigurationException( + "Port(s) '" + + unavailablePorts + + "' already in use. Check for other processes using the port(s)."); + } + } + /** * * Gets the list of effective ports (ports that are enabled). * diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index dd6a51363..6ce11e83c 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -100,6 +100,7 @@ import org.hyperledger.besu.util.platform.PlatformDetector; import java.io.File; import java.io.IOException; import java.math.BigInteger; +import java.net.ServerSocket; import java.net.URI; import java.net.URL; import java.nio.file.Files; @@ -5371,4 +5372,17 @@ public class BesuCommandTest extends CommandTestAbstract { assertThat(commandErrorOutput.toString(UTF_8)) .contains("--Xpos-block-creation-max-time must be positive and ≤ 12000"); } + + @Test + public void portInUseReportsError() throws IOException { + final ServerSocket serverSocket = new ServerSocket(8545); + + parseCommand("--rpc-http-enabled"); + + assertThat(commandOutput.toString(UTF_8)).isEmpty(); + assertThat(commandErrorOutput.toString(UTF_8)) + .contains("Port(s) '[8545]' already in use. Check for other processes using the port(s)."); + + serverSocket.close(); + } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java index 874919bac..b19963980 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcHttpService.java @@ -219,6 +219,7 @@ public class JsonRpcHttpService { final CompletableFuture resultFuture = new CompletableFuture<>(); try { + // Create the HTTP server and a router object. httpServer = vertx.createHttpServer(getHttpServerOptions()); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketService.java index f6131339c..4b4e0db92 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketService.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/WebSocketService.java @@ -104,7 +104,6 @@ public class WebSocketService { "Starting Websocket service on {}:{}", configuration.getHost(), configuration.getPort()); final CompletableFuture resultFuture = new CompletableFuture<>(); - httpServer = vertx .createHttpServer( diff --git a/util/src/main/java/org/hyperledger/besu/util/NetworkUtility.java b/util/src/main/java/org/hyperledger/besu/util/NetworkUtility.java index 22d57a71f..7e9560a03 100644 --- a/util/src/main/java/org/hyperledger/besu/util/NetworkUtility.java +++ b/util/src/main/java/org/hyperledger/besu/util/NetworkUtility.java @@ -14,20 +14,27 @@ */ package org.hyperledger.besu.util; +import java.io.IOException; +import java.net.DatagramSocket; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; +import java.net.ServerSocket; import java.net.SocketException; import java.net.UnknownHostException; import java.util.function.Supplier; import com.google.common.base.Suppliers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class NetworkUtility { public static final String INADDR_ANY = "0.0.0.0"; public static final String INADDR6_ANY = "0:0:0:0:0:0:0:0"; + private static final Logger LOG = LoggerFactory.getLogger(NetworkUtility.class); + private NetworkUtility() {} private static final Supplier ipv6Available = @@ -98,4 +105,30 @@ public class NetworkUtility { "%s port requires a value between 1 and 65535. Got %d.", portTypeName, port)); } } + + public static boolean isPortAvailableForTcp(final int port) { + try (final ServerSocket serverSocket = new ServerSocket()) { + serverSocket.setReuseAddress(true); + serverSocket.bind(new InetSocketAddress(port)); + return true; + } catch (IOException ex) { + LOG.trace(String.format("Failed to open port %d for TCP", port), ex); + } + return false; + } + + private static boolean isPortAvailableForUdp(final int port) { + try (final DatagramSocket datagramSocket = new DatagramSocket(null)) { + datagramSocket.setReuseAddress(true); + datagramSocket.bind(new InetSocketAddress(port)); + return true; + } catch (IOException ex) { + LOG.trace(String.format("failed to open port %d for UDP", port), ex); + } + return false; + } + + public static boolean isPortAvailable(final int port) { + return isPortAvailableForTcp(port) && isPortAvailableForUdp(port); + } } diff --git a/util/src/test/java/org/hyperledger/besu/util/NetworkUtilityTest.java b/util/src/test/java/org/hyperledger/besu/util/NetworkUtilityTest.java index 95068aeee..6b0ac1946 100644 --- a/util/src/test/java/org/hyperledger/besu/util/NetworkUtilityTest.java +++ b/util/src/test/java/org/hyperledger/besu/util/NetworkUtilityTest.java @@ -16,7 +16,9 @@ package org.hyperledger.besu.util; import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; import java.net.InetSocketAddress; +import java.net.ServerSocket; import org.junit.Test; @@ -31,4 +33,11 @@ public class NetworkUtilityTest { final InetSocketAddress ipv6 = new InetSocketAddress("1:2:3:4:5:6:7:8", 80); assertThat(NetworkUtility.urlForSocketAddress("http", ipv6)).contains("[1:2:3:4:5:6:7:8]"); } + + @Test + public void assertPortIsNotAvailable() throws IOException { + final ServerSocket serverSocket = new ServerSocket(8541); + assertThat(!NetworkUtility.isPortAvailable(8541)).isEqualTo(true); + serverSocket.close(); + } }