diff --git a/java/org/apache/catalina/filters/RemoteNetmaskFilter.java b/java/org/apache/catalina/filters/RemoteNetmaskFilter.java
new file mode 100644
index 0000000..b6e40a4
--- /dev/null
+++ b/java/org/apache/catalina/filters/RemoteNetmaskFilter.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.catalina.filters;
+
+
+import org.apache.catalina.comet.CometEvent;
+import org.apache.catalina.comet.CometFilterChain;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.io.IOException;
+
+
+/**
+ * Concrete implementation of RequestNetmaskFilter
that filters
+ * based on the string representation of the remote client's IP address.
+ *
+ */
+
+public final class RemoteNetmaskFilter
+ extends RequestFilter {
+
+ // ----------------------------------------------------- Instance Variables
+ private static final Log log = LogFactory.getLog(RemoteNetmaskFilter.class);
+
+
+ // ------------------------------------------------------------- Properties
+
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Extract the desired request property, and pass it (along with the
+ * specified request and response objects and associated filter chain) to
+ * the protected process()
method to perform the actual
+ * filtering.
+ *
+ * @param request The servlet request to be processed
+ * @param response The servlet response to be created
+ * @param chain The filter chain for this request
+ *
+ * @exception java.io.IOException if an input/output error occurs
+ * @exception javax.servlet.ServletException if a servlet error occurs
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+
+ process(request.getRemoteAddr(), request, response, chain);
+
+ }
+
+ /**
+ * Extract the desired request property, and pass it (along with the comet
+ * event and filter chain) to the protected process()
method
+ * to perform the actual filtering.
+ *
+ * @param event The comet event to be processed
+ * @param chain The filter chain for this event
+ *
+ * @exception java.io.IOException if an input/output error occurs
+ * @exception javax.servlet.ServletException if a servlet error occurs
+ */
+ @Override
+ public void doFilterEvent(CometEvent event, CometFilterChain chain)
+ throws IOException, ServletException {
+ processCometEvent(event.getHttpServletRequest().getRemoteHost(),
+ event, chain);
+ }
+
+ @Override
+ protected Log getLogger() {
+ return log;
+ }
+}
diff --git a/java/org/apache/catalina/filters/RequestNetmaskFilter.java b/java/org/apache/catalina/filters/RequestNetmaskFilter.java
new file mode 100644
index 0000000..25088dd
--- /dev/null
+++ b/java/org/apache/catalina/filters/RequestNetmaskFilter.java
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.catalina.filters;
+
+
+import org.apache.catalina.comet.CometEvent;
+import org.apache.catalina.comet.CometFilter;
+import org.apache.catalina.comet.CometFilterChain;
+import org.apache.catalina.util.NetMask;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of a Filter that performs filtering based on comparing the
+ * appropriate request property (selected based on which subclass you choose
+ * to configure into your Container's pipeline) against the regular expressions
+ * configured for this Filter.
+ *
+ * This filter is configured by setting the allow
and/or
+ * deny
properties to a regular expressions (in the syntax
+ * supported by {@link java.util.regex.Pattern}) to which the appropriate request property will
+ * be compared. Evaluation proceeds as follows:
+ *
process()
method.
+ * process()
method to perform the actual filtering.
+ * This method must be implemented by a concrete subclass.
+ *
+ * @param request The servlet request to be processed
+ * @param response The servlet response to be created
+ * @param chain The filter chain
+ *
+ * @exception java.io.IOException if an input/output error occurs
+ * @exception javax.servlet.ServletException if a servlet error occurs
+ */
+ @Override
+ public abstract void doFilter(ServletRequest request,
+ ServletResponse response, FilterChain chain) throws IOException,
+ ServletException;
+
+
+ // ------------------------------------------------------ Protected Methods
+
+
+ /**
+ * Perform the filtering that has been configured for this Filter, matching
+ * against the specified request property.
+ *
+ * @param property The request property on which to filter
+ * @param request The servlet request to be processed
+ * @param response The servlet response to be processed
+ * @param chain The filter chain
+ *
+ * @exception java.io.IOException if an input/output error occurs
+ * @exception javax.servlet.ServletException if a servlet error occurs
+ */
+ protected void process(String property, ServletRequest request,
+ ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+
+ if (isAllowed(property)) {
+ chain.doFilter(request, response);
+ return;
+ }
+
+ if (!(response instanceof HttpServletResponse)) {
+ sendErrorWhenNotHttp(response);
+ return;
+ }
+
+ ((HttpServletResponse) response)
+ .sendError(HttpServletResponse.SC_FORBIDDEN);
+ }
+
+ /**
+ * Perform the filtering that has been configured for this Filter, matching
+ * against the specified request property.
+ *
+ * @param property The property to check against the allow/deny rules
+ * @param event The comet event to be filtered
+ * @param chain The comet filter chain
+ * @exception java.io.IOException if an input/output error occurs
+ * @exception javax.servlet.ServletException if a servlet error occurs
+ */
+ protected void processCometEvent(String property, CometEvent event,
+ CometFilterChain chain) throws IOException, ServletException {
+ HttpServletResponse response = event.getHttpServletResponse();
+
+ if (isAllowed(property)) {
+ chain.doFilterEvent(event);
+ return;
+ }
+
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ event.close();
+ }
+
+ /**
+ * Process the allow and deny rules for the provided property.
+ *
+ * @param property The property to test against the allow and deny lists
+ * @return true
if this request should be allowed,
+ * false
otherwise
+ */
+ private boolean isAllowed(String property) {
+ final InetAddress addr;
+
+ try {
+ addr = InetAddress.getByName(property);
+ } catch (UnknownHostException e) {
+ //Eh?
+ return false;
+ }
+
+ for (final NetMask nm: deny)
+ if (nm.matches(addr))
+ return false;
+
+ for (final NetMask nm: allow)
+ if (nm.matches(addr))
+ return true;
+
+ // Allow if denies specified but not allows
+ if (!deny.isEmpty() && allow.isEmpty())
+ return true;
+
+ // Deny this request
+ return false;
+ }
+
+ private void sendErrorWhenNotHttp(ServletResponse response)
+ throws IOException {
+ response.setContentType(PLAIN_TEXT_MIME_TYPE);
+ response.getWriter().write(sm.getString("http.403"));
+ response.getWriter().flush();
+ }
+}
diff --git a/java/org/apache/catalina/util/NetMask.java b/java/org/apache/catalina/util/NetMask.java
new file mode 100644
index 0000000..b834443
--- /dev/null
+++ b/java/org/apache/catalina/util/NetMask.java
@@ -0,0 +1,103 @@
+package org.apache.catalina.util;
+
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * A class representing a netmask, which is at the core of this valve.
+ *
+ * The constructor takes a {@link java.lang.String} representing a + * CIDR netmask as an argument and extracts two informations from it: the + * network address and the CIDR. It then turns the address into a {@link + * java.math.BigInteger}, calculates the right shift and shifts that + * BigInteger by it.
+ *The process to verify whether an IP address falls within the mask + * is to also convert it to a BigInteger, shifting it right and comparing + * it to the stored BigInteger. + *
+ */ + +public final class NetMask { + /** + * The argument to the constructor, used for .toString() + */ + private final String expression; + + /** + * The number of bits a matching candidate needs to be shifted right + * in order to see if it matches + */ + private final int shift; + + /** + * The network address, already shifted right + */ + private final BigInteger mask; + + /** + * Constructor. + * + * @param expression the CIDR netmask + * @throws IllegalArgumentException if the netmask is not correct + * (invalid address specification, malformed CIDR prefix, etc) + */ + public NetMask(final String expression) { + final int idx = expression.indexOf("/"); + final int cidr, addrlen; + final String addressPart; + final InetAddress addr; + final byte[] bytes; + + if (idx == -1) { + cidr = -1; + addressPart = expression; + } else { + final String substring = expression.substring(idx + 1); + try { + cidr = Integer.parseInt(substring); + if (cidr < 0) + throw new NumberFormatException("CIDR is negative"); + } catch (NumberFormatException ignored) { + throw new IllegalArgumentException("provided CIDR mask (" + + substring + ") is invalid"); + } + addressPart = expression.substring(0, idx); + } + + try { + addr = InetAddress.getByName(addressPart); + } catch (UnknownHostException e) { + throw new IllegalArgumentException("provided address (" + + addressPart + ") is invalid"); + } + + bytes = addr.getAddress(); + addrlen = bytes.length * 8; + shift = cidr == -1 ? 0 : addrlen - cidr; + + if (shift < 0) + throw new IllegalArgumentException("CIDR prefix (" + cidr + + ") is greater than address length (" + addrlen + ")"); + mask = new BigInteger(bytes).shiftRight(shift); + this.expression = expression; + } + + /** + * Test if a given address matches this netmask + * + * @param addr The {@link java.net.InetAddress} to test + * @return true on match, false otherwise + */ + public boolean matches (final InetAddress addr) { + final BigInteger provided = new BigInteger(addr.getAddress()) + .shiftRight(shift); + + return mask.equals(provided); + } + + @Override + public String toString() { + return expression; + } +} diff --git a/java/org/apache/catalina/valves/RemoteNetmaskValve.java b/java/org/apache/catalina/valves/RemoteNetmaskValve.java new file mode 100644 index 0000000..af21f84 --- /dev/null +++ b/java/org/apache/catalina/valves/RemoteNetmaskValve.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.catalina.valves; + + +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; + +import javax.servlet.ServletException; +import java.io.IOException; + + +/** + * Concrete implementation ofRequestFilterValve
that filters
+ * based on the string representation of the remote client's IP address.
+ *
+ * @author Craig R. McClanahan
+ * @version $Id$
+ */
+
+public final class RemoteNetmaskValve
+ extends RequestNetmaskValve
+{
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * The descriptive information related to this implementation.
+ */
+ private static final String info =
+ "org.apache.catalina.valves.RemoteNetmaskValve/1.0";
+
+
+ // ------------------------------------------------------------- Properties
+
+
+ /**
+ * Return descriptive information about this Valve implementation.
+ */
+ @Override
+ public String getInfo() {
+ return (info);
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Extract the desired request property, and pass it (along with the
+ * specified request and response objects) to the protected
+ * process()
method to perform the actual filtering.
+ * This method must be implemented by a concrete subclass.
+ *
+ * @param request The servlet request to be processed
+ * @param response The servlet response to be created
+ *
+ * @exception java.io.IOException if an input/output error occurs
+ * @exception javax.servlet.ServletException if a servlet error occurs
+ */
+ @Override
+ public void invoke(Request request, Response response)
+ throws IOException, ServletException {
+ process(request.getRequest().getRemoteAddr(), request, response);
+ }
+
+
+}
diff --git a/java/org/apache/catalina/valves/RequestNetmaskValve.java b/java/org/apache/catalina/valves/RequestNetmaskValve.java
new file mode 100644
index 0000000..6689c0a
--- /dev/null
+++ b/java/org/apache/catalina/valves/RequestNetmaskValve.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.catalina.valves;
+
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.NetMask;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Implementation of a Valve that performs IP filtering of a remote host
+ * based on the remote's IP address. This valve replicates (some of) Apache's
+ * "Order", "Allow from" and "Deny" directives with a few limitations:
+ * Note that IPv6 is supported. Also note that invalid netmasks will be + * ignored.
+ *+ * This Valve may be attached to any Container, depending on the granularity + * of the filtering you wish to perform.
+ * + * @author Francis Galiegue + */ + +public abstract class RequestNetmaskValve + extends ValveBase { + + //------------------------------------------------------ Constructor + public RequestNetmaskValve() { + super(true); + } + + // ----------------------------------------------------- Class Variables + + + /** + * The descriptive information related to this implementation. + */ + private static final String info = + "org.apache.catalina.valves.RequestNetmaskValve/1.0"; + + // ----------------------------------------------------- Instance Variables + + /** + * List of allowed netmasks, if any + */ + + protected final Listprocess()
method to perform the actual filtering.
+ * This method must be implemented by a concrete subclass.
+ *
+ * @param request The servlet request to be processed
+ * @param response The servlet response to be created
+ *
+ * @exception java.io.IOException if an input/output error occurs
+ * @exception javax.servlet.ServletException if a servlet error occurs
+ */
+ @Override
+ public abstract void invoke(Request request, Response response)
+ throws IOException, ServletException;
+
+
+ // ------------------------------------------------------ Protected Methods
+
+
+ /**
+ * Perform the filtering that has been configured for this Valve, matching
+ * against the specified request property.
+ *
+ * @param property The request property on which to filter
+ * @param request The servlet request to be processed
+ * @param response The servlet response to be processed
+ *
+ * @exception java.io.IOException if an input/output error occurs
+ * @exception javax.servlet.ServletException if a servlet error occurs
+ */
+ protected void process(String property, Request request, Response response)
+ throws IOException, ServletException {
+
+ final InetAddress addr = InetAddress.getByName(property);
+
+ for (final NetMask nm: deny)
+ if (nm.matches(addr)) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ if (allow.isEmpty()) {
+ getNext().invoke(request, response);
+ return;
+ }
+
+ for (final NetMask nm: allow)
+ if (nm.matches(addr)) {
+ getNext().invoke(request, response);
+ return;
+ }
+
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ }
+}