android: Add class to manage a set of IP address ranges/subnets
This commit is contained in:
parent
bcba14504a
commit
d852a02717
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (C) 2012-2017 Tobias Brunner
|
||||
* HSR Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Class that represents a set of IP address ranges (not necessarily proper subnets) and allows
|
||||
* modifying the set and enumerating the resulting subnets.
|
||||
*/
|
||||
public class IPRangeSet
|
||||
{
|
||||
private TreeSet<IPRange> mRanges = new TreeSet<>();
|
||||
|
||||
/**
|
||||
* Parse the given string (space separated subnets in CIDR notation) and return the resulting
|
||||
* set or {@code null} if the string was invalid. And empty set is returned if the given string
|
||||
* is {@code null}.
|
||||
*/
|
||||
public static IPRangeSet fromString(String ranges)
|
||||
{
|
||||
IPRangeSet set = new IPRangeSet();
|
||||
if (ranges != null)
|
||||
{
|
||||
for (String range : ranges.split("\\s+"))
|
||||
{
|
||||
try
|
||||
{
|
||||
set.add(new IPRange(range));
|
||||
}
|
||||
catch (Exception unused)
|
||||
{ /* besides due to invalid strings exceptions might get thrown if the string
|
||||
* contains a hostname (NetworkOnMainThreadException) */
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a range to this set. Automatically gets merged with existing ranges.
|
||||
*/
|
||||
public void add(IPRange range)
|
||||
{
|
||||
if (mRanges.contains(range))
|
||||
{
|
||||
return;
|
||||
}
|
||||
reinsert:
|
||||
while (true)
|
||||
{
|
||||
Iterator<IPRange> iterator = mRanges.iterator();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
IPRange existing = iterator.next();
|
||||
IPRange replacement = existing.merge(range);
|
||||
if (replacement != null)
|
||||
{
|
||||
iterator.remove();
|
||||
range = replacement;
|
||||
continue reinsert;
|
||||
}
|
||||
}
|
||||
mRanges.add(range);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given range from this set. Existing ranges are automatically adjusted.
|
||||
*/
|
||||
public void remove(IPRange range)
|
||||
{
|
||||
ArrayList <IPRange> additions = new ArrayList<>();
|
||||
Iterator<IPRange> iterator = mRanges.iterator();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
IPRange existing = iterator.next();
|
||||
List<IPRange> result = existing.remove(range);
|
||||
if (result.size() == 0)
|
||||
{
|
||||
iterator.remove();
|
||||
}
|
||||
else if (!result.get(0).equals(existing))
|
||||
{
|
||||
iterator.remove();
|
||||
additions.addAll(result);
|
||||
}
|
||||
}
|
||||
mRanges.addAll(additions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subnets derived from all the ranges in this set.
|
||||
*/
|
||||
public Enumeration<IPRange> getSubnets()
|
||||
{
|
||||
return new Enumeration<IPRange>()
|
||||
{
|
||||
private Iterator<IPRange> mIterator = mRanges.iterator();
|
||||
private List<IPRange> mSubnets;
|
||||
|
||||
@Override
|
||||
public boolean hasMoreElements()
|
||||
{
|
||||
return (mSubnets != null && mSubnets.size() > 0) || mIterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPRange nextElement()
|
||||
{
|
||||
if (mSubnets == null || mSubnets.size() == 0)
|
||||
{
|
||||
IPRange range = mIterator.next();
|
||||
mSubnets = range.toSubnets();
|
||||
}
|
||||
return mSubnets.remove(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of ranges, not subnets.
|
||||
*/
|
||||
public int size()
|
||||
{
|
||||
return mRanges.size();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Tobias Brunner
|
||||
* HSR Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
package org.strongswan.android.test;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.strongswan.android.utils.IPRange;
|
||||
import org.strongswan.android.utils.IPRangeSet;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class IPRangeSetTest
|
||||
{
|
||||
private void assertSubnets(Enumeration<IPRange> subnets, IPRange...exp)
|
||||
{
|
||||
if (exp.length == 0)
|
||||
{
|
||||
assertEquals("no subnets", false, subnets.hasMoreElements());
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < exp.length; i++)
|
||||
{
|
||||
assertEquals("has subnet", true, subnets.hasMoreElements());
|
||||
assertEquals("range", exp[i], subnets.nextElement());
|
||||
}
|
||||
assertEquals("done", false, subnets.hasMoreElements());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmpty()
|
||||
{
|
||||
IPRangeSet set = new IPRangeSet();
|
||||
assertEquals("size", 0, set.size());
|
||||
assertSubnets(set.getSubnets());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddDistinct() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = new IPRangeSet();
|
||||
set.add(new IPRange("192.168.1.0/24"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.1.0/24"));
|
||||
set.add(new IPRange("10.0.1.0/24"));
|
||||
assertEquals("size", 2, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("10.0.1.0/24"), new IPRange("192.168.1.0/24"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddAdjacent() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = new IPRangeSet();
|
||||
set.add(new IPRange("192.168.1.0/24"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.1.0/24"));
|
||||
set.add(new IPRange("192.168.2.0/24"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.1.0/24"), new IPRange("192.168.2.0/24"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddAdjacentJoin() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = new IPRangeSet();
|
||||
set.add(new IPRange("192.168.1.0/24"));
|
||||
set.add(new IPRange("192.168.3.0/24"));
|
||||
assertEquals("size", 2, set.size());
|
||||
set.add(new IPRange("192.168.2.0/24"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.1.0/24"), new IPRange("192.168.2.0/23"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSame() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = new IPRangeSet();
|
||||
set.add(new IPRange("192.168.1.0/24"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.1.0/24"));
|
||||
set.add(new IPRange("192.168.1.0/24"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.1.0/24"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLarger() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = new IPRangeSet();
|
||||
set.add(new IPRange("192.168.1.0/24"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.1.0/24"));
|
||||
set.add(new IPRange("192.168.0.0/16"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.0.0/16"));
|
||||
set.add(new IPRange("0.0.0.0/0"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("0.0.0.0/0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLargerMulti() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = new IPRangeSet();
|
||||
set.add(new IPRange("192.168.1.0/24"));
|
||||
set.add(new IPRange("10.0.1.0/24"));
|
||||
set.add(new IPRange("255.255.255.255/32"));
|
||||
assertEquals("size", 3, set.size());
|
||||
set.add(new IPRange("0.0.0.0/0"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("0.0.0.0/0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveNothing() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = new IPRangeSet();
|
||||
set.add(new IPRange("192.168.1.0/24"));
|
||||
set.remove(new IPRange("192.168.2.0/24"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.1.0/24"));
|
||||
set.remove(new IPRange("10.0.1.0/24"));
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.1.0/24"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAll() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = new IPRangeSet();
|
||||
set.add(new IPRange("192.168.1.0/24"));
|
||||
set.remove(new IPRange("192.168.0.0/16"));
|
||||
assertEquals("size", 0, set.size());
|
||||
assertSubnets(set.getSubnets());
|
||||
set.add(new IPRange("192.168.1.0/24"));
|
||||
set.add(new IPRange("10.0.1.0/24"));
|
||||
set.add(new IPRange("255.255.255.255/32"));
|
||||
assertEquals("size", 3, set.size());
|
||||
set.remove(new IPRange("0.0.0.0/0"));
|
||||
assertEquals("size", 0, set.size());
|
||||
assertSubnets(set.getSubnets());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveOverlap() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = new IPRangeSet();
|
||||
set.add(new IPRange("192.168.1.0/24"));
|
||||
set.add(new IPRange("192.168.2.0/24"));
|
||||
set.remove(new IPRange("192.168.1.128", "192.168.2.127"));
|
||||
assertEquals("size", 2, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.1.0/25"), new IPRange("192.168.2.128/25"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromStringSingle() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = IPRangeSet.fromString("192.168.1.0/24");
|
||||
assertEquals("size", 1, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("192.168.1.0/24"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromString() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = IPRangeSet.fromString("192.168.1.0/24 fec1::/64 10.0.1.0/24 255.255.255.255");
|
||||
assertEquals("size", 4, set.size());
|
||||
assertSubnets(set.getSubnets(), new IPRange("10.0.1.0/24"), new IPRange("192.168.1.0/24"),
|
||||
new IPRange("255.255.255.255/32"), new IPRange("fec1::/64"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromStringInvalidRange() throws UnknownHostException
|
||||
{
|
||||
IPRangeSet set = IPRangeSet.fromString("192.168.1.0/65");
|
||||
assertEquals("failed", null, set);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue