diff --git a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/InetAddressFilter.java b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/InetAddressFilter.java
index 2dfd54be693..94ebc86ebe0 100644
--- a/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/InetAddressFilter.java
+++ b/module/spring-boot-http-client/src/main/java/org/springframework/boot/http/client/InetAddressFilter.java
@@ -185,12 +185,16 @@ public interface InetAddressFilter {
/**
* Return a filter that will match external (non-private) IP addresses. External
- * addresses are all non-{@link #internalAddresses() internal addresses}
+ * addresses are all {@link #routable() routable} addresses that are not:
+ *
+ * - {@link #multicast() multicast}
+ * - {@link #specialPurpose() special purpose}
+ *
* @return a filter for external IP addresses
* @see #internalAddresses()
*/
static InetAddressFilter externalAddresses() {
- return routable().andNot(InternalInetAddressFilter.instance);
+ return routable().andNot(multicast(), specialPurpose());
}
/**
@@ -224,6 +228,27 @@ public interface InetAddressFilter {
};
}
+ /**
+ * Returns a filter that will match all multicast addresses.
+ * @return a filter for multicast IP addresses
+ */
+ static InetAddressFilter multicast() {
+ return InetAddress::isMulticastAddress;
+ }
+
+ /**
+ * Returns a filter that will match special purpose IP addresses as defined by
+ * RFC 6890.
+ * @return a filter for this network
+ */
+ static InetAddressFilter specialPurpose() {
+ return of("0.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", "169.254.0.0/16", "192.0.0.0/24", "192.0.0.0/29",
+ "192.0.2.0/24", "192.88.99.0/24", "198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24", "240.0.0.0/4",
+ "255.255.255.255/32", "::1/128", "::/128", "64:ff9b::/96", "100::/64", "2001::/23", "2001::/32",
+ "2001:2::/48", "2001:db8::/32", "2001:10::/28", "2002::/16", "fc00::/7", "fe80::/10")
+ .or(InternalInetAddressFilter.instance);
+ }
+
/**
* Return a filter that is the negation of all the given addresses.
* @param addresses the addresses to negate in any form supported by
diff --git a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/InetAddressFilterTests.java b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/InetAddressFilterTests.java
index 3eb8d408913..31106bacb94 100644
--- a/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/InetAddressFilterTests.java
+++ b/module/spring-boot-http-client/src/test/java/org/springframework/boot/http/client/InetAddressFilterTests.java
@@ -327,6 +327,65 @@ class InetAddressFilterTests {
assertThat(filter).doesNotMatch("::");
}
+ @Test
+ void ipv4CurrentLocalThisNetwork() {
+ InetAddressFilter filter = InetAddressFilter.externalAddresses();
+ assertThat(filter).doesNotMatch("0.0.0.4");
+ }
+
+ @Test
+ void ipv4Subnet() {
+ InetAddressFilter filter = InetAddressFilter.externalAddresses();
+ assertThat(filter).doesNotMatch("169.254.0.1");
+ assertThat(filter).doesNotMatch("255.255.255.255");
+ }
+
+ @Test
+ void ipv4Documentation() {
+ InetAddressFilter filter = InetAddressFilter.externalAddresses();
+ assertThat(filter).doesNotMatch("192.0.2.1");
+ assertThat(filter).doesNotMatch("203.0.113.1");
+ }
+
+ @Test
+ void ipv4Multicast() {
+ InetAddressFilter filter = InetAddressFilter.externalAddresses();
+ assertThat(filter).doesNotMatch("224.0.0.1");
+ }
+
+ @Test
+ void ipv4ReservedForFutureUse() {
+ InetAddressFilter filter = InetAddressFilter.externalAddresses();
+ assertThat(filter).doesNotMatch("240.0.0.1");
+ }
+
+ @Test
+ void ipv4SharedAddressSpace() {
+ InetAddressFilter filter = InetAddressFilter.externalAddresses();
+ assertThat(filter).doesNotMatch("100.64.0.1");
+ assertThat(filter).doesNotMatch("100.127.255.254");
+ }
+
+ @Test
+ void ipv4PerformanceBenchmarking() {
+ InetAddressFilter filter = InetAddressFilter.externalAddresses();
+ assertThat(filter).doesNotMatch("198.18.0.1");
+ assertThat(filter).doesNotMatch("198.19.255.255");
+ }
+
+ @Test
+ void ipv4AllHostsMulticastGroup() {
+ InetAddressFilter filter = InetAddressFilter.externalAddresses();
+ assertThat(filter).doesNotMatch("224.0.0.1");
+ assertThat(filter).doesNotMatch("239.255.255.250");
+ }
+
+ @Test
+ void ipv6NodeLinkMulticastAddress() {
+ InetAddressFilter filter = InetAddressFilter.externalAddresses();
+ assertThat(filter).doesNotMatch("ff02::1");
+ }
+
}
@Nested
@@ -521,6 +580,163 @@ class InetAddressFilterTests {
}
+ @Nested
+ class Multicast {
+
+ @Test
+ void multicast() {
+ InetAddressFilter filter = InetAddressFilter.multicast();
+ assertThat(filter).matches("239.255.255.255");
+ assertThat(filter).matches("FF00:0000:0000:0000:0000:0000:0000:0001");
+ }
+
+ }
+
+ @Nested
+ class SpecialPurpose {
+
+ @Test
+ void publicAddress() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).doesNotMatch("8.8.8.8");
+ }
+
+ @Test
+ void thisHostNetwork() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("0.0.0.1");
+ }
+
+ @Test
+ void privateUseNetwork() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("10.0.0.1");
+ assertThat(filter).matches("172.16.0.1");
+ assertThat(filter).matches("192.168.0.1");
+ }
+
+ @Test
+ void sharedAddressSpace() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("100.64.0.1");
+ }
+
+ @Test
+ void loopback() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("127.0.0.0");
+ assertThat(filter).matches("::1");
+ }
+
+ @Test
+ void linkLocal() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("169.254.0.1");
+ }
+
+ @Test
+ void protocolAssignments() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("192.0.0.1");
+ assertThat(filter).matches("2001:0000:0000:0000:0000:0000:0000:0001");
+ }
+
+ @Test
+ void dsLite() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("192.0.0.1");
+ }
+
+ @Test
+ void documentation() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("192.0.2.1");
+ assertThat(filter).matches("198.51.100.1");
+ assertThat(filter).matches("203.0.113.1");
+ assertThat(filter).matches("2001:0db8:0000:0000:0000:0000:0000:0001");
+ }
+
+ @Test
+ void SixToFourRelay() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("192.88.99.1");
+ }
+
+ @Test
+ void deviceBenchmarking() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("198.18.0.1");
+ assertThat(filter).matches("2001:0002:0000:0000:0000:0000:0000:0001");
+ }
+
+ @Test
+ void futureUse() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("240.0.0.1");
+ }
+
+ @Test
+ void limitedBroadcast() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("255.255.255.255");
+ }
+
+ @Test
+ void unspecifiedAddress() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("0000:0000:0000:0000:0000:0000:0000:0001");
+ }
+
+ @Test
+ void ipv4ToIpv6AddressTranslation() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("0064:ff9b:0000:0000:0000:0000:0000:0001");
+ }
+
+ @Test
+ void ipv4MappedAddress() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("0000:0000:0000:0000:0000:ffff:0000:0001");
+ }
+
+ @Test
+ void discardOnly() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("0100:0000:0000:0000:0000:0000:0000:0001");
+ }
+
+ @Test
+ void teredo() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("2001:0000:0000:0000:0000:0000:0000:0001");
+ }
+
+ @Test
+ void orchid() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("2001:10:0000:0000:0000:0000:0000:0001");
+ }
+
+ @Test
+ void sixToFour() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("2002:0000:0000:0000:0000:0000:0000:0001");
+ }
+
+ @Test
+ void uniqueLocal() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("fc00:0000:0000:0000:0000:0000:0000:0001");
+ }
+
+ @Test
+ void linkedScopedUnicast() {
+ InetAddressFilter filter = InetAddressFilter.specialPurpose();
+ assertThat(filter).matches("fe80:0000:0000:0000:0000:0000:0000:0001");
+ }
+
+ }
+
@Nested
class NotTests {