From 7fec17101e351a4e9317aea45861612ef7a8bae1 Mon Sep 17 00:00:00 2001
From: HHoflittlefish777 <77738092+HHoflittlefish777@users.noreply.github.com>
Date: Wed, 11 Jan 2023 21:53:13 +0800
Subject: [PATCH] filter invalid IPv6 (#3026)
* fix: filter invalid IPv6
---
.../pom.xml | 7 +
.../cloud/nacos/NacosDiscoveryProperties.java | 14 +-
.../cloud/nacos/util/InetIPv6Utils.java | 121 +++++++-----------
.../cloud/nacos/util/InetIPv6UtilsTest.java | 60 +++++++++
4 files changed, 121 insertions(+), 81 deletions(-)
create mode 100644 spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/util/InetIPv6UtilsTest.java
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/pom.xml
index 81ca9adad..279c2ccc3 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/pom.xml
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/pom.xml
@@ -136,6 +136,13 @@
2.0.0
test
+
+
+ commons-validator
+ commons-validator
+ 1.7
+ test
+
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java
index 38717767a..c5cf5f780 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java
@@ -169,10 +169,9 @@ public class NacosDiscoveryProperties {
private String networkInterface = "";
/**
- * choose IPv4 or IPv6 or both_IPv4_IPv6. if you don't set it will choose
- * both_IPv4_IPv6. When IPv6 is chosen but no IPv6 can be found, system will
- * automatically find IPv4 to ensure there is an available service address. If
- * both_IPv4_IPv6 is set,both IPv4 and IPv6 will be register.
+ * choose IPv4 or IPv6,if you don't set it will choose IPv4. When IPv6 is chosen but
+ * no IPv6 can be found, system will automatically find IPv4 to ensure there is an
+ * available service address.
*/
private String ipType;
@@ -275,7 +274,10 @@ public class NacosDiscoveryProperties {
}
else if (ipType == null) {
ip = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
- metadata.put(IPV6, inetIPv6Utils.findIPv6Address());
+ String iPv6Address = inetIPv6Utils.findIPv6Address();
+ if (iPv6Address != null) {
+ metadata.put(IPV6, iPv6Address);
+ }
}
else {
throw new IllegalArgumentException(
@@ -295,7 +297,7 @@ public class NacosDiscoveryProperties {
InetAddress currentAddress = inetAddress.nextElement();
if (currentAddress instanceof Inet4Address
|| currentAddress instanceof Inet6Address
- && !currentAddress.isLoopbackAddress()) {
+ && !currentAddress.isLoopbackAddress()) {
ip = currentAddress.getHostAddress();
break;
}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/util/InetIPv6Utils.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/util/InetIPv6Utils.java
index d69522332..4a24c680a 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/util/InetIPv6Utils.java
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/util/InetIPv6Utils.java
@@ -16,20 +16,14 @@
package com.alibaba.cloud.nacos.util;
-import java.io.Closeable;
import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
-import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import com.alibaba.cloud.commons.lang.StringUtils;
+import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -39,64 +33,51 @@ import org.springframework.cloud.commons.util.InetUtilsProperties;
/**
* @author HH
*/
-public class InetIPv6Utils implements Closeable {
+public class InetIPv6Utils {
- private final ExecutorService executorService;
-
- private final Log log = LogFactory.getLog(InetIPv6Utils.class);
+ private final static Log log = LogFactory.getLog(InetIPv6Utils.class);
private final InetUtilsProperties properties;
- @Override
- public void close() {
- this.executorService.shutdown();
- }
-
public InetIPv6Utils(final InetUtilsProperties properties) {
this.properties = properties;
- this.executorService = Executors.newSingleThreadExecutor((r) -> {
- Thread thread = new Thread(r);
- thread.setName("spring.cloud.alibaba.inetutilsIPV6");
- thread.setDaemon(true);
- return thread;
- });
}
- public InetUtils.HostInfo findFirstNonLoopbackHostInfo() {
- InetAddress address = this.findFirstNonLoopbackIPv6Address();
- if (address != null) {
- return this.convertAddress(address);
- }
- return null;
+ private InetUtils.HostInfo findFirstValidHostInfo() {
+ InetAddress address = this.findFirstValidIPv6Address();
+ return address != null ? this.getHostInfo(address) : null;
}
- public InetAddress findFirstNonLoopbackIPv6Address() {
+ private InetAddress findFirstValidIPv6Address() {
InetAddress address = null;
try {
- int lowest = Integer.MAX_VALUE;
for (Enumeration nics = NetworkInterface
.getNetworkInterfaces(); nics.hasMoreElements(); ) {
NetworkInterface ifc = nics.nextElement();
- if (ifc.isUp()) {
- log.trace("Testing interface:" + ifc.getDisplayName());
- if (ifc.getIndex() < lowest || address == null) {
- lowest = ifc.getIndex();
+ if (ifc.isUp() || !ifc.isVirtual() || !ifc.isLoopback()) {
+ if (address != null) {
+ break;
}
- else if (address != null) {
- continue;
- }
-
if (!ignoreInterface(ifc.getDisplayName())) {
for (Enumeration addrs = ifc
.getInetAddresses(); addrs.hasMoreElements(); ) {
InetAddress inetAddress = addrs.nextElement();
if (inetAddress instanceof Inet6Address
+ // filter ::1
&& !inetAddress.isLoopbackAddress()
+ // filter fe80::/10
+ && !inetAddress.isLinkLocalAddress()
+ // filter ::/128
+ && !inetAddress.isAnyLocalAddress()
+ // filter fec0::/10,which was discarded,but some
+ // address may be deployed.
+ && !inetAddress.isSiteLocalAddress()
+ // filter fd00::/8
+ && !isUniqueLocalAddress(inetAddress)
&& isPreferredAddress(inetAddress)) {
- log.trace("Found non-loopback interface: "
- + ifc.getDisplayName());
address = inetAddress;
+ break;
}
}
}
@@ -106,37 +87,19 @@ public class InetIPv6Utils implements Closeable {
catch (IOException e) {
log.error("Cannot get first non-loopback address", e);
}
- if (address == null) {
- try {
- InetAddress localHost = InetAddress.getLocalHost();
- if (localHost instanceof Inet6Address && !localHost.isLoopbackAddress()
- && isPreferredAddress(localHost)) {
- address = localHost;
- }
- }
- catch (UnknownHostException e) {
- log.warn("Unable to retrieve localhost");
- }
- }
return address;
}
public String findIPv6Address() {
- InetUtils.HostInfo hostInfo = findFirstNonLoopbackHostInfo();
- String ip = hostInfo != null ? hostInfo.getIpAddress() : "";
- if (!StringUtils.isEmpty(ip)) {
- int index = ip.indexOf('%');
- ip = index > 0 ? ip.substring(0, index) : ip;
- return iPv6Format(ip);
- }
- return ip;
+ InetUtils.HostInfo hostInfo = findFirstValidHostInfo();
+ return hostInfo != null ? normalizeIPv6(hostInfo.getIpAddress()) : null;
}
- public String iPv6Format(String ip) {
+ private String normalizeIPv6(String ip) {
return "[" + ip + "]";
}
- boolean isPreferredAddress(InetAddress address) {
+ private boolean isPreferredAddress(InetAddress address) {
if (this.properties.isUseOnlySiteLocalInterfaces()) {
final boolean siteLocalAddress = address.isSiteLocalAddress();
if (!siteLocalAddress) {
@@ -154,35 +117,43 @@ public class InetIPv6Utils implements Closeable {
return true;
}
}
- log.trace("Ignoring address: " + address.getHostAddress());
return false;
}
boolean ignoreInterface(String interfaceName) {
for (String regex : this.properties.getIgnoredInterfaces()) {
if (interfaceName.matches(regex)) {
- log.trace("Ignoring interface: " + interfaceName);
return true;
}
}
return false;
}
- public InetUtils.HostInfo convertAddress(final InetAddress address) {
+ private InetUtils.HostInfo getHostInfo(final InetAddress address) {
InetUtils.HostInfo hostInfo = new InetUtils.HostInfo();
- Future result = this.executorService.submit(address::getHostName);
-
- String hostname;
- try {
- hostname = result.get(this.properties.getTimeoutSeconds(), TimeUnit.SECONDS);
+ String hostName = address.getHostName();
+ if (hostName == null) {
+ hostName = "localhost";
+ }
+ hostInfo.setHostname(hostName);
+ if (StringUtils.isNotEmpty(address.getHostAddress())) {
+ hostInfo.setIpAddress(address.getHostAddress());
}
- catch (Exception e) {
- log.info("Cannot determine local hostname");
- hostname = "localhost";
+ else {
+ hostInfo.setIpAddress(StringUtils.EMPTY);
}
- hostInfo.setHostname(hostname);
- hostInfo.setIpAddress(address.getHostAddress());
return hostInfo;
}
+ /**
+ * If the address is Unique Local Address.
+ *
+ * @param inetAddress {@link InetAddress}
+ * @return {@code true} if the address is Unique Local Address,otherwise {@code false}
+ */
+ private boolean isUniqueLocalAddress(InetAddress inetAddress) {
+ byte[] ip = inetAddress.getAddress();
+ return (ip[0] & 0xff) == 0xfd;
+ }
+
}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/util/InetIPv6UtilsTest.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/util/InetIPv6UtilsTest.java
new file mode 100644
index 000000000..db478cbdb
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/util/InetIPv6UtilsTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2013-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.cloud.nacos.util;
+
+import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
+import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration;
+import org.apache.commons.validator.routines.InetAddressValidator;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+
+@RunWith(PowerMockRunner.class)
+@PowerMockRunnerDelegate(SpringRunner.class)
+@SpringBootTest(
+ classes = InetIPv6UtilsTest.TestConfig.class,
+ properties = {"spring.cloud.nacos.discovery.ipType=IPv6"},
+ webEnvironment = RANDOM_PORT)
+public class InetIPv6UtilsTest {
+ @Autowired
+ NacosDiscoveryProperties properties;
+
+ @Test
+ public void getIPv6() {
+ String ip = properties.getIp();
+ ip = ip.substring(1, ip.length() - 1);
+ assert (InetAddressValidator.getInstance().isValidInet6Address(ip));
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration(
+ NacosDiscoveryClientConfiguration.class)
+ public static class TestConfig {
+
+ }
+}