From 1ffb1aea75c36def56b709bd0892b19df78d9249 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Fri, 12 Nov 2010 10:20:03 +0900 Subject: [PATCH] NETTY-364 Application level IP filter * Merged Frederic's ipfilter patch (needs some review and documentation) --- .../jboss/netty/handler/ipfilter/CIDR.java | 264 ++++++++++++++ .../jboss/netty/handler/ipfilter/CIDR4.java | 197 +++++++++++ .../jboss/netty/handler/ipfilter/CIDR6.java | 201 +++++++++++ .../handler/ipfilter/IpFilterListener.java | 74 ++++ .../netty/handler/ipfilter/IpFilterRule.java | 37 ++ .../handler/ipfilter/IpFilterRuleHandler.java | 328 ++++++++++++++++++ .../handler/ipfilter/IpFilterRuleList.java | 100 ++++++ .../handler/ipfilter/IpFilteringHandler.java | 41 +++ .../ipfilter/IpFilteringHandlerImpl.java | 207 +++++++++++ .../jboss/netty/handler/ipfilter/IpSet.java | 36 ++ .../netty/handler/ipfilter/IpSubnet.java | 194 +++++++++++ .../handler/ipfilter/IpSubnetFilterRule.java | 93 +++++ .../netty/handler/ipfilter/IpV4Subnet.java | 328 ++++++++++++++++++ .../ipfilter/IpV4SubnetFilterRule.java | 88 +++++ .../handler/ipfilter/OneIpFilterHandler.java | 92 +++++ .../netty/handler/ipfilter/PatternRule.java | 211 +++++++++++ .../netty/handler/ipfilter/package-info.java | 93 +++++ 17 files changed, 2584 insertions(+) create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/CIDR.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/CIDR4.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/CIDR6.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilterListener.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRule.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleHandler.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleList.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandler.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandlerImpl.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpSet.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpSubnet.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpSubnetFilterRule.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpV4Subnet.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/IpV4SubnetFilterRule.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/OneIpFilterHandler.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/PatternRule.java create mode 100644 src/main/java/org/jboss/netty/handler/ipfilter/package-info.java diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/CIDR.java b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR.java new file mode 100644 index 0000000000..a2968feea6 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR.java @@ -0,0 +1,264 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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.jboss.netty.handler.ipfilter; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.StringTokenizer; + +/** + * @author frederic bregier + */ +public abstract class CIDR implements Comparable +{ + /** + * The base address of the CIDR notation + */ + protected InetAddress baseAddress; + + /** + * The mask used in the CIDR notation + */ + protected int cidrMask; + + /** + * Create CIDR using the CIDR Notation + * @param baseAddress + * @param cidrMask + * @return the generated CIDR + * @throws UnknownHostException + */ + public static CIDR newCIDR(InetAddress baseAddress, int cidrMask) throws UnknownHostException + { + if (cidrMask < 0) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + if (baseAddress instanceof Inet4Address) + { + if (cidrMask > 32) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + return new CIDR4((Inet4Address) baseAddress, cidrMask); + } + // IPv6. + if (cidrMask > 128) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + return new CIDR6((Inet6Address) baseAddress, cidrMask); + } + + /** + * Create CIDR using the normal Notation + * @param baseAddress + * @param scidrMask + * @return the generated CIDR + * @throws UnknownHostException + */ + public static CIDR newCIDR(InetAddress baseAddress, String scidrMask) throws UnknownHostException + { + int cidrMask = getNetMask(scidrMask); + if (cidrMask < 0) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + if (baseAddress instanceof Inet4Address) + { + if (cidrMask > 32) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + return new CIDR4((Inet4Address) baseAddress, cidrMask); + } + cidrMask += 96; + // IPv6. + if (cidrMask > 128) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + return new CIDR6((Inet6Address) baseAddress, cidrMask); + } + + /** + * Create CIDR using the CIDR or normal Notation
+ * i.e.: + * CIDR subnet = newCIDR ("10.10.10.0/24"); or + * CIDR subnet = newCIDR ("1fff:0:0a88:85a3:0:0:ac1f:8001/24"); or + * CIDR subnet = newCIDR ("10.10.10.0/255.255.255.0"); + * @param cidr + * @return the generated CIDR + * @throws UnknownHostException + */ + public static CIDR newCIDR(String cidr) throws UnknownHostException + { + int p = cidr.indexOf("/"); + if (p < 0) + { + throw new UnknownHostException("Invalid CIDR notation used: " + cidr); + } + String addrString = cidr.substring(0, p); + String maskString = cidr.substring(p + 1); + InetAddress addr = addressStringToInet(addrString); + int mask = 0; + if (maskString.indexOf(".") < 0) + { + mask = parseInt(maskString, -1); + } + else + { + mask = getNetMask(maskString); + if (addr instanceof Inet6Address) + { + mask += 96; + } + } + if (mask < 0) + { + throw new UnknownHostException("Invalid mask length used: " + maskString); + } + return newCIDR(addr, mask); + } + + /** @return the baseAddress of the CIDR block. */ + public InetAddress getBaseAddress() + { + return baseAddress; + } + + /** @return the Mask length. */ + public int getMask() + { + return cidrMask; + } + + /** @return the textual CIDR notation. */ + @Override + public String toString() + { + return baseAddress.getHostAddress() + "/" + cidrMask; + } + + /** @return the end address of this block. */ + public abstract InetAddress getEndAddress(); + + /** + * Compares the given InetAddress against the CIDR and returns true if + * the ip is in the subnet-ip-range and false if not. + * @param inetAddress + * @return returns true if the given IP address is inside the currently + * set network. + */ + public abstract boolean contains(InetAddress inetAddress); + + /** Convert an IPv4 or IPv6 textual representation into an + * InetAddress. + * @param addr + * @return the created InetAddress + * @throws UnknownHostException + */ + private static InetAddress addressStringToInet(String addr) throws UnknownHostException + { + return InetAddress.getByName(addr); + } + + /** + * Get the Subnet's Netmask in Decimal format.
+ * i.e.: getNetMask("255.255.255.0") returns the integer CIDR mask + * @param netMask a network mask + * @return the integer CIDR mask + * */ + private static int getNetMask(String netMask) + { + StringTokenizer nm = new StringTokenizer(netMask, "."); + int i = 0; + int[] netmask = new int[4]; + while (nm.hasMoreTokens()) + { + netmask[i] = Integer.parseInt(nm.nextToken()); + i++; + } + int mask1 = 0; + for (i = 0; i < 4; i++) + { + mask1 += Integer.bitCount(netmask[i]); + } + return mask1; + } + + /** @param intstr a string containing an integer. + * @param def the default if the string does not contain a valid + * integer. + * @return the inetAddress from the integer + */ + private static int parseInt(String intstr, int def) + { + Integer res; + if (intstr == null) + { + return def; + } + try + { + res = Integer.decode(intstr); + } + catch (Exception e) + { + res = new Integer(def); + } + return res.intValue(); + } + + /** + * Compute a byte representation of IpV4 from a IpV6 + * @param address + * @return the byte representation + * @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4 + */ + public static byte[] getIpV4FromIpV6(Inet6Address address) throws IllegalArgumentException + { + byte[] baddr = address.getAddress(); + for (int i = 0; i < 9; i++) + { + if (baddr[i] != 0) + { + throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context"); + } + } + if (baddr[10] != 0 && baddr[10] != 0xFF || baddr[11] != 0 && baddr[11] != 0xFF) + { + throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context"); + } + return new byte[] + {baddr[12], baddr[13], baddr[14], baddr[15]}; + } + + /** + * Compute a byte representation of IpV6 from a IpV4 + * @param address + * @return the byte representation + * @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4 + */ + public static byte[] getIpV6FromIpV4(Inet4Address address) throws IllegalArgumentException + { + byte[] baddr = address.getAddress(); + return new byte[] + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, baddr[0], baddr[1], baddr[2], baddr[3]}; + } +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/CIDR4.java b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR4.java new file mode 100644 index 0000000000..80b42fd90f --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR4.java @@ -0,0 +1,197 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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.jboss.netty.handler.ipfilter; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * @author frederic bregier + */ +public class CIDR4 extends CIDR +{ + /** + * The integer for the base address + */ + private int addressInt; + + /** + * The integer for the end address + */ + private final int addressEndInt; + + /** + * @param newaddr + * @param mask + */ + protected CIDR4(Inet4Address newaddr, int mask) + { + cidrMask = mask; + addressInt = ipv4AddressToInt(newaddr); + int newmask = ipv4PrefixLengthToMask(mask); + addressInt &= newmask; + try + { + baseAddress = intToIPv4Address(addressInt); + } + catch (UnknownHostException e) + { + // this should never happen + } + addressEndInt = addressInt + ipv4PrefixLengthToLength(cidrMask) - 1; + } + + @Override + public InetAddress getEndAddress() + { + try + { + return intToIPv4Address(addressEndInt); + } + catch (UnknownHostException e) + { + // this should never happen + return null; + } + } + + @Override +public int compareTo(CIDR arg) + { + if (arg instanceof CIDR6) + { + byte[] address = getIpV4FromIpV6((Inet6Address) arg.baseAddress); + int net = ipv4AddressToInt(address); + if (net == addressInt && arg.cidrMask == cidrMask) + { + return 0; + } + if (net < addressInt) + { + return 1; + } + else if (net > addressInt) + { + return -1; + } + else if (arg.cidrMask < cidrMask) + { + return -1; + } + return 1; + } + CIDR4 o = (CIDR4) arg; + if (o.addressInt == addressInt && o.cidrMask == cidrMask) + { + return 0; + } + if (o.addressInt < addressInt) + { + return 1; + } + else if (o.addressInt > addressInt) + { + return -1; + } + else if (o.cidrMask < cidrMask) + { + // greater Mask means less IpAddresses so -1 + return -1; + } + return 1; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.CIDR#contains(java.net.InetAddress) + */ + @Override + public boolean contains(InetAddress inetAddress) + { + int search = ipv4AddressToInt(inetAddress); + return search >= addressInt && search <= addressEndInt; + } + + /** Given an IPv4 baseAddress length, return the block length. I.e., a + * baseAddress length of 24 will return 256. */ + private static int ipv4PrefixLengthToLength(int prefix_length) + { + return 1 << 32 - prefix_length; + } + + /** Given a baseAddress length, return a netmask. I.e, a baseAddress length + * of 24 will return 0xFFFFFF00. */ + private static int ipv4PrefixLengthToMask(int prefix_length) + { + return ~((1 << 32 - prefix_length) - 1); + } + + /** Convert an integer into an (IPv4) InetAddress. + * @param addr + * @return the created InetAddress + * @throws UnknownHostException + * @throws UnknownHostException + */ + private static InetAddress intToIPv4Address(int addr) throws UnknownHostException + { + byte[] a = new byte[4]; + a[0] = (byte) (addr >> 24 & 0xFF); + a[1] = (byte) (addr >> 16 & 0xFF); + a[2] = (byte) (addr >> 8 & 0xFF); + a[3] = (byte) (addr & 0xFF); + return InetAddress.getByAddress(a); + } + + /** Given an IPv4 address, convert it into an integer. + * @param addr + * @return the integer representation of the InetAddress + * + * @throws IllegalArgumentException if the address is really an + * IPv6 address. + */ + private static int ipv4AddressToInt(InetAddress addr) + { + byte[] address = null; + if (addr instanceof Inet6Address) + { + address = getIpV4FromIpV6((Inet6Address) addr); + } + else + { + address = addr.getAddress(); + } + return ipv4AddressToInt(address); + } + + /** Given an IPv4 address as array of bytes, convert it into an integer. + * @param address + * @return the integer representation of the InetAddress + * + * @throws IllegalArgumentException if the address is really an + * IPv6 address. + */ + private static int ipv4AddressToInt(byte[] address) + { + int net = 0; + for (byte addres : address) + { + net <<= 8; + net |= addres & 0xFF; + } + return net; + } +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/CIDR6.java b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR6.java new file mode 100644 index 0000000000..160a86e43e --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/CIDR6.java @@ -0,0 +1,201 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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.jboss.netty.handler.ipfilter; + +import java.math.BigInteger; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +/** + * @author frederic bregier + */ +public class CIDR6 extends CIDR +{ + + private static final InternalLogger logger = InternalLoggerFactory.getInstance(CIDR6.class); + + /** + * The big integer for the base address + */ + private BigInteger addressBigInt; + + /** + * The big integer for the end address + */ + private final BigInteger addressEndBigInt; + + /** + * @param newaddress + * @param newmask + */ + protected CIDR6(Inet6Address newaddress, int newmask) + { + cidrMask = newmask; + addressBigInt = ipv6AddressToBigInteger(newaddress); + BigInteger mask = ipv6CidrMaskToMask(newmask); + try + { + addressBigInt = addressBigInt.and(mask); + baseAddress = bigIntToIPv6Address(addressBigInt); + } + catch (UnknownHostException e) + { + // this should never happen. + } + addressEndBigInt = addressBigInt.add(ipv6CidrMaskToBaseAddress(cidrMask)).subtract(BigInteger.ONE); + } + + @Override + public InetAddress getEndAddress() + { + try + { + return bigIntToIPv6Address(addressEndBigInt); + } + catch (UnknownHostException e) + { + logger.error("invalid ip address calculated as an end address"); + return null; + } + } + + @Override +public int compareTo(CIDR arg) + { + if (arg instanceof CIDR4) + { + BigInteger net = ipv6AddressToBigInteger(arg.baseAddress); + int res = net.compareTo(addressBigInt); + if (res == 0) + { + if (arg.cidrMask == cidrMask) + { + return 0; + } + else if (arg.cidrMask < cidrMask) + { + return -1; + } + return 1; + } + return res; + } + CIDR6 o = (CIDR6) arg; + if (o.addressBigInt.equals(addressBigInt) && o.cidrMask == cidrMask) + { + return 0; + } + int res = o.addressBigInt.compareTo(addressBigInt); + if (res == 0) + { + if (o.cidrMask < cidrMask) + { + // greater Mask means less IpAddresses so -1 + return -1; + } + return 1; + } + return res; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.CIDR#contains(java.net.InetAddress) + */ + @Override + public boolean contains(InetAddress inetAddress) + { + BigInteger search = ipv6AddressToBigInteger(inetAddress); + return search.compareTo(addressBigInt) >= 0 && search.compareTo(addressEndBigInt) <= 0; + } + + /** Given an IPv6 baseAddress length, return the block length. I.e., a + * baseAddress length of 96 will return 2**32. */ + private static BigInteger ipv6CidrMaskToBaseAddress(int cidrMask) + { + return BigInteger.ONE.shiftLeft(128 - cidrMask); + } + + private static BigInteger ipv6CidrMaskToMask(int cidrMask) + { + return BigInteger.ONE.shiftLeft(128 - cidrMask).subtract(BigInteger.ONE).not(); + } + + /** Given an IPv6 address, convert it into a BigInteger. + * @param addr + * @return the integer representation of the InetAddress + * + * @throws IllegalArgumentException if the address is not an IPv6 + * address. + */ + private static BigInteger ipv6AddressToBigInteger(InetAddress addr) + { + byte[] ipv6; + if (addr instanceof Inet4Address) + { + ipv6 = getIpV6FromIpV4((Inet4Address) addr); + } + else + { + ipv6 = addr.getAddress(); + } + if (ipv6[0] == -1) + { + return new BigInteger(1, ipv6); + } + return new BigInteger(ipv6); + } + + /** Convert a big integer into an IPv6 address. + * @param addr + * @return the inetAddress from the integer + * + * @throws UnknownHostException if the big integer is too large, + * and thus an invalid IPv6 address. + */ + private static InetAddress bigIntToIPv6Address(BigInteger addr) throws UnknownHostException + { + byte[] a = new byte[16]; + byte[] b = addr.toByteArray(); + if (b.length > 16 && !(b.length == 17 && b[0] == 0)) + { + throw new UnknownHostException("invalid IPv6 address (too big)"); + } + if (b.length == 16) + { + return InetAddress.getByAddress(b); + } + // handle the case where the IPv6 address starts with "FF". + if (b.length == 17) + { + System.arraycopy(b, 1, a, 0, 16); + } + else + { + // copy the address into a 16 byte array, zero-filled. + int p = 16 - b.length; + for (int i = 0; i < b.length; i++) + { + a[p + i] = b[i]; + } + } + return InetAddress.getByAddress(a); + } +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterListener.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterListener.java new file mode 100644 index 0000000000..605ce143a8 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterListener.java @@ -0,0 +1,74 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 org.jboss.netty.handler.ipfilter; + +import java.net.InetSocketAddress; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandlerContext; + +/** + * The listener interface for receiving ipFilter events. + * + * @see IpFilteringHandler + * + * @author Ron + */ +public interface IpFilterListener +{ + + /** + * Called when the channel has the CONNECTED status and the channel was allowed by a previous call to accept(). + * This method enables your implementation to send a message back to the client before closing + * or whatever you need. This method returns a ChannelFuture on which the implementation + * can wait uninterruptibly before continuing.
+ * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned. + * @param ctx + * @param e + * @param inetSocketAddress the remote {@link InetSocketAddress} from client + * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed. + */ + public ChannelFuture allowed(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress); + + /** + * Called when the channel has the CONNECTED status and the channel was refused by a previous call to accept(). + * This method enables your implementation to send a message back to the client before closing + * or whatever you need. This method returns a ChannelFuture on which the implementation + * will wait uninterruptibly before closing the channel.
+ * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned. + * @param ctx + * @param e + * @param inetSocketAddress the remote {@link InetSocketAddress} from client + * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed. + */ + public ChannelFuture refused(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress); + + /** + * Called in handleUpstream, if this channel was previously blocked, + * to check if whatever the event, it should be passed to the next entry in the pipeline.
+ * If one wants to not block events, just overridden this method by returning always true.

+ * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since + * those events come out before the CONNECTED event and so the possibility to filter the connection. + * @param ctx + * @param e + * @return True if the event should continue, False if the event should not continue + * since this channel was blocked by this filter + */ + public boolean continues(ChannelHandlerContext ctx, ChannelEvent e); + +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRule.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRule.java new file mode 100644 index 0000000000..cae6b16270 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRule.java @@ -0,0 +1,37 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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.jboss.netty.handler.ipfilter; + +/** + * This Interface defines an Ip Filter Rule. + * + * @author frederic bregier + * + */ +public interface IpFilterRule extends IpSet +{ + /** + * + * @return True if this Rule is an ALLOW rule + */ + public boolean isAllowRule(); + + /** + * + * @return True if this Rule is a DENY rule + */ + public boolean isDenyRule(); +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleHandler.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleHandler.java new file mode 100644 index 0000000000..a9c35c5ebb --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleHandler.java @@ -0,0 +1,328 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelHandler.Sharable; +import org.jboss.netty.channel.ChannelHandlerContext; + +/** + * Implementation of Filter of IP based on ALLOW and DENY rules.
+ *

+ * This implementation could be changed by implementing a new {@link IpFilterRule} than default + * {@link IpV4SubnetFilterRule} (IPV4 support only), {@link IpSubnetFilterRule} (IPV4 and IPV6 support) or {@link IpFilterRule} (IP and host name string pattern support) .
+ *
+ * The check is done by going from step to step in the underlying array of IpFilterRule.
+ * Each {@link IpFilterRule} answers to the method accept if the {@link InetAddress} is accepted or not, + * according to its implementation. If an InetAddress arrives at the end of the list, as in Firewall + * usual rules, the InetAddress is therefore accepted by default.
+ *
+ *
+ * An empty list means allow all (no limitation).

+ * For efficiency reason, you should not add/remove too frequently IpFilterRules to/from this handler. + * You should prefer to replace an entry (set method) with an ALLOW/DENY ALL IpFilterRule + * if possible.


+ * This handler should be created only once and reused on every pipeline since it handles + * a global status of what is allowed or blocked.

+ * + * Note that {@link IpSubnetFilterRule} which supports IPV4 and IPV6 should be used with as much as + * possible no mixed IP protocol. Both IPV4 and IPV6 are supported but a mix (IpFilter in IPV6 notation + * and the address from the channel in IPV4, or the reverse) can lead to wrong result. + * @author frederic bregier + * + */ +@Sharable +public class IpFilterRuleHandler extends IpFilteringHandlerImpl +{ + /** + * List of {@link IpFilterRule} + */ + private final CopyOnWriteArrayList ipFilterRuleList = new CopyOnWriteArrayList(); + + /** + * Constructor from a new list of IpFilterRule + * @param newList + */ + public IpFilterRuleHandler(List newList) + { + if (newList != null) + { + ipFilterRuleList.addAll(newList); + } + } + + /** + * Empty constructor (no IpFilterRule in the List at construction). In such a situation, + * empty list implies allow all. + */ + public IpFilterRuleHandler() + { + super(); + } + + // Below are methods directly inspired from CopyOnWriteArrayList methods + /** + * Add an ipFilterRule in the list at the end + * @param ipFilterRule + */ + public void add(IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + ipFilterRuleList.add(ipFilterRule); + } + + /** + * Add an ipFilterRule in the list at the specified position (shifting to the right other elements) + * @param index + * @param ipFilterRule + */ + public void add(int index, IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + ipFilterRuleList.add(index, ipFilterRule); + } + + /** + * Appends all of the elements in the specified collection to the end of this list, + * in the order that they are returned by the specified collection's iterator. + * @param c + */ + public void addAll(Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + ipFilterRuleList.addAll(c); + } + + /** + * Inserts all of the elements in the specified collection into this list, starting at the specified position. + * @param index + * @param c + */ + public void addAll(int index, Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + ipFilterRuleList.addAll(index, c); + } + + /** + * Append the element if not present. + * @param c + * @return the number of elements added + */ + public int addAllAbsent(Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + return ipFilterRuleList.addAllAbsent(c); + } + + /** + * Append the element if not present. + * @param ipFilterRule + * @return true if the element was added + */ + public boolean addIfAbsent(IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + return ipFilterRuleList.addIfAbsent(ipFilterRule); + } + + /** + * Clear the list + */ + public void clear() + { + ipFilterRuleList.clear(); + } + + /** + * Returns true if this list contains the specified element + * @param ipFilterRule + * @return true if this list contains the specified element + */ + public boolean contains(IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + return ipFilterRuleList.contains(ipFilterRule); + } + + /** + * Returns true if this list contains all of the elements of the specified collection + * @param c + * @return true if this list contains all of the elements of the specified collection + */ + public boolean containsAll(Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + return ipFilterRuleList.containsAll(c); + } + + /** + * Returns the element at the specified position in this list + * @param index + * @return the element at the specified position in this list + */ + public IpFilterRule get(int index) + { + return ipFilterRuleList.get(index); + } + + /** + * Returns true if this list contains no elements + * @return true if this list contains no elements + */ + public boolean isEmpty() + { + return ipFilterRuleList.isEmpty(); + } + + /** + * Remove the ipFilterRule from the list + * @param ipFilterRule + */ + public void remove(IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + ipFilterRuleList.remove(ipFilterRule); + } + + /** + * Removes the element at the specified position in this list + * @param index + * @return the element previously at the specified position + */ + public IpFilterRule remove(int index) + { + return ipFilterRuleList.remove(index); + } + + /** + * Removes from this list all of its elements that are contained in the specified collection + * @param c + */ + public void removeAll(Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + ipFilterRuleList.removeAll(c); + } + + /** + * Retains only the elements in this list that are contained in the specified collection + * @param c + */ + public void retainAll(Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + ipFilterRuleList.retainAll(c); + } + + /** + * Replaces the element at the specified position in this list with the specified element + * @param index + * @param ipFilterRule + * @return the element previously at the specified position + */ + public IpFilterRule set(int index, IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + return ipFilterRuleList.set(index, ipFilterRule); + } + + /** + * Returns the number of elements in this list. + * @return the number of elements in this list. + */ + public int size() + { + return ipFilterRuleList.size(); + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilteringHandler#accept(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelEvent, java.net.InetSocketAddress) + */ + @Override + protected boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress) + throws Exception + { + if (ipFilterRuleList.isEmpty()) + { + // No limitation neither in deny or allow, so accept + return true; + } + InetAddress inetAddress = inetSocketAddress.getAddress(); + Iterator iterator = ipFilterRuleList.iterator(); + IpFilterRule ipFilterRule = null; + while (iterator.hasNext()) + { + ipFilterRule = iterator.next(); + if (ipFilterRule.contains(inetAddress)) + { + // Match founds, is it a ALLOW or DENY rule + return ipFilterRule.isAllowRule(); + } + } + // No limitation founds and no allow either, but as it is like Firewall rules, it is therefore accepted + return true; + } + +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleList.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleList.java new file mode 100644 index 0000000000..6d2a249633 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilterRuleList.java @@ -0,0 +1,100 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 org.jboss.netty.handler.ipfilter; + +import java.net.UnknownHostException; +import java.util.ArrayList; + +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +/** + * The Class IpFilterRuleList is a helper class to generate a List of Rules from a string. + * In case of parse errors no exceptions are thrown. The error is logged. + *
+ * Rule List Syntax: + *
+ *
+ * RuleList ::= Rule[,Rule]*
+ * Rule ::= AllowRule | BlockRule
+ * AllowRule ::= +Filter
+ * BlockRule ::= -Filter
+ * Filter ::= PatternFilter | CIDRFilter
+ * PatternFilter ::= @see PatternRule
+ * CIDRFilter ::= c:CIDRFilter
+ * CIDRFilter ::= @see CIDR.newCIDR(String)
+ * 
+ *
+ * Example: allow only localhost: + *
+ * new IPFilterRuleHandler().addAll(new IpFilterRuleList("+n:localhost, -n:*")); + *
+ * + * @author Ron + */ +public class IpFilterRuleList extends ArrayList +{ + private static final long serialVersionUID = -6164162941749588780L; + + private static final InternalLogger logger = InternalLoggerFactory.getInstance(IpFilterRuleList.class); + + /** + * Instantiates a new ip filter rule list. + * + * @param rules the rules + */ + public IpFilterRuleList(String rules) + { + super(); + parseRules(rules); + } + + private void parseRules(String rules) + { + String[] ruless = rules.split(","); + for (String rule : ruless) + { + parseRule(rule.trim()); + } + } + + private void parseRule(String rule) + { + if (rule == null || rule.length() == 0) + return; + if (!(rule.startsWith("+") || rule.startsWith("-"))) + { + logger.error("syntax error in ip filter rule:" + rule); + return; + } + + boolean allow = rule.startsWith("+"); + if (rule.charAt(1) == 'n' || rule.charAt(1) == 'i') + this.add(new PatternRule(allow, rule.substring(1))); + else if (rule.charAt(1) == 'c') + try + { + this.add(new IpSubnetFilterRule(allow, rule.substring(3))); + } + catch (UnknownHostException e) + { + logger.error("error parsing ip filter " + rule, e); + } + else + logger.error("syntax error in ip filter rule:" + rule); + } +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandler.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandler.java new file mode 100644 index 0000000000..decf74ba58 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandler.java @@ -0,0 +1,41 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 org.jboss.netty.handler.ipfilter; + +/** + * The Interface IpFilteringHandler. + * A Filter of IP. + *
+ * Users can add an {@link IpFilterListener} to add specific actions in case a connection is allowed or refused. + * + * @author Ron + */ +public interface IpFilteringHandler +{ + + /** + * Sets the filter listener. + * + * @param listener the new ip filter listener + */ + public void setIpFilterListener(IpFilterListener listener); + + /** + * Remove the filter listener. + */ + public void removeIpFilterListener(); +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandlerImpl.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandlerImpl.java new file mode 100644 index 0000000000..b0cb22c4a8 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpFilteringHandlerImpl.java @@ -0,0 +1,207 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 org.jboss.netty.handler.ipfilter; + +import java.net.InetSocketAddress; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelFutureListener; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.ChannelUpstreamHandler; +import org.jboss.netty.channel.Channels; + +// TODO: Auto-generated Javadoc +/** + * General class that handle Ip Filtering. + * + * @author frederic bregier + */ +public abstract class IpFilteringHandlerImpl implements ChannelUpstreamHandler, IpFilteringHandler +{ + + private IpFilterListener listener = null; + + /** + * Called when the channel is connected. It returns True if the corresponding connection + * is to be allowed. Else it returns False. + * @param ctx + * @param e + * @param inetSocketAddress the remote {@link InetSocketAddress} from client + * @return True if the corresponding connection is allowed, else False. + * @throws Exception + */ + protected abstract boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress) + throws Exception; + + /** + * Called when the channel has the CONNECTED status and the channel was refused by a previous call to accept(). + * This method enables your implementation to send a message back to the client before closing + * or whatever you need. This method returns a ChannelFuture on which the implementation + * will wait uninterruptibly before closing the channel.
+ * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned. + * @param ctx + * @param e + * @param inetSocketAddress the remote {@link InetSocketAddress} from client + * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed. + * @throws Exception + */ + protected ChannelFuture handleRefusedChannel(ChannelHandlerContext ctx, ChannelEvent e, + InetSocketAddress inetSocketAddress) throws Exception + { + if (listener == null) + return null; + ChannelFuture result = listener.refused(ctx, e, inetSocketAddress); + return result; + } + + protected ChannelFuture handleAllowedChannel(ChannelHandlerContext ctx, ChannelEvent e, + InetSocketAddress inetSocketAddress) throws Exception + { + if (listener == null) + return null; + ChannelFuture result = listener.allowed(ctx, e, inetSocketAddress); + return result; + } + + /** + * Internal method to test if the current channel is blocked. Should not be overridden. + * @param ctx + * @return True if the current channel is blocked, else False + */ + protected boolean isBlocked(ChannelHandlerContext ctx) + { + return ctx.getAttachment() != null; + } + + /** + * Called in handleUpstream, if this channel was previously blocked, + * to check if whatever the event, it should be passed to the next entry in the pipeline.
+ * If one wants to not block events, just overridden this method by returning always true.

+ * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since + * those events come out before the CONNECTED event and so the possibility to filter the connection. + * @param ctx + * @param e + * @return True if the event should continue, False if the event should not continue + * since this channel was blocked by this filter + * @throws Exception + */ + protected boolean continues(ChannelHandlerContext ctx, ChannelEvent e) throws Exception + { + if (listener != null) + return listener.continues(ctx, e); + else + return false; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelUpstreamHandler#handleUpstream(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelEvent) + */ + @Override +public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception + { + if (e instanceof ChannelStateEvent) + { + ChannelStateEvent evt = (ChannelStateEvent) e; + switch (evt.getState()) + { + case OPEN : + case BOUND : + // Special case: OPEND and BOUND events are before CONNECTED, + // but CLOSED and UNBOUND events are after DISCONNECTED: should those events be blocked too? + if (isBlocked(ctx) && !continues(ctx, evt)) + { + // don't pass to next level since channel was blocked early + return; + } + else + { + ctx.sendUpstream(e); + return; + } + case CONNECTED : + if (evt.getValue() != null) + { + // CONNECTED + InetSocketAddress inetSocketAddress = (InetSocketAddress) e.getChannel().getRemoteAddress(); + if (!accept(ctx, e, inetSocketAddress)) + { + ctx.setAttachment(Boolean.TRUE); + ChannelFuture future = handleRefusedChannel(ctx, e, inetSocketAddress); + if (future != null) + { + future.addListener(ChannelFutureListener.CLOSE); + } + else + { + Channels.close(e.getChannel()); + } + if (isBlocked(ctx) && !continues(ctx, evt)) + { + // don't pass to next level since channel was blocked early + return; + } + } + else + { + handleAllowedChannel(ctx, e, inetSocketAddress); + } + // This channel is not blocked + ctx.setAttachment(null); + } + else + { + // DISCONNECTED + if (isBlocked(ctx) && !continues(ctx, evt)) + { + // don't pass to next level since channel was blocked early + return; + } + } + break; + } + } + if (isBlocked(ctx) && !continues(ctx, e)) + { + // don't pass to next level since channel was blocked early + return; + } + // Whatever it is, if not blocked, goes to the next level + ctx.sendUpstream(e); + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilteringHandler#setIpFilterListener(org.jboss.netty.handler.ipfilter.IpFilterListener) + */ + @Override +public void setIpFilterListener(IpFilterListener listener) + { + this.listener = listener; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilteringHandler#removeIpFilterListener() + */ + @Override +public void removeIpFilterListener() + { + this.listener = null; + + } + +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpSet.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpSet.java new file mode 100644 index 0000000000..a349b3f4f1 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpSet.java @@ -0,0 +1,36 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; + +/** + * This Interface defines an IpSet object. + * + * @author frederic bregier + * + */ +public interface IpSet +{ + /** + * Compares the given InetAddress against the IpSet and returns true if + * the InetAddress is contained in this Rule and false if not. + * @param inetAddress1 + * @return returns true if the given IP address is contained in the current + * IpSet. + */ + public boolean contains(InetAddress inetAddress1); +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnet.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnet.java new file mode 100644 index 0000000000..85d4be9079 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnet.java @@ -0,0 +1,194 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * This class allows to check if an IP V4 or V6 Address is contained in a subnet.
+ * + * Supported IP V4 Formats for the Subnets are: 1.1.1.1/255.255.255.255 or 1.1.1.1/32 (CIDR-Notation) + * and (InetAddress,Mask) where Mask is a integer for CIDR-notation or a String for Standard Mask notation.
+ *

Example1:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/24");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ *
Example1 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, 24);
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ *

Example2:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/255.255.255.0");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ *
Example2 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, "255.255.255.0");
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ *
+ * Supported IP V6 Formats for the Subnets are: a:b:c:d:e:f:g:h/NN (CIDR-Notation) + * or any IPV6 notations (like a:b:c:d::/NN, a:b:c:d:e:f:w.x.y.z/NN) + * and (InetAddress,Mask) where Mask is a integer for CIDR-notation + * and (InetAddress,subnet).
+ *

Example1:
+ * IpSubnet ips = new IpSubnet("1fff:0:0a88:85a3:0:0:0:0/24");
+ * IpSubnet ips = new IpSubnet("1fff:0:0a88:85a3::/24");
+ * System.out.println("Result: "+ ips.contains("1fff:0:0a88:85a3:0:0:ac1f:8001"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ *
Example1 bis:
+ * IpSubnet ips = new IpSubnet(inetAddress, 24);
+ * where inetAddress2 is 1fff:0:0a88:85a3:0:0:ac1f:8001
+ * + * @author frederic bregier + * + */ +public class IpSubnet implements IpSet, Comparable +{ + /** + * Internal representation + */ + private CIDR cidr = null; + + /** + * Create IpSubnet for ALL (used for ALLOW or DENY ALL) + */ + public IpSubnet() + { + // ALLOW or DENY ALL + cidr = null; + } + + /** + * Create IpSubnet using the CIDR or normal Notation
+ * i.e.:
+ * IpSubnet subnet = new IpSubnet("10.10.10.0/24"); or
+ * IpSubnet subnet = new IpSubnet("10.10.10.0/255.255.255.0"); or
+ * IpSubnet subnet = new IpSubnet("1fff:0:0a88:85a3:0:0:0:0/24"); + * @param netAddress a network address as string. + * @throws UnknownHostException + * */ + public IpSubnet(String netAddress) throws UnknownHostException + { + cidr = CIDR.newCIDR(netAddress); + } + + /** + * Create IpSubnet using the CIDR Notation + * @param inetAddress + * @param cidrNetMask + * @throws UnknownHostException + */ + public IpSubnet(InetAddress inetAddress, int cidrNetMask) throws UnknownHostException + { + cidr = CIDR.newCIDR(inetAddress, cidrNetMask); + } + + /** + * Create IpSubnet using the normal Notation + * @param inetAddress + * @param netMask + * @throws UnknownHostException + */ + public IpSubnet(InetAddress inetAddress, String netMask) throws UnknownHostException + { + cidr = CIDR.newCIDR(inetAddress, netMask); + } + + /** + * Compares the given IP-Address against the Subnet and returns true if + * the ip is in the subnet-ip-range and false if not. + * @param ipAddr an ipaddress + * @return returns true if the given IP address is inside the currently + * set network. + * @throws UnknownHostException + * */ + public boolean contains(String ipAddr) throws UnknownHostException + { + InetAddress inetAddress1 = InetAddress.getByName(ipAddr); + return this.contains(inetAddress1); + } + + /** + * Compares the given InetAddress against the Subnet and returns true if + * the ip is in the subnet-ip-range and false if not. + * @param inetAddress + * @return returns true if the given IP address is inside the currently + * set network. + * */ + @Override +public boolean contains(InetAddress inetAddress) + { + if (cidr == null) + { + // ANY + return true; + } + return cidr.contains(inetAddress); + } + + @Override + public String toString() + { + return cidr.toString(); + } + + @Override + public boolean equals(Object o) + { + if (!(o instanceof IpSubnet)) + { + return false; + } + IpSubnet ipSubnet = (IpSubnet) o; + return ipSubnet.cidr.equals(cidr); + } + + /** + * Compare two IpSubnet + */ + @Override +public int compareTo(IpSubnet o) + { + return cidr.toString().compareTo(o.cidr.toString()); + } + + /** + * Simple test functions + * @param args + * where args[0] is the netmask (standard or CIDR notation) and optional args[1] is + * the inetAddress to test with this IpSubnet + */ + public static void main(String[] args) throws Exception + { + if (args.length != 0) + { + IpSubnet ipSubnet = null; + try + { + ipSubnet = new IpSubnet(args[0]); + } + catch (UnknownHostException e) + { + return; + } + System.out.println("IpSubnet: " + ipSubnet.toString() + " from " + ipSubnet.cidr.getBaseAddress() + " to " + + ipSubnet.cidr.getEndAddress() + " mask " + ipSubnet.cidr.getMask()); + if (args.length > 1) + { + System.out.println("Is IN: " + args[1] + " " + ipSubnet.contains(args[1])); + } + } + } +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnetFilterRule.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnetFilterRule.java new file mode 100644 index 0000000000..d4009c09b3 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpSubnetFilterRule.java @@ -0,0 +1,93 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Ip V4 and Ip V6 filter rule.
+ *
+ * Note that mix of IPV4 and IPV6 is allowed but it is not recommended. So it is preferable to not + * mix IPV4 addresses with IPV6 rules, even if it should work. + * @author frederic bregier + * + */ +public class IpSubnetFilterRule extends IpSubnet implements IpFilterRule +{ + /** + * Is this IpV4Subnet an ALLOW or DENY rule + */ + private boolean isAllowRule = true; + + /** + * Constructor for a ALLOW or DENY ALL + * @param allow True for ALLOW, False for DENY + */ + public IpSubnetFilterRule(boolean allow) + { + super(); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param inetAddress + * @param cidrNetMask + * @throws UnknownHostException + */ + public IpSubnetFilterRule(boolean allow, InetAddress inetAddress, int cidrNetMask) throws UnknownHostException + { + super(inetAddress, cidrNetMask); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param inetAddress + * @param netMask + * @throws UnknownHostException + */ + public IpSubnetFilterRule(boolean allow, InetAddress inetAddress, String netMask) throws UnknownHostException + { + super(inetAddress, netMask); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param netAddress + * @throws UnknownHostException + */ + public IpSubnetFilterRule(boolean allow, String netAddress) throws UnknownHostException + { + super(netAddress); + isAllowRule = allow; + } + + @Override +public boolean isAllowRule() + { + return isAllowRule; + } + + @Override +public boolean isDenyRule() + { + return !isAllowRule; + } + +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpV4Subnet.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpV4Subnet.java new file mode 100644 index 0000000000..80374a9eb2 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpV4Subnet.java @@ -0,0 +1,328 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + * This class allows to check if an IP-V4-Address is contained in a subnet.
+ * Supported Formats for the Subnets are: 1.1.1.1/255.255.255.255 or 1.1.1.1/32 (CIDR-Notation) + * and (InetAddress,Mask) where Mask is a integer for CIDR-notation or a String for Standard Mask notation.
+ *

Example1:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/24");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ *
Example1 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, 24);
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ *

Example2:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/255.255.255.0");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ *
Example2 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, "255.255.255.0");
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ + * @author frederic bregier + * + */ +public class IpV4Subnet implements IpSet, Comparable +{ + private static final int SUBNET_MASK = 0x80000000; + + private static final int BYTE_ADDRESS_MASK = 0xFF; + + private InetAddress inetAddress; + + private int subnet; + + private int mask; + + private int cidrMask; + + /** + * Create IpV4Subnet for ALL (used for ALLOW or DENY ALL) + */ + public IpV4Subnet() + { + // ALLOW or DENY ALL + mask = -1; + // other will be ignored + inetAddress = null; + subnet = 0; + cidrMask = 0; + } + + /** + * Create IpV4Subnet using the CIDR or normal Notation
+ * i.e.: + * IpV4Subnet subnet = new IpV4Subnet("10.10.10.0/24"); or + * IpV4Subnet subnet = new IpV4Subnet("10.10.10.0/255.255.255.0"); + * @param netAddress a network address as string. + * @throws UnknownHostException + * */ + public IpV4Subnet(String netAddress) throws UnknownHostException + { + setNetAddress(netAddress); + } + + /** + * Create IpV4Subnet using the CIDR Notation + * @param inetAddress + * @param cidrNetMask + */ + public IpV4Subnet(InetAddress inetAddress, int cidrNetMask) + { + setNetAddress(inetAddress, cidrNetMask); + } + + /** + * Create IpV4Subnet using the normal Notation + * @param inetAddress + * @param netMask + */ + public IpV4Subnet(InetAddress inetAddress, String netMask) + { + setNetAddress(inetAddress, netMask); + } + + /** + * Sets the Network Address in either CIDR or Decimal Notation.
+ * i.e.: setNetAddress("1.1.1.1/24"); or
+ * setNetAddress("1.1.1.1/255.255.255.0");
+ * @param netAddress a network address as string. + * @throws UnknownHostException + * */ + private void setNetAddress(String netAddress) throws UnknownHostException + { + Vector vec = new Vector(); + StringTokenizer st = new StringTokenizer(netAddress, "/"); + while (st.hasMoreTokens()) + { + vec.add(st.nextElement()); + } + + if (vec.get(1).toString().length() < 3) + { + setNetId(vec.get(0).toString()); + setCidrNetMask(Integer.parseInt(vec.get(1).toString())); + } + else + { + setNetId(vec.get(0).toString()); + setNetMask(vec.get(1).toString()); + } + } + + /** + * Sets the Network Address in CIDR Notation. + * @param inetAddress + * @param cidrNetMask + * */ + private void setNetAddress(InetAddress inetAddress, int cidrNetMask) + { + setNetId(inetAddress); + setCidrNetMask(cidrNetMask); + } + + /** + * Sets the Network Address in Decimal Notation. + * @param inetAddress + * @param netMask + * */ + private void setNetAddress(InetAddress inetAddress, String netMask) + { + setNetId(inetAddress); + setNetMask(netMask); + } + + /** + * Sets the BaseAdress of the Subnet.
+ * i.e.: setNetId("192.168.1.0"); + * @param netId a network ID + * @throws UnknownHostException + * */ + private void setNetId(String netId) throws UnknownHostException + { + InetAddress inetAddress1 = InetAddress.getByName(netId); + this.setNetId(inetAddress1); + } + + /** + * Compute integer representation of InetAddress + * @param inetAddress1 + * @return the integer representation + */ + private int toInt(InetAddress inetAddress1) + { + byte[] address = inetAddress1.getAddress(); + int net = 0; + for (byte addres : address) + { + net <<= 8; + net |= addres & BYTE_ADDRESS_MASK; + } + return net; + } + + /** + * Sets the BaseAdress of the Subnet. + * @param inetAddress + * */ + private void setNetId(InetAddress inetAddress) + { + this.inetAddress = inetAddress; + subnet = toInt(inetAddress); + } + + /** + * Sets the Subnet's Netmask in Decimal format.
+ * i.e.: setNetMask("255.255.255.0"); + * @param netMask a network mask + * */ + private void setNetMask(String netMask) + { + StringTokenizer nm = new StringTokenizer(netMask, "."); + int i = 0; + int[] netmask = new int[4]; + while (nm.hasMoreTokens()) + { + netmask[i] = Integer.parseInt(nm.nextToken()); + i++; + } + int mask1 = 0; + for (i = 0; i < 4; i++) + { + mask1 += Integer.bitCount(netmask[i]); + } + setCidrNetMask(mask1); + } + + /** + * Sets the CIDR Netmask
+ * i.e.: setCidrNetMask(24); + * @param cidrNetMask a netmask in CIDR notation + * */ + private void setCidrNetMask(int cidrNetMask) + { + cidrMask = cidrNetMask; + mask = SUBNET_MASK >> cidrMask - 1; + } + + /** + * Compares the given IP-Address against the Subnet and returns true if + * the ip is in the subnet-ip-range and false if not. + * @param ipAddr an ipaddress + * @return returns true if the given IP address is inside the currently + * set network. + * @throws UnknownHostException + * */ + public boolean contains(String ipAddr) throws UnknownHostException + { + InetAddress inetAddress1 = InetAddress.getByName(ipAddr); + return this.contains(inetAddress1); + } + + /** + * Compares the given InetAddress against the Subnet and returns true if + * the ip is in the subnet-ip-range and false if not. + * @param inetAddress1 + * @return returns true if the given IP address is inside the currently + * set network. + * */ + @Override +public boolean contains(InetAddress inetAddress1) + { + if (mask == -1) + { + // ANY + return true; + } + return (toInt(inetAddress1) & mask) == subnet; + } + + @Override + public String toString() + { + return inetAddress.getHostAddress() + "/" + cidrMask; + } + + @Override + public boolean equals(Object o) + { + if (!(o instanceof IpV4Subnet)) + { + return false; + } + IpV4Subnet ipV4Subnet = (IpV4Subnet) o; + return ipV4Subnet.subnet == subnet && ipV4Subnet.cidrMask == cidrMask; + } + + /** + * Compare two IpV4Subnet + */ + @Override +public int compareTo(IpV4Subnet o) + { + if (o.subnet == subnet && o.cidrMask == cidrMask) + { + return 0; + } + if (o.subnet < subnet) + { + return 1; + } + else if (o.subnet > subnet) + { + return -1; + } + else if (o.cidrMask < cidrMask) + { + // greater Mask means less IpAddresses so -1 + return -1; + } + return 1; + } + + /** + * Simple test functions + * @param args + * where args[0] is the netmask (standard or CIDR notation) and optional args[1] is + * the inetAddress to test with this IpV4Subnet + */ + public static void main(String[] args) throws Exception + { + if (args.length != 0) + { + IpV4Subnet ipV4Subnet = null; + try + { + ipV4Subnet = new IpV4Subnet(args[0]); + } + catch (UnknownHostException e) + { + return; + } + if (args.length > 1) + { + System.out.println("Is IN: " + args[1] + " " + ipV4Subnet.contains(args[1])); + } + } + } +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/IpV4SubnetFilterRule.java b/src/main/java/org/jboss/netty/handler/ipfilter/IpV4SubnetFilterRule.java new file mode 100644 index 0000000000..106e9c72ac --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/IpV4SubnetFilterRule.java @@ -0,0 +1,88 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * IpV4 only Filter Rule + * @author frederic bregier + * + */ +public class IpV4SubnetFilterRule extends IpV4Subnet implements IpFilterRule +{ + /** + * Is this IpV4Subnet an ALLOW or DENY rule + */ + private boolean isAllowRule = true; + + /** + * Constructor for a ALLOW or DENY ALL + * @param allow True for ALLOW, False for DENY + */ + public IpV4SubnetFilterRule(boolean allow) + { + super(); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param inetAddress + * @param cidrNetMask + */ + public IpV4SubnetFilterRule(boolean allow, InetAddress inetAddress, int cidrNetMask) + { + super(inetAddress, cidrNetMask); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param inetAddress + * @param netMask + */ + public IpV4SubnetFilterRule(boolean allow, InetAddress inetAddress, String netMask) + { + super(inetAddress, netMask); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param netAddress + * @throws UnknownHostException + */ + public IpV4SubnetFilterRule(boolean allow, String netAddress) throws UnknownHostException + { + super(netAddress); + isAllowRule = allow; + } + + @Override +public boolean isAllowRule() + { + return isAllowRule; + } + + @Override +public boolean isDenyRule() + { + return !isAllowRule; + } + +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/OneIpFilterHandler.java b/src/main/java/org/jboss/netty/handler/ipfilter/OneIpFilterHandler.java new file mode 100644 index 0000000000..b901f2a7a0 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/OneIpFilterHandler.java @@ -0,0 +1,92 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelHandler.Sharable; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelState; +import org.jboss.netty.channel.ChannelStateEvent; + +/** + * Handler that block any new connection if there are already a currently active + * channel connected with the same InetAddress (IP).
+ *
+ * + * Take care to not change isBlocked method except if you know what you are doing + * since it is used to test if the current closed connection is to be removed + * or not from the map of currently connected channel. + * + * @author frederic bregier + * + */ +@Sharable +public class OneIpFilterHandler extends IpFilteringHandlerImpl +{ + /** + * HashMap of current remote connected InetAddress + */ + private final ConcurrentMap connectedSet = new ConcurrentHashMap(); + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilteringHandler#accept(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelEvent, java.net.InetSocketAddress) + */ + @Override + protected boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress) + throws Exception + { + InetAddress inetAddress = inetSocketAddress.getAddress(); + if (connectedSet.containsKey(inetAddress)) + { + return false; + } + connectedSet.put(inetAddress, Boolean.TRUE); + return true; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilteringHandler#handleUpstream(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelEvent) + */ + @Override + public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception + { + super.handleUpstream(ctx, e); + // Try to remove entry from Map if already exists + if (e instanceof ChannelStateEvent) + { + ChannelStateEvent evt = (ChannelStateEvent) e; + if (evt.getState() == ChannelState.CONNECTED) + { + if (evt.getValue() == null) + { + // DISCONNECTED but was this channel blocked or not + if (isBlocked(ctx)) + { + // remove inetsocketaddress from set since this channel was not blocked before + InetSocketAddress inetSocketAddress = (InetSocketAddress) e.getChannel().getRemoteAddress(); + connectedSet.remove(inetSocketAddress.getAddress()); + } + } + } + } + } + +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/PatternRule.java b/src/main/java/org/jboss/netty/handler/ipfilter/PatternRule.java new file mode 100644 index 0000000000..421f54b057 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/PatternRule.java @@ -0,0 +1,211 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * 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 org.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.regex.Pattern; + +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +/** + * The Class PatternRule represents an IP filter rule using string patterns. + *
+ * Rule Syntax: + *
+ *
+ * Rule ::= [n|i]:address          n stands for computer name, i for ip address
+ * address ::= <regex> | localhost
+ * regex is a regular expression with '*' as multi character and '?' as single character wild card
+ * 
+ *
+ * Example: allow localhost: + *
+ * new PatternRule(true, "n:localhost") + *
+ * Example: allow local lan: + *
+ * new PatternRule(true, "i:192.168.0.*") + *
+ * Example: block all + *
+ * new PatternRule(false, "n:*") + *
+ * + * @author Ron + */ +public class PatternRule implements IpFilterRule, Comparable +{ + private static final InternalLogger logger = InternalLoggerFactory.getInstance(PatternRule.class); + + private Pattern ipPattern = null; + + private Pattern namePattern = null; + + private boolean isAllowRule = true; + + private boolean localhost = false; + + private String pattern = null; + + /** + * Instantiates a new pattern rule. + * + * @param allow indicates if this is an allow or block rule + * @param pattern the filter pattern + */ + public PatternRule(boolean allow, String pattern) + { + this.isAllowRule = allow; + this.pattern = pattern; + parse(pattern); + } + + /** + * returns the pattern. + * + * @return the pattern + */ + public String getPattern() + { + return this.pattern; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilterRule#isAllowRule() + */ + @Override + public boolean isAllowRule() + { + return isAllowRule; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilterRule#isDenyRule() + */ + @Override + public boolean isDenyRule() + { + return !isAllowRule; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpSet#contains(java.net.InetAddress) + */ + @Override + public boolean contains(InetAddress inetAddress) + { + if (localhost) + if (isLocalhost(inetAddress)) + return true; + if (ipPattern != null) + if (ipPattern.matcher(inetAddress.getHostAddress()).matches()) + return true; + if (namePattern != null) + if (namePattern.matcher(inetAddress.getHostName()).matches()) + return true; + return false; + } + + private void parse(String pattern) + { + if (pattern == null) + return; + + String[] acls = pattern.split(","); + + String ip = ""; + String name = ""; + for (String c : acls) + { + c = c.trim(); + if (c.equals("n:localhost")) + this.localhost = true; + else if (c.startsWith("n:")) + name = addRule(name, c.substring(2)); + else if (c.startsWith("i:")) + ip = addRule(ip, c.substring(2)); + } + if (ip.length() != 0) + ipPattern = Pattern.compile(ip); + if (name.length() != 0) + namePattern = Pattern.compile(name); + + } + + private String addRule(String pattern, String rule) + { + if (rule == null || rule.length() == 0) + return pattern; + if (pattern.length() != 0) + pattern += "|"; + rule = rule.replaceAll("\\.", "\\\\."); + rule = rule.replaceAll("\\*", ".*"); + rule = rule.replaceAll("\\?", "."); + pattern += "(" + rule + ")"; + return pattern; + } + + private boolean isLocalhost(InetAddress address) + { + try + { + if (address.equals(InetAddress.getLocalHost())) + return true; + } + catch (UnknownHostException e) + { + logger.info("error getting ip of localhost", e); + } + try + { + InetAddress[] addrs = InetAddress.getAllByName("127.0.0.1"); + for (InetAddress addr : addrs) + if (addr.equals(address)) + return true; + } + catch (UnknownHostException e) + { + logger.info("error getting ip of localhost", e); + } + return false; + + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(Object o) + { + if (o == null) + return -1; + if (!(o instanceof PatternRule)) + return -1; + PatternRule p = (PatternRule) o; + if (p.isAllowRule() && !this.isAllowRule) + return -1; + if (this.pattern == null && p.pattern == null) + return 0; + if (this.pattern != null) + return this.pattern.compareTo(p.getPattern()); + return -1; + } + +} diff --git a/src/main/java/org/jboss/netty/handler/ipfilter/package-info.java b/src/main/java/org/jboss/netty/handler/ipfilter/package-info.java new file mode 100644 index 0000000000..7d11e64140 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/ipfilter/package-info.java @@ -0,0 +1,93 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat 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. + */ + +/** + * Implementation of a Ip based Filter handlers.
+ *

+ * + * + *

The main goal of this package is to allow to filter connections based on IP rules. + * The main interface is {@link org.jboss.netty.handler.ipfilter.IpFilteringHandler} which all filters will extend.

+ * + *

Two IP filtering are proposed:
+ *

    + *
  • {@link org.jboss.netty.handler.ipfilter.OneIpFilterHandler}: This filter proposes to allow only one connection by client's IP Address. + * I.E. this filter will prevent two connections from the same client based on its IP address.


  • + * + *
  • {@link org.jboss.netty.handler.ipfilter.IpFilterRuleHandler}: This filter proposes to allow or block IP range (based on standard notation + * or on CIDR notation) when the connection is running. It relies on another class like + * IpV4SubnetFilterRule (IPV4 support only), IpSubnetFilterRule (IPV4 and IPV6 support) or PatternRule (string pattern support) + * which implements those Ip ranges.


  • + * + *

+ * + *

Standard use could be as follow: The accept method must be overridden (of course you can + * override others).

+ * + *

    + *
  • accept method allows to specify your way of choosing if a new connection is + * to be allowed or not.

  • + * In OneIpFilterHandler and IpFilterRuleHandler, + * this method is already implemented.
    + *
    + * + *
  • handleRefusedChannel method is executed when the accept method filters (blocks, so returning false) + * the new connection. This method allows you to implement specific actions to be taken before the channel is + * closed. After this method is called, the channel is immediately closed.

  • + * So if you want to send back a message to the client, don't forget to return a respectful ChannelFuture, + * otherwise the message could be missed since the channel will be closed immediately after this + * call and the waiting on this channelFuture (at least with respect of asynchronous operations).

    + * Per default implementation this method invokes an {@link org.jboss.netty.handler.ipfilter.IpFilterListener} or returns null if no listener has been set. + *

    + * + *
  • continues is called when any event appears after CONNECTED event and only for + * blocked channels.

  • + * It should return True if this new event has to go to next handlers + * in the pipeline if any, and False (default) if no events has to be passed to the next + * handlers when a channel is blocked. This is intend to prevent any unnecessary action since the connection is refused.
    + * However, you could change its behavior for instance because you don't want that any event + * will be blocked by this filter by returning always true or according to some events.
    + * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since + * those events come out before the CONNECTED event, so there is no possibility to filter those two events + * before the CONNECTED event shows up. Therefore, you might want to let CLOSED and UNBOUND be passed + * to the next entry in the pipeline.

    + * Per default implementation this method invokes an {@link org.jboss.netty.handler.ipfilter.IpFilterListener} or returns false if no listener has been set. + *

    + * + *
  • Finally handleUpstream traps the CONNECTED and DISCONNECTED events.

  • + * If in the CONNECTED events the channel is blocked (accept refused the channel), + * then any new events on this channel will be blocked.
    + * However, you could change its behavior for instance because you don't want that all events + * will be blocked by this filter by testing the result of isBlocked, and if so, + * calling ctx.sendUpstream(e); after calling the super method or by changing the continues method.

    + + *



+ * + * A typical setup for ip filter for TCP/IP socket would be: + * + *
+ * {@link org.jboss.netty.channel.ChannelPipeline} pipeline = ...;
+ *
+ * IpFilterRuleHandler firewall = new IpFilterRuleHandler();
+ * firewall.addAll(new IpFilterRuleList("+n:localhost, +c:192.168.0.0/27, -n:*"));
+ * pipeline.addFirst("firewall", firewall);
+ * 
+ * + * @apiviz.exclude ^java\.lang\. + */ +package org.jboss.netty.handler.ipfilter; + +