diff --git a/core/src/main/java/google/registry/flows/host/HostFlowUtils.java b/core/src/main/java/google/registry/flows/host/HostFlowUtils.java index d104264e4c2..447f2176de4 100644 --- a/core/src/main/java/google/registry/flows/host/HostFlowUtils.java +++ b/core/src/main/java/google/registry/flows/host/HostFlowUtils.java @@ -116,15 +116,21 @@ public static void validateInetAddresses(ImmutableSet inetAddresses if (inetAddresses == null) { return; } - if (inetAddresses.stream().anyMatch(InetAddress::isLoopbackAddress)) { - throw new LoopbackIpNotValidForHostException(); + for (InetAddress inetAddress : inetAddresses) { + if (inetAddress.isLoopbackAddress() + || inetAddress.isLinkLocalAddress() + || inetAddress.isSiteLocalAddress() + || inetAddress.isAnyLocalAddress() + || inetAddress.isMulticastAddress()) { + throw new IpAddressNotRoutableException(inetAddress.getHostAddress()); + } } } - /** Loopback IPs are not valid for hosts. */ - static class LoopbackIpNotValidForHostException extends ParameterValuePolicyErrorException { - public LoopbackIpNotValidForHostException() { - super("Loopback IPs are not valid for hosts"); + /** IP address is not a public, routable address. */ + static class IpAddressNotRoutableException extends ParameterValuePolicyErrorException { + public IpAddressNotRoutableException(String ipAddress) { + super(String.format("IP address %s is not a public, globally routable address", ipAddress)); } } diff --git a/core/src/test/java/google/registry/flows/host/HostCreateFlowTest.java b/core/src/test/java/google/registry/flows/host/HostCreateFlowTest.java index 12e04d1f14f..6ac065e64a3 100644 --- a/core/src/test/java/google/registry/flows/host/HostCreateFlowTest.java +++ b/core/src/test/java/google/registry/flows/host/HostCreateFlowTest.java @@ -48,7 +48,7 @@ import google.registry.flows.host.HostFlowUtils.HostNameTooLongException; import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException; import google.registry.flows.host.HostFlowUtils.InvalidHostNameException; -import google.registry.flows.host.HostFlowUtils.LoopbackIpNotValidForHostException; +import google.registry.flows.host.HostFlowUtils.IpAddressNotRoutableException; import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException; import google.registry.flows.host.HostFlowUtils.SuperordinateDomainInPendingDeleteException; import google.registry.model.ForeignKeyUtils; @@ -354,22 +354,62 @@ void testFailure_ccTldInBailiwick() { } @Test - void testFailure_localhostInetAddress_ipv4() { + void testFailure_loopbackInetAddress_ipv4() { createTld("tld"); persistActiveDomain("example.tld"); setEppHostCreateInput("ns1.example.tld", "127.0.0.1"); assertAboutEppExceptions() - .that(assertThrows(LoopbackIpNotValidForHostException.class, this::runFlow)) + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) .marshalsToXml(); } @Test - void testFailure_localhostInetAddress_ipv6() { + void testFailure_loopbackInetAddress_ipv6() { createTld("tld"); persistActiveDomain("example.tld"); setEppHostCreateInput("ns1.example.tld", "::1"); assertAboutEppExceptions() - .that(assertThrows(LoopbackIpNotValidForHostException.class, this::runFlow)) + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) + .marshalsToXml(); + } + + @Test + void testFailure_linkLocalInetAddress_ipv4() { + createTld("tld"); + persistActiveDomain("example.tld"); + setEppHostCreateInput("ns1.example.tld", "169.254.1.1"); + assertAboutEppExceptions() + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) + .marshalsToXml(); + } + + @Test + void testFailure_linkLocalInetAddress_ipv6() { + createTld("tld"); + persistActiveDomain("example.tld"); + setEppHostCreateInput("ns1.example.tld", "fe80::1"); + assertAboutEppExceptions() + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) + .marshalsToXml(); + } + + @Test + void testFailure_privateInetAddress_ipv4() { + createTld("tld"); + persistActiveDomain("example.tld"); + setEppHostCreateInput("ns1.example.tld", "192.168.1.1"); + assertAboutEppExceptions() + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) + .marshalsToXml(); + } + + @Test + void testFailure_anyLocalInetAddress_ipv4() { + createTld("tld"); + persistActiveDomain("example.tld"); + setEppHostCreateInput("ns1.example.tld", "0.0.0.0"); + assertAboutEppExceptions() + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) .marshalsToXml(); } diff --git a/core/src/test/java/google/registry/flows/host/HostUpdateFlowTest.java b/core/src/test/java/google/registry/flows/host/HostUpdateFlowTest.java index 5848205755a..4c5890df678 100644 --- a/core/src/test/java/google/registry/flows/host/HostUpdateFlowTest.java +++ b/core/src/test/java/google/registry/flows/host/HostUpdateFlowTest.java @@ -65,7 +65,7 @@ import google.registry.flows.host.HostFlowUtils.HostNameNotPunyCodedException; import google.registry.flows.host.HostFlowUtils.HostNameTooLongException; import google.registry.flows.host.HostFlowUtils.HostNameTooShallowException; -import google.registry.flows.host.HostFlowUtils.LoopbackIpNotValidForHostException; +import google.registry.flows.host.HostFlowUtils.IpAddressNotRoutableException; import google.registry.flows.host.HostFlowUtils.SuperordinateDomainDoesNotExistException; import google.registry.flows.host.HostFlowUtils.SuperordinateDomainInPendingDeleteException; import google.registry.flows.host.HostUpdateFlow.CannotAddIpToExternalHostException; @@ -1391,24 +1391,68 @@ void testFailure_ccTldInBailiwick() throws Exception { } @Test - void testFailure_localhostInetAddress_ipv4() throws Exception { + void testFailure_loopbackInetAddress_ipv4() throws Exception { createTld("tld"); persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld")); setEppHostUpdateInput( "ns1.example.tld", "ns2.example.tld", "127.0.0.1", null); assertAboutEppExceptions() - .that(assertThrows(LoopbackIpNotValidForHostException.class, this::runFlow)) + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) .marshalsToXml(); } @Test - void testFailure_localhostInetAddress_ipv6() throws Exception { + void testFailure_loopbackInetAddress_ipv6() throws Exception { createTld("tld"); persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld")); setEppHostUpdateInput( "ns1.example.tld", "ns2.example.tld", "::1", null); assertAboutEppExceptions() - .that(assertThrows(LoopbackIpNotValidForHostException.class, this::runFlow)) + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) + .marshalsToXml(); + } + + @Test + void testFailure_linkLocalInetAddress_ipv4() throws Exception { + createTld("tld"); + persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld")); + setEppHostUpdateInput( + "ns1.example.tld", "ns2.example.tld", "169.254.1.1", null); + assertAboutEppExceptions() + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) + .marshalsToXml(); + } + + @Test + void testFailure_linkLocalInetAddress_ipv6() throws Exception { + createTld("tld"); + persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld")); + setEppHostUpdateInput( + "ns1.example.tld", "ns2.example.tld", "fe80::1", null); + assertAboutEppExceptions() + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) + .marshalsToXml(); + } + + @Test + void testFailure_privateInetAddress_ipv4() throws Exception { + createTld("tld"); + persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld")); + setEppHostUpdateInput( + "ns1.example.tld", "ns2.example.tld", "192.168.1.1", null); + assertAboutEppExceptions() + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) + .marshalsToXml(); + } + + @Test + void testFailure_anyLocalInetAddress_ipv4() throws Exception { + createTld("tld"); + persistActiveSubordinateHost(oldHostName(), persistActiveDomain("example.tld")); + setEppHostUpdateInput( + "ns1.example.tld", "ns2.example.tld", "0.0.0.0", null); + assertAboutEppExceptions() + .that(assertThrows(IpAddressNotRoutableException.class, this::runFlow)) .marshalsToXml(); }