use commons-net instead
This commit is contained in:
@@ -3,7 +3,8 @@ libraryDependencies ++= Seq(
|
||||
"com.google.android" % "analytics" % "3.01",
|
||||
"com.google.android" % "admob" % "6.4.1",
|
||||
"dnsjava" % "dnsjava" % "2.1.5",
|
||||
"org.scalaj" %% "scalaj-http" % "0.3.10"
|
||||
"org.scalaj" %% "scalaj-http" % "0.3.10",
|
||||
"commons-net" % "commons-net" % "3.3"
|
||||
)
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@ import sbtandroid._
|
||||
import sbtandroid.AndroidPlugin._
|
||||
|
||||
object App {
|
||||
val version = "2.0.4"
|
||||
val versionCode = 55
|
||||
val version = "2.0.5"
|
||||
val versionCode = 56
|
||||
}
|
||||
|
||||
object General {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
APP_ABI := armeabi x86
|
||||
APP_PLATFORM := android-9
|
||||
APP_STL := stlport_static
|
||||
NDK_TOOLCHAIN_VERSION := 4.8
|
||||
NDK_TOOLCHAIN_VERSION := 4.6
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package be.jvb.iptypes
|
||||
|
||||
import java.net.InetAddress
|
||||
|
||||
/**
|
||||
* Represents an IPv4 address.
|
||||
*
|
||||
* @author <a href="http://janvanbesien.blogspot.com">Jan Van Besien</a>
|
||||
*
|
||||
* @param value 32bit value of the ip address (only 32 least significant bits are used)
|
||||
*/
|
||||
class IpAddress(val value: Long) extends SmallByteArray {
|
||||
/**
|
||||
* Construct from a CIDR format string representation (e.g. "192.168.0.1").
|
||||
*/
|
||||
def this(address: String) = this (SmallByteArray.parseAsLong(address, IpAddress.N_BYTES, DEC()))
|
||||
|
||||
/**
|
||||
* Construct from a java.net.InetAddress.
|
||||
*/
|
||||
def this(inetAddress: InetAddress) = {
|
||||
this (if (inetAddress == null) throw new IllegalArgumentException("can not create from [null]") else inetAddress
|
||||
.getHostAddress)
|
||||
}
|
||||
|
||||
protected def nBytes = IpAddress.N_BYTES
|
||||
|
||||
protected def radix = DEC()
|
||||
|
||||
/**
|
||||
* Addition. Will never overflow, but wraps around when the highest ip address has been reached.
|
||||
*/
|
||||
def +(value: Long) = new IpAddress((this.value + value) & maxValue)
|
||||
|
||||
/**
|
||||
* Substraction. Will never underflow, but wraps around when the lowest ip address has been reached.
|
||||
*/
|
||||
def -(value: Long) = new IpAddress((this.value - value) & maxValue)
|
||||
|
||||
/**
|
||||
* Convert to java.net.InetAddress.
|
||||
*/
|
||||
def toInetAddress: InetAddress = InetAddress.getByName(toString)
|
||||
|
||||
}
|
||||
|
||||
object IpAddress {
|
||||
val N_BYTES = 4
|
||||
|
||||
def apply(string: String): IpAddress = new IpAddress(SmallByteArray.parseAsLong(string, N_BYTES, DEC()))
|
||||
|
||||
def unapply(ipAddress: IpAddress): Option[String] = {
|
||||
Some(ipAddress.toString)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
package be.jvb.iptypes
|
||||
|
||||
import scala.collection.immutable._
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
/**
|
||||
* Represents a pool of IPv4 addresses. A pool is a range of addresses, from which some can be "allocated" and some can be
|
||||
* "free".
|
||||
*
|
||||
* @author <a href="http://janvanbesien.blogspot.com">Jan Van Besien</a>
|
||||
*
|
||||
* @param first first address in the pool
|
||||
* @param last last address in the pool
|
||||
* @param freeRanges sorted set of all free ranges in this pool. All free ranges should be contained in the range from first to last;
|
||||
* none of the free ranges should overlap; and the set of free ranges should contain as little fragments as necessary.
|
||||
*/
|
||||
class IpAddressPool private(override val first: IpAddress, override val last: IpAddress, val freeRanges: SortedSet[IpAddressRange])
|
||||
extends IpAddressRange(first, last) {
|
||||
validateFreeRanges(freeRanges)
|
||||
|
||||
/**
|
||||
* Construct a pool which is completely free.
|
||||
*/
|
||||
def this(first: IpAddress, last: IpAddress) = {
|
||||
// the whole ranges is free initially
|
||||
this (first, last, TreeSet[IpAddressRange](new IpAddressRange(first, last)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a pool which is completely free from an existing address range.
|
||||
*/
|
||||
def this(range: IpAddressRange) = {
|
||||
this (range.first, range.last)
|
||||
}
|
||||
|
||||
private def validateFreeRanges(toValidate: SortedSet[IpAddressRange]) = {
|
||||
if (!toValidate.isEmpty && !checkWithinBounds(toValidate))
|
||||
throw new IllegalArgumentException("invalid free ranges: not all within pool range")
|
||||
}
|
||||
|
||||
private def checkWithinBounds(toValidate: SortedSet[IpAddressRange]): Boolean = {
|
||||
toValidate.firstKey.first >= first && toValidate.lastKey.last <= last
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate the first free address in the pool.
|
||||
*
|
||||
* @returns the pool after allocation and the allocated address (or None if no address was free)
|
||||
*/
|
||||
def allocate(): (IpAddressPool, Option[IpAddress]) = {
|
||||
if (!isExhausted) {
|
||||
// get the first range of free addresses, and take the first address of that range
|
||||
val range: IpAddressRange = freeRanges.firstKey
|
||||
val toAllocate: IpAddress = range.first
|
||||
doAllocate(toAllocate, range)
|
||||
} else {
|
||||
(this, None)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate the given address in the pool.
|
||||
*
|
||||
* @param toAllocate address to allocate in the pool
|
||||
* @returns the pool after allocation and the allocated address (or None if the address was not free in this pool)
|
||||
*/
|
||||
def allocate(toAllocate: IpAddress): (IpAddressPool, Option[IpAddress]) = {
|
||||
if (!contains(toAllocate))
|
||||
throw new IllegalArgumentException("can not allocate address which is not contained in the range of the pool [" + toAllocate + "]")
|
||||
|
||||
// go find the range that contains the requested address
|
||||
findFreeRangeContaining(toAllocate) match {
|
||||
case Some(range) => doAllocate(toAllocate, range) // allocate in the range we found
|
||||
case None => {
|
||||
(this, None) // no free range found for the requested address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def findFreeRangeContaining(toAllocate: IpAddress): Option[IpAddressRange] = {
|
||||
// split around the address to allocate
|
||||
val head = freeRanges.until(new IpAddressRange(toAllocate, toAllocate))
|
||||
val tail = freeRanges.from(new IpAddressRange(toAllocate, toAllocate))
|
||||
|
||||
// the range we need is either the first of the tail, or the last of the head, or it doesn't exist
|
||||
if (!head.isEmpty && head.lastKey.contains(toAllocate)) {
|
||||
Some(head.lastKey)
|
||||
}
|
||||
else if (!tail.isEmpty && tail.firstKey.contains(toAllocate)) {
|
||||
Some(tail.firstKey)
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate the given address in the given range. It is assumed at this point that the range actually contains
|
||||
* the address, and that the range is one of the free ranges of the pool.
|
||||
*/
|
||||
private def doAllocate(toAllocate: IpAddress, range: IpAddressRange): (IpAddressPool, Option[IpAddress]) = {
|
||||
// remove the range and replace with ranges without the allocated address
|
||||
val newRanges = range - toAllocate
|
||||
// note: the cast to SortedSet is a workaround until scala 2.8 (http://stackoverflow.com/questions/1271426/scala-immutable-sortedset-are-not-stable-on-deletion)
|
||||
val remainingRanges = (freeRanges ++ newRanges).asInstanceOf[SortedSet[IpAddressRange]] - range
|
||||
(new IpAddressPool(this.first, this.last, remainingRanges.asInstanceOf[SortedSet[IpAddressRange]]), Some(toAllocate))
|
||||
}
|
||||
|
||||
/**
|
||||
* Deallocate the given address in the pool. The given address will be free in the returned pool.
|
||||
*
|
||||
* @returns the pool after deallocation
|
||||
*/
|
||||
def deAllocate(address: IpAddress): IpAddressPool = {
|
||||
if (!contains(address))
|
||||
throw new IllegalArgumentException("can not deallocate address which is not contained in the range of the pool [" + address + "]")
|
||||
|
||||
new IpAddressPool(first, last, addAddressToFreeRanges(address))
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given address as a free address in the set of free ranges. The implementation tries to merge existing ranges
|
||||
* as much as possible to prevent fragmentation.
|
||||
*/
|
||||
private def addAddressToFreeRanges(address: IpAddress): SortedSet[IpAddressRange] = {
|
||||
val freeRangeBeforeAddress: Iterable[IpAddressRange] = freeRanges.filter(element => element.last + 1 == address)
|
||||
val freeRangeAfterAddress: Iterable[IpAddressRange] = freeRanges.filter(element => element.first - 1 == address)
|
||||
|
||||
if (freeRangeBeforeAddress.isEmpty && freeRangeAfterAddress.isEmpty) {
|
||||
// no match -> nothing to "defragment"
|
||||
freeRanges + new IpAddressRange(address, address)
|
||||
} else {
|
||||
if (!freeRangeBeforeAddress.isEmpty && !freeRangeAfterAddress.isEmpty) {
|
||||
// first and last match -> merge the 2 existing ranges
|
||||
(freeRanges -
|
||||
freeRangeBeforeAddress.toSeq(0) -
|
||||
freeRangeAfterAddress.toSeq(0) +
|
||||
new IpAddressRange(freeRangeBeforeAddress.toSeq(0).first, freeRangeAfterAddress.toSeq(0).last)).
|
||||
asInstanceOf[SortedSet[IpAddressRange]] // workaround, see above
|
||||
} else if (!freeRangeBeforeAddress.isEmpty) {
|
||||
// append
|
||||
(freeRanges -
|
||||
freeRangeBeforeAddress.toSeq(0) +
|
||||
(freeRangeBeforeAddress.toSeq(0) + address)).
|
||||
asInstanceOf[SortedSet[IpAddressRange]] // workaround, see above
|
||||
} else {
|
||||
// prepend
|
||||
(freeRanges -
|
||||
freeRangeAfterAddress.toSeq(0) +
|
||||
(freeRangeAfterAddress.toSeq(0) + address)).
|
||||
asInstanceOf[SortedSet[IpAddressRange]] // workaround, see above
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def isExhausted: Boolean = {
|
||||
freeRanges.isEmpty
|
||||
}
|
||||
|
||||
def isFree(address: IpAddress): Boolean = {
|
||||
// lookup the address in all free ranges, if true in one, the result is true
|
||||
freeRanges.map(_ contains address).reduceLeft((x, y) => x || y)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of free ranges fragments
|
||||
*/
|
||||
def fragments(): Int = {
|
||||
freeRanges.size
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a stream of all free addresses in the pool
|
||||
*/
|
||||
def freeAddresses(): Stream[IpAddress] = {
|
||||
toAddressesStream(freeRanges.toList)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a stream of all allocated addresses in the pool
|
||||
*/
|
||||
def allocatedAddresses(): Stream[IpAddress] = {
|
||||
toAddressesStream(allocatedRanges.toList)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ranges of allocated addresses in the pool
|
||||
* @note the pool implementation keeps track of the free addresses, so this operation is more expensive than the freeRanges() one
|
||||
*/
|
||||
def allocatedRanges: List[IpAddressRange] = {
|
||||
if (freeRanges.isEmpty) {
|
||||
// one big range of allocated addresses
|
||||
List[IpAddressRange](new IpAddressRange(first, last))
|
||||
} else {
|
||||
makeNonEmptyListOfAllocatedRanges()
|
||||
}
|
||||
}
|
||||
|
||||
private def makeNonEmptyListOfAllocatedRanges(): List[IpAddressRange] = {
|
||||
val allocatedRanges: ListBuffer[IpAddressRange] = new ListBuffer[IpAddressRange]()
|
||||
var currentStart: IpAddress = first
|
||||
var currentEnd: IpAddress = null
|
||||
for (freeRange <- freeRanges) {
|
||||
currentEnd = freeRange.first
|
||||
if (currentEnd != currentStart) { // occurs if first free range starts at the beginning of the pool
|
||||
allocatedRanges.append(new IpAddressRange(currentStart, currentEnd - 1))
|
||||
}
|
||||
currentStart = freeRange.last + 1
|
||||
}
|
||||
if (currentStart <= last) { // occurs if the last free range didn't reach until the end of the pool
|
||||
allocatedRanges.append(new IpAddressRange(currentStart, last))
|
||||
}
|
||||
allocatedRanges.toList
|
||||
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return ranges of free addresses in the pool
|
||||
// */
|
||||
// def freeRanges(): SortedSet[IpAddressRange] = freeRanges
|
||||
|
||||
private def toAddressesStream(ranges: List[IpAddressRange]): Stream[IpAddress] = {
|
||||
if (ranges.isEmpty) {
|
||||
Stream.empty
|
||||
} else {
|
||||
Stream.concat(ranges.head.addresses, toAddressesStream(ranges.tail))
|
||||
}
|
||||
}
|
||||
|
||||
override def toString: String = {
|
||||
"IpAddressPool " + super.toString + " with free ranges " + freeRanges.toString
|
||||
}
|
||||
|
||||
// TODO: toStringRepresentation which can also be parsed back into an IpAddressPool
|
||||
|
||||
// equals implemented as suggested in staircase book
|
||||
|
||||
override def equals(other: Any): Boolean = {
|
||||
other match {
|
||||
case that: IpAddressPool => super.equals(that) && this.freeRanges == that.freeRanges
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
protected override def canEqual(other: Any): Boolean = {
|
||||
other.isInstanceOf[IpAddressPool]
|
||||
}
|
||||
|
||||
override def hashCode = {
|
||||
41 * (41 + first.hashCode) + last.hashCode
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package be.jvb.iptypes
|
||||
|
||||
import scala.math.Ordered
|
||||
|
||||
/**
|
||||
* Represents a continuous range of IPv4 ip addresses (bounds included).
|
||||
*
|
||||
* @author <a href="http://janvanbesien.blogspot.com">Jan Van Besien</a>
|
||||
*
|
||||
* @param first first address in the range
|
||||
* @param last last address in the range
|
||||
*/
|
||||
class IpAddressRange(val first: IpAddress, val last: IpAddress) extends Ordered[IpAddressRange] {
|
||||
if (last < first)
|
||||
throw new IllegalArgumentException("Cannot create ip address range with last address > first address")
|
||||
|
||||
def contains(address: IpAddress): Boolean = {
|
||||
address >= first && address <= last
|
||||
}
|
||||
|
||||
def contains(range: IpAddressRange): Boolean = {
|
||||
contains(range.first) && contains(range.last)
|
||||
}
|
||||
|
||||
def overlaps(range: IpAddressRange): Boolean = {
|
||||
contains(range.first) || contains(range.last) || range.contains(first) || range.contains(last)
|
||||
}
|
||||
|
||||
def length = {
|
||||
last.value - first.value + 1
|
||||
}
|
||||
|
||||
def addresses: Stream[IpAddress] = {
|
||||
if (first < last) {
|
||||
Stream.cons(first, new IpAddressRange(first + 1, last).addresses)
|
||||
} else {
|
||||
Stream.cons(first, Stream.empty)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ip address ranges are ordered on the first address, or on the second address if the first is equal.
|
||||
*/
|
||||
def compare(that: IpAddressRange): Int = {
|
||||
if (this.first != that.first)
|
||||
this.first.compare(that.first)
|
||||
else
|
||||
this.last.compare(that.last)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an address from the range, resulting in one, none or two new ranges.
|
||||
*/
|
||||
def -(address: IpAddress): List[IpAddressRange] = {
|
||||
if (address eq null)
|
||||
throw new IllegalArgumentException("invalid address [null]")
|
||||
|
||||
if (!contains(address))
|
||||
List(this)
|
||||
else if (address == first && address == last)
|
||||
List()
|
||||
else if (address == first)
|
||||
List(new IpAddressRange(first + 1, last))
|
||||
else if (address == last)
|
||||
List(new IpAddressRange(first, last - 1))
|
||||
else
|
||||
List(new IpAddressRange(first, address - 1), new IpAddressRange(address + 1, last))
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the range just enough at its head or tail such that the given address is included.
|
||||
*/
|
||||
def +(address: IpAddress): IpAddressRange = {
|
||||
if (address < first)
|
||||
new IpAddressRange(address, last)
|
||||
else if (address > last)
|
||||
new IpAddressRange(first, address)
|
||||
else
|
||||
this
|
||||
}
|
||||
|
||||
override def toString: String = {
|
||||
first.toString + " - " + last.toString
|
||||
}
|
||||
|
||||
// equals implemented as suggested in staircase book
|
||||
|
||||
override def equals(other: Any): Boolean = {
|
||||
other match {
|
||||
case that: IpAddressRange => that.canEqual(this) && this.first == that.first && this.last == that.last
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
protected def canEqual(other: Any): Boolean = {
|
||||
other.isInstanceOf[IpAddressRange]
|
||||
}
|
||||
|
||||
override def hashCode = {
|
||||
41 * (41 + first.hashCode) + last.hashCode
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package be.jvb.iptypes
|
||||
|
||||
import java.lang.String
|
||||
|
||||
/**
|
||||
* Represents an Ipv4 network (i.e. an address and a mask).
|
||||
*
|
||||
* @author <a href="http://janvanbesien.blogspot.com">Jan Van Besien</a>
|
||||
*
|
||||
* @param address network address of the network
|
||||
* @param mask network mask of the network
|
||||
*/
|
||||
class IpNetwork(val address: IpAddress, val mask: IpNetworkMask)
|
||||
extends IpAddressRange(IpNetwork.first(address, mask), IpNetwork.last(address, mask)) {
|
||||
/**
|
||||
* Construct a network from two addresses. This will create the smallest possible network ("longest prefix match") which contains
|
||||
* both addresses.
|
||||
*/
|
||||
def this(first: IpAddress, last: IpAddress) = this (first, IpNetworkMask.longestPrefixNetwork(first, last))
|
||||
|
||||
private def this(addressAndMask: (IpAddress, IpNetworkMask)) = this (addressAndMask._1, addressAndMask._2)
|
||||
|
||||
/**
|
||||
* Construct a network from a CIDR notation (e.g. "192.168.0.0/24" or "192.168.0.0/255.255.255.0")
|
||||
*/
|
||||
def this(network: String) = this (IpNetwork.parseAddressAndMaskFromCidrNotation(network))
|
||||
|
||||
/**
|
||||
* @return CIDR notation
|
||||
*/
|
||||
override def toString: String = first.toString + "/" + mask.prefixLength
|
||||
}
|
||||
|
||||
object IpNetwork {
|
||||
/**
|
||||
* get the first address from a network which contains the given address.
|
||||
*/
|
||||
private[iptypes] def first(address: IpAddress, mask: IpNetworkMask): IpAddress = {
|
||||
new IpAddress(address.value & mask.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* get the last address from a network which contains the given address.
|
||||
*/
|
||||
private[iptypes] def last(address: IpAddress, mask: IpNetworkMask): IpAddress = {
|
||||
first(address, mask) + (0xFFFFFFFFL >> mask.prefixLength)
|
||||
}
|
||||
|
||||
private[iptypes] def parseAddressAndMaskFromCidrNotation(cidrString: String): (IpAddress, IpNetworkMask) = {
|
||||
if (!cidrString.contains("/"))
|
||||
throw new IllegalArgumentException("no CIDR format [" + cidrString + "]")
|
||||
|
||||
val addressAndMask: (String, String) = splitInAddressAndMask(cidrString)
|
||||
|
||||
val address = new IpAddress(addressAndMask._1)
|
||||
val mask = parseNetworkMask(addressAndMask._2)
|
||||
|
||||
(address, mask)
|
||||
}
|
||||
|
||||
private def splitInAddressAndMask(cidrString: String): (String, String) = {
|
||||
val addressAndMask: Array[String] = cidrString.split("/")
|
||||
if (addressAndMask.length != 2)
|
||||
throw new IllegalArgumentException("no CIDR format [" + cidrString + "]")
|
||||
(addressAndMask(0), addressAndMask(1))
|
||||
}
|
||||
|
||||
private def parseNetworkMask(mask: String) = {
|
||||
try
|
||||
{
|
||||
if (mask.contains("."))
|
||||
new IpNetworkMask(mask)
|
||||
else
|
||||
IpNetworkMask.fromPrefixLength(Integer.parseInt(mask))
|
||||
} catch {
|
||||
case e: Exception => throw new IllegalArgumentException("not a valid network mask [" + mask + "]", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package be.jvb.iptypes
|
||||
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
/**
|
||||
* Represents an IPv4 network mask.
|
||||
*
|
||||
* @author <a href="http://janvanbesien.blogspot.com">Jan Van Besien</a>
|
||||
*/
|
||||
class IpNetworkMask(override val value: Long) extends IpAddress(value) {
|
||||
def this(address: String) = this (SmallByteArray.parseAsLong(address, IpAddress.N_BYTES, DEC()))
|
||||
|
||||
checkMaskValidity()
|
||||
|
||||
private def checkMaskValidity() = {
|
||||
if (!IpNetworkMask.VALID_MASK_VALUES.contains(value))
|
||||
throw new IllegalArgumentException("Not a valid ip network mask [" + this + "]")
|
||||
}
|
||||
|
||||
def prefixLength = {
|
||||
IpNetworkMask.fromLongToPrefixLength(value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object IpNetworkMask {
|
||||
private[iptypes] val VALID_MASK_VALUES = for (prefixLength <- 0 to 32) yield fromPrefixLenthToLong(
|
||||
prefixLength)
|
||||
|
||||
/**
|
||||
* Convert a prefix length (e.g. 24) into a network mask (e.g. 255.255.255.0). IpNetworkMask hasn't got a public constructor for this, because
|
||||
* it would be confusing with the constructor that takes a long.
|
||||
*/
|
||||
def fromPrefixLength(prefixLength: Int): IpNetworkMask = {
|
||||
new IpNetworkMask(fromPrefixLenthToLong(prefixLength))
|
||||
}
|
||||
|
||||
private[iptypes] def fromPrefixLenthToLong(prefixLength: Int): Long = {
|
||||
(((1L << 32) - 1) << (32 - prefixLength)) & 0xFFFFFFFFL
|
||||
}
|
||||
|
||||
private[iptypes] def fromLongToPrefixLength(value: Long): Int = {
|
||||
val lsb: Long = value & 0xFFFFFFFFL
|
||||
var result: Int = 0
|
||||
var bit: Long = 1L << 31
|
||||
|
||||
while (((lsb & bit) != 0) && (result < 32)) {
|
||||
bit = bit >> 1
|
||||
result += 1
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a network mask which has the longest matching prefix to still contain both given addresses.
|
||||
*/
|
||||
def longestPrefixNetwork(first: IpAddress, last: IpAddress): IpNetworkMask = {
|
||||
IpNetworkMask.fromPrefixLength(IpNetworkMask.fromLongToPrefixLength(~first.value ^ last.value))
|
||||
}
|
||||
|
||||
def apply(string: String): IpNetworkMask = new IpNetworkMask(SmallByteArray.parseAsLong(string, IpAddress.N_BYTES, DEC()))
|
||||
|
||||
def unapply(ipNetworkMask: IpNetworkMask): Option[String] = {
|
||||
Some(ipNetworkMask.toString)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package be.jvb.iptypes
|
||||
|
||||
/**
|
||||
* Represents a MAC address.
|
||||
*
|
||||
* @author <a href="http://janvanbesien.blogspot.com">Jan Van Besien</a>
|
||||
*/
|
||||
class MacAddress(val value: Long) extends SmallByteArray {
|
||||
|
||||
def this(address: String) = this(SmallByteArray.parseAsLong(address, MacAddress.N_BYTES, HEX()))
|
||||
|
||||
protected def nBytes = MacAddress.N_BYTES
|
||||
|
||||
protected def radix = HEX()
|
||||
|
||||
override def zeroPaddingUpTo = 2
|
||||
|
||||
/**
|
||||
* Addition. Will never overflow, but wraps around when the highest MAC address has been reached.
|
||||
*/
|
||||
def +(value: Long) = new MacAddress((this.value + value) & maxValue)
|
||||
|
||||
/**
|
||||
* Substraction. Will never underflow, but wraps around when the lowest MAC address has been reached.
|
||||
*/
|
||||
def -(value: Long) = new MacAddress((this.value - value) & maxValue)
|
||||
|
||||
}
|
||||
|
||||
object MacAddress {
|
||||
val N_BYTES = 6
|
||||
|
||||
// def apply(string: String): MacAddress = new MacAddress(SmallByteArray.parseAsLong(string, N_BYTES))
|
||||
|
||||
// def unapply(macAddress: MacAddress): Some[String] = macAddress.toString
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package be.jvb.iptypes
|
||||
|
||||
/**
|
||||
* Enumeration of possible radix values (hexadecimal and decimal).
|
||||
*
|
||||
* @author <a href="http://janvanbesien.blogspot.com">Jan Van Besien</a>
|
||||
*/
|
||||
private[iptypes] sealed trait Radix {
|
||||
val radix:Int
|
||||
}
|
||||
private[iptypes] final case class HEX() extends Radix {
|
||||
override val radix = 16
|
||||
}
|
||||
private[iptypes] final case class DEC() extends Radix {
|
||||
override val radix = 10
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
package be.jvb.iptypes
|
||||
|
||||
import java.util.Arrays
|
||||
import scala.math.Ordered
|
||||
import java.util
|
||||
|
||||
/**
|
||||
* Represents a byte array which is small enough to fit in a long (max 8 bytes).
|
||||
*
|
||||
* @author <a href="http://janvanbesien.blogspot.com">Jan Van Besien</a>
|
||||
*/
|
||||
private[iptypes] trait SmallByteArray extends Ordered[SmallByteArray] {
|
||||
val value: Long
|
||||
|
||||
/** How many bytes do we use (max 8). */
|
||||
protected def nBytes: Int
|
||||
|
||||
/** In what radix do we represent the bytes when converting to a string. */
|
||||
protected def radix: Radix
|
||||
|
||||
/** Do we need to padd the bytes with zeros when converting to a string. */
|
||||
protected def zeroPaddingUpTo: Int = 0
|
||||
|
||||
val maxValue = Math.pow(2, nBytes * 8).toLong - 1
|
||||
|
||||
if (nBytes < 0 || nBytes > 8) {
|
||||
throw new IllegalArgumentException("SmallByteArray can be used for arrays of length 0 to 8 only")
|
||||
}
|
||||
|
||||
if (value > maxValue || value < 0) {
|
||||
throw new IllegalArgumentException("out of range [0x" + java.lang.Long.toHexString(value) + "] with [" + nBytes + "] bytes")
|
||||
}
|
||||
|
||||
/**
|
||||
* @return integer array of the bytes in this byte array.
|
||||
*/
|
||||
def toIntArray: Array[Int] = {
|
||||
val ints = new Array[Int](nBytes)
|
||||
|
||||
for (i <- 0 until nBytes) {
|
||||
ints(i) = (((value << i * 8) >>> 8 * (nBytes - 1)) & 0xFF).asInstanceOf[Int]
|
||||
}
|
||||
|
||||
ints.foreach((anInt) => assert(anInt >= 0 && anInt <= 255))
|
||||
|
||||
ints
|
||||
}
|
||||
|
||||
/**
|
||||
* @return byte array of the bytes in this byte array.
|
||||
*/
|
||||
def toByteArray: Array[Byte] = {
|
||||
val bytes = new Array[Byte](nBytes)
|
||||
|
||||
for (i <- 0 until nBytes) {
|
||||
bytes(i) = (((value << i * 8) >>> 8 * (nBytes - 1)) & 0xFF).asInstanceOf[Byte]
|
||||
}
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
override def compare(that: SmallByteArray): Int = {
|
||||
this.value.compare(that.value)
|
||||
}
|
||||
|
||||
// equals implemented as suggested in staircase book
|
||||
|
||||
override def equals(other: Any): Boolean = {
|
||||
other match {
|
||||
case that: SmallByteArray => that.canEqual(this) && this.value == that.value
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
protected def canEqual(other: Any): Boolean = {
|
||||
other.isInstanceOf[SmallByteArray]
|
||||
}
|
||||
|
||||
override def hashCode = {
|
||||
value.hashCode
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String representation of the byte array, with "." between every byte.
|
||||
*/
|
||||
override def toString: String = {
|
||||
val ints = toIntArray
|
||||
val strings = for (i <- 0 until nBytes) yield String.format(formatString, ints(i).asInstanceOf[Object])
|
||||
|
||||
strings.mkString(".")
|
||||
}
|
||||
|
||||
private lazy val formatString = {
|
||||
radix match {
|
||||
case HEX() => {
|
||||
if (zeroPaddingUpTo != 0)
|
||||
"%0" + zeroPaddingUpTo + "X"
|
||||
else
|
||||
"%X"
|
||||
}
|
||||
case DEC() => {
|
||||
if (zeroPaddingUpTo != 0)
|
||||
"%0" + zeroPaddingUpTo + "d"
|
||||
else
|
||||
"%d"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object SmallByteArray {
|
||||
private[iptypes] def parseAsLong(string: String, length: Int, radix: Radix): Long = {
|
||||
if (string eq null)
|
||||
throw new IllegalArgumentException("can not parse [null]")
|
||||
|
||||
val longArray = parseAsLongArray(string, radix)
|
||||
|
||||
validate(longArray, length)
|
||||
mergeBytesOfArrayIntoLong(longArray)
|
||||
}
|
||||
|
||||
private def parseAsLongArray(string: String, radix: Radix): Array[Long] = {
|
||||
string.split("\\.").map(java.lang.Long.parseLong(_, radix.radix))
|
||||
}
|
||||
|
||||
private def validate(array: Array[Long], length: Int) = {
|
||||
if (array.length != length)
|
||||
throw new IllegalArgumentException("can not parse values [" + util.Arrays.toString(array) + "] into a SmallByteArray of [" + length + "] bytes")
|
||||
|
||||
if (!array.filter(_ < 0).isEmpty)
|
||||
throw new IllegalArgumentException("each element should be positive [" + util.Arrays.toString(array) + "]")
|
||||
|
||||
if (!array.filter(_ > 255).isEmpty)
|
||||
throw new IllegalArgumentException("each element should be less than 255 [" + util.Arrays.toString(array) + "]")
|
||||
}
|
||||
|
||||
private def mergeBytesOfArrayIntoLong(array: Array[Long]): Long = {
|
||||
var result = 0L
|
||||
for (i <- 0 until array.length) {
|
||||
result |= (array(i) << ((array.length - i - 1) * 8))
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,8 @@ import org.apache.http.conn.util.InetAddressUtils
|
||||
import android.os.Message
|
||||
import scala.Some
|
||||
import scala.concurrent.ops._
|
||||
import be.jvb.iptypes.{IpNetwork, IpAddress}
|
||||
import org.apache.commons.net.util.SubnetUtils
|
||||
import java.net.InetAddress
|
||||
|
||||
object ShadowVpnService {
|
||||
def isServiceStarted(context: Context): Boolean = {
|
||||
@@ -251,10 +252,7 @@ class ShadowVpnService extends VpnService {
|
||||
|
||||
def startVpn() {
|
||||
|
||||
val proxy_address = new IpAddress(config.proxy)
|
||||
|
||||
val private_networks = Array[IpNetwork](new IpNetwork("127.0.0.0/8"),
|
||||
new IpNetwork("10.0.0.0/8"), new IpNetwork("172.16.0.0/12"), new IpNetwork("192.168.0.0/16"))
|
||||
val proxy_address = config.proxy
|
||||
|
||||
val builder = new Builder()
|
||||
builder
|
||||
@@ -267,24 +265,27 @@ class ShadowVpnService extends VpnService {
|
||||
builder.addRoute("0.0.0.0", 0)
|
||||
} else if (config.isGFWList) {
|
||||
val gfwList = getResources.getStringArray(R.array.gfw_list)
|
||||
gfwList.foreach(addr => {
|
||||
val network = new IpNetwork(addr)
|
||||
if (!network.contains(proxy_address)) {
|
||||
builder.addRoute(network.address.toString, network.mask.prefixLength)
|
||||
gfwList.foreach(cidr => {
|
||||
val net = new SubnetUtils(cidr).getInfo
|
||||
if (!net.isInRange(proxy_address)) {
|
||||
val addr = cidr.split('/')
|
||||
builder.addRoute(addr(0), addr(1).toInt)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
for (i <- 1 to 254) {
|
||||
val network = new IpNetwork(i.toString + ".0.0.0/8")
|
||||
if (!network.contains(proxy_address) &&
|
||||
private_networks.forall(range => !network.contains(range))) {
|
||||
builder.addRoute(i + ".0.0.0", 8)
|
||||
val addr = i.toString + ".0.0.0"
|
||||
val cidr = addr + "/8"
|
||||
val net = new SubnetUtils(cidr).getInfo
|
||||
if (!net.isInRange(proxy_address)) {
|
||||
if (!InetAddress.getByName(addr).isSiteLocalAddress) builder.addRoute(addr, 8)
|
||||
} else {
|
||||
for (j <- 0 to 255) {
|
||||
val subNetwork = new IpNetwork(i.toString + "." + j.toString + ".0.0/16")
|
||||
if (!subNetwork.contains(proxy_address) &&
|
||||
private_networks.forall(range => !range.overlaps(subNetwork))) {
|
||||
builder.addRoute(subNetwork.address.toString, subNetwork.mask.prefixLength)
|
||||
val addr = i.toString + "." + j.toString + ".0.0"
|
||||
val cidr = addr + "/16"
|
||||
val net = new SubnetUtils(cidr).getInfo
|
||||
if (!net.isInRange(proxy_address)) {
|
||||
if (!InetAddress.getByName(addr).isSiteLocalAddress) builder.addRoute(addr, 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,6 +303,8 @@ class ShadowsocksService extends Service {
|
||||
startDnsDaemon()
|
||||
startRedsocksDaemon()
|
||||
setupIptables
|
||||
flushDNS()
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
@@ -496,6 +498,11 @@ class ShadowsocksService extends Service {
|
||||
Service.START_STICKY
|
||||
}
|
||||
|
||||
def flushDNS() {
|
||||
Utils.runRootCommand("ndc resolver flushdefaultif\n"
|
||||
+ "ndc resolver flushif wlan0\n")
|
||||
}
|
||||
|
||||
def setupIptables: Boolean = {
|
||||
val init_sb = new StringBuilder
|
||||
val http_sb = new StringBuilder
|
||||
|
||||
@@ -343,10 +343,14 @@ object Utils {
|
||||
|
||||
private def toggleAboveApiLevel17() {
|
||||
// Android 4.2 and above
|
||||
Utils.runRootCommand("settings put global airplane_mode_on 1\n"
|
||||
+ "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true\n"
|
||||
+ "settings put global airplane_mode_on 0\n"
|
||||
+ "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false\n")
|
||||
|
||||
Utils.runRootCommand("ndc resolver flushdefaultif\n"
|
||||
+ "ndc resolver flushif wlan0\n")
|
||||
|
||||
//Utils.runRootCommand("settings put global airplane_mode_on 1\n"
|
||||
// + "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true\n"
|
||||
// + "settings put global airplane_mode_on 0\n"
|
||||
// + "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false\n")
|
||||
}
|
||||
|
||||
private def toggleBelowApiLevel17(context: Context) {
|
||||
|
||||
Reference in New Issue
Block a user