/* * Copyright 2012 The Netty Project * * The Netty Project 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 org.jboss.netty.channel.ChannelEvent; import org.jboss.netty.channel.ChannelHandler.Sharable; import org.jboss.netty.channel.ChannelHandlerContext; 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; /** * Implementation of Filter of IP based on ALLOW and DENY rules.<br> * <br><br> * 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) .<br> * <br> * The check is done by going from step to step in the underlying array of IpFilterRule.<br> * 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.<br> * <ul> * <li>If it was constructed with True as first argument, * the IpFilterRule is an ALLOW rule (every InetAddress that fits in the rule will be accepted).</li> * <li>If it was constructed with False as first argument, * the IpFilterRule is a DENY rule (every InetAddress that fits in the rule will be refused).</li> * </ul><br> * <br> * An empty list means allow all (no limitation).<br><br> * <b>For efficiency reason, you should not add/remove too frequently IpFilterRules to/from this handler. * You should prefer to replace an entry (<tt>set</tt> method) with an ALLOW/DENY ALL IpFilterRule * if possible.</b><br><br><br> * <b>This handler should be created only once and reused on every pipeline since it handles * a global status of what is allowed or blocked.</b><br><br> * <p/> * 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. */ @Sharable public class IpFilterRuleHandler extends IpFilteringHandlerImpl { /** List of {@link IpFilterRule} */ private final CopyOnWriteArrayList<IpFilterRule> ipFilterRuleList = new CopyOnWriteArrayList<IpFilterRule>(); /** Constructor from a new list of IpFilterRule */ public IpFilterRuleHandler(List<IpFilterRule> 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() { } // Below are methods directly inspired from CopyOnWriteArrayList methods /** Add an ipFilterRule in the list at the end */ 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) */ 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. */ public void addAll(Collection<IpFilterRule> 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. */ public void addAll(int index, Collection<IpFilterRule> c) { if (c == null) { throw new NullPointerException("Collection can not be null"); } ipFilterRuleList.addAll(index, c); } /** * Append the element if not present. * * @return the number of elements added */ public int addAllAbsent(Collection<IpFilterRule> c) { if (c == null) { throw new NullPointerException("Collection can not be null"); } return ipFilterRuleList.addAllAbsent(c); } /** * Append the element if not present. * * @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 * * @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 * * @return true if this list contains all of the elements of the specified collection */ public boolean containsAll(Collection<IpFilterRule> 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 * * @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 */ 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 * * @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 */ public void removeAll(Collection<IpFilterRule> 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 */ public void retainAll(Collection<IpFilterRule> 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 * * @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(); } @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<IpFilterRule> iterator = ipFilterRuleList.iterator(); IpFilterRule ipFilterRule; 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; } }