/**
*
* Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
**/
package lucee.runtime.net.ipsettings;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Comparator;
import java.util.List;
import lucee.commons.lang.ExceptionUtil;
public class IPRangeNode<T> implements Comparable<IPRangeNode>, Comparator<IPRangeNode> {
private InetAddress lower;
private InetAddress upper;
private boolean isSingle;
private T data;
private IPRangeCollection children;
public IPRangeNode( InetAddress lower, InetAddress upper) {
int c = comparerIAddr.compare(lower, upper);
if ( c <= 0 ) {
this.lower = lower;
this.upper = upper;
}
else {
this.lower = upper;
this.upper = lower;
}
this.isSingle = ( c == 0 );
this.children = new IPRangeCollection();
}
public IPRangeNode(String lower, String upper) throws UnknownHostException {
this( InetAddress.getByName( lower ), InetAddress.getByName( upper ) );
}
public IPRangeNode(String addr) throws UnknownHostException {
this( addr, addr );
}
public boolean isSingleAddress() {
return isSingle;
}
public boolean isInRange(InetAddress addr) {
if ( this.isV4() != IPSettings.isV4(addr) )
return false;
return comparerIAddr.compare(lower, addr) <= 0
&& comparerIAddr.compare(upper, addr) >= 0;
}
public boolean containsRange(IPRangeNode other) {
if ( this.isV4() != other.isV4() )
return false;
return this.isInRange(other.lower) && this.isInRange(other.upper);
}
/**
*
* @param child
* @param doCheck - passing false will avoid searching for a "better" parent, for a more efficient insert in large data sets (e.g. Country Codes of all known ranges)
* @return - true if the child was added
*/
synchronized boolean addChild( IPRangeNode child, boolean doCheck ) {
if ( !this.containsRange( child ) )
return false;
IPRangeNode parent = this;
if ( doCheck )
parent = findRange( child );
// TODO: check for eqaulity of new child and found parent
parent.children.add( child, doCheck );
return true;
}
/** calls addChild( child, true ) */
public boolean addChild( IPRangeNode child ) {
return addChild(child, true);
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public IPRangeNode findRange(IPRangeNode child) {
IPRangeNode result = null;
if ( this.containsRange(child) ) {
result = this;
IPRangeNode temp = this.children.findRange( child );
if ( temp != null )
result = temp;
}
return result;
}
public IPRangeNode findAddr(InetAddress iaddr) {
IPRangeNode result = null;
if ( this.isInRange(iaddr) ) {
result = this;
if ( this.hasChildren() ) {
IPRangeNode temp = children.findAddr( iaddr );
if ( temp != null )
result = temp;
}
}
return result;
}
public IPRangeNode findAddr(String addr) {
try {
return findAddr( InetAddress.getByName(addr) );
}
catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
return null;
}
public IPRangeNode findFast(InetAddress iaddr, List<IPRangeNode> parents) {
IPRangeNode result = null;
if ( this.isInRange(iaddr) ) {
result = this;
if ( parents != null ) parents.add( result );
if ( this.hasChildren() ) {
IPRangeNode temp = children.findFast( iaddr, parents );
if ( temp != null )
result = temp;
}
}
return result;
}
public IPRangeNode findFast(InetAddress iaddr) {
return findFast(iaddr, null);
}
/*/ works
public IPRangeNode findFast(InetAddress iaddr) {
IPRangeNode result = null;
if ( this.isInRange(iaddr) ) {
result = this;
if ( this.hasChildren() ) {
IPRangeNode temp = children.findFast( iaddr );
if ( temp != null )
result = temp;
}
}
return result;
} //*/
public IPRangeNode findFast(String addr) {
try {
return findFast( InetAddress.getByName(addr) );
}
catch(Throwable t) {ExceptionUtil.rethrowIfNecessary(t);}
return null;
}
IPRangeCollection getChildren() {
return children;
}
boolean hasChildren() {
return children.size() > 0;
}
@Override
public int compareTo(IPRangeNode other) {
int c = comparerIAddr.compare(this.lower, other.lower);
if ( c != 0 )
return c;
c = comparerIAddr.compare(this.upper, other.upper);
return c;
}
@Override
public int compare(IPRangeNode lhs, IPRangeNode rhs) {
return lhs.compareTo(rhs);
}
@Override
public boolean equals( Object o ) {
if ( o instanceof IPRangeNode ) {
return this.compareTo((IPRangeNode)o) == 0;
}
return false;
}
@Override
public int hashCode() {
return this.lower.hashCode();
}
@Override
public String toString() {
if ( isSingle )
return this.lower.toString().substring(1) + String.format(" (%d)", this.children.size());
return this.lower.toString().substring(1) + " - " + this.upper.toString().substring(1) + String.format(" (%d)", this.children.size());
}
public boolean isV4() {
return IPSettings.isV4( this.lower );
}
public boolean isV6() {
return IPSettings.isV6( this.lower );
}
public static final Comparator<IPRangeNode> comparerRange = new Comparator<IPRangeNode>() {
@Override
public int compare(IPRangeNode lhs, IPRangeNode rhs) {
return lhs.compareTo(rhs);
}
};
public static final Comparator<InetAddress> comparerIAddr = new Comparator<InetAddress>() {
@Override
public int compare(InetAddress lhs, InetAddress rhs) {
if ( (lhs instanceof Inet4Address) != (rhs instanceof Inet4Address) )
throw new IllegalArgumentException("Both arguments must be of the same IP Version");
byte[] barrLhs = lhs.getAddress();
byte[] barrRhs = rhs.getAddress();
for (int i=0; i < barrLhs.length; i++) {
int l = barrLhs[i] & 0xff; // fix signed bit in byte
int r = barrRhs[i] & 0xff;
if (l < r)
return -1;
else if (l > r)
return 1;
}
return 0; // equal
}
};
}