Replace NEKit dependency with reduced subset

This commit is contained in:
relikd
2020-03-24 21:12:58 +01:00
parent 2473e77519
commit cbec3981bb
103 changed files with 24996 additions and 264 deletions

View File

@@ -0,0 +1,76 @@
//import Foundation
//
//enum ChangeType {
// case Address, Port
//}
//
//public class IPMutablePacket {
// // Support only IPv4 for now
//
// let version: IPVersion
// let proto: TransportType
// let IPHeaderLength: Int
// var sourceAddress: IPv4Address {
// get {
// return IPv4Address(fromBytesInNetworkOrder: payload.bytes.advancedBy(12))
// }
// set {
// setIPv4Address(sourceAddress, newAddress: newValue, at: 12)
// }
// }
// var destinationAddress: IPv4Address {
// get {
// return IPv4Address(fromBytesInNetworkOrder: payload.bytes.advancedBy(16))
// }
// set {
// setIPv4Address(destinationAddress, newAddress: newValue, at: 16)
// }
// }
//
// let payload: NSMutableData
//
// public init(payload: NSData) {
// let vl = UnsafePointer<UInt8>(payload.bytes).memory
// version = IPVersion(rawValue: vl >> 4)!
// IPHeaderLength = Int(vl & 0x0F) * 4
// let p = UnsafePointer<UInt8>(payload.bytes.advancedBy(9)).memory
// proto = TransportType(rawValue: p)!
// self.payload = NSMutableData(data: payload)
// }
//
// func updateChecksum(oldValue: UInt16, newValue: UInt16, type: ChangeType) {
// if type == .Address {
// updateChecksum(oldValue, newValue: newValue, at: 10)
// }
// }
//
// // swiftlint:disable:next variable_name
// internal func updateChecksum(oldValue: UInt16, newValue: UInt16, at: Int) {
// let oldChecksum = UnsafePointer<UInt16>(payload.bytes.advancedBy(at)).memory
// let oc32 = UInt32(~oldChecksum)
// let ov32 = UInt32(~oldValue)
// let nv32 = UInt32(newValue)
// var newChecksum32 = oc32 &+ ov32 &+ nv32
// newChecksum32 = (newChecksum32 & 0xFFFF) + (newChecksum32 >> 16)
// newChecksum32 = (newChecksum32 & 0xFFFF) &+ (newChecksum32 >> 16)
// var newChecksum = ~UInt16(newChecksum32)
// payload.replaceBytesInRange(NSRange(location: at, length: 2), withBytes: &newChecksum, length: 2)
// }
//
// // swiftlint:disable:next variable_name
// private func foldChecksum(checksum: UInt32) -> UInt32 {
// var checksum = checksum
// while checksum > 0xFFFF {
// checksum = (checksum & 0xFFFF) + (checksum >> 16)
// }
// return checksum
// }
//
// // swiftlint:disable:next variable_name
// private func setIPv4Address(oldAddress: IPv4Address, newAddress: IPv4Address, at: Int) {
// payload.replaceBytesInRange(NSRange(location: at, length: 4), withBytes: newAddress.bytesInNetworkOrder, length: 4)
// updateChecksum(UnsafePointer<UInt16>(oldAddress.bytesInNetworkOrder).memory, newValue: UnsafePointer<UInt16>(newAddress.bytesInNetworkOrder).memory, type: .Address)
// updateChecksum(UnsafePointer<UInt16>(oldAddress.bytesInNetworkOrder).advancedBy(1).memory, newValue: UnsafePointer<UInt16>(newAddress.bytesInNetworkOrder).advancedBy(1).memory, type: .Address)
// }
//
//}

View File

@@ -0,0 +1,330 @@
import Foundation
public enum IPVersion: UInt8 {
case iPv4 = 4, iPv6 = 6
}
public enum TransportProtocol: UInt8 {
case icmp = 1, tcp = 6, udp = 17
}
/// The class to process and build IP packet.
///
/// - note: Only IPv4 is supported as of now.
open class IPPacket {
/**
Get the version of the IP Packet without parsing the whole packet.
- parameter data: The data containing the whole IP packet.
- returns: The version of the packet. Returns `nil` if failed to parse the packet.
*/
public static func peekIPVersion(_ data: Data) -> IPVersion? {
guard data.count >= 20 else {
return nil
}
let version = (data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count).pointee >> 4
return IPVersion(rawValue: version)
}
/**
Get the protocol of the IP Packet without parsing the whole packet.
- parameter data: The data containing the whole IP packet.
- returns: The protocol of the packet. Returns `nil` if failed to parse the packet.
*/
public static func peekProtocol(_ data: Data) -> TransportProtocol? {
guard data.count >= 20 else {
return nil
}
return TransportProtocol(rawValue: (data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count).advanced(by: 9).pointee)
}
/**
Get the source IP address of the IP packet without parsing the whole packet.
- parameter data: The data containing the whole IP packet.
- returns: The source IP address of the packet. Returns `nil` if failed to parse the packet.
*/
public static func peekSourceAddress(_ data: Data) -> IPAddress? {
guard data.count >= 20 else {
return nil
}
return IPAddress(fromBytesInNetworkOrder: (data as NSData).bytes.advanced(by: 12))
}
/**
Get the destination IP address of the IP packet without parsing the whole packet.
- parameter data: The data containing the whole IP packet.
- returns: The destination IP address of the packet. Returns `nil` if failed to parse the packet.
*/
public static func peekDestinationAddress(_ data: Data) -> IPAddress? {
guard data.count >= 20 else {
return nil
}
return IPAddress(fromBytesInNetworkOrder: (data as NSData).bytes.advanced(by: 16))
}
/**
Get the source port of the IP packet without parsing the whole packet.
- parameter data: The data containing the whole IP packet.
- returns: The source IP address of the packet. Returns `nil` if failed to parse the packet.
- note: Only TCP and UDP packet has port field.
*/
public static func peekSourcePort(_ data: Data) -> Port? {
guard let proto = peekProtocol(data) else {
return nil
}
guard proto == .tcp || proto == .udp else {
return nil
}
let headerLength = Int((data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count).pointee & 0x0F * 4)
// Make sure there are bytes for source and destination bytes.
guard data.count > headerLength + 4 else {
return nil
}
return Port(bytesInNetworkOrder: (data as NSData).bytes.advanced(by: headerLength))
}
/**
Get the destination port of the IP packet without parsing the whole packet.
- parameter data: The data containing the whole IP packet.
- returns: The destination IP address of the packet. Returns `nil` if failed to parse the packet.
- note: Only TCP and UDP packet has port field.
*/
public static func peekDestinationPort(_ data: Data) -> Port? {
guard let proto = peekProtocol(data) else {
return nil
}
guard proto == .tcp || proto == .udp else {
return nil
}
let headerLength = Int((data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count).pointee & 0x0F * 4)
// Make sure there are bytes for source and destination bytes.
guard data.count > headerLength + 4 else {
return nil
}
return Port(bytesInNetworkOrder: (data as NSData).bytes.advanced(by: headerLength + 2))
}
/// The version of the current IP packet.
open var version: IPVersion = .iPv4
/// The length of the IP packet header.
open var headerLength: UInt8 = 20
/// This contains the DSCP and ECN of the IP packet.
///
/// - note: Since we can not send custom IP packet out with NetworkExtension, this is useless and simply ignored.
open var tos: UInt8 = 0
/// This should be the length of the datagram.
/// This value is not read from header since NEPacketTunnelFlow has already taken care of it for us.
open var totalLength: UInt16 {
return UInt16(packetData.count)
}
/// Identification of the current packet.
///
/// - note: Since we do not support fragment, this is ignored and always will be zero.
/// - note: Theoratically, this should be a sequentially increasing number. It probably will be implemented.
var identification: UInt16 = 0
/// Offset of the current packet.
///
/// - note: Since we do not support fragment, this is ignored and always will be zero.
var offset: UInt16 = 0
/// TTL of the packet.
var TTL: UInt8 = 64
/// Source IP address.
var sourceAddress: IPAddress!
/// Destination IP address.
var destinationAddress: IPAddress!
/// Transport protocol of the packet.
var transportProtocol: TransportProtocol!
/// Parser to parse the payload in IP packet.
var protocolParser: TransportProtocolParserProtocol!
/// The data representing the packet.
var packetData: Data!
/**
Initailize a new instance to build IP packet.
*/
init() {}
/**
Initailize an `IPPacket` with data.
- parameter packetData: The data containing a whole packet.
*/
init?(packetData: Data) {
// no need to validate the packet.
self.packetData = packetData
let scanner = BinaryDataScanner(data: packetData, littleEndian: false)
let vhl = scanner.readByte()!
guard let v = IPVersion(rawValue: vhl >> 4) else {
DDLogError("Got unknown ip packet version \(vhl >> 4)")
return nil
}
version = v
headerLength = vhl & 0x0F * 4
guard packetData.count >= Int(headerLength) else {
return nil
}
tos = scanner.readByte()!
guard totalLength == scanner.read16()! else {
DDLogError("Packet length mismatches from header.")
return nil
}
identification = scanner.read16()!
offset = scanner.read16()!
TTL = scanner.readByte()!
guard let proto = TransportProtocol(rawValue: scanner.readByte()!) else {
DDLogWarn("Get unsupported packet protocol.")
return nil
}
transportProtocol = proto
// ignore checksum
_ = scanner.read16()!
switch version {
case .iPv4:
sourceAddress = IPAddress(ipv4InNetworkOrder: CFSwapInt32(scanner.read32()!))
destinationAddress = IPAddress(ipv4InNetworkOrder: CFSwapInt32(scanner.read32()!))
default:
// IPv6 is not supported yet.
DDLogWarn("IPv6 is not supported yet.")
return nil
}
switch transportProtocol! {
case .udp:
guard let parser = UDPProtocolParser(packetData: packetData, offset: Int(headerLength)) else {
return nil
}
self.protocolParser = parser
default:
DDLogError("Can not parse packet header of type \(String(describing: transportProtocol)) yet")
return nil
}
}
func computePseudoHeaderChecksum() -> UInt32 {
var result: UInt32 = 0
if let address = sourceAddress {
result += address.UInt32InNetworkOrder! >> 16 + address.UInt32InNetworkOrder! & 0xFFFF
}
if let address = destinationAddress {
result += address.UInt32InNetworkOrder! >> 16 + address.UInt32InNetworkOrder! & 0xFFFF
}
result += UInt32(transportProtocol.rawValue) << 8
result += CFSwapInt32(UInt32(protocolParser.bytesLength))
return result
}
func buildPacket() {
packetData = NSMutableData(length: Int(headerLength) + protocolParser.bytesLength) as Data?
// set header
setPayloadWithUInt8(headerLength / 4 + version.rawValue << 4, at: 0)
setPayloadWithUInt8(tos, at: 1)
setPayloadWithUInt16(totalLength, at: 2)
setPayloadWithUInt16(identification, at: 4)
setPayloadWithUInt16(offset, at: 6)
setPayloadWithUInt8(TTL, at: 8)
setPayloadWithUInt8(transportProtocol.rawValue, at: 9)
// clear checksum bytes
resetPayloadAt(10, length: 2)
setPayloadWithUInt32(sourceAddress.UInt32InNetworkOrder!, at: 12, swap: false)
setPayloadWithUInt32(destinationAddress.UInt32InNetworkOrder!, at: 16, swap: false)
// let TCP or UDP packet build
protocolParser.packetData = packetData
protocolParser.offset = Int(headerLength)
protocolParser.buildSegment(computePseudoHeaderChecksum())
packetData = protocolParser.packetData
setPayloadWithUInt16(Checksum.computeChecksum(packetData, from: 0, to: Int(headerLength)), at: 10, swap: false)
}
func setPayloadWithUInt8(_ value: UInt8, at: Int) {
var v = value
withUnsafeBytes(of: &v) {
packetData.replaceSubrange(at..<at+1, with: $0)
}
}
func setPayloadWithUInt16(_ value: UInt16, at: Int, swap: Bool = true) {
var v: UInt16
if swap {
v = CFSwapInt16HostToBig(value)
} else {
v = value
}
withUnsafeBytes(of: &v) {
packetData.replaceSubrange(at..<at+2, with: $0)
}
}
func setPayloadWithUInt32(_ value: UInt32, at: Int, swap: Bool = true) {
var v: UInt32
if swap {
v = CFSwapInt32HostToBig(value)
} else {
v = value
}
withUnsafeBytes(of: &v) {
packetData.replaceSubrange(at..<at+4, with: $0)
}
}
func setPayloadWithData(_ data: Data, at: Int, length: Int? = nil, from: Int = 0) {
var length = length
if length == nil {
length = data.count - from
}
packetData.replaceSubrange(at..<at+length!, with: data)
}
func resetPayloadAt(_ at: Int, length: Int) {
packetData.resetBytes(in: at..<at+length)
}
}

View File

@@ -0,0 +1,72 @@
import Foundation
protocol TransportProtocolParserProtocol {
var packetData: Data! { get set }
var offset: Int { get set }
var bytesLength: Int { get }
var payload: Data! { get set }
func buildSegment(_ pseudoHeaderChecksum: UInt32)
}
/// Parser to process UDP packet and build packet.
class UDPProtocolParser: TransportProtocolParserProtocol {
/// The source port.
var sourcePort: Port!
/// The destination port.
var destinationPort: Port!
/// The data containing the UDP segment.
var packetData: Data!
/// The offset of the UDP segment in the `packetData`.
var offset: Int = 0
/// The payload to be encapsulated.
var payload: Data!
/// The length of the UDP segment.
var bytesLength: Int {
return payload.count + 8
}
init() {}
init?(packetData: Data, offset: Int) {
guard packetData.count >= offset + 8 else {
return nil
}
self.packetData = packetData
self.offset = offset
sourcePort = Port(bytesInNetworkOrder: (packetData as NSData).bytes.advanced(by: offset))
destinationPort = Port(bytesInNetworkOrder: (packetData as NSData).bytes.advanced(by: offset + 2))
payload = packetData.subdata(in: offset+8..<packetData.count)
}
func buildSegment(_ pseudoHeaderChecksum: UInt32) {
sourcePort.withUnsafeBufferPointer {
self.packetData.replaceSubrange(offset..<offset+2, with: $0)
}
destinationPort.withUnsafeBufferPointer {
self.packetData.replaceSubrange(offset+2..<offset+4, with: $0)
}
var length = NSSwapHostShortToBig(UInt16(bytesLength))
withUnsafeBytes(of: &length) {
packetData.replaceSubrange(offset+4..<offset+6, with: $0)
}
packetData.replaceSubrange(offset+8..<offset+8+payload.count, with: payload)
packetData.resetBytes(in: offset+6..<offset+8)
var checksum = Checksum.computeChecksum(packetData, from: offset, to: nil, withPseudoHeaderChecksum: pseudoHeaderChecksum)
withUnsafeBytes(of: &checksum) {
packetData.replaceSubrange(offset+6..<offset+8, with: $0)
}
}
}

