| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.util; |
| |
| import java.net.InetAddress; |
| import java.util.AbstractSet; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * A set of InetAddress patterns. |
| * <p>This is a {@link Set} of String patterns that are used to match |
| * a {@link Predicate} over InetAddress for containment semantics. |
| * The patterns that may be set are: |
| * </p> |
| * <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 by {@link IPAddressMap} for IPv4 only. |
| * eg. "10.10.10-14.0-128"</dd> |
| * </dl> |
| * <p>This class is designed to work with {@link IncludeExcludeSet}</p> |
| * @see IncludeExcludeSet |
| */ |
| public class InetAddressSet extends AbstractSet<String> implements Set<String>, Predicate<InetAddress> |
| { |
| private Map<String,InetPattern> _patterns = new HashMap<>(); |
| |
| @Override |
| public boolean add(String pattern) |
| { |
| return _patterns.put(pattern,newInetRange(pattern))==null; |
| } |
| |
| protected InetPattern newInetRange(String pattern) |
| { |
| if (pattern==null) |
| return null; |
| |
| int slash = pattern.lastIndexOf('/'); |
| int dash = pattern.lastIndexOf('-'); |
| try |
| { |
| if (slash>=0) |
| return new CidrInetRange(pattern,InetAddress.getByName(pattern.substring(0,slash).trim()),StringUtil.toInt(pattern,slash+1)); |
| |
| if (dash>=0) |
| return new MinMaxInetRange(pattern,InetAddress.getByName(pattern.substring(0,dash).trim()),InetAddress.getByName(pattern.substring(dash+1).trim())); |
| |
| return new SingletonInetRange(pattern,InetAddress.getByName(pattern)); |
| } |
| catch(Exception e) |
| { |
| try |
| { |
| if (slash<0 && dash>0) |
| return new LegacyInetRange(pattern); |
| } |
| catch(Exception e2) |
| { |
| e.addSuppressed(e2); |
| } |
| throw new IllegalArgumentException("Bad pattern: "+pattern,e); |
| } |
| } |
| |
| @Override |
| public boolean remove(Object pattern) |
| { |
| return _patterns.remove(pattern)!=null; |
| } |
| |
| @Override |
| public Iterator<String> iterator() |
| { |
| return _patterns.keySet().iterator(); |
| } |
| |
| @Override |
| public int size() |
| { |
| return _patterns.size(); |
| } |
| |
| |
| @Override |
| public boolean test(InetAddress address) |
| { |
| if (address==null) |
| return false; |
| byte[] raw = address.getAddress(); |
| for (InetPattern pattern : _patterns.values()) |
| if (pattern.test(address,raw)) |
| return true; |
| return false; |
| } |
| |
| abstract static class InetPattern |
| { |
| final String _pattern; |
| |
| InetPattern(String pattern) |
| { |
| _pattern=pattern; |
| } |
| |
| abstract boolean test(InetAddress address, byte[] raw); |
| |
| @Override |
| public String toString() |
| { |
| return _pattern; |
| } |
| } |
| |
| static class SingletonInetRange extends InetPattern |
| { |
| final InetAddress _address; |
| public SingletonInetRange(String pattern, InetAddress address) |
| { |
| super(pattern); |
| _address=address; |
| } |
| |
| public boolean test(InetAddress address, byte[] raw) |
| { |
| return _address.equals(address); |
| } |
| } |
| |
| |
| static class MinMaxInetRange extends InetPattern |
| { |
| final int[] _min; |
| final int[] _max; |
| |
| public MinMaxInetRange(String pattern, InetAddress min, InetAddress max) |
| { |
| super(pattern); |
| |
| byte[] raw_min = min.getAddress(); |
| byte[] raw_max = max.getAddress(); |
| if (raw_min.length!=raw_max.length) |
| throw new IllegalArgumentException("Cannot mix IPv4 and IPv6: "+pattern); |
| |
| if (raw_min.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[raw_min.length]; |
| _max = new int[raw_min.length]; |
| |
| for (int i=0;i<_min.length;i++) |
| { |
| _min[i]=0xff&raw_min[i]; |
| _max[i]=0xff&raw_max[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; |
| } |
| } |
| |
| public boolean test(InetAddress item, byte[] raw) |
| { |
| if (raw.length!=_min.length) |
| return false; |
| |
| boolean min_ok = false; |
| boolean max_ok = false; |
| |
| for (int i=0;i<_min.length;i++) |
| { |
| int r = 0xff&raw[i]; |
| if (!min_ok) |
| { |
| if (r<_min[i]) |
| return false; |
| if (r>_min[i]) |
| min_ok=true; |
| } |
| if (!max_ok) |
| { |
| if (r>_max[i]) |
| return false; |
| if (r<_max[i]) |
| max_ok=true; |
| } |
| |
| if (min_ok && max_ok) |
| break; |
| } |
| |
| return true; |
| } |
| } |
| |
| |
| static class CidrInetRange extends InetPattern |
| { |
| final byte[] _raw; |
| final int _octets; |
| final int _mask; |
| final int _masked; |
| |
| public CidrInetRange(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 && _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); |
| } |
| |
| public boolean test(InetAddress item, byte[] raw) |
| { |
| if (raw.length!=_raw.length) |
| return false; |
| |
| for (int o=0;o<_octets;o++) |
| if (_raw[o]!=raw[o]) |
| return false; |
| |
| if (_mask!=0 && (raw[_octets]&_mask)!=_masked) |
| return false; |
| return true; |
| } |
| } |
| |
| static class LegacyInetRange extends InetPattern |
| { |
| int[] _min = new int[4]; |
| int[] _max = new int[4]; |
| |
| public LegacyInetRange(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); |
| } |
| } |
| |
| public boolean test(InetAddress item, byte[] raw) |
| { |
| 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; |
| } |
| } |
| } |