Replace NEKit dependency with reduced subset
This commit is contained in:
18
GlassVPN/zhuhaow-NEKit/IPStack/DNS/DNSEnums.swift
Executable file
18
GlassVPN/zhuhaow-NEKit/IPStack/DNS/DNSEnums.swift
Executable file
@@ -0,0 +1,18 @@
|
||||
import Foundation
|
||||
|
||||
public enum DNSType: UInt16 {
|
||||
// swiftlint:disable:next type_name
|
||||
case invalid = 0, a, ns, md, mf, cname, soa, mb, mg, mr, null, wks, ptr, hinfo, minfo, mx, txt, rp, afsdb, x25, isdn, rt, nsap, nsapptr, sig, key, px, gpos, aaaa, loc, nxt, eid, nimloc, srv, atma, naptr, kx, cert, a6, dname, sink, opt, apl, ds, sshfp, rrsig = 46, nsec, dnskey, tkey = 249, tsig, ixfr, axfr, mailb, maila, any
|
||||
}
|
||||
|
||||
public enum DNSMessageType: UInt8 {
|
||||
case query, response
|
||||
}
|
||||
|
||||
public enum DNSReturnStatus: UInt8 {
|
||||
case success = 0, formatError, serverFailure, nameError, notImplemented, refused
|
||||
}
|
||||
|
||||
public enum DNSClass: UInt16 {
|
||||
case internet = 1
|
||||
}
|
||||
389
GlassVPN/zhuhaow-NEKit/IPStack/DNS/DNSMessage.swift
Executable file
389
GlassVPN/zhuhaow-NEKit/IPStack/DNS/DNSMessage.swift
Executable file
@@ -0,0 +1,389 @@
|
||||
import Foundation
|
||||
|
||||
open class DNSMessage {
|
||||
// var sourceAddress: IPv4Address?
|
||||
// var sourcePort: Port?
|
||||
// var destinationAddress: IPv4Address?
|
||||
// var destinationPort: Port?
|
||||
open var transactionID: UInt16 = 0
|
||||
open var messageType: DNSMessageType = .query
|
||||
open var authoritative: Bool = false
|
||||
open var truncation: Bool = false
|
||||
open var recursionDesired: Bool = false
|
||||
open var recursionAvailable: Bool = false
|
||||
open var status: DNSReturnStatus = .success
|
||||
open var queries: [DNSQuery] = []
|
||||
open var answers: [DNSResource] = []
|
||||
open var nameservers: [DNSResource] = []
|
||||
open var addtionals: [DNSResource] = []
|
||||
|
||||
var payload: Data!
|
||||
|
||||
var bytesLength: Int {
|
||||
var len = 12 + queries.reduce(0) {
|
||||
$0 + $1.bytesLength
|
||||
}
|
||||
len += answers.reduce(0) {
|
||||
$0 + $1.bytesLength
|
||||
}
|
||||
len += nameservers.reduce(0) {
|
||||
$0 + $1.bytesLength
|
||||
}
|
||||
len += addtionals.reduce(0) {
|
||||
$0 + $1.bytesLength
|
||||
}
|
||||
return len
|
||||
}
|
||||
|
||||
var resolvedIPv4Address: IPAddress? {
|
||||
for answer in answers {
|
||||
if let address = answer.ipv4Address {
|
||||
return address
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var type: DNSType? {
|
||||
return queries.first?.type
|
||||
}
|
||||
|
||||
init() {}
|
||||
|
||||
init?(payload: Data) {
|
||||
self.payload = payload
|
||||
let scanner = BinaryDataScanner(data: payload, littleEndian: false)
|
||||
|
||||
transactionID = scanner.read16()!
|
||||
|
||||
var bytes = scanner.readByte()!
|
||||
if bytes & 0x80 > 0 {
|
||||
messageType = .response
|
||||
} else {
|
||||
messageType = .query
|
||||
}
|
||||
|
||||
// ignore OP code
|
||||
|
||||
authoritative = bytes & 0x04 > 0
|
||||
truncation = bytes & 0x02 > 0
|
||||
recursionDesired = bytes & 0x01 > 0
|
||||
|
||||
bytes = scanner.readByte()!
|
||||
recursionAvailable = bytes & 0x80 > 0
|
||||
if let status = DNSReturnStatus(rawValue: bytes & 0x0F) {
|
||||
self.status = status
|
||||
} else {
|
||||
DDLogError("Received DNS response with unknown status: \(bytes & 0x0F).")
|
||||
self.status = .serverFailure
|
||||
}
|
||||
|
||||
let queryCount = scanner.read16()!
|
||||
let answerCount = scanner.read16()!
|
||||
let nameserverCount = scanner.read16()!
|
||||
let addtionalCount = scanner.read16()!
|
||||
|
||||
for _ in 0..<queryCount {
|
||||
queries.append(DNSQuery(payload: payload, offset: scanner.position, base: 0)!)
|
||||
scanner.advance(by: queries.last!.bytesLength)
|
||||
}
|
||||
|
||||
for _ in 0..<answerCount {
|
||||
answers.append(DNSResource(payload: payload, offset: scanner.position, base: 0)!)
|
||||
scanner.advance(by: answers.last!.bytesLength)
|
||||
}
|
||||
|
||||
for _ in 0..<nameserverCount {
|
||||
nameservers.append(DNSResource(payload: payload, offset: scanner.position, base: 0)!)
|
||||
scanner.advance(by: nameservers.last!.bytesLength)
|
||||
}
|
||||
|
||||
for _ in 0..<addtionalCount {
|
||||
addtionals.append(DNSResource(payload: payload, offset: scanner.position, base: 0)!)
|
||||
scanner.advance(by: addtionals.last!.bytesLength)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func buildMessage() -> Bool {
|
||||
payload = Data(count: bytesLength)
|
||||
if transactionID == 0 {
|
||||
transactionID = UInt16(arc4random_uniform(UInt32(UInt16.max)))
|
||||
}
|
||||
setPayloadWithUInt16(transactionID, at: 0, swap: true)
|
||||
var byte: UInt8 = 0
|
||||
byte += messageType.rawValue << 7
|
||||
if authoritative {
|
||||
byte += 4
|
||||
}
|
||||
if truncation {
|
||||
byte += 2
|
||||
}
|
||||
if recursionDesired {
|
||||
byte += 1
|
||||
}
|
||||
setPayloadWithUInt8(byte, at: 2)
|
||||
byte = 0
|
||||
if recursionAvailable {
|
||||
byte += 128
|
||||
}
|
||||
|
||||
byte += status.rawValue
|
||||
|
||||
setPayloadWithUInt8(byte, at: 3)
|
||||
setPayloadWithUInt16(UInt16(queries.count), at: 4, swap: true)
|
||||
setPayloadWithUInt16(UInt16(answers.count), at: 6, swap: true)
|
||||
setPayloadWithUInt16(UInt16(nameservers.count), at: 8, swap: true)
|
||||
setPayloadWithUInt16(UInt16(addtionals.count), at: 10, swap: true)
|
||||
|
||||
return writeAllRecordAt(12)
|
||||
}
|
||||
|
||||
// swiftlint:disable variable_name
|
||||
func setPayloadWithUInt8(_ value: UInt8, at: Int) {
|
||||
var v = value
|
||||
withUnsafeBytes(of: &v) {
|
||||
payload.replaceSubrange(at..<at+1, with: $0)
|
||||
}
|
||||
}
|
||||
|
||||
func setPayloadWithUInt16(_ value: UInt16, at: Int, swap: Bool = false) {
|
||||
var v: UInt16
|
||||
if swap {
|
||||
v = NSSwapHostShortToBig(value)
|
||||
} else {
|
||||
v = value
|
||||
}
|
||||
withUnsafeBytes(of: &v) {
|
||||
payload.replaceSubrange(at..<at+2, with: $0)
|
||||
}
|
||||
}
|
||||
|
||||
func setPayloadWithUInt32(_ value: UInt32, at: Int, swap: Bool = false) {
|
||||
var v: UInt32
|
||||
if swap {
|
||||
v = NSSwapHostIntToBig(value)
|
||||
} else {
|
||||
v = value
|
||||
}
|
||||
withUnsafeBytes(of: &v) {
|
||||
payload.replaceSubrange(at..<at+4, with: $0)
|
||||
}
|
||||
}
|
||||
|
||||
func setPayloadWithData(_ data: Data, at: Int, length: Int? = nil, from: Int = 0) {
|
||||
let length = length ?? data.count - from
|
||||
|
||||
payload.withUnsafeMutableBytes { ptr in
|
||||
data.copyBytes(to: ptr.baseAddress!.advanced(by: at).assumingMemoryBound(to: UInt8.self), from: from..<from+length)
|
||||
}
|
||||
}
|
||||
|
||||
func resetPayloadAt(_ at: Int, length: Int) {
|
||||
payload.resetBytes(in: at..<at+length)
|
||||
}
|
||||
|
||||
fileprivate func writeAllRecordAt(_ at: Int) -> Bool {
|
||||
var position = at
|
||||
for query in queries {
|
||||
guard writeDNSQuery(query, at: position) else {
|
||||
return false
|
||||
}
|
||||
position += query.bytesLength
|
||||
}
|
||||
for resources in [answers, nameservers, addtionals] {
|
||||
for resource in resources {
|
||||
guard writeDNSResource(resource, at: position) else {
|
||||
return false
|
||||
}
|
||||
position += resource.bytesLength
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fileprivate func writeDNSQuery(_ query: DNSQuery, at: Int) -> Bool {
|
||||
guard DNSNameConverter.setName(query.name, toData: &payload!, at: at) else {
|
||||
return false
|
||||
}
|
||||
setPayloadWithUInt16(query.type.rawValue, at: at + query.nameBytesLength, swap: true)
|
||||
setPayloadWithUInt16(query.klass.rawValue, at: at + query.nameBytesLength + 2, swap: true)
|
||||
return true
|
||||
}
|
||||
|
||||
fileprivate func writeDNSResource(_ resource: DNSResource, at: Int) -> Bool {
|
||||
guard DNSNameConverter.setName(resource.name, toData: &payload!, at: at) else {
|
||||
return false
|
||||
}
|
||||
setPayloadWithUInt16(resource.type.rawValue, at: at + resource.nameBytesLength, swap: true)
|
||||
setPayloadWithUInt16(resource.klass.rawValue, at: at + resource.nameBytesLength + 2, swap: true)
|
||||
setPayloadWithUInt32(resource.TTL, at: at + resource.nameBytesLength + 4, swap: true)
|
||||
setPayloadWithUInt16(resource.dataLength, at: at + resource.nameBytesLength + 8, swap: true)
|
||||
setPayloadWithData(resource.data, at: at + resource.nameBytesLength + 10)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
open class DNSQuery {
|
||||
public let name: String
|
||||
public let type: DNSType
|
||||
public let klass: DNSClass
|
||||
let nameBytesLength: Int
|
||||
|
||||
init(name: String, type: DNSType = .a, klass: DNSClass = .internet) {
|
||||
self.name = name.trimmingCharacters(in: CharacterSet(charactersIn: "."))
|
||||
self.type = type
|
||||
self.klass = klass
|
||||
self.nameBytesLength = name.utf8.count + 2
|
||||
}
|
||||
|
||||
init?(payload: Data, offset: Int, base: Int = 0) {
|
||||
(self.name, self.nameBytesLength) = DNSNameConverter.getNamefromData(payload, offset: offset, base: base)
|
||||
|
||||
let scanner = BinaryDataScanner(data: payload, littleEndian: false)
|
||||
scanner.skip(to: offset + self.nameBytesLength)
|
||||
|
||||
guard let type = DNSType(rawValue: scanner.read16()!) else {
|
||||
DDLogError("Received DNS packet with unknown type.")
|
||||
return nil
|
||||
}
|
||||
self.type = type
|
||||
|
||||
guard let klass = DNSClass(rawValue: scanner.read16()!) else {
|
||||
DDLogError("Received DNS packet with unknown class.")
|
||||
return nil
|
||||
}
|
||||
self.klass = klass
|
||||
|
||||
}
|
||||
|
||||
var bytesLength: Int {
|
||||
return nameBytesLength + 4
|
||||
}
|
||||
}
|
||||
|
||||
open class DNSResource {
|
||||
public let name: String
|
||||
public let type: DNSType
|
||||
public let klass: DNSClass
|
||||
public let TTL: UInt32
|
||||
let dataLength: UInt16
|
||||
public let data: Data
|
||||
|
||||
let nameBytesLength: Int
|
||||
|
||||
init(name: String, type: DNSType = .a, klass: DNSClass = .internet, TTL: UInt32 = 300, data: Data) {
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.klass = klass
|
||||
self.TTL = TTL
|
||||
dataLength = UInt16(data.count)
|
||||
self.data = data
|
||||
self.nameBytesLength = name.utf8.count + 2
|
||||
}
|
||||
|
||||
static func ARecord(_ name: String, TTL: UInt32 = 300, address: IPAddress) -> DNSResource {
|
||||
return DNSResource(name: name, type: .a, klass: .internet, TTL: TTL, data: address.dataInNetworkOrder)
|
||||
}
|
||||
|
||||
init?(payload: Data, offset: Int, base: Int = 0) {
|
||||
(self.name, self.nameBytesLength) = DNSNameConverter.getNamefromData(payload, offset: offset, base: base)
|
||||
|
||||
let scanner = BinaryDataScanner(data: payload, littleEndian: false)
|
||||
scanner.skip(to: offset + self.nameBytesLength)
|
||||
|
||||
guard let type = DNSType(rawValue: scanner.read16()!) else {
|
||||
DDLogError("Received DNS packet with unknown type.")
|
||||
return nil
|
||||
}
|
||||
self.type = type
|
||||
|
||||
guard let klass = DNSClass(rawValue: scanner.read16()!) else {
|
||||
DDLogError("Received DNS packet with unknown class.")
|
||||
return nil
|
||||
}
|
||||
self.klass = klass
|
||||
self.TTL = scanner.read32()!
|
||||
dataLength = scanner.read16()!
|
||||
self.data = payload.subdata(in: scanner.position..<scanner.position+Int(dataLength))
|
||||
}
|
||||
|
||||
var bytesLength: Int {
|
||||
return nameBytesLength + 10 + Int(dataLength)
|
||||
}
|
||||
|
||||
var ipv4Address: IPAddress? {
|
||||
guard type == .a else {
|
||||
return nil
|
||||
}
|
||||
return IPAddress(fromBytesInNetworkOrder: (data as NSData).bytes)
|
||||
}
|
||||
}
|
||||
|
||||
class DNSNameConverter {
|
||||
static func setName(_ name: String, toData data: inout Data, at: Int) -> Bool {
|
||||
let labels = name.components(separatedBy: CharacterSet(charactersIn: "."))
|
||||
var position = at
|
||||
|
||||
for label in labels {
|
||||
let len = label.utf8.count
|
||||
guard len != 0 else {
|
||||
// invalid domain name
|
||||
return false
|
||||
}
|
||||
data[position] = UInt8(len)
|
||||
position += 1
|
||||
|
||||
data.replaceSubrange(position..<position+len, with: label.data(using: .utf8)!)
|
||||
position += len
|
||||
}
|
||||
data[position] = 0
|
||||
return true
|
||||
}
|
||||
|
||||
static func getNamefromData(_ data: Data, offset: Int, base: Int = 0) -> (String, Int) {
|
||||
let scanner = BinaryDataScanner(data: data, littleEndian: false)
|
||||
scanner.skip(to: offset)
|
||||
|
||||
var len: UInt8 = 0
|
||||
var name = ""
|
||||
var currentReadBytes = 0
|
||||
var jumped = false
|
||||
var nameBytesLength = 0
|
||||
repeat {
|
||||
let length = scanner.read16()!
|
||||
// is this a pointer?
|
||||
if length & 0xC000 == 0xC000 {
|
||||
if !jumped {
|
||||
// save the length position
|
||||
nameBytesLength = 2 + currentReadBytes
|
||||
jumped = true
|
||||
}
|
||||
scanner.skip(to: Int(length & 0x3FFF) + base)
|
||||
} else {
|
||||
scanner.advance(by: -2)
|
||||
}
|
||||
|
||||
len = scanner.readByte()!
|
||||
currentReadBytes += 1
|
||||
if len == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
currentReadBytes += Int(len)
|
||||
|
||||
guard let label = String(bytes: scanner.data.subdata(in: scanner.position..<scanner.position+Int(len)), encoding: .utf8) else {
|
||||
return ("", currentReadBytes)
|
||||
}
|
||||
// this is not efficient, but won't take much time, so maybe I'll optimize it later
|
||||
name = name.appendingFormat(".%@", label)
|
||||
scanner.advance(by: Int(len))
|
||||
} while true
|
||||
|
||||
if !jumped {
|
||||
nameBytesLength = currentReadBytes
|
||||
}
|
||||
|
||||
return (name.trimmingCharacters(in: CharacterSet(charactersIn: ".")), nameBytesLength)
|
||||
}
|
||||
}
|
||||
37
GlassVPN/zhuhaow-NEKit/IPStack/DNS/DNSResolver.swift
Executable file
37
GlassVPN/zhuhaow-NEKit/IPStack/DNS/DNSResolver.swift
Executable file
@@ -0,0 +1,37 @@
|
||||
import Foundation
|
||||
|
||||
public protocol DNSResolverProtocol: class {
|
||||
var delegate: DNSResolverDelegate? { get set }
|
||||
func resolve(session: DNSSession)
|
||||
func stop()
|
||||
}
|
||||
|
||||
public protocol DNSResolverDelegate: class {
|
||||
func didReceive(rawResponse: Data)
|
||||
}
|
||||
|
||||
open class UDPDNSResolver: DNSResolverProtocol, NWUDPSocketDelegate {
|
||||
let socket: NWUDPSocket
|
||||
public weak var delegate: DNSResolverDelegate?
|
||||
|
||||
public init(address: IPAddress, port: Port) {
|
||||
socket = NWUDPSocket(host: address.presentation, port: Int(port.value))!
|
||||
socket.delegate = self
|
||||
}
|
||||
|
||||
public func resolve(session: DNSSession) {
|
||||
socket.write(data: session.requestMessage.payload)
|
||||
}
|
||||
|
||||
public func stop() {
|
||||
socket.disconnect()
|
||||
}
|
||||
|
||||
public func didReceive(data: Data, from: NWUDPSocket) {
|
||||
delegate?.didReceive(rawResponse: data)
|
||||
}
|
||||
|
||||
public func didCancel(socket: NWUDPSocket) {
|
||||
|
||||
}
|
||||
}
|
||||
269
GlassVPN/zhuhaow-NEKit/IPStack/DNS/DNSServer.swift
Executable file
269
GlassVPN/zhuhaow-NEKit/IPStack/DNS/DNSServer.swift
Executable file
@@ -0,0 +1,269 @@
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
|
||||
/// A DNS server designed as an `IPStackProtocol` implementation which works with TUN interface.
|
||||
///
|
||||
/// This class is thread-safe.
|
||||
open class DNSServer: DNSResolverDelegate, IPStackProtocol {
|
||||
/// Current DNS server.
|
||||
///
|
||||
/// - warning: There is at most one DNS server running at the same time. If a DNS server is registered to `TUNInterface` then it must also be set here.
|
||||
public static var currentServer: DNSServer?
|
||||
|
||||
/// The address of DNS server.
|
||||
let serverAddress: IPAddress
|
||||
|
||||
/// The port of DNS server
|
||||
let serverPort: Port
|
||||
|
||||
fileprivate let queue: DispatchQueue = QueueFactory.getQueue()
|
||||
fileprivate var fakeSessions: [IPAddress: DNSSession] = [:]
|
||||
fileprivate var pendingSessions: [UInt16: DNSSession] = [:]
|
||||
fileprivate let pool: IPPool?
|
||||
fileprivate var resolvers: [DNSResolverProtocol] = []
|
||||
|
||||
open var outputFunc: (([Data], [NSNumber]) -> Void)!
|
||||
|
||||
// Only match A record as of now, all other records should be passed directly.
|
||||
fileprivate let matchedType = [DNSType.a]
|
||||
|
||||
/**
|
||||
Initailize a DNS server.
|
||||
|
||||
- parameter address: The IP address of the server.
|
||||
- parameter port: The listening port of the server.
|
||||
- parameter fakeIPPool: The pool of fake IP addresses. Set to nil if no fake IP is needed.
|
||||
*/
|
||||
public init(address: IPAddress, port: Port, fakeIPPool: IPPool? = nil) {
|
||||
serverAddress = address
|
||||
serverPort = port
|
||||
pool = fakeIPPool
|
||||
}
|
||||
|
||||
/**
|
||||
Clean up fake IP.
|
||||
|
||||
- parameter address: The fake IP address.
|
||||
- parameter delay: How long should the fake IP be valid.
|
||||
*/
|
||||
fileprivate func cleanUpFakeIP(_ address: IPAddress, after delay: Int) {
|
||||
queue.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay) * Int64(NSEC_PER_SEC)) / Double(NSEC_PER_SEC)) {
|
||||
[weak self] in
|
||||
_ = self?.fakeSessions.removeValue(forKey: address)
|
||||
self?.pool?.release(ip: address)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Clean up pending session.
|
||||
|
||||
- parameter session: The pending session.
|
||||
- parameter delay: How long before the pending session be cleaned up.
|
||||
*/
|
||||
fileprivate func cleanUpPendingSession(_ session: DNSSession, after delay: Int) {
|
||||
queue.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay) * Int64(NSEC_PER_SEC)) / Double(NSEC_PER_SEC)) {
|
||||
[weak self] in
|
||||
_ = self?.pendingSessions.removeValue(forKey: session.requestMessage.transactionID)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func lookup(_ session: DNSSession) {
|
||||
guard shouldMatch(session) else {
|
||||
session.matchResult = .real
|
||||
lookupRemotely(session)
|
||||
return
|
||||
}
|
||||
|
||||
RuleManager.currentManager.matchDNS(session, type: .domain)
|
||||
|
||||
switch session.matchResult! {
|
||||
case .fake:
|
||||
guard setUpFakeIP(session) else {
|
||||
// failed to set up a fake IP, return the result directly
|
||||
session.matchResult = .real
|
||||
lookupRemotely(session)
|
||||
return
|
||||
}
|
||||
outputSession(session)
|
||||
case .real, .unknown:
|
||||
lookupRemotely(session)
|
||||
default:
|
||||
DDLogError("The rule match result should never be .Pass.")
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func lookupRemotely(_ session: DNSSession) {
|
||||
pendingSessions[session.requestMessage.transactionID] = session
|
||||
cleanUpPendingSession(session, after: Opt.DNSPendingSessionLifeTime)
|
||||
sendQueryToRemote(session)
|
||||
}
|
||||
|
||||
fileprivate func sendQueryToRemote(_ session: DNSSession) {
|
||||
for resolver in resolvers {
|
||||
resolver.resolve(session: session)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Input IP packet into the DNS server.
|
||||
|
||||
- parameter packet: The IP packet.
|
||||
- parameter version: The version of the IP packet.
|
||||
|
||||
- returns: If the packet is taken in by this DNS server.
|
||||
*/
|
||||
open func input(packet: Data, version: NSNumber?) -> Bool {
|
||||
guard IPPacket.peekProtocol(packet) == .udp else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard IPPacket.peekDestinationAddress(packet) == serverAddress else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard IPPacket.peekDestinationPort(packet) == serverPort else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard let ipPacket = IPPacket(packetData: packet) else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard let session = DNSSession(packet: ipPacket) else {
|
||||
return false
|
||||
}
|
||||
|
||||
queue.async {
|
||||
self.lookup(session)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public func start() {
|
||||
|
||||
}
|
||||
|
||||
open func stop() {
|
||||
for resolver in resolvers {
|
||||
resolver.stop()
|
||||
}
|
||||
resolvers = []
|
||||
|
||||
// The blocks scheduled with `dispatch_after` are ignored since they are hard to cancel. But there should be no consequence, everything will be released except for a few `IPAddress`es and the `queue` which will be released later.
|
||||
}
|
||||
|
||||
fileprivate func outputSession(_ session: DNSSession) {
|
||||
guard let result = session.matchResult else {
|
||||
return
|
||||
}
|
||||
|
||||
let udpParser = UDPProtocolParser()
|
||||
udpParser.sourcePort = serverPort
|
||||
// swiftlint:disable:next force_cast
|
||||
udpParser.destinationPort = (session.requestIPPacket!.protocolParser as! UDPProtocolParser).sourcePort
|
||||
switch result {
|
||||
case .real:
|
||||
udpParser.payload = session.realResponseMessage!.payload
|
||||
case .fake:
|
||||
let response = DNSMessage()
|
||||
response.transactionID = session.requestMessage.transactionID
|
||||
response.messageType = .response
|
||||
response.recursionAvailable = true
|
||||
// since we only support ipv4 as of now, it must be an answer of type A
|
||||
response.answers.append(DNSResource.ARecord(session.requestMessage.queries[0].name, TTL: UInt32(Opt.DNSFakeIPTTL), address: session.fakeIP!))
|
||||
session.expireAt = Date().addingTimeInterval(Double(Opt.DNSFakeIPTTL))
|
||||
guard response.buildMessage() else {
|
||||
DDLogError("Failed to build DNS response.")
|
||||
return
|
||||
}
|
||||
|
||||
udpParser.payload = response.payload
|
||||
default:
|
||||
return
|
||||
}
|
||||
let ipPacket = IPPacket()
|
||||
ipPacket.sourceAddress = serverAddress
|
||||
ipPacket.destinationAddress = session.requestIPPacket!.sourceAddress
|
||||
ipPacket.protocolParser = udpParser
|
||||
ipPacket.transportProtocol = .udp
|
||||
ipPacket.buildPacket()
|
||||
|
||||
outputFunc([ipPacket.packetData], [NSNumber(value: AF_INET as Int32)])
|
||||
}
|
||||
|
||||
fileprivate func shouldMatch(_ session: DNSSession) -> Bool {
|
||||
return matchedType.contains(session.requestMessage.type!)
|
||||
}
|
||||
|
||||
func isFakeIP(_ ipAddress: IPAddress) -> Bool {
|
||||
return pool?.contains(ip: ipAddress) ?? false
|
||||
}
|
||||
|
||||
func lookupFakeIP(_ address: IPAddress) -> DNSSession? {
|
||||
var session: DNSSession?
|
||||
QueueFactory.executeOnQueueSynchronizedly {
|
||||
session = self.fakeSessions[address]
|
||||
}
|
||||
return session
|
||||
}
|
||||
|
||||
/**
|
||||
Add new DNS resolver to DNS server.
|
||||
|
||||
- parameter resolver: The resolver to add.
|
||||
*/
|
||||
open func registerResolver(_ resolver: DNSResolverProtocol) {
|
||||
resolver.delegate = self
|
||||
resolvers.append(resolver)
|
||||
}
|
||||
|
||||
fileprivate func setUpFakeIP(_ session: DNSSession) -> Bool {
|
||||
|
||||
guard let fakeIP = pool?.fetchIP() else {
|
||||
DDLogVerbose("Failed to get a fake IP.")
|
||||
return false
|
||||
}
|
||||
session.fakeIP = fakeIP
|
||||
fakeSessions[fakeIP] = session
|
||||
session.expireAt = Date().addingTimeInterval(TimeInterval(Opt.DNSFakeIPTTL))
|
||||
// keep the fake session for 2 TTL
|
||||
cleanUpFakeIP(fakeIP, after: Opt.DNSFakeIPTTL * 2)
|
||||
return true
|
||||
}
|
||||
|
||||
open func didReceive(rawResponse: Data) {
|
||||
guard let message = DNSMessage(payload: rawResponse) else {
|
||||
DDLogError("Failed to parse response from remote DNS server.")
|
||||
return
|
||||
}
|
||||
|
||||
queue.async {
|
||||
guard let session = self.pendingSessions.removeValue(forKey: message.transactionID) else {
|
||||
// this should not be a problem if there are multiple DNS servers or the DNS server is hijacked.
|
||||
DDLogVerbose("Do not find the corresponding DNS session for the response.")
|
||||
return
|
||||
}
|
||||
|
||||
session.realResponseMessage = message
|
||||
|
||||
session.realIP = message.resolvedIPv4Address
|
||||
|
||||
if session.matchResult != .fake && session.matchResult != .real {
|
||||
RuleManager.currentManager.matchDNS(session, type: .ip)
|
||||
}
|
||||
|
||||
switch session.matchResult! {
|
||||
case .fake:
|
||||
if !self.setUpFakeIP(session) {
|
||||
// return real response
|
||||
session.matchResult = .real
|
||||
}
|
||||
self.outputSession(session)
|
||||
case .real:
|
||||
self.outputSession(session)
|
||||
default:
|
||||
DDLogError("The rule match result should never be .Pass or .Unknown in IP mode.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
GlassVPN/zhuhaow-NEKit/IPStack/DNS/DNSSession.swift
Executable file
49
GlassVPN/zhuhaow-NEKit/IPStack/DNS/DNSSession.swift
Executable file
@@ -0,0 +1,49 @@
|
||||
import Foundation
|
||||
|
||||
open class DNSSession {
|
||||
public let requestMessage: DNSMessage
|
||||
var requestIPPacket: IPPacket?
|
||||
open var realIP: IPAddress?
|
||||
open var fakeIP: IPAddress?
|
||||
open var realResponseMessage: DNSMessage?
|
||||
var realResponseIPPacket: IPPacket?
|
||||
open var matchedRule: Rule?
|
||||
open var matchResult: DNSSessionMatchResult?
|
||||
var indexToMatch = 0
|
||||
var expireAt: Date?
|
||||
// lazy var countryCode: String? = {
|
||||
// [unowned self] in
|
||||
// guard self.realIP != nil else {
|
||||
// return nil
|
||||
// }
|
||||
// return Utils.GeoIPLookup.Lookup(self.realIP!.presentation)
|
||||
// }()
|
||||
|
||||
init?(message: DNSMessage) {
|
||||
guard message.messageType == .query else {
|
||||
DDLogError("DNSSession can only be initailized by a DNS query.")
|
||||
return nil
|
||||
}
|
||||
|
||||
guard message.queries.count == 1 else {
|
||||
DDLogError("Expecting the DNS query has exact one query entry.")
|
||||
return nil
|
||||
}
|
||||
|
||||
requestMessage = message
|
||||
}
|
||||
|
||||
convenience init?(packet: IPPacket) {
|
||||
guard let message = DNSMessage(payload: packet.protocolParser.payload) else {
|
||||
return nil
|
||||
}
|
||||
self.init(message: message)
|
||||
requestIPPacket = packet
|
||||
}
|
||||
}
|
||||
|
||||
extension DNSSession: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "<\(type(of: self)) domain: \(self.requestMessage.queries.first!.name) realIP: \(String(describing: realIP)) fakeIP: \(String(describing: fakeIP))>"
|
||||
}
|
||||
}
|
||||
34
GlassVPN/zhuhaow-NEKit/IPStack/IPStackProtocol.swift
Executable file
34
GlassVPN/zhuhaow-NEKit/IPStack/IPStackProtocol.swift
Executable file
@@ -0,0 +1,34 @@
|
||||
import Foundation
|
||||
|
||||
/// The protocol defines an IP stack.
|
||||
public protocol IPStackProtocol: class {
|
||||
/**
|
||||
Input a packet into the stack.
|
||||
|
||||
- parameter packet: The IP packet.
|
||||
- parameter version: The version of the IP packet, i.e., AF_INET, AF_INET6.
|
||||
|
||||
- returns: If the stack takes in this packet. If the packet is taken in, then it won't be processed by other IP stacks.
|
||||
*/
|
||||
func input(packet: Data, version: NSNumber?) -> Bool
|
||||
|
||||
/// This is called when this stack decided to output some IP packet. This is set automatically when the stack is registered to some interface.
|
||||
///
|
||||
/// The parameter is the safe as the `inputPacket`.
|
||||
///
|
||||
/// - note: This block is thread-safe.
|
||||
var outputFunc: (([Data], [NSNumber]) -> Void)! { get set }
|
||||
|
||||
func start()
|
||||
|
||||
/**
|
||||
Stop the stack from running.
|
||||
|
||||
This is called when the interface this stack is registered to stop to processing packets and will be released soon.
|
||||
*/
|
||||
func stop()
|
||||
}
|
||||
|
||||
extension IPStackProtocol {
|
||||
public func stop() {}
|
||||
}
|
||||
76
GlassVPN/zhuhaow-NEKit/IPStack/Packet/IPMutablePacket.swift
Executable file
76
GlassVPN/zhuhaow-NEKit/IPStack/Packet/IPMutablePacket.swift
Executable 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)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
330
GlassVPN/zhuhaow-NEKit/IPStack/Packet/IPPacket.swift
Executable file
330
GlassVPN/zhuhaow-NEKit/IPStack/Packet/IPPacket.swift
Executable 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)
|
||||
}
|
||||
|
||||
}
|
||||
72
GlassVPN/zhuhaow-NEKit/IPStack/Packet/PacketProtocolParser.swift
Executable file
72
GlassVPN/zhuhaow-NEKit/IPStack/Packet/PacketProtocolParser.swift
Executable 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
32
GlassVPN/zhuhaow-NEKit/IPStack/Packet/TCPMutablePacket.swift
Executable file
32
GlassVPN/zhuhaow-NEKit/IPStack/Packet/TCPMutablePacket.swift
Executable 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)
|
||||
// }
|
||||
//}
|
||||
Reference in New Issue
Block a user