import java.io.IOException;
import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.httpclient.HttpStatus;
import org.joda.time.DateTime;
import org.joda.time.chrono.ISOChronology;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.opensaml.xml.security.x509.X509Credential;
import org.opensaml.xml.util.Base64;
+import org.opensaml.xml.util.DatatypeHelper;
+import org.opensaml.xml.util.LazyList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolver;
import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfigurationManager;
import edu.internet2.middleware.shibboleth.idp.util.HttpServletHelper;
+import edu.internet2.middleware.shibboleth.idp.util.IPRange;
/** A Servlet for displaying the status of the IdP. */
public class StatusServlet extends HttpServlet {
/** Serial version UID. */
- private static final long serialVersionUID = 7917509317276109266L;
+ private static final long serialVersionUID = -5280549109235107879L;
+
+ private final String IP_PARAM_NAME = "AllowedIPs";
+
+ private final Logger log = LoggerFactory.getLogger(StatusServlet.class);
+
+ private LazyList<IPRange> allowedIPs;
/** Formatter used when print date/times. */
private DateTimeFormatter dateFormat;
public void init(ServletConfig config) throws ServletException {
super.init(config);
+ allowedIPs = new LazyList<IPRange>();
+
+ String cidrBlocks = DatatypeHelper.safeTrimOrNullString(config.getInitParameter(IP_PARAM_NAME));
+ if (cidrBlocks != null) {
+ for (String cidrBlock : cidrBlocks.split(" ")) {
+ allowedIPs.add(IPRange.parseCIDRBlock(cidrBlock));
+ }
+ }
+
dateFormat = ISODateTimeFormat.dateTimeNoMillis();
startTime = new DateTime(ISOChronology.getInstanceUTC());
attributeResolver = HttpServletHelper.getAttributeResolver(config.getServletContext());
}
/** {@inheritDoc} */
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- resp.setContentType("text/plain");
- PrintWriter output = resp.getWriter();
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ if (!isAuthenticated(request)) {
+ response.sendError(HttpStatus.SC_UNAUTHORIZED);
+ return;
+ }
+
+ response.setContentType("text/plain");
+ PrintWriter output = response.getWriter();
printOperatingEnvironmentInformation(output);
output.println();
printIdPInformation(output);
output.println();
- printRelyingPartyConfigurationsInformation(output, req.getParameter("relyingParty"));
+ printRelyingPartyConfigurationsInformation(output, request.getParameter("relyingParty"));
output.flush();
}
/**
+ * Checks whether the client is authenticated.
+ *
+ * @param request client request
+ *
+ * @return true if the client is authenticated, false if not
+ */
+ protected boolean isAuthenticated(HttpServletRequest request) throws ServletException {
+ log.debug("Attempting to authenticate client '{}'", request.getRemoteAddr());
+ try {
+ InetAddress clientAddress = InetAddress.getByName(request.getRemoteAddr());
+
+ for (IPRange range : allowedIPs) {
+ if (range.contains(clientAddress)) {
+ return true;
+ }
+ }
+
+ return false;
+ } catch (UnknownHostException e) {
+ throw new ServletException(e);
+ }
+ }
+
+ /**
* Prints out information about the operating environment. This includes the operating system name, version and
* architecture, the JDK version, available CPU cores, memory currently used by the JVM process, the maximum amount
* of memory that may be used by the JVM, and the current time in UTC.
protected void printRelyingPartyConfigurationInformation(PrintWriter out, RelyingPartyConfiguration config) {
out.println("relying_party_id: " + config.getRelyingPartyId());
out.println("idp_entity_id: " + config.getProviderId());
-
+
if (config.getDefaultAuthenticationMethod() != null) {
out.println("default_authentication_method: " + config.getDefaultAuthenticationMethod());
} else {
out.println("default_authentication_method: none");
}
- try{
+ try {
X509Credential signingCredential = (X509Credential) config.getDefaultSigningCredential();
- out.println("default_signing_tls_key: " + Base64.encodeBytes(signingCredential.getEntityCertificate().getEncoded(), Base64.DONT_BREAK_LINES));
- }catch(Throwable t){
+ out
+ .println("default_signing_tls_key: "
+ + Base64.encodeBytes(signingCredential.getEntityCertificate().getEncoded(),
+ Base64.DONT_BREAK_LINES));
+ } catch (Throwable t) {
// swallow error
}
-
+
for (String profileId : config.getProfileConfigurations().keySet()) {
out.println("configured_communication_profile: " + profileId);
}
/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package edu.internet2.middleware.shibboleth.idp.authn.provider;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
-import java.util.BitSet;
+import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.opensaml.xml.util.DatatypeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.slf4j.helpers.MessageFormatter;
import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationEngine;
+import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationException;
import edu.internet2.middleware.shibboleth.idp.authn.LoginHandler;
+import edu.internet2.middleware.shibboleth.idp.util.IPRange;
/**
* IP Address authentication handler.
*
* This "authenticates" a user based on their IP address. It operates in either default deny or default allow mode, and
* evaluates a given request against a list of blocked or permitted IPs. It supports both IPv4 and IPv6.
- *
- * If an Authentication Context Class or DeclRef URI is not specified, it will default to
- * "urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol".
*/
public class IPAddressLoginHandler extends AbstractLoginHandler {
private final Logger log = LoggerFactory.getLogger(IPAddressLoginHandler.class);
/** The username to use for IP-address "authenticated" users. */
- private String username;
-
- /** Are the IPs in ipList a permitted list or a deny list. */
- private boolean defaultDeny;
+ private String authenticatedUser;
- /** The list of denied or permitted IPs. */
- private List<IPEntry> ipList;
+ /** List of configured IP ranged. */
+ private List<IPRange> ipRanges;
- /**
- * Set the permitted IP addresses.
- *
- * If <code>defaultDeny</code> is <code>true</code> then only the IP addresses in <code>ipList</code> will be
- * "authenticated." If <code>defaultDeny</code> is <code>false</code>, then all IP addresses except those in
- * <code>ipList</code> will be authenticated.
- *
- * @param entries A list of IP addresses (with CIDR masks).
- * @param defaultDeny Does <code>ipList</code> contain a deny or permit list.
- */
- public void setEntries(final List<String> entries, boolean defaultDeny) {
+ /** Whether a user is "authenticated" if their IP address is within a configured IP range. */
+ private boolean ipInRangeIsAuthenticated;
- this.defaultDeny = defaultDeny;
- ipList = new CopyOnWriteArrayList<IPEntry>();
+ public IPAddressLoginHandler(String user, List<IPRange> ranges, boolean ipInRangeIsAuthenticated) {
+ authenticatedUser = DatatypeHelper.safeTrimOrNullString(user);
+ if (authenticatedUser == null) {
+ throw new IllegalArgumentException("The authenticated user ID may not be null or empty");
+ }
- for (String addr : entries) {
- try {
- ipList.add(new edu.internet2.middleware.shibboleth.idp.authn.provider.IPAddressLoginHandler.IPEntry(
- addr));
- } catch (UnknownHostException ex) {
- log.warn("IPAddressHandler: Error parsing IP entry \"" + addr + "\". Ignoring.");
- }
+ if (ranges == null || ranges.isEmpty()) {
+ throw new IllegalArgumentException("The list of IP ranges may not be null or empty");
}
+ ipRanges = new ArrayList<IPRange>(ranges);
+
+ this.ipInRangeIsAuthenticated = ipInRangeIsAuthenticated;
}
/** {@inheritDoc} */
return true;
}
- /**
- * Get the username for all IP-address authenticated users.
- *
- * @return The username for IP-address authenticated users.
- */
- public String getUsername() {
- return username;
- }
-
- /**
- * Set the username to use for all IP-address authenticated users.
- *
- * @param name The username for IP-address authenticated users.
- */
- public void setUsername(String name) {
- username = name;
- }
-
/** {@inheritDoc} */
public void login(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
-
- if (defaultDeny) {
- handleDefaultDeny(httpRequest, httpResponse);
- } else {
- handleDefaultAllow(httpRequest, httpResponse);
- }
-
- AuthenticationEngine.returnToAuthenticationEngine(httpRequest, httpResponse);
- }
-
- protected void handleDefaultDeny(HttpServletRequest request, HttpServletResponse response) {
-
- boolean ipAllowed = searchIpList(request);
-
- if (ipAllowed) {
- log.debug("Authenticated user by IP address");
- request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, username);
- }
- }
-
- protected void handleDefaultAllow(HttpServletRequest request, HttpServletResponse response) {
-
- boolean ipDenied = searchIpList(request);
-
- if (!ipDenied) {
- log.debug("Authenticated user by IP address");
- request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, username);
- }
- }
-
- /**
- * Search the list of InetAddresses for the client's address.
- *
- * @param request The ServletReqeust
- *
- * @return <code>true</code> if the client's address is in <code>ipList</code>
- */
- private boolean searchIpList(ServletRequest request) {
-
- boolean found = false;
-
+ log.debug("Attempting to authenticated client '{}'", httpRequest.getRemoteAddr());
try {
- InetAddress addr = InetAddress.getByName(request.getRemoteAddr());
- BitSet addrbits = byteArrayToBitSet(addr.getAddress());
-
- for (IPEntry entry : ipList) {
-
- BitSet netaddr = entry.getNetworkAddress();
- BitSet netmask = entry.getNetmask();
-
- addrbits.and(netmask);
- if (addrbits.equals(netaddr)) {
- found = true;
- break;
- }
+ InetAddress clientAddress = InetAddress.getByName(httpRequest.getRemoteAddr());
+ if (authenticate(clientAddress)) {
+ log.debug("Authenticated user by IP address");
+ httpRequest.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, authenticatedUser);
+ } else {
+ log.debug("Client IP address {} failed authentication.", httpRequest.getRemoteAddr());
+ httpRequest.setAttribute(LoginHandler.AUTHENTICATION_ERROR_KEY, new AuthenticationException(
+ "Client failed IP address authentication"));
}
-
- } catch (UnknownHostException ex) {
- log.error("Error resolving hostname.", ex);
- return false;
+ } catch (UnknownHostException e) {
+ String msg = MessageFormatter.format("Unable to resolve {} in to an IP address", httpRequest
+ .getRemoteAddr());
+ log.warn(msg);
+ httpRequest.setAttribute(LoginHandler.AUTHENTICATION_ERROR_KEY, new AuthenticationException(msg));
}
- return found;
+ AuthenticationEngine.returnToAuthenticationEngine(httpRequest, httpResponse);
}
/**
- * Converts a byte array to a BitSet.
+ * Authenticates the client address.
*
- * The supplied byte array is assumed to have the most signifigant bit in element 0.
+ * @param clientAddress the client address
*
- * @param bytes the byte array with most signifigant bit in element 0.
- *
- * @return the BitSet
- */
- protected BitSet byteArrayToBitSet(final byte[] bytes) {
-
- BitSet bits = new BitSet();
-
- for (int i = 0; i < bytes.length * 8; i++) {
- if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) {
- bits.set(i);
- }
- }
-
- return bits;
- }
-
- /**
- * Encapsulates a network address and a netmask on ipList.
+ * @return true if the client address is authenticated, false it not
*/
- protected class IPEntry {
-
- /** The network address. */
- private final BitSet networkAddress;
-
- /** The netmask. */
- private final BitSet netmask;
-
- /**
- * Construct a new IPEntry given a network address in CIDR format.
- *
- * @param entry A CIDR-formatted network address/netmask
- *
- * @throws UnknownHostException If entry is malformed.
- */
- public IPEntry(String entry) throws UnknownHostException {
-
- // quick sanity checks
- if (entry == null || entry.length() == 0) {
- throw new UnknownHostException("entry is null.");
- }
-
- int cidrOffset = entry.indexOf("/");
- if (cidrOffset == -1) {
- log.warn("Invalid entry \"" + entry + "\" -- it lacks a netmask component.");
- throw new UnknownHostException("entry lacks a netmask component.");
- }
-
- // ensure that only one "/" is present.
- if (entry.indexOf("/", cidrOffset + 1) != -1) {
- log.warn("Invalid entry \"" + entry + "\" -- too many \"/\" present.");
- throw new UnknownHostException("entry has too many netmask components.");
- }
-
- String networkString = entry.substring(0, cidrOffset);
- String netmaskString = entry.substring(cidrOffset + 1, entry.length());
-
- InetAddress tempAddr = InetAddress.getByName(networkString);
- networkAddress = byteArrayToBitSet(tempAddr.getAddress());
-
- int masklen = Integer.parseInt(netmaskString);
-
- int addrlen;
- if (tempAddr instanceof Inet4Address) {
- addrlen = 32;
- } else if (tempAddr instanceof Inet6Address) {
- addrlen = 128;
- }else{
- throw new UnknownHostException("Unable to determine Inet protocol version");
+ protected boolean authenticate(InetAddress clientAddress) {
+ if (ipInRangeIsAuthenticated) {
+ for (IPRange range : ipRanges) {
+ if (range.contains(clientAddress)) {
+ return true;
+ }
}
-
- // ensure that the netmask isn't too large
- if ((tempAddr instanceof Inet4Address) && (masklen > 32)) {
- throw new UnknownHostException("Netmask is too large for an IPv4 address: " + masklen);
- } else if ((tempAddr instanceof Inet6Address) && masklen > 128) {
- throw new UnknownHostException("Netmask is too large for an IPv6 address: " + masklen);
+ } else {
+ for (IPRange range : ipRanges) {
+ if (!range.contains(clientAddress)) {
+ return true;
+ }
}
-
- netmask = new BitSet(addrlen);
- netmask.set(addrlen - masklen, addrlen, true);
- }
-
- /**
- * Get the network address.
- *
- * @return the network address.
- */
- public BitSet getNetworkAddress() {
- return networkAddress;
}
- /**
- * Get the netmask.
- *
- * @return the netmask.
- */
- public BitSet getNetmask() {
- return netmask;
- }
+ return false;
}
}
\ No newline at end of file
if (authenticationMethods != null) {
handler.getSupportedAuthenticationMethods().addAll(authenticationMethods);
}
- handler.setAuthenticationDurection(authenticationDuration * 60 * 1000);
+ handler.setAuthenticationDuration(authenticationDuration * 60 * 1000);
}
}
package edu.internet2.middleware.shibboleth.idp.config.profile.authn;
-import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import javax.xml.namespace.QName;
import org.opensaml.xml.util.DatatypeHelper;
+import org.opensaml.xml.util.LazyList;
import org.opensaml.xml.util.XMLHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.w3c.dom.Element;
import edu.internet2.middleware.shibboleth.idp.config.profile.ProfileHandlerNamespaceHandler;
+import edu.internet2.middleware.shibboleth.idp.util.IPRange;
-/**
- * Spring bean definition parser for IP address authentication handlers.
- */
+/** Spring bean definition parser for IP address authentication handlers. */
public class IPAddressLoginHandlerBeanDefinitionParser extends AbstractLoginHandlerBeanDefinitionParser {
/** Schema type. */
public static final QName SCHEMA_TYPE = new QName(ProfileHandlerNamespaceHandler.NAMESPACE, "IPAddress");
- /** Name of ip entry elements. */
- public static final QName IP_ENTRY_ELEMENT_NAME = new QName(ProfileHandlerNamespaceHandler.NAMESPACE, "IPEntry");
-
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(IPAddressLoginHandlerBeanDefinitionParser.class);
protected void doParse(Element config, BeanDefinitionBuilder builder) {
super.doParse(config, builder);
+ String username = DatatypeHelper.safeTrimOrNullString(config.getAttributeNS(null, "username"));
+ if (username == null) {
+ String msg = "No username specified.";
+ log.error(msg);
+ throw new BeanCreationException(msg);
+ }
+ log.debug("authenticated username: {}", username);
+ builder.addPropertyValue("authenticatedUser", username);
+
+ List<IPRange> ranges = getIPRanges(config);
+ log.debug("registered IP ranges: {}", ranges.size());
+ builder.addPropertyValue("ipRanges", ranges);
+
boolean defaultDeny = XMLHelper.getAttributeValueAsBoolean(config.getAttributeNodeNS(null, "defaultDeny"));
- log.debug("Setting defaultDeny to: {}", defaultDeny);
- builder.addPropertyValue("defaultDeny", defaultDeny);
-
- String username = DatatypeHelper.safeTrim(config.getAttributeNS(null, "username"));
- log.debug("Setting username to: {}", username);
- builder.addPropertyValue("username", username);
-
- Map<QName, List<Element>> children = XMLHelper.getChildElements(config);
- List<Element> ipEntries = children.get(IP_ENTRY_ELEMENT_NAME);
- List<String> addresses = new ArrayList<String>();
-
- for (Element element : ipEntries) {
- String address = DatatypeHelper.safeTrimOrNullString(element.getTextContent());
- if (address != null) {
- log.debug("Adding IP Address: {}", address);
- addresses.add(address);
- }
+ log.debug("default deny: {}", defaultDeny);
+ builder.addPropertyValue("ipInRangeIsAuthenticated", defaultDeny);
+ }
+
+ /**
+ * Gets the list of IP ranges given in the configuration.
+ *
+ * @param config current configuration
+ *
+ * @return list of IP ranges
+ */
+ protected List<IPRange> getIPRanges(Element config) {
+ List<Element> ipEntries = XMLHelper.getChildElementsByTagNameNS(config,
+ ProfileHandlerNamespaceHandler.NAMESPACE, "IPEntry");
+ if (ipEntries == null || ipEntries.isEmpty()) {
+ String msg = "At least one IPEntry must be specified.";
+ log.error(msg);
+ throw new BeanCreationException(msg);
}
- builder.addPropertyValue("addresses", addresses);
+
+ List<IPRange> ranges = new LazyList<IPRange>();
+ for (Element ipEntry : ipEntries) {
+ ranges.add(IPRange.parseCIDRBlock(ipEntry.getTextContent()));
+ }
+
+ return ranges;
}
-}
+}
\ No newline at end of file
import java.util.List;
import edu.internet2.middleware.shibboleth.idp.authn.provider.IPAddressLoginHandler;
+import edu.internet2.middleware.shibboleth.idp.util.IPRange;
/**
* Spring factory for {@link IPAddressLoginHandler}.
*/
public class IPAddressLoginHandlerFactoryBean extends AbstractLoginHandlerFactoryBean {
- /** The list of denied or permitted IPs. */
- private List<String> addresses;
-
/** The username to use for IP-address "authenticated" users. */
- private String username;
+ private String authenticatedUser;
- /** Are the IPs in ipList a permitted list or a deny list. */
- private boolean defaultDeny;
+ /** List of configured IP ranged. */
+ private List<IPRange> ipRanges;
- /** {@inheritDoc} */
- protected Object createInstance() throws Exception {
- IPAddressLoginHandler handler = new IPAddressLoginHandler();
- handler.setUsername(getUsername());
- handler.setEntries(getAddresses(), isDefaultDeny());
- populateHandler(handler);
- return handler;
- }
+ /** Whether a user is "authenticated" if their IP address is within a configured IP range. */
+ private boolean ipInRangeIsAuthenticated;
/** {@inheritDoc} */
public Class getObjectType() {
return IPAddressLoginHandler.class;
}
-
- /**
- * Get the list of denied or permitted IPs.
- *
- * @return list of denied or permitted IPs
- */
- public List<String> getAddresses() {
- return addresses;
- }
-
- /**
- * Set the list of denied or permitted IPs.
- *
- * @param newAddresses list of denied or permitted IPs
- */
- public void setAddresses(List<String> newAddresses) {
- addresses = newAddresses;
- }
-
+
/**
- * Get the username to use for IP-address "authenticated" users.
- *
- * @return username to use for IP-address "authenticated" users
+ * @param user The authenticatedUser to set.
*/
- public String getUsername() {
- return username;
+ public void setAuthenticatedUser(String user) {
+ authenticatedUser = user;
}
/**
- * Set the username to use for IP-address "authenticated" users.
- *
- * @param newUsername username to use for IP-address "authenticated" users
+ * @param ranges The ipRanges to set.
*/
- public void setUsername(String newUsername) {
- username = newUsername;
+ public void setIpRanges(List<IPRange> ranges) {
+ ipRanges = ranges;
}
/**
- * Get whether the IPs in ipList a permitted list or a deny list.
- *
- * @return whether the IPs in ipList a permitted list or a deny list
+ * @param authenticated The ipInRangeIsAuthenticated to set.
*/
- public boolean isDefaultDeny() {
- return defaultDeny;
+ public void setIpInRangeIsAuthenticated(boolean authenticated) {
+ ipInRangeIsAuthenticated = authenticated;
}
- /**
- * Set whether the IPs in ipList a permitted list or a deny list.
- *
- * @param newDefaultDeny whether the IPs in ipList a permitted list or a deny list.
- */
- public void setDefaultDeny(boolean newDefaultDeny) {
- defaultDeny = newDefaultDeny;
+ /** {@inheritDoc} */
+ protected Object createInstance() throws Exception {
+ IPAddressLoginHandler handler = new IPAddressLoginHandler(authenticatedUser, ipRanges, ipInRangeIsAuthenticated);
+ populateHandler(handler);
+ return handler;
}
-
-}
+}
\ No newline at end of file
/**
* A simple profile handler that returns the string "ok" if the IdP is able to answer the request. This may be used for
* very basic monitoring of the IdP.
+ *
+ * @deprecated
*/
public class StatusProfileHandler extends AbstractRequestURIMappedProfileHandler {
--- /dev/null
+/*
+ * Copyright 2009 University Corporation for Advanced Internet Development, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.util;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.BitSet;
+
+import org.opensaml.xml.util.DatatypeHelper;
+
+/** Represents a range of IP addresses. */
+public class IPRange {
+
+ /** Number of bits within */
+ private int addressLength;
+
+ /** The IP network address for the range. */
+ private BitSet network;
+
+ /** The netmask for the range. */
+ private BitSet mask;
+
+ /**
+ * Constructor
+ *
+ * @param networkAddress the network address for the range
+ * @param maskSize the number of bits in the netmask
+ */
+ public IPRange(InetAddress networkAddress, int maskSize) {
+ this(networkAddress.getAddress(), maskSize);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param networkAddress the network address for the range
+ * @param maskSize the number of bits in the netmask
+ */
+ public IPRange(byte[] networkAddress, int maskSize) {
+ addressLength = networkAddress.length * 8;
+ if (addressLength != 32 && addressLength != 128) {
+ throw new IllegalArgumentException("Network address was neither an IPv4 or IPv6 address");
+ }
+
+ network = toBitSet(networkAddress);
+ mask = new BitSet(addressLength);
+ mask.set(addressLength - maskSize, addressLength, true);
+ }
+
+ /**
+ * Parses a CIDR block definition in to an IP range.
+ *
+ * @param cidrBlock the CIDR block definition
+ *
+ * @return the resultant IP range
+ */
+ public static IPRange parseCIDRBlock(String cidrBlock){
+ String block = DatatypeHelper.safeTrimOrNullString(cidrBlock);
+ if(block == null){
+ throw new IllegalArgumentException("CIDR block definition may not be null");
+ }
+
+ String[] blockParts = block.split("/");
+ try{
+ InetAddress networkAddress = InetAddress.getByName(blockParts[0]);
+ int maskSize = Integer.parseInt(blockParts[1]);
+ return new IPRange(networkAddress, maskSize);
+ }catch(UnknownHostException e){
+ throw new IllegalArgumentException("Invalid IP address");
+ }catch(NumberFormatException e){
+ throw new IllegalArgumentException("Invalid netmask size");
+ }
+ }
+
+ /**
+ * Determines whether the given address is contained in the IP range.
+ *
+ * @param address the address to check
+ *
+ * @return true if the address is in the range, false it not
+ */
+ public boolean contains(InetAddress address) {
+ return contains(address.getAddress());
+ }
+
+ /**
+ * Determines whether the given address is contained in the IP range.
+ *
+ * @param address the address to check
+ *
+ * @return true if the address is in the range, false it not
+ */
+ public boolean contains(byte[] address) {
+ if (address.length * 8 != addressLength) {
+ return false;
+ }
+
+ BitSet addrNetwork = toBitSet(address);
+ addrNetwork.and(mask);
+
+ return addrNetwork.equals(network);
+ }
+
+ /**
+ * Converts a byte array to a BitSet.
+ *
+ * The supplied byte array is assumed to have the most significant bit in element 0.
+ *
+ * @param bytes the byte array with most significant bit in element 0.
+ *
+ * @return the BitSet
+ */
+ protected BitSet toBitSet(byte[] bytes) {
+ BitSet bits = new BitSet(bytes.length * 8);
+
+ for (int i = 0; i < bytes.length * 8; i++) {
+ if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) {
+ bits.set(i);
+ }
+ }
+
+ return bits;
+ }
+}
\ No newline at end of file