Merge pull request #4615 from eclipse/jetty-10.0.x-4598-InetAccessHandler
Issue #4598 - rework InetAccessHandler to use path mappings
This commit is contained in:
commit
6151fc0c36
|
@ -26,16 +26,20 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
|
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.server.HttpChannel;
|
import org.eclipse.jetty.server.HttpChannel;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.util.IncludeExclude;
|
|
||||||
import org.eclipse.jetty.util.IncludeExcludeSet;
|
import org.eclipse.jetty.util.IncludeExcludeSet;
|
||||||
|
import org.eclipse.jetty.util.InetAddressPattern;
|
||||||
import org.eclipse.jetty.util.InetAddressSet;
|
import org.eclipse.jetty.util.InetAddressSet;
|
||||||
import org.eclipse.jetty.util.component.DumpableCollection;
|
import org.eclipse.jetty.util.component.DumpableCollection;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
import static org.eclipse.jetty.server.handler.InetAccessSet.AccessTuple;
|
||||||
|
import static org.eclipse.jetty.server.handler.InetAccessSet.PatternTuple;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* InetAddress Access Handler
|
* InetAddress Access Handler
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -43,18 +47,13 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
* provided by and {@link IncludeExcludeSet} over a {@link InetAddressSet}. This
|
* provided by and {@link IncludeExcludeSet} over a {@link InetAddressSet}. This
|
||||||
* handler uses the real internet address of the connection, not one reported in
|
* handler uses the real internet address of the connection, not one reported in
|
||||||
* the forwarded for headers, as this cannot be as easily forged.
|
* the forwarded for headers, as this cannot be as easily forged.
|
||||||
* <p>
|
* </p>
|
||||||
* Additionally, there may be times when you want to only apply this handler to
|
|
||||||
* a subset of your connectors. In this situation you can use
|
|
||||||
* <b>connectorNames</b> to specify the connector names that you want this IP
|
|
||||||
* access filter to apply to.
|
|
||||||
*/
|
*/
|
||||||
public class InetAccessHandler extends HandlerWrapper
|
public class InetAccessHandler extends HandlerWrapper
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(InetAccessHandler.class);
|
private static final Logger LOG = Log.getLogger(InetAccessHandler.class);
|
||||||
|
|
||||||
private final IncludeExcludeSet<String, InetAddress> _addrs = new IncludeExcludeSet<>(InetAddressSet.class);
|
private final IncludeExcludeSet<PatternTuple, AccessTuple> _set = new IncludeExcludeSet<>(InetAccessSet.class);
|
||||||
private final IncludeExclude<String> _names = new IncludeExclude<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all the includes, excludes, included connector names and excluded
|
* Clears all the includes, excludes, included connector names and excluded
|
||||||
|
@ -62,92 +61,150 @@ public class InetAccessHandler extends HandlerWrapper
|
||||||
*/
|
*/
|
||||||
public void clear()
|
public void clear()
|
||||||
{
|
{
|
||||||
_addrs.clear();
|
_set.clear();
|
||||||
_names.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Includes an InetAddress pattern
|
* Includes an InetAccess pattern with an optional connector name, address and URI mapping.
|
||||||
*
|
*
|
||||||
* @param pattern InetAddress pattern to include
|
* <p>The connector name is separated from the InetAddress pattern with an '@' character,
|
||||||
|
* and the InetAddress pattern is separated from the URI pattern using the "|" (pipe)
|
||||||
|
* character. URI patterns follow the servlet specification for simple * prefix and
|
||||||
|
* suffix wild cards (e.g. /, /foo, /foo/bar, /foo/bar/*, *.baz).</p>
|
||||||
|
*
|
||||||
|
* <br>Examples:
|
||||||
|
* <ul>
|
||||||
|
* <li>"connector1@127.0.0.1|/foo"</li>
|
||||||
|
* <li>"127.0.0.1|/foo"</li>
|
||||||
|
* <li>"connector1@127.0.0.1"</li>
|
||||||
|
* <li>"127.0.0.1"</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param pattern InetAccess pattern to include
|
||||||
* @see InetAddressSet
|
* @see InetAddressSet
|
||||||
*/
|
*/
|
||||||
public void include(String pattern)
|
public void include(String pattern)
|
||||||
{
|
{
|
||||||
_addrs.include(pattern);
|
_set.include(PatternTuple.from(pattern));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Includes InetAddress patterns
|
* Includes InetAccess patterns
|
||||||
*
|
*
|
||||||
* @param patterns InetAddress patterns to include
|
* @param patterns InetAddress patterns to include
|
||||||
* @see InetAddressSet
|
* @see InetAddressSet
|
||||||
*/
|
*/
|
||||||
public void include(String... patterns)
|
public void include(String... patterns)
|
||||||
{
|
{
|
||||||
_addrs.include(patterns);
|
for (String pattern : patterns)
|
||||||
|
include(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Excludes an InetAddress pattern
|
* Includes an InetAccess entry.
|
||||||
|
* @param connectorName optional name of a connector to include.
|
||||||
|
* @param addressPattern optional InetAddress pattern to include.
|
||||||
|
* @param pathSpec optional pathSpec to include.
|
||||||
|
*/
|
||||||
|
public void include(String connectorName, String addressPattern, PathSpec pathSpec)
|
||||||
|
{
|
||||||
|
_set.include(new PatternTuple(connectorName, InetAddressPattern.from(addressPattern), pathSpec));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Excludes an InetAccess entry pattern with an optional connector name, address and URI mapping.
|
||||||
|
*
|
||||||
|
* <p>The connector name is separated from the InetAddress pattern with an '@' character,
|
||||||
|
* and the InetAddress pattern is separated from the URI pattern using the "|" (pipe)
|
||||||
|
* character. URI patterns follow the servlet specification for simple * prefix and
|
||||||
|
* suffix wild cards (e.g. /, /foo, /foo/bar, /foo/bar/*, *.baz).</p>
|
||||||
|
*
|
||||||
|
* <br>Examples:
|
||||||
|
* <ul>
|
||||||
|
* <li>"connector1@127.0.0.1|/foo"</li>
|
||||||
|
* <li>"127.0.0.1|/foo"</li>
|
||||||
|
* <li>"connector1@127.0.0.1"</li>
|
||||||
|
* <li>"127.0.0.1"</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param pattern InetAddress pattern to exclude
|
* @param pattern InetAddress pattern to exclude
|
||||||
* @see InetAddressSet
|
* @see InetAddressSet
|
||||||
*/
|
*/
|
||||||
public void exclude(String pattern)
|
public void exclude(String pattern)
|
||||||
{
|
{
|
||||||
_addrs.exclude(pattern);
|
_set.exclude(PatternTuple.from(pattern));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Excludes InetAddress patterns
|
* Excludes InetAccess patterns
|
||||||
*
|
*
|
||||||
* @param patterns InetAddress patterns to exclude
|
* @param patterns InetAddress patterns to exclude
|
||||||
* @see InetAddressSet
|
* @see InetAddressSet
|
||||||
*/
|
*/
|
||||||
public void exclude(String... patterns)
|
public void exclude(String... patterns)
|
||||||
{
|
{
|
||||||
_addrs.exclude(patterns);
|
for (String pattern : patterns)
|
||||||
|
exclude(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Excludes an InetAccess entry.
|
||||||
|
* @param connectorName optional name of a connector to exclude.
|
||||||
|
* @param addressPattern optional InetAddress pattern to exclude.
|
||||||
|
* @param pathSpec optional pathSpec to exclude.
|
||||||
|
*/
|
||||||
|
public void exclude(String connectorName, String addressPattern, PathSpec pathSpec)
|
||||||
|
{
|
||||||
|
_set.exclude(new PatternTuple(connectorName, InetAddressPattern.from(addressPattern), pathSpec));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Includes a connector name.
|
* Includes a connector name.
|
||||||
*
|
*
|
||||||
* @param name Connector name to include in this handler.
|
* @param name Connector name to include in this handler.
|
||||||
|
* @deprecated use {@link InetAccessHandler#include(String)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void includeConnector(String name)
|
public void includeConnector(String name)
|
||||||
{
|
{
|
||||||
_names.include(name);
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Excludes a connector name.
|
* Excludes a connector name.
|
||||||
*
|
*
|
||||||
* @param name Connector name to exclude in this handler.
|
* @param name Connector name to exclude in this handler.
|
||||||
|
* @deprecated use {@link InetAccessHandler#include(String)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void excludeConnector(String name)
|
public void excludeConnector(String name)
|
||||||
{
|
{
|
||||||
_names.exclude(name);
|
_set.exclude(new PatternTuple(name, null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Includes connector names.
|
* Includes connector names.
|
||||||
*
|
*
|
||||||
* @param names Connector names to include in this handler.
|
* @param names Connector names to include in this handler.
|
||||||
|
* @deprecated use {@link InetAccessHandler#include(String)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void includeConnectors(String... names)
|
public void includeConnectors(String... names)
|
||||||
{
|
{
|
||||||
_names.include(names);
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Excludes connector names.
|
* Excludes connector names.
|
||||||
*
|
*
|
||||||
* @param names Connector names to exclude in this handler.
|
* @param names Connector names to exclude in this handler.
|
||||||
|
* @deprecated use {@link InetAccessHandler#include(String)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void excludeConnectors(String... names)
|
public void excludeConnectors(String... names)
|
||||||
{
|
{
|
||||||
_names.exclude(names);
|
for (String name : names)
|
||||||
|
excludeConnector(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -187,26 +244,15 @@ public class InetAccessHandler extends HandlerWrapper
|
||||||
*/
|
*/
|
||||||
protected boolean isAllowed(InetAddress addr, Request baseRequest, HttpServletRequest request)
|
protected boolean isAllowed(InetAddress addr, Request baseRequest, HttpServletRequest request)
|
||||||
{
|
{
|
||||||
String name = baseRequest.getHttpChannel().getConnector().getName();
|
String connectorName = baseRequest.getHttpChannel().getConnector().getName();
|
||||||
boolean filterAppliesToConnector = _names.test(name);
|
return _set.test(new AccessTuple(connectorName, addr, baseRequest.getPathInfo()));
|
||||||
boolean allowedByAddr = _addrs.test(addr);
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
{
|
|
||||||
LOG.debug("name = {}/{} addr={}/{} appliesToConnector={} allowedByAddr={}",
|
|
||||||
name, _names, addr, _addrs, filterAppliesToConnector, allowedByAddr);
|
|
||||||
}
|
|
||||||
if (!filterAppliesToConnector)
|
|
||||||
return true;
|
|
||||||
return allowedByAddr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dump(Appendable out, String indent) throws IOException
|
public void dump(Appendable out, String indent) throws IOException
|
||||||
{
|
{
|
||||||
dumpObjects(out, indent,
|
dumpObjects(out, indent,
|
||||||
new DumpableCollection("included", _addrs.getIncluded()),
|
new DumpableCollection("included", _set.getIncluded()),
|
||||||
new DumpableCollection("excluded", _addrs.getExcluded()),
|
new DumpableCollection("excluded", _set.getExcluded()));
|
||||||
new DumpableCollection("includedConnector", _names.getIncluded()),
|
|
||||||
new DumpableCollection("excludedConnector", _names.getExcluded()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.server.handler;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||||
|
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||||
|
import org.eclipse.jetty.util.InetAddressPattern;
|
||||||
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
|
|
||||||
|
public class InetAccessSet extends AbstractSet<InetAccessSet.PatternTuple> implements Set<InetAccessSet.PatternTuple>, Predicate<InetAccessSet.AccessTuple>
|
||||||
|
{
|
||||||
|
private ArrayList<PatternTuple> tuples = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(PatternTuple storageTuple)
|
||||||
|
{
|
||||||
|
return tuples.add(storageTuple);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o)
|
||||||
|
{
|
||||||
|
return tuples.remove(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<PatternTuple> iterator()
|
||||||
|
{
|
||||||
|
return tuples.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size()
|
||||||
|
{
|
||||||
|
return tuples.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(AccessTuple entry)
|
||||||
|
{
|
||||||
|
if (entry == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (PatternTuple tuple : tuples)
|
||||||
|
{
|
||||||
|
if (tuple.test(entry))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PatternTuple implements Predicate<AccessTuple>
|
||||||
|
{
|
||||||
|
private final String connector;
|
||||||
|
private final InetAddressPattern address;
|
||||||
|
private final PathSpec pathSpec;
|
||||||
|
|
||||||
|
public static PatternTuple from(String pattern)
|
||||||
|
{
|
||||||
|
|
||||||
|
String path = null;
|
||||||
|
int pathIndex = pattern.indexOf('|');
|
||||||
|
if (pathIndex >= 0)
|
||||||
|
path = pattern.substring(pathIndex + 1);
|
||||||
|
|
||||||
|
String connector = null;
|
||||||
|
int connectorIndex = pattern.indexOf('@');
|
||||||
|
if (connectorIndex >= 0)
|
||||||
|
connector = pattern.substring(0, connectorIndex);
|
||||||
|
|
||||||
|
String addr = null;
|
||||||
|
int addrStart = (connectorIndex < 0) ? 0 : connectorIndex + 1;
|
||||||
|
int addrEnd = (pathIndex < 0) ? pattern.length() : pathIndex;
|
||||||
|
if (addrStart != addrEnd)
|
||||||
|
addr = pattern.substring(addrStart, addrEnd);
|
||||||
|
|
||||||
|
return new PatternTuple(connector, InetAddressPattern.from(addr),
|
||||||
|
StringUtil.isEmpty(path) ? null : new ServletPathSpec(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PatternTuple(String connector, InetAddressPattern address, PathSpec pathSpec)
|
||||||
|
{
|
||||||
|
this.connector = connector;
|
||||||
|
this.address = address;
|
||||||
|
this.pathSpec = pathSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(AccessTuple entry)
|
||||||
|
{
|
||||||
|
// Match for connector.
|
||||||
|
if ((connector != null) && !connector.equals(entry.getConnector()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If we have a path we must must be at this path to match for an address.
|
||||||
|
if ((pathSpec != null) && !pathSpec.matches(entry.getPath()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Match for InetAddress.
|
||||||
|
if ((address != null) && !address.test(entry.getAddress()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AccessTuple
|
||||||
|
{
|
||||||
|
private final String connector;
|
||||||
|
private final InetAddress address;
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
public AccessTuple(String connector, InetAddress address, String path)
|
||||||
|
{
|
||||||
|
this.connector = connector;
|
||||||
|
this.address = address;
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConnector()
|
||||||
|
{
|
||||||
|
return connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InetAddress getAddress()
|
||||||
|
{
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath()
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,6 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
@ -36,6 +35,7 @@ import org.eclipse.jetty.server.Connector;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
@ -67,7 +67,6 @@ public class InetAccessHandlerTest
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
throws IOException, ServletException
|
|
||||||
{
|
{
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
response.setStatus(HttpStatus.OK_200);
|
response.setStatus(HttpStatus.OK_200);
|
||||||
|
@ -85,7 +84,7 @@ public class InetAccessHandlerTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("data")
|
@MethodSource("data")
|
||||||
public void testHandler(String include, String exclude, String includeConnectors, String excludeConnectors, String code)
|
public void testHandler(String path, String include, String exclude, String includeConnectors, String excludeConnectors, String code)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
_handler.clear();
|
_handler.clear();
|
||||||
|
@ -107,14 +106,14 @@ public class InetAccessHandlerTest
|
||||||
{
|
{
|
||||||
if (inc.length() > 0)
|
if (inc.length() > 0)
|
||||||
{
|
{
|
||||||
_handler.includeConnector(inc);
|
_handler.include(inc + "@");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (String exc : excludeConnectors.split(";", -1))
|
for (String exc : excludeConnectors.split(";", -1))
|
||||||
{
|
{
|
||||||
if (exc.length() > 0)
|
if (exc.length() > 0)
|
||||||
{
|
{
|
||||||
_handler.excludeConnector(exc);
|
_handler.exclude(exc + "@");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,11 +126,11 @@ public class InetAccessHandlerTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testConnector(_connector1.getLocalPort(), include, exclude, includeConnectors, excludeConnectors, codePerConnector.get(0));
|
testConnector(_connector1.getLocalPort(), path, include, exclude, includeConnectors, excludeConnectors, codePerConnector.get(0));
|
||||||
testConnector(_connector2.getLocalPort(), include, exclude, includeConnectors, excludeConnectors, codePerConnector.get(1));
|
testConnector(_connector2.getLocalPort(), path, include, exclude, includeConnectors, excludeConnectors, codePerConnector.get(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testConnector(int port, String include, String exclude, String includeConnectors, String excludeConnectors, String code) throws IOException
|
private void testConnector(int port, String path, String include, String exclude, String includeConnectors, String excludeConnectors, String code) throws IOException
|
||||||
{
|
{
|
||||||
try (Socket socket = new Socket("127.0.0.1", port);)
|
try (Socket socket = new Socket("127.0.0.1", port);)
|
||||||
{
|
{
|
||||||
|
@ -139,7 +138,7 @@ public class InetAccessHandlerTest
|
||||||
|
|
||||||
HttpTester.Request request = HttpTester.newRequest();
|
HttpTester.Request request = HttpTester.newRequest();
|
||||||
request.setMethod("GET");
|
request.setMethod("GET");
|
||||||
request.setURI("/path");
|
request.setURI(StringUtil.isEmpty(path) ? "/" : path);
|
||||||
request.setHeader("Host", "127.0.0.1");
|
request.setHeader("Host", "127.0.0.1");
|
||||||
request.setVersion(HttpVersion.HTTP_1_0);
|
request.setVersion(HttpVersion.HTTP_1_0);
|
||||||
|
|
||||||
|
@ -166,60 +165,81 @@ public class InetAccessHandlerTest
|
||||||
Object[][] data = new Object[][]
|
Object[][] data = new Object[][]
|
||||||
{
|
{
|
||||||
// Empty lists 1
|
// Empty lists 1
|
||||||
{"", "", "", "", "200;200"},
|
{"", "", "", "", "", "200;200"},
|
||||||
|
|
||||||
// test simple filters
|
// test simple filters
|
||||||
{"127.0.0.1", "", "", "", "200;200"},
|
{"", "127.0.0.1", "", "", "", "200;200"},
|
||||||
{"127.0.0.1-127.0.0.254", "", "", "", "200;200"},
|
{"", "127.0.0.1-127.0.0.254", "", "", "", "200;200"},
|
||||||
{"192.0.0.1", "", "", "", "403;403"},
|
{"", "127.0.0.1-127.0.0.254", "", "", "", "200;200"},
|
||||||
{"192.0.0.1-192.0.0.254", "", "", "", "403;403"},
|
{"", "192.0.0.1", "", "", "", "403;403"},
|
||||||
|
{"", "192.0.0.1-192.0.0.254", "", "", "", "403;403"},
|
||||||
|
|
||||||
// test includeConnector
|
// test includeConnector
|
||||||
{"127.0.0.1", "", "http_connector1", "", "200;200"},
|
{"", "127.0.0.1", "", "http_connector1", "", "200;200"},
|
||||||
{"127.0.0.1-127.0.0.254", "", "http_connector1", "", "200;200"},
|
{"", "127.0.0.1-127.0.0.254", "", "http_connector1", "", "200;200"},
|
||||||
{"192.0.0.1", "", "http_connector1", "", "403;200"},
|
{"", "192.0.0.1", "", "http_connector1", "", "200;403"},
|
||||||
{"192.0.0.1-192.0.0.254", "", "http_connector1", "", "403;200"},
|
{"", "192.0.0.1-192.0.0.254", "", "http_connector1", "", "200;403"},
|
||||||
{"192.0.0.1", "", "http_connector2", "", "200;403"},
|
{"", "192.0.0.1", "", "http_connector2", "", "403;200"},
|
||||||
{"192.0.0.1-192.0.0.254", "", "http_connector2", "", "200;403"},
|
{"", "192.0.0.1-192.0.0.254", "", "http_connector2", "", "403;200"},
|
||||||
|
|
||||||
// test includeConnector names where none of them match
|
// test includeConnector names where none of them match
|
||||||
{"127.0.0.1", "", "nothttp", "", "200;200"},
|
{"", "127.0.0.1", "", "nothttp", "", "200;200"},
|
||||||
{"127.0.0.1-127.0.0.254", "", "nothttp", "", "200;200"},
|
{"", "127.0.0.1-127.0.0.254", "", "nothttp", "", "200;200"},
|
||||||
{"192.0.0.1", "", "nothttp", "", "200;200"},
|
{"", "192.0.0.1", "", "nothttp", "", "403;403"},
|
||||||
{"192.0.0.1-192.0.0.254", "", "nothttp", "", "200;200"},
|
{"", "192.0.0.1-192.0.0.254", "", "nothttp", "", "403;403"},
|
||||||
|
|
||||||
// text excludeConnector
|
// text excludeConnector
|
||||||
{"127.0.0.1", "", "", "http_connector1", "200;200"},
|
{"", "127.0.0.1", "", "", "http_connector1", "403;200"},
|
||||||
{"127.0.0.1-127.0.0.254", "", "", "http_connector1", "200;200"},
|
{"", "127.0.0.1-127.0.0.254", "", "", "http_connector1", "403;200"},
|
||||||
{"192.0.0.1", "", "", "http_connector1", "200;403"},
|
{"", "192.0.0.1", "", "", "http_connector1", "403;403"},
|
||||||
{"192.0.0.1-192.0.0.254", "", "", "http_connector1", "200;403"},
|
{"", "192.0.0.1-192.0.0.254", "", "", "http_connector1", "403;403"},
|
||||||
{"192.0.0.1", "", "", "http_connector2", "403;200"},
|
{"", "192.0.0.1", "", "", "http_connector2", "403;403"},
|
||||||
{"192.0.0.1-192.0.0.254", "", "", "http_connector2", "403;200"},
|
{"", "192.0.0.1-192.0.0.254", "", "", "http_connector2", "403;403"},
|
||||||
|
|
||||||
// test excludeConnector where none of them match.
|
// test excludeConnector where none of them match.
|
||||||
{"127.0.0.1", "", "", "nothttp", "200;200"},
|
{"", "127.0.0.1", "", "", "nothttp", "200;200"},
|
||||||
{"127.0.0.1-127.0.0.254", "", "", "nothttp", "200;200"},
|
{"", "127.0.0.1-127.0.0.254", "", "", "nothttp", "200;200"},
|
||||||
{"192.0.0.1", "", "", "nothttp", "403;403"},
|
{"", "192.0.0.1", "", "", "nothttp", "403;403"},
|
||||||
{"192.0.0.1-192.0.0.254", "", "", "nothttp", "403;403"},
|
{"", "192.0.0.1-192.0.0.254", "", "", "nothttp", "403;403"},
|
||||||
|
|
||||||
// both connectors are excluded
|
// both connectors are excluded
|
||||||
{"127.0.0.1", "", "", "http_connector1;http_connector2", "200;200"},
|
{"", "127.0.0.1", "", "", "http_connector1;http_connector2", "403;403"},
|
||||||
{"127.0.0.1-127.0.0.254", "", "", "http_connector1;http_connector2", "200;200"},
|
{"", "127.0.0.1-127.0.0.254", "", "", "http_connector1;http_connector2", "403;403"},
|
||||||
{"192.0.0.1", "", "", "http_connector1;http_connector2", "200;200"},
|
{"", "192.0.0.1", "", "", "http_connector1;http_connector2", "403;403"},
|
||||||
{"192.0.0.1-192.0.0.254", "", "", "http_connector1;http_connector2", "200;200"},
|
{"", "192.0.0.1-192.0.0.254", "", "", "http_connector1;http_connector2", "403;403"},
|
||||||
|
|
||||||
// both connectors are included
|
// both connectors are included
|
||||||
{"127.0.0.1", "", "http_connector1;http_connector2", "", "200;200"},
|
{"", "127.0.0.1", "", "http_connector1;http_connector2", "", "200;200"},
|
||||||
{"127.0.0.1-127.0.0.254", "", "http_connector1;http_connector2", "", "200;200"},
|
{"", "127.0.0.1-127.0.0.254", "", "http_connector1;http_connector2", "", "200;200"},
|
||||||
{"192.0.0.1", "", "http_connector1;http_connector2", "", "403;403"},
|
{"", "192.0.0.1", "", "http_connector1;http_connector2", "", "200;200"},
|
||||||
{"192.0.0.1-192.0.0.254", "", "http_connector1;http_connector2", "", "403;403"},
|
{"", "192.0.0.1-192.0.0.254", "", "http_connector1;http_connector2", "", "200;200"},
|
||||||
|
{"", "", "127.0.0.1", "http_connector1;http_connector2", "", "403;403"},
|
||||||
|
|
||||||
// exclude takes precedence over include
|
// exclude takes precedence over include
|
||||||
{"127.0.0.1", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "200;200"},
|
{"", "127.0.0.1", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "403;403"},
|
||||||
{"127.0.0.1-127.0.0.254", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "200;200"},
|
{"", "127.0.0.1-127.0.0.254", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "403;403"},
|
||||||
{"192.0.0.1", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "200;200"},
|
{"", "192.0.0.1", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "403;403"},
|
||||||
{"192.0.0.1-192.0.0.254", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "200;200"}
|
{"", "192.0.0.1-192.0.0.254", "", "http_connector1;http_connector2", "http_connector1;http_connector2", "403;403"},
|
||||||
|
|
||||||
|
// Test path specific include/exclude.
|
||||||
|
{"/testPath", "", "", "http_connector1", "", "200;403"},
|
||||||
|
{"/", "127.0.0.1", "127.0.0.1|/testPath", "http_connector1", "", "200;200"},
|
||||||
|
{"/testPath", "127.0.0.1", "127.0.0.1|/testPath", "http_connector1", "", "403;403"},
|
||||||
|
{"/notTestPath", "127.0.1.11|/testPath", "", "http_connector1", "", "200;403"},
|
||||||
|
{"/testPath", "127.0.1.11|/testPath", "", "http_connector1", "", "200;403"},
|
||||||
|
{"/testPath", "127.0.0.13|/testPath;127.0.0.1|/testPath", "", "http_connector1", "", "200;200"},
|
||||||
|
{"/testPath", "127.0.0.1", "127.0.0.1|/testPath", "http_connector1", "", "403;403"},
|
||||||
|
{"/", "127.0.0.1", "127.0.0.1|/testPath", "http_connector1", "", "200;200"},
|
||||||
|
{"/a/b", "", "127.0.0.1|/a/*", "", "", "403;403"},
|
||||||
|
{"/b/a", "", "127.0.0.1|/a/*", "", "", "200;200"},
|
||||||
|
{"/org/eclipse/jetty/test.html", "127.0.0.1|*.html", "127.0.0.1|*.xml", "", "", "200;200"},
|
||||||
|
{"/org/eclipse/jetty/test.xml", "127.0.0.1|*.html", "127.0.0.1|*.xml", "", "", "403;403"},
|
||||||
|
{"/org/eclipse/jetty/test.pdf", "127.0.0.1|*.html", "127.0.0.1|*.xml", "", "", "403;403"},
|
||||||
|
{"/a/test.html", "", "127.0.0.1|*.html;127.0.0.10|/a/*", "", "", "403;403"},
|
||||||
|
{"/foo/bar/test.xml", "127.0.0.1|/foo/*", "127.0.0.0-127.0.0.2|*.html", "", "", "200;200"},
|
||||||
|
{"/foo/bar/test.html", "127.0.0.1|/foo/*", "127.0.0.0-127.0.0.2|*.html", "", "", "403;403"},
|
||||||
|
{"/foo/bar/test.xml", "127.0.0.1|/foo/bar/*", "127.0.0.1|/foo/*", "", "", "403;403"},
|
||||||
};
|
};
|
||||||
return Arrays.asList(data).stream().map(Arguments::of);
|
return Arrays.stream(data).map(Arguments::of);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.util;
|
package org.eclipse.jetty.util;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -145,10 +146,7 @@ public class IncludeExcludeSet<T, P> implements Predicate<P>
|
||||||
|
|
||||||
public void include(T... element)
|
public void include(T... element)
|
||||||
{
|
{
|
||||||
for (T e : element)
|
_includes.addAll(Arrays.asList(element));
|
||||||
{
|
|
||||||
_includes.add(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void exclude(T element)
|
public void exclude(T element)
|
||||||
|
@ -158,10 +156,7 @@ public class IncludeExcludeSet<T, P> implements Predicate<P>
|
||||||
|
|
||||||
public void exclude(T... element)
|
public void exclude(T... element)
|
||||||
{
|
{
|
||||||
for (T e : element)
|
_excludes.addAll(Arrays.asList(element));
|
||||||
{
|
|
||||||
_excludes.add(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -233,34 +228,4 @@ public class IncludeExcludeSet<T, P> implements Predicate<P>
|
||||||
{
|
{
|
||||||
return _includes.isEmpty() && _excludes.isEmpty();
|
return _includes.isEmpty() && _excludes.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Match items in combined IncludeExcludeSets.
|
|
||||||
* @param item1 The item to match against set1
|
|
||||||
* @param set1 A IncludeExcludeSet to match item1 against
|
|
||||||
* @param item2 The item to match against set2
|
|
||||||
* @param set2 A IncludeExcludeSet to match item2 against
|
|
||||||
* @param <T1> The type of item1
|
|
||||||
* @param <T2> The type of item2
|
|
||||||
* @return True IFF <ul>
|
|
||||||
* <li>Neither item is excluded from their respective sets</li>
|
|
||||||
* <li>Both sets have no includes OR at least one of the items is included in its respective set</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
public static <T1,T2> boolean matchCombined(T1 item1, IncludeExcludeSet<?,T1> set1, T2 item2, IncludeExcludeSet<?,T2> set2)
|
|
||||||
{
|
|
||||||
Boolean match1 = set1.isIncludedAndNotExcluded(item1);
|
|
||||||
Boolean match2 = set2.isIncludedAndNotExcluded(item2);
|
|
||||||
|
|
||||||
// if we are excluded from either set, then we do not match
|
|
||||||
if (match1 == Boolean.FALSE || match2 == Boolean.FALSE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If either set has any includes, then we must be included by one of them
|
|
||||||
if (set1.hasIncludes() || set2.hasIncludes())
|
|
||||||
return match1 == Boolean.TRUE || match2 == Boolean.TRUE;
|
|
||||||
|
|
||||||
// If not excluded and no includes, then we match
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,282 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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 {@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,62 +30,20 @@ import java.util.function.Predicate;
|
||||||
* A set of InetAddress patterns.
|
* A set of InetAddress patterns.
|
||||||
* <p>This is a {@link Set} of String patterns that are used to match
|
* <p>This is a {@link Set} of String patterns that are used to match
|
||||||
* a {@link Predicate} over InetAddress for containment semantics.
|
* a {@link Predicate} over InetAddress for containment semantics.
|
||||||
* The patterns that may be set are:
|
* The patterns that may be set are defined in {@link InetAddressPattern}.
|
||||||
* </p>
|
* </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>
|
|
||||||
* </dl>
|
|
||||||
* <p>This class is designed to work with {@link IncludeExcludeSet}</p>
|
* <p>This class is designed to work with {@link IncludeExcludeSet}</p>
|
||||||
*
|
*
|
||||||
* @see IncludeExcludeSet
|
* @see IncludeExcludeSet
|
||||||
*/
|
*/
|
||||||
public class InetAddressSet extends AbstractSet<String> implements Set<String>, Predicate<InetAddress>
|
public class InetAddressSet extends AbstractSet<String> implements Set<String>, Predicate<InetAddress>
|
||||||
{
|
{
|
||||||
private Map<String, InetPattern> _patterns = new HashMap<>();
|
private Map<String, InetAddressPattern> _patterns = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean add(String pattern)
|
public boolean add(String pattern)
|
||||||
{
|
{
|
||||||
return _patterns.put(pattern, newInetRange(pattern)) == null;
|
return _patterns.put(pattern, InetAddressPattern.from(pattern)) == null;
|
||||||
}
|
|
||||||
|
|
||||||
private 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
|
@Override
|
||||||
|
@ -111,219 +69,11 @@ public class InetAddressSet extends AbstractSet<String> implements Set<String>,
|
||||||
{
|
{
|
||||||
if (address == null)
|
if (address == null)
|
||||||
return false;
|
return false;
|
||||||
byte[] raw = address.getAddress();
|
for (InetAddressPattern pattern : _patterns.values())
|
||||||
for (InetPattern pattern : _patterns.values())
|
|
||||||
{
|
{
|
||||||
if (pattern.test(address, raw))
|
if (pattern.test(address))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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[] 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 item, byte[] raw)
|
|
||||||
{
|
|
||||||
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 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 && (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 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue