IpAddressMatcher.java
/*
* Logback: the reliable, generic, fast and flexible logging framework.
* Copyright (C) 1999-2026, QOS.ch. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v2.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
*/
package ch.qos.logback.core.util;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Matches an {@link InetAddress} against a single IP address or a CIDR network
* range.
* <p>
* Accepted forms:
* <ul>
* <li>Single address: {@code 192.168.1.10}, {@code 2001:db8::1}</li>
* <li>CIDR range: {@code 192.168.1.0/24}, {@code 2001:db8::/32}</li>
* </ul>
*
* @author Ceki Gülcü
* @since 1.6.2
*/
public class IpAddressMatcher {
private final String original;
private final byte[] networkAddress;
private final int prefixLength;
/**
* Creates a matcher for the given address or CIDR specification.
*
* @param addressOrCidr a single IP or {@code address/prefixLength}
* @throws IllegalArgumentException if the specification is invalid
*/
public IpAddressMatcher(String addressOrCidr) {
if (addressOrCidr == null) {
throw new IllegalArgumentException("addressOrCidr must not be null");
}
this.original = addressOrCidr.trim();
if (this.original.isEmpty()) {
throw new IllegalArgumentException("addressOrCidr must not be empty");
}
String addressPart;
Integer explicitPrefix = null;
int slashIndex = this.original.indexOf('/');
if (slashIndex >= 0) {
addressPart = this.original.substring(0, slashIndex).trim();
String prefixPart = this.original.substring(slashIndex + 1).trim();
if (addressPart.isEmpty() || prefixPart.isEmpty()) {
throw new IllegalArgumentException("Invalid CIDR specification [" + addressOrCidr + "]");
}
try {
explicitPrefix = Integer.parseInt(prefixPart);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
"Invalid prefix length in [" + addressOrCidr + "]", e);
}
} else {
addressPart = this.original;
}
InetAddress parsed;
try {
parsed = InetAddress.getByName(addressPart);
} catch (UnknownHostException e) {
throw new IllegalArgumentException("Unknown host or invalid IP address [" + addressPart + "]", e);
}
this.networkAddress = parsed.getAddress();
int maxPrefix = this.networkAddress.length * 8;
if (explicitPrefix != null) {
if (explicitPrefix < 0 || explicitPrefix > maxPrefix) {
throw new IllegalArgumentException("Prefix length " + explicitPrefix
+ " is out of range for address [" + addressPart + "] (0-" + maxPrefix + ")");
}
this.prefixLength = explicitPrefix;
} else {
this.prefixLength = maxPrefix;
}
}
/**
* Returns {@code true} if the given address matches this matcher.
*
* @param address the address to test; may be {@code null}
* @return {@code true} if {@code address} matches
*/
public boolean matches(InetAddress address) {
if (address == null) {
return false;
}
return matchesBytes(address.getAddress());
}
/**
* Returns {@code true} if the given address string matches this matcher.
*
* @param address an IP address string
* @return {@code true} if the address matches
* @throws IllegalArgumentException if {@code address} cannot be parsed
*/
public boolean matches(String address) {
if (address == null) {
throw new IllegalArgumentException("address must not be null");
}
try {
return matches(InetAddress.getByName(address.trim()));
} catch (UnknownHostException e) {
throw new IllegalArgumentException("Unknown host or invalid IP address [" + address + "]", e);
}
}
private boolean matchesBytes(byte[] candidate) {
if (candidate == null || candidate.length != networkAddress.length) {
return false;
}
int fullBytes = prefixLength / 8;
int remainingBits = prefixLength % 8;
for (int i = 0; i < fullBytes; i++) {
if (candidate[i] != networkAddress[i]) {
return false;
}
}
if (remainingBits > 0) {
int mask = 0xFF << (8 - remainingBits);
if ((candidate[fullBytes] & mask) != (networkAddress[fullBytes] & mask)) {
return false;
}
}
return true;
}
/**
* The original specification used to construct this matcher.
*/
public String getSpecification() {
return original;
}
@Override
public String toString() {
return "IpAddressMatcher[" + original + "]";
}
}