zhuhaow/NEKit

View on GitHub
src/Utils/IPRange.swift

Summary

Maintainability
B
6 hrs
Test Coverage
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)
        }
    }

}