/* | |
* 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.ftpserver.ipfilter; | |
import java.net.InetAddress; | |
import java.net.InetSocketAddress; | |
import java.net.UnknownHostException; | |
import java.util.Collection; | |
import java.util.HashSet; | |
import java.util.concurrent.CopyOnWriteArraySet; | |
import org.apache.mina.core.session.IoSession; | |
import org.apache.mina.filter.firewall.Subnet; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
/** | |
* An implementation of the <code>SessionFilter</code> interface, to filter | |
* sessions based on the remote IP address. | |
* | |
* @author <a href="http://mina.apache.org">Apache MINA Project</a> | |
* | |
*/ | |
public class RemoteIpFilter extends CopyOnWriteArraySet<Subnet> implements | |
SessionFilter { | |
/** | |
* Logger | |
*/ | |
Logger LOGGER = LoggerFactory.getLogger(RemoteIpFilter.class); | |
/** | |
* Serial version UID | |
*/ | |
private static final long serialVersionUID = 4887092372700628783L; | |
/** | |
* filter type | |
*/ | |
private IpFilterType type = null; | |
/** | |
* Creates a new instance of <code>RemoteIpFilter</code>. | |
* | |
* @param type | |
* the filter type | |
*/ | |
public RemoteIpFilter(IpFilterType type) { | |
this(type, new HashSet<Subnet>(0)); | |
} | |
/** | |
* Creates a new instance of <code>RemoteIpFilter</code>. | |
* | |
* @param type | |
* the filter type | |
* @param collection | |
* a collection of <code>Subnet</code>s to filter out/in. | |
*/ | |
public RemoteIpFilter(IpFilterType type, | |
Collection<? extends Subnet> collection) { | |
super(collection); | |
this.type = type; | |
} | |
/** | |
* Creates a new instance of <code>RemoteIpFilter</code>. | |
* | |
* @param type | |
* the filter type | |
* @param addresses | |
* a comma, space, tab, CR, LF separated list of IP | |
* addresses/CIDRs. | |
* @throws UnknownHostException | |
* propagated | |
* @throws NumberFormatException | |
* propagated | |
*/ | |
public RemoteIpFilter(IpFilterType type, String addresses) | |
throws NumberFormatException, UnknownHostException { | |
super(); | |
this.type = type; | |
if (addresses != null) { | |
String[] tokens = addresses.split("[\\s,]+"); | |
for (String token : tokens) { | |
if (token.trim().length() > 0) { | |
add(token); | |
} | |
} | |
} | |
if (LOGGER.isDebugEnabled()) { | |
LOGGER.debug( | |
"Created DefaultIpFilter of type {} with the subnets {}", | |
type, this); | |
} | |
} | |
/** | |
* Returns the type of this filter. | |
* | |
* @return the type of this filter. | |
*/ | |
public IpFilterType getType() { | |
return type; | |
} | |
/** | |
* Sets the type of this filter. | |
* | |
* @param type | |
* the type of this filter. | |
*/ | |
public void setType(IpFilterType type) { | |
this.type = type; | |
} | |
/** | |
* Adds the given string representation of InetAddress or CIDR notation to | |
* this filter. | |
* | |
* @param str | |
* the string representation of InetAddress or CIDR notation | |
* @return if the given element was added or not. <code>true</code>, if the | |
* given element was added to the filter; <code>false</code>, if the | |
* element already exists in the filter. | |
* @throws NumberFormatException | |
* propagated | |
* @throws UnknownHostException | |
* propagated | |
*/ | |
public boolean add(String str) throws NumberFormatException, | |
UnknownHostException { | |
// This is required so we do not block loopback address if some one adds | |
// a string with blanks as the InetAddress class assumes loopback | |
// address on a blank string. | |
if (str.trim().length() < 1) { | |
throw new IllegalArgumentException("Invalid IP Address or Subnet: " | |
+ str); | |
} | |
String[] tokens = str.split("/"); | |
if (tokens.length == 2) { | |
return add(new Subnet(InetAddress.getByName(tokens[0]), Integer | |
.parseInt(tokens[1]))); | |
} else { | |
return add(new Subnet(InetAddress.getByName(tokens[0]), 32)); | |
} | |
} | |
public boolean accept(IoSession session) { | |
InetAddress address = ((InetSocketAddress) session.getRemoteAddress()) | |
.getAddress(); | |
switch (type) { | |
case ALLOW: | |
for (Subnet subnet : this) { | |
if (subnet.inSubnet(address)) { | |
if (LOGGER.isDebugEnabled()) { | |
LOGGER | |
.debug( | |
"Allowing connection from {} because it matches with the whitelist subnet {}", | |
new Object[] { address, subnet }); | |
} | |
return true; | |
} | |
} | |
if (LOGGER.isDebugEnabled()) { | |
LOGGER | |
.debug( | |
"Denying connection from {} because it does not match any of the whitelist subnets", | |
new Object[] { address }); | |
} | |
return false; | |
case DENY: | |
if (isEmpty()) { | |
if (LOGGER.isDebugEnabled()) { | |
LOGGER | |
.debug( | |
"Allowing connection from {} because blacklist is empty", | |
new Object[] { address }); | |
} | |
return true; | |
} | |
for (Subnet subnet : this) { | |
if (subnet.inSubnet(address)) { | |
if (LOGGER.isDebugEnabled()) { | |
LOGGER | |
.debug( | |
"Denying connection from {} because it matches with the blacklist subnet {}", | |
new Object[] { address, subnet }); | |
} | |
return false; | |
} | |
} | |
if (LOGGER.isDebugEnabled()) { | |
LOGGER | |
.debug( | |
"Allowing connection from {} because it does not match any of the blacklist subnets", | |
new Object[] { address }); | |
} | |
return true; | |
default: | |
throw new RuntimeException("Unknown or unimplemented filter type: " | |
+ type); | |
} | |
} | |
} |