//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.util;
import java.net.InetAddress;
import java.util.function.Predicate;
A pattern representing a single or range of InetAddress
. To create a pattern use the from(String)
method, which will create a pattern given a string conforming to one of the following formats.
- InetAddress
- A single InetAddress either in hostname or address format. All formats supported by
InetAddress
are accepted. Not ethat using hostname matches may force domain lookups. eg. "[::1]", "1.2.3.4", "::ffff:127.0.0.1"
- InetAddress/CIDR
- An InetAddress with a integer number of bits to indicate
the significant prefix. eg. "192.168.0.0/16" will match from "192.168.0.0" to
"192.168.255.255"
- InetAddress-InetAddress
- An inclusive range of InetAddresses.
eg. "[a000::1]-[afff::]", "192.168.128.0-192.168.128.255"
- Legacy format
- The legacy format used for IPv4 only.
eg. "10.10.10-14.0-128"
/**
* A pattern representing a single or range of {@link InetAddress}. To create a pattern use
* the {@link InetAddressPattern#from(String)} method, which will create a pattern given a
* string conforming to one of the following formats.
*
* <dl>
* <dt>InetAddress</dt>
* <dd>A single InetAddress either in hostname or address format.
* All formats supported by {@link InetAddress} are accepted. Not ethat using hostname
* matches may force domain lookups. eg. "[::1]", "1.2.3.4", "::ffff:127.0.0.1"</dd>
* <dt>InetAddress/CIDR</dt>
* <dd>An InetAddress with a integer number of bits to indicate
* the significant prefix. eg. "192.168.0.0/16" will match from "192.168.0.0" to
* "192.168.255.255" </dd>
* <dt>InetAddress-InetAddress</dt>
* <dd>An inclusive range of InetAddresses.
* eg. "[a000::1]-[afff::]", "192.168.128.0-192.168.128.255"</dd>
* <dt>Legacy format</dt>
* <dd>The legacy format used for IPv4 only.
* eg. "10.10.10-14.0-128"</dd>
* </dl>
*/
public abstract class InetAddressPattern implements Predicate<InetAddress>
{
protected final String _pattern;
public static InetAddressPattern from(String pattern)
{
if (pattern == null)
return null;
int slash = pattern.lastIndexOf('/');
int dash = pattern.lastIndexOf('-');
try
{
if (slash >= 0)
return new CidrInetAddressRange(pattern, InetAddress.getByName(pattern.substring(0, slash).trim()), StringUtil.toInt(pattern, slash + 1));
if (dash >= 0)
return new MinMaxInetAddressRange(pattern, InetAddress.getByName(pattern.substring(0, dash).trim()), InetAddress.getByName(pattern.substring(dash + 1).trim()));
return new SingletonInetAddressRange(pattern, InetAddress.getByName(pattern));
}
catch (Exception e)
{
try
{
if (slash < 0 && dash > 0)
return new LegacyInetAddressRange(pattern);
}
catch (Exception ex2)
{
e.addSuppressed(ex2);
}
throw new IllegalArgumentException("Bad pattern: " + pattern, e);
}
}
public InetAddressPattern(String pattern)
{
_pattern = pattern;
}
@Override
public String toString()
{
return _pattern;
}
static class SingletonInetAddressRange extends InetAddressPattern
{
final InetAddress _address;
public SingletonInetAddressRange(String pattern, InetAddress address)
{
super(pattern);
_address = address;
}
@Override
public boolean test(InetAddress address)
{
return _address.equals(address);
}
}
static class MinMaxInetAddressRange extends InetAddressPattern
{
final int[] _min;
final int[] _max;
public MinMaxInetAddressRange(String pattern, InetAddress min, InetAddress max)
{
super(pattern);
byte[] rawMin = min.getAddress();
byte[] rawMax = max.getAddress();
if (rawMin.length != rawMax.length)
throw new IllegalArgumentException("Cannot mix IPv4 and IPv6: " + pattern);
if (rawMin.length == 4)
{
// there must be 6 '.' or this is likely to be a legacy pattern
int count = 0;
for (char c : pattern.toCharArray())
{
if (c == '.')
count++;
}
if (count != 6)
throw new IllegalArgumentException("Legacy pattern: " + pattern);
}
_min = new int[rawMin.length];
_max = new int[rawMin.length];
for (int i = 0; i < _min.length; i++)
{
_min[i] = 0xff & rawMin[i];
_max[i] = 0xff & rawMax[i];
}
for (int i = 0; i < _min.length; i++)
{
if (_min[i] > _max[i])
throw new IllegalArgumentException("min is greater than max: " + pattern);
if (_min[i] < _max[i])
break;
}
}
@Override
public boolean test(InetAddress address)
{
byte[] raw = address.getAddress();
if (raw.length != _min.length)
return false;
boolean minOk = false;
boolean maxOk = false;
for (int i = 0; i < _min.length; i++)
{
int r = 0xff & raw[i];
if (!minOk)
{
if (r < _min[i])
return false;
if (r > _min[i])
minOk = true;
}
if (!maxOk)
{
if (r > _max[i])
return false;
if (r < _max[i])
maxOk = true;
}
if (minOk && maxOk)
break;
}
return true;
}
}
static class CidrInetAddressRange extends InetAddressPattern
{
final byte[] _raw;
final int _octets;
final int _mask;
final int _masked;
public CidrInetAddressRange(String pattern, InetAddress address, int cidr)
{
super(pattern);
_raw = address.getAddress();
_octets = cidr / 8;
_mask = 0xff & (0xff << (8 - cidr % 8));
_masked = _mask == 0 ? 0 : _raw[_octets] & _mask;
if (cidr > (_raw.length * 8))
throw new IllegalArgumentException("CIDR too large: " + pattern);
if (_mask != 0 && (0xff & _raw[_octets]) != _masked)
throw new IllegalArgumentException("CIDR bits non zero: " + pattern);
for (int o = _octets + (_mask == 0 ? 0 : 1); o < _raw.length; o++)
{
if (_raw[o] != 0)
throw new IllegalArgumentException("CIDR bits non zero: " + pattern);
}
}
@Override
public boolean test(InetAddress address)
{
byte[] raw = address.getAddress();
if (raw.length != _raw.length)
return false;
for (int o = 0; o < _octets; o++)
{
if (_raw[o] != raw[o])
return false;
}
return _mask == 0 || (raw[_octets] & _mask) == _masked;
}
}
static class LegacyInetAddressRange extends InetAddressPattern
{
int[] _min = new int[4];
int[] _max = new int[4];
public LegacyInetAddressRange(String pattern)
{
super(pattern);
String[] parts = pattern.split("\\.");
if (parts.length != 4)
throw new IllegalArgumentException("Bad legacy pattern: " + pattern);
for (int i = 0; i < 4; i++)
{
String part = parts[i].trim();
int dash = part.indexOf('-');
if (dash < 0)
_min[i] = _max[i] = Integer.parseInt(part);
else
{
_min[i] = (dash == 0) ? 0 : StringUtil.toInt(part, 0);
_max[i] = (dash == part.length() - 1) ? 255 : StringUtil.toInt(part, dash + 1);
}
if (_min[i] < 0 || _min[i] > _max[i] || _max[i] > 255)
throw new IllegalArgumentException("Bad legacy pattern: " + pattern);
}
}
@Override
public boolean test(InetAddress address)
{
byte[] raw = address.getAddress();
if (raw.length != 4)
return false;
for (int i = 0; i < 4; i++)
{
if ((0xff & raw[i]) < _min[i] || (0xff & raw[i]) > _max[i])
return false;
}
return true;
}
}
}