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 { + + } +}