diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java index e9dae1d88..1dfd09819 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java @@ -6,9 +6,13 @@ import com.google.common.net.InetAddresses; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.Set; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * A predicate on an address. Matches against a domain and an ip address. @@ -107,12 +111,35 @@ public boolean matches(InetAddress socketAddress) { final class PrivatePattern implements AddressPredicate { static final PrivatePattern INSTANCE = new PrivatePattern(); + private static final Set additionalAddresses = Arrays.stream(new String[]{ + // Block various cloud providers internal IPs. + "100.100.100.200", // Alibaba + "192.0.0.192", // Oracle + }).map(InetAddresses::forString).collect(Collectors.toUnmodifiableSet()); + @Override public boolean matches(InetAddress socketAddress) { - return socketAddress.isAnyLocalAddress() - || socketAddress.isLoopbackAddress() - || socketAddress.isLinkLocalAddress() - || socketAddress.isSiteLocalAddress(); + return + socketAddress.isAnyLocalAddress() // 0.0.0.0, ::0 + || socketAddress.isLoopbackAddress() // 127.0.0.0/8, ::1 + || socketAddress.isLinkLocalAddress() // 169.254.0.0/16, fe80::/10 + || socketAddress.isSiteLocalAddress() // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fec0::/10 + || socketAddress.isMulticastAddress() // 224.0.0.0/4, ff00::/8 + || isUniqueLocalAddress(socketAddress) // fd00::/8 + || additionalAddresses.contains(socketAddress); + } + + /** + * Determine if an IP address lives inside the ULA address range. + * + * @param address The IP address to test. + * @return Whether this address sits in the ULA address range. + * @see Unique local address on Wikipedia + */ + private boolean isUniqueLocalAddress(InetAddress address) { + // ULA is actually defined as fc00::/7 (so both fc00::/8 and fd00::/8). However, only the latter is actually + // defined right now, so let's be conservative. + return address instanceof Inet6Address && (address.getAddress()[0] & 0xff) == 0xfd; } } diff --git a/projects/core/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java b/projects/core/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java index 4682d45da..a7a7bf488 100644 --- a/projects/core/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java +++ b/projects/core/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java @@ -31,7 +31,14 @@ public void matchesPort() { @ValueSource(strings = { "0.0.0.0", "[::]", "localhost", "127.0.0.1.nip.io", "127.0.0.1", "[::1]", - "172.17.0.1", "192.168.1.114", "[0:0:0:0:0:ffff:c0a8:172]", "10.0.0.1" + "172.17.0.1", "192.168.1.114", "[0:0:0:0:0:ffff:c0a8:172]", "10.0.0.1", + // Multicast + "224.0.0.1", "ff02::1", + // Cloud metadata providers + "100.100.100.200", // Alibaba + "192.0.0.192", // Oracle + "fd00:ec2::254", // AWS + "169.254.169.254", // AWS, Digital Ocean, GCP, etc.. }) public void blocksLocalDomains(String domain) { assertEquals(apply(CoreConfig.httpRules, domain, 80).action, Action.DENY);