From 6609619c49b104792ef0d9280b4bbcdee78f0bcb Mon Sep 17 00:00:00 2001 From: ttugrul <13340482+ttugrul@users.noreply.github.com> Date: Wed, 24 Dec 2025 20:28:15 +0300 Subject: [PATCH 1/2] fix: skip OS-level port check for addresses outside network namespace --- .../factory/BaseLocalCloudServiceFactory.java | 5 ++++ .../node/impl/util/NetworkUtil.java | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/node/impl/src/main/java/eu/cloudnetservice/node/impl/service/defaults/factory/BaseLocalCloudServiceFactory.java b/node/impl/src/main/java/eu/cloudnetservice/node/impl/service/defaults/factory/BaseLocalCloudServiceFactory.java index 076098d7ac..0039ffd455 100644 --- a/node/impl/src/main/java/eu/cloudnetservice/node/impl/service/defaults/factory/BaseLocalCloudServiceFactory.java +++ b/node/impl/src/main/java/eu/cloudnetservice/node/impl/service/defaults/factory/BaseLocalCloudServiceFactory.java @@ -126,6 +126,11 @@ protected boolean isPortInUse(@NonNull CloudServiceManager manager, @NonNull Str } } + // skip OS-level port check for addresses outside our network namespace (e.g. containerized environments) + if (!NetworkUtil.isBindableAddress(hostAddress)) { + return false; + } + // validate that the port is free return NetworkUtil.isInUse(hostAddress, port); } diff --git a/node/impl/src/main/java/eu/cloudnetservice/node/impl/util/NetworkUtil.java b/node/impl/src/main/java/eu/cloudnetservice/node/impl/util/NetworkUtil.java index a7e3dc97dd..6163d5b9bc 100644 --- a/node/impl/src/main/java/eu/cloudnetservice/node/impl/util/NetworkUtil.java +++ b/node/impl/src/main/java/eu/cloudnetservice/node/impl/util/NetworkUtil.java @@ -95,6 +95,35 @@ public static boolean checkAssignable(@NonNull HostAndPort hostAndPort) { } } + /** + * Checks if the given host address is bindable in the current network namespace. + */ + public static boolean isBindableAddress(@NonNull String hostAddress) { + // resolve the address to check against available addresses + String resolvedAddress; + try { + // try to parse as IP address first + var address = InetAddresses.forString(hostAddress); + // wildcard addresses are always bindable + if (address.isAnyLocalAddress()) { + return true; + } + resolvedAddress = extractHostAddress(address); + } catch (IllegalArgumentException exception) { + // not a raw IP address, try to resolve as hostname + try { + var address = InetAddress.getByName(hostAddress); + resolvedAddress = extractHostAddress(address); + } catch (UnknownHostException e) { + // cannot resolve hostname, not bindable + return false; + } + } + + // check if the resolved address is available on this system + return availableIPAddresses().contains(resolvedAddress); + } + public static @Nullable HostAndPort parseAssignableHostAndPort(@NonNull String address, boolean withPort) { // try to parse host and port from the given string var hostAndPort = parseHostAndPort(address, withPort); From e3f6eb19095950a3fdacf7bb54f4fcfa73b122d1 Mon Sep 17 00:00:00 2001 From: ttugrul <13340482+ttugrul@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:26:14 +0300 Subject: [PATCH 2/2] fix: scope port bindability check to dockerized services only --- .../DockerizedLocalCloudServiceFactory.java | 12 ++++++++++ .../factory/BaseLocalCloudServiceFactory.java | 22 ++++++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/modules/dockerized-services/impl/src/main/java/eu/cloudnetservice/modules/docker/impl/DockerizedLocalCloudServiceFactory.java b/modules/dockerized-services/impl/src/main/java/eu/cloudnetservice/modules/docker/impl/DockerizedLocalCloudServiceFactory.java index 9c57d6fbd8..a746af6a59 100644 --- a/modules/dockerized-services/impl/src/main/java/eu/cloudnetservice/modules/docker/impl/DockerizedLocalCloudServiceFactory.java +++ b/modules/dockerized-services/impl/src/main/java/eu/cloudnetservice/modules/docker/impl/DockerizedLocalCloudServiceFactory.java @@ -27,6 +27,7 @@ import eu.cloudnetservice.node.impl.service.InternalCloudServiceManager; import eu.cloudnetservice.node.impl.service.defaults.factory.BaseLocalCloudServiceFactory; import eu.cloudnetservice.node.impl.tick.DefaultTickLoop; +import eu.cloudnetservice.node.impl.util.NetworkUtil; import eu.cloudnetservice.node.impl.version.ServiceVersionProvider; import eu.cloudnetservice.node.service.CloudService; import eu.cloudnetservice.node.service.CloudServiceManager; @@ -95,4 +96,15 @@ public DockerizedLocalCloudServiceFactory( public @NonNull String name() { return this.dockerConfiguration.factoryName(); } + + @Override + protected boolean isPortInUseAtOsLevel(@NonNull String hostAddress, int port) { + // only do OS-level port check if we can actually reach this address + // this handles the case where CloudNet runs in a container with an external address configured + if (!NetworkUtil.isBindableAddress(hostAddress)) { + return false; + } + + return super.isPortInUseAtOsLevel(hostAddress, port); + } } diff --git a/node/impl/src/main/java/eu/cloudnetservice/node/impl/service/defaults/factory/BaseLocalCloudServiceFactory.java b/node/impl/src/main/java/eu/cloudnetservice/node/impl/service/defaults/factory/BaseLocalCloudServiceFactory.java index 0039ffd455..335d769f4e 100644 --- a/node/impl/src/main/java/eu/cloudnetservice/node/impl/service/defaults/factory/BaseLocalCloudServiceFactory.java +++ b/node/impl/src/main/java/eu/cloudnetservice/node/impl/service/defaults/factory/BaseLocalCloudServiceFactory.java @@ -119,19 +119,29 @@ protected int findFreeServicePort( protected boolean isPortInUse(@NonNull CloudServiceManager manager, @NonNull String hostAddress, int port) { // check if any local service has the port + if (this.isPortUsedByLocalService(manager, hostAddress, port)) { + return true; + } + + // validate that the port is free at OS level + return this.isPortInUseAtOsLevel(hostAddress, port); + } + + protected boolean isPortUsedByLocalService( + @NonNull CloudServiceManager manager, + @NonNull String hostAddress, + int port + ) { for (var cloudService : manager.localCloudServices()) { var address = cloudService.serviceInfo().address(); if (address.host().equals(hostAddress) && address.port() == port) { return true; } } + return false; + } - // skip OS-level port check for addresses outside our network namespace (e.g. containerized environments) - if (!NetworkUtil.isBindableAddress(hostAddress)) { - return false; - } - - // validate that the port is free + protected boolean isPortInUseAtOsLevel(@NonNull String hostAddress, int port) { return NetworkUtil.isInUse(hostAddress, port); } }