123 lines
3.5 KiB
Swift
Executable File
123 lines
3.5 KiB
Swift
Executable File
import Foundation
|
|
|
|
public enum IPRangeError: Error {
|
|
case invalidCIDRFormat, invalidRangeFormat, invalidRange, invalidFormat, addressIncompatible, noRange, intervalInvalid, invalidMask
|
|
}
|
|
|
|
public class IPRange {
|
|
public let startIP: IPAddress
|
|
// including, so we can include 255.255.255.255 in range.
|
|
public let endIP: IPAddress
|
|
|
|
public let family: IPAddress.Family
|
|
|
|
public init(startIP: IPAddress, endIP: IPAddress) throws {
|
|
guard startIP.family == endIP.family else {
|
|
throw IPRangeError.addressIncompatible
|
|
}
|
|
|
|
guard startIP <= endIP else {
|
|
throw IPRangeError.invalidRange
|
|
}
|
|
|
|
self.startIP = startIP
|
|
self.endIP = endIP
|
|
family = startIP.family
|
|
}
|
|
|
|
public convenience init(startIP: IPAddress, interval: IPInterval) throws {
|
|
guard let endIP = startIP.advanced(by: interval) else {
|
|
throw IPRangeError.intervalInvalid
|
|
}
|
|
|
|
try self.init(startIP: startIP, endIP: endIP)
|
|
}
|
|
|
|
public convenience init(startIP: IPAddress, mask: IPMask) throws {
|
|
guard let (startIP, endIP) = mask.mask(baseIP: startIP) else {
|
|
throw IPRangeError.invalidMask
|
|
}
|
|
|
|
try self.init(startIP: startIP, endIP: endIP)
|
|
}
|
|
|
|
public func contains(ip: IPAddress) -> Bool {
|
|
guard ip.family == family else {
|
|
return false
|
|
}
|
|
|
|
return ip >= startIP && ip <= endIP
|
|
}
|
|
}
|
|
|
|
extension IPRange {
|
|
public convenience init(withCIDRString rep: String) throws {
|
|
let info = rep.components(separatedBy: "/")
|
|
guard info.count == 2 else {
|
|
throw IPRangeError.invalidCIDRFormat
|
|
}
|
|
|
|
guard let ip = IPAddress(fromString: info[0]) else {
|
|
throw IPRangeError.invalidCIDRFormat
|
|
}
|
|
|
|
var mask: IPMask
|
|
switch ip.family {
|
|
case .IPv4:
|
|
guard let m = UInt32(info[1]) else {
|
|
throw IPRangeError.invalidCIDRFormat
|
|
}
|
|
mask = IPMask.IPv4(m)
|
|
case .IPv6:
|
|
guard let m6 = try? UInt128(info[1]) else {
|
|
throw IPRangeError.invalidCIDRFormat
|
|
}
|
|
mask = IPMask.IPv6(m6)
|
|
}
|
|
|
|
try self.init(startIP: ip, mask: mask)
|
|
}
|
|
|
|
public convenience init(withRangeString rep: String) throws {
|
|
let info = rep.components(separatedBy: "+")
|
|
guard info.count == 2 else {
|
|
throw IPRangeError.invalidRangeFormat
|
|
}
|
|
|
|
guard let startIP = IPAddress(fromString: info[0]) else {
|
|
throw IPRangeError.invalidRangeFormat
|
|
}
|
|
|
|
var interval: IPInterval
|
|
switch startIP.family {
|
|
case .IPv4:
|
|
guard let m = UInt32(info[1]) else {
|
|
throw IPRangeError.invalidRangeFormat
|
|
}
|
|
interval = IPInterval.IPv4(m)
|
|
case .IPv6:
|
|
guard let m6 = try? UInt128(info[1]) else {
|
|
throw IPRangeError.invalidRangeFormat
|
|
}
|
|
interval = IPInterval.IPv6(m6)
|
|
}
|
|
|
|
try self.init(startIP: startIP, interval: interval)
|
|
}
|
|
|
|
public convenience init(withString rep: String) throws {
|
|
if rep.contains("/") {
|
|
try self.init(withCIDRString: rep)
|
|
} else if rep.contains("+") {
|
|
try self.init(withRangeString: rep)
|
|
} else {
|
|
guard let ip = IPAddress(fromString: rep) else {
|
|
throw IPRangeError.invalidFormat
|
|
}
|
|
|
|
try self.init(startIP: ip, endIP: ip)
|
|
}
|
|
}
|
|
|
|
}
|