Skip to content

Commit

Permalink
Polish IpAddressMatcher
Browse files Browse the repository at this point in the history
  • Loading branch information
sjohnr committed Nov 15, 2024
1 parent 3a29819 commit 83a7915
Showing 1 changed file with 33 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Objects;
import java.util.regex.Pattern;

import jakarta.servlet.http.HttpServletRequest;
Expand All @@ -33,16 +34,17 @@
* IPv4 address will never match a request which returns an IPv6 address, and vice-versa.
*
* @author Luke Taylor
* @author Steve Riesenberg
* @since 3.0.2
*/
public final class IpAddressMatcher implements RequestMatcher {

private static Pattern IPV4 = Pattern.compile("\\d{0,3}.\\d{0,3}.\\d{0,3}.\\d{0,3}(/\\d{0,3})?");

private final int nMaskBits;

private final InetAddress requiredAddress;

private final int nMaskBits;

/**
* Takes a specific IP address or a range specified using the IP/Netmask (e.g.
* 192.168.1.0/24 or 202.24.0.0/14).
Expand All @@ -52,33 +54,37 @@ public final class IpAddressMatcher implements RequestMatcher {
public IpAddressMatcher(String ipAddress) {
Assert.hasText(ipAddress, "ipAddress cannot be empty");
assertNotHostName(ipAddress);

String requiredAddress;
int nMaskBits;
if (ipAddress.indexOf('/') > 0) {
String[] addressAndMask = StringUtils.split(ipAddress, "/");
ipAddress = addressAndMask[0];
this.nMaskBits = Integer.parseInt(addressAndMask[1]);
String[] parts = Objects.requireNonNull(StringUtils.split(ipAddress, "/"));
requiredAddress = parts[0];
nMaskBits = Integer.parseInt(parts[1]);
}
else {
this.nMaskBits = -1;
requiredAddress = ipAddress;
nMaskBits = -1;
}
this.requiredAddress = parseAddress(ipAddress);
String finalIpAddress = ipAddress;
this.requiredAddress = parseAddress(requiredAddress);
this.nMaskBits = nMaskBits;
Assert.isTrue(this.requiredAddress.getAddress().length * 8 >= this.nMaskBits, () -> String
.format("IP address %s is too short for bitmask of length %d", finalIpAddress, this.nMaskBits));
.format("IP address %s is too short for bitmask of length %d", requiredAddress, this.nMaskBits));
}

@Override
public boolean matches(HttpServletRequest request) {
return matches(request.getRemoteAddr());
}

public boolean matches(String address) {
public boolean matches(String ipAddress) {
// Do not match null or blank address
if (!StringUtils.hasText(address)) {
if (!StringUtils.hasText(ipAddress)) {
return false;
}

assertNotHostName(address);
InetAddress remoteAddress = parseAddress(address);
assertNotHostName(ipAddress);
InetAddress remoteAddress = parseAddress(ipAddress);
if (!this.requiredAddress.getClass().equals(remoteAddress.getClass())) {
return false;
}
Expand All @@ -88,26 +94,31 @@ public boolean matches(String address) {
byte[] remAddr = remoteAddress.getAddress();
byte[] reqAddr = this.requiredAddress.getAddress();
int nMaskFullBytes = this.nMaskBits / 8;
byte finalByte = (byte) (0xFF00 >> (this.nMaskBits & 0x07));
for (int i = 0; i < nMaskFullBytes; i++) {
if (remAddr[i] != reqAddr[i]) {
return false;
}
}
byte finalByte = (byte) (0xFF00 >> (this.nMaskBits & 0x07));
if (finalByte != 0) {
return (remAddr[nMaskFullBytes] & finalByte) == (reqAddr[nMaskFullBytes] & finalByte);
}
return true;
}

private void assertNotHostName(String ipAddress) {
boolean isIpv4 = IPV4.matcher(ipAddress).matches();
if (isIpv4) {
return;
}
String error = "ipAddress " + ipAddress + " doesn't look like an IP Address. Is it a host name?";
Assert.isTrue(ipAddress.charAt(0) == '[' || ipAddress.charAt(0) == ':'
|| (Character.digit(ipAddress.charAt(0), 16) != -1 && ipAddress.contains(":")), error);
private static void assertNotHostName(String ipAddress) {
Assert.isTrue(isIpAddress(ipAddress),
() -> String.format("ipAddress %s doesn't look like an IP Address. Is it a host name?", ipAddress));
}

private static boolean isIpAddress(String ipAddress) {
// @formatter:off
return IPV4.matcher(ipAddress).matches()
|| ipAddress.charAt(0) == '['
|| ipAddress.charAt(0) == ':'
|| Character.digit(ipAddress.charAt(0), 16) != -1
&& ipAddress.indexOf(':') > 0;
// @formatter:on
}

private InetAddress parseAddress(String address) {
Expand Down

0 comments on commit 83a7915

Please sign in to comment.