1818
1919import java .net .InetAddress ;
2020import java .net .UnknownHostException ;
21+ import java .util .Objects ;
2122import java .util .regex .Pattern ;
2223
2324import jakarta .servlet .http .HttpServletRequest ;
3334 * IPv4 address will never match a request which returns an IPv6 address, and vice-versa.
3435 *
3536 * @author Luke Taylor
37+ * @author Steve Riesenberg
3638 * @since 3.0.2
3739 */
3840public final class IpAddressMatcher implements RequestMatcher {
3941
4042 private static Pattern IPV4 = Pattern .compile ("\\ d{0,3}.\\ d{0,3}.\\ d{0,3}.\\ d{0,3}(/\\ d{0,3})?" );
4143
42- private final int nMaskBits ;
43-
4444 private final InetAddress requiredAddress ;
4545
46+ private final int nMaskBits ;
47+
4648 /**
4749 * Takes a specific IP address or a range specified using the IP/Netmask (e.g.
4850 * 192.168.1.0/24 or 202.24.0.0/14).
@@ -52,33 +54,37 @@ public final class IpAddressMatcher implements RequestMatcher {
5254 public IpAddressMatcher (String ipAddress ) {
5355 Assert .hasText (ipAddress , "ipAddress cannot be empty" );
5456 assertNotHostName (ipAddress );
57+
58+ String requiredAddress ;
59+ int nMaskBits ;
5560 if (ipAddress .indexOf ('/' ) > 0 ) {
56- String [] addressAndMask = StringUtils .split (ipAddress , "/" );
57- ipAddress = addressAndMask [0 ];
58- this . nMaskBits = Integer .parseInt (addressAndMask [1 ]);
61+ String [] parts = Objects . requireNonNull ( StringUtils .split (ipAddress , "/" ) );
62+ requiredAddress = parts [0 ];
63+ nMaskBits = Integer .parseInt (parts [1 ]);
5964 }
6065 else {
61- this .nMaskBits = -1 ;
66+ requiredAddress = ipAddress ;
67+ nMaskBits = -1 ;
6268 }
63- this .requiredAddress = parseAddress (ipAddress );
64- String finalIpAddress = ipAddress ;
69+ this .requiredAddress = parseAddress (requiredAddress );
70+ this . nMaskBits = nMaskBits ;
6571 Assert .isTrue (this .requiredAddress .getAddress ().length * 8 >= this .nMaskBits , () -> String
66- .format ("IP address %s is too short for bitmask of length %d" , finalIpAddress , this .nMaskBits ));
72+ .format ("IP address %s is too short for bitmask of length %d" , requiredAddress , this .nMaskBits ));
6773 }
6874
6975 @ Override
7076 public boolean matches (HttpServletRequest request ) {
7177 return matches (request .getRemoteAddr ());
7278 }
7379
74- public boolean matches (String address ) {
80+ public boolean matches (String ipAddress ) {
7581 // Do not match null or blank address
76- if (!StringUtils .hasText (address )) {
82+ if (!StringUtils .hasText (ipAddress )) {
7783 return false ;
7884 }
7985
80- assertNotHostName (address );
81- InetAddress remoteAddress = parseAddress (address );
86+ assertNotHostName (ipAddress );
87+ InetAddress remoteAddress = parseAddress (ipAddress );
8288 if (!this .requiredAddress .getClass ().equals (remoteAddress .getClass ())) {
8389 return false ;
8490 }
@@ -88,26 +94,31 @@ public boolean matches(String address) {
8894 byte [] remAddr = remoteAddress .getAddress ();
8995 byte [] reqAddr = this .requiredAddress .getAddress ();
9096 int nMaskFullBytes = this .nMaskBits / 8 ;
91- byte finalByte = (byte ) (0xFF00 >> (this .nMaskBits & 0x07 ));
9297 for (int i = 0 ; i < nMaskFullBytes ; i ++) {
9398 if (remAddr [i ] != reqAddr [i ]) {
9499 return false ;
95100 }
96101 }
102+ byte finalByte = (byte ) (0xFF00 >> (this .nMaskBits & 0x07 ));
97103 if (finalByte != 0 ) {
98104 return (remAddr [nMaskFullBytes ] & finalByte ) == (reqAddr [nMaskFullBytes ] & finalByte );
99105 }
100106 return true ;
101107 }
102108
103- private void assertNotHostName (String ipAddress ) {
104- boolean isIpv4 = IPV4 .matcher (ipAddress ).matches ();
105- if (isIpv4 ) {
106- return ;
107- }
108- String error = "ipAddress " + ipAddress + " doesn't look like an IP Address. Is it a host name?" ;
109- Assert .isTrue (ipAddress .charAt (0 ) == '[' || ipAddress .charAt (0 ) == ':'
110- || (Character .digit (ipAddress .charAt (0 ), 16 ) != -1 && ipAddress .contains (":" )), error );
109+ private static void assertNotHostName (String ipAddress ) {
110+ Assert .isTrue (isIpAddress (ipAddress ),
111+ () -> String .format ("ipAddress %s doesn't look like an IP Address. Is it a host name?" , ipAddress ));
112+ }
113+
114+ private static boolean isIpAddress (String ipAddress ) {
115+ // @formatter:off
116+ return IPV4 .matcher (ipAddress ).matches ()
117+ || ipAddress .charAt (0 ) == '['
118+ || ipAddress .charAt (0 ) == ':'
119+ || Character .digit (ipAddress .charAt (0 ), 16 ) != -1
120+ && ipAddress .indexOf (':' ) > 0 ;
121+ // @formatter:on
111122 }
112123
113124 private InetAddress parseAddress (String address ) {
0 commit comments