View File

@@ -0,0 +1,32 @@
//import Foundation
//
//class TCPMutablePacket: IPMutablePacket {
// var sourcePort: Port {
// get {
// return Port(bytesInNetworkOrder: payload.bytes.advancedBy(IPHeaderLength))
// }
// set {
// setPort(sourcePort, newPort: newValue, at: 0)
// }
// }
//
// var destinationPort: Port {
// get {
// return Port(bytesInNetworkOrder: payload.bytes.advancedBy(IPHeaderLength + 2))
// }
// set {
// setPort(destinationPort, newPort: newValue, at: 2)
// }
// }
//
// override func updateChecksum(oldValue: UInt16, newValue: UInt16, type: ChangeType) {
// super.updateChecksum(oldValue, newValue: newValue, type: type)
// updateChecksum(oldValue, newValue: newValue, at: IPHeaderLength + 16)
// }
//
// // swiftlint:disable:next variable_name
// private func setPort(oldPort: Port, newPort: Port, at: Int) {
// payload.replaceBytesInRange(NSRange(location: at + IPHeaderLength, length: 2), withBytes: newPort.bytesInNetworkOrder, length: 2)
// updateChecksum(oldPort.valueInNetworkOrder, newValue: newPort.valueInNetworkOrder, type: .Port)
// }
//}