diff --git a/.jazzy.yaml b/.jazzy.yaml index 2b9e783..3995fec 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -17,7 +17,6 @@ custom_categories: - IOInterface - LinkInterface - TunnelInterface - - PacketStream - BidirectionalState - StaticKey - SessionProxy diff --git a/TunnelKit.xcodeproj/project.pbxproj b/TunnelKit.xcodeproj/project.pbxproj index 44cd85f..1acd58e 100644 --- a/TunnelKit.xcodeproj/project.pbxproj +++ b/TunnelKit.xcodeproj/project.pbxproj @@ -67,6 +67,10 @@ 0E3B65772249254000EFF4DA /* tunnelbear.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E3B65712249247E00EFF4DA /* tunnelbear.key */; }; 0E3E0F212108A8CC00B371C1 /* SessionProxy+SessionReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+SessionReply.swift */; }; 0E3E0F222108A8CC00B371C1 /* SessionProxy+SessionReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+SessionReply.swift */; }; + 0E48AC642271ADA9009B1A98 /* PacketStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E48AC622271ADA8009B1A98 /* PacketStream.h */; }; + 0E48AC652271ADA9009B1A98 /* PacketStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E48AC622271ADA8009B1A98 /* PacketStream.h */; }; + 0E48AC662271ADA9009B1A98 /* PacketStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E48AC632271ADA9009B1A98 /* PacketStream.m */; }; + 0E48AC672271ADA9009B1A98 /* PacketStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E48AC632271ADA9009B1A98 /* PacketStream.m */; }; 0E500EA622493B5B00CAE560 /* tunnelbear.enc.1.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E500EA522493B5B00CAE560 /* tunnelbear.enc.1.key */; }; 0E500EA722493B5B00CAE560 /* tunnelbear.enc.1.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E500EA522493B5B00CAE560 /* tunnelbear.enc.1.key */; }; 0E50D57521634E0A00FC87A8 /* ControlChannelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */; }; @@ -307,6 +311,8 @@ 0E3B656E224923EC00EFF4DA /* tunnelbear.enc.1.ovpn */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.enc.1.ovpn; sourceTree = ""; }; 0E3B65712249247E00EFF4DA /* tunnelbear.key */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.key; sourceTree = ""; }; 0E3E0F202108A8CC00B371C1 /* SessionProxy+SessionReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+SessionReply.swift"; sourceTree = ""; }; + 0E48AC622271ADA8009B1A98 /* PacketStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PacketStream.h; sourceTree = ""; }; + 0E48AC632271ADA9009B1A98 /* PacketStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PacketStream.m; sourceTree = ""; }; 0E500EA522493B5B00CAE560 /* tunnelbear.enc.1.key */ = {isa = PBXFileReference; lastKnownFileType = text; path = tunnelbear.enc.1.key; sourceTree = ""; }; 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlChannelTests.swift; sourceTree = ""; }; 0E58BF2F22405410006FB157 /* lzoconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzoconf.h; sourceTree = ""; }; @@ -653,6 +659,8 @@ 0EFEB43E2006D3C800F81029 /* Packet.swift */, 0EE7A79420F61EDC00B42E6A /* PacketMacros.h */, 0EE7A79720F6296F00B42E6A /* PacketMacros.m */, + 0E48AC622271ADA8009B1A98 /* PacketStream.h */, + 0E48AC632271ADA9009B1A98 /* PacketStream.m */, 0EFEB4382006D3C800F81029 /* ProtocolMacros.swift */, 0EFEB4392006D3C800F81029 /* ReplayProtector.h */, 0EFEB4482006D3C800F81029 /* ReplayProtector.m */, @@ -736,6 +744,7 @@ 0EF5CF262141E142004FF1BD /* PacketMacros.h in Headers */, 0EFEB4642006D3C800F81029 /* ReplayProtector.h in Headers */, 0EFEB4612006D3C800F81029 /* Errors.h in Headers */, + 0E48AC642271ADA9009B1A98 /* PacketStream.h in Headers */, 0E58BF3722405410006FB157 /* minilzo.h in Headers */, 0E07596E20EF79B400F38FD8 /* CryptoCBC.h in Headers */, 0E58BF3522405410006FB157 /* lzodefs.h in Headers */, @@ -764,6 +773,7 @@ 0EF5CF272141E15B004FF1BD /* PacketMacros.h in Headers */, 0EEC49E620B5F7F6008FEB91 /* MSS.h in Headers */, 0EEC49E520B5F7F6008FEB91 /* Errors.h in Headers */, + 0E48AC652271ADA9009B1A98 /* PacketStream.h in Headers */, 0E58BF3822405410006FB157 /* minilzo.h in Headers */, 0E07596F20EF79B400F38FD8 /* CryptoCBC.h in Headers */, 0E58BF3622405410006FB157 /* lzodefs.h in Headers */, @@ -1219,6 +1229,7 @@ 0EFEB4662006D3C800F81029 /* ZeroingData.swift in Sources */, 0EBBF2F3208505D300E36B40 /* NEUDPInterface.swift in Sources */, 0EFEB4682006D3C800F81029 /* MSS.m in Sources */, + 0E48AC662271ADA9009B1A98 /* PacketStream.m in Sources */, 0EFEB45B2006D3C800F81029 /* TLSBox.m in Sources */, 0EFEB4792006D3C800F81029 /* TunnelKitProvider+Interaction.swift in Sources */, 0E58BF3922405410006FB157 /* minilzo.c in Sources */, @@ -1287,6 +1298,7 @@ 0E07598120F0060E00F38FD8 /* CryptoAEAD.m in Sources */, 0EFEB49C2006D7F300F81029 /* Data+Manipulation.swift in Sources */, 0EBBF2F4208505D400E36B40 /* NEUDPInterface.swift in Sources */, + 0E48AC672271ADA9009B1A98 /* PacketStream.m in Sources */, 0EFEB4902006D7F300F81029 /* TunnelInterface.swift in Sources */, 0EFEB49E2006D7F300F81029 /* Allocation.m in Sources */, 0E58BF3A22405410006FB157 /* minilzo.c in Sources */, diff --git a/TunnelKit/Sources/AppExtension/Transport/NETCPInterface.swift b/TunnelKit/Sources/AppExtension/Transport/NETCPInterface.swift index 1543793..9843821 100644 --- a/TunnelKit/Sources/AppExtension/Transport/NETCPInterface.swift +++ b/TunnelKit/Sources/AppExtension/Transport/NETCPInterface.swift @@ -38,6 +38,7 @@ import Foundation import NetworkExtension import SwiftyBeaver +import __TunnelKitNative private let log = SwiftyBeaver.self @@ -223,7 +224,8 @@ class NETCPLink: LinkInterface { var newBuffer = buffer newBuffer.append(contentsOf: data) - let (until, packets) = PacketStream.packets(from: newBuffer) + var until = 0 + let packets = PacketStream.packets(fromStream: newBuffer, until: &until) newBuffer = newBuffer.subdata(in: until.. Void)?) { - let stream = PacketStream.stream(from: packet) + let stream = PacketStream.stream(fromPacket: packet) impl.write(stream) { (error) in completionHandler?(error) } } func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) { - let stream = PacketStream.stream(from: packets) + let stream = PacketStream.stream(fromPackets: packets) impl.write(stream) { (error) in completionHandler?(error) } diff --git a/TunnelKit/Sources/Core/Packet.swift b/TunnelKit/Sources/Core/Packet.swift index 19cd4d2..bb4504c 100644 --- a/TunnelKit/Sources/Core/Packet.swift +++ b/TunnelKit/Sources/Core/Packet.swift @@ -40,65 +40,65 @@ import __TunnelKitNative // TODO: convert to C for efficiency -/// Reads and writes packets as a stream. Useful for stream-oriented links (e.g TCP/IP). -public class PacketStream { - - /** - Parses packets from a stream. - - - Parameter stream: The data stream. - - Returns: A pair where the first value is the `Int` offset up to which - the stream could be parsed, and the second value is an array containing - the parsed packets up to such offset. - */ - public static func packets(from stream: Data) -> (Int, [Data]) { - var ni = 0 - var parsed: [Data] = [] - while (ni + 2 <= stream.count) { - let packlen = Int(stream.networkUInt16Value(from: ni)) - let start = ni + 2 - let end = start + packlen - guard (end <= stream.count) else { - break - } - let packet = stream.subdata(offset: start, count: end - start) - parsed.append(packet) - ni = end - } - return (ni, parsed) - } - - /** - Creates a contiguous stream of packets. - - - Parameter packet: The packet. - - Returns: A stream made of the packet. - */ - public static func stream(from packet: Data) -> Data { - var raw = Data(capacity: 2 + packet.count) - raw.append(UInt16(packet.count).bigEndian) - raw.append(contentsOf: packet) - return raw - } - - /** - Creates a contiguous stream of packets. - - - Parameter packets: The array of packets. - - Returns: A stream made of the array of packets. - */ - public static func stream(from packets: [Data]) -> Data { - var raw = Data() - for payload in packets { - raw.append(UInt16(payload.count).bigEndian) - raw.append(payload) - } - return raw - } - - private init() { - } -} +///// Reads and writes packets as a stream. Useful for stream-oriented links (e.g TCP/IP). +//public class PacketStream { +// +// /** +// Parses packets from a stream. +// +// - Parameter stream: The data stream. +// - Returns: A pair where the first value is the `Int` offset up to which +// the stream could be parsed, and the second value is an array containing +// the parsed packets up to such offset. +// */ +// public static func packets(from stream: Data) -> (Int, [Data]) { +// var ni = 0 +// var parsed: [Data] = [] +// while (ni + 2 <= stream.count) { +// let packlen = Int(stream.networkUInt16Value(from: ni)) +// let start = ni + 2 +// let end = start + packlen +// guard (end <= stream.count) else { +// break +// } +// let packet = stream.subdata(offset: start, count: end - start) +// parsed.append(packet) +// ni = end +// } +// return (ni, parsed) +// } +// +// /** +// Creates a contiguous stream of packets. +// +// - Parameter packet: The packet. +// - Returns: A stream made of the packet. +// */ +// public static func stream(from packet: Data) -> Data { +// var raw = Data(capacity: 2 + packet.count) +// raw.append(UInt16(packet.count).bigEndian) +// raw.append(contentsOf: packet) +// return raw +// } +// +// /** +// Creates a contiguous stream of packets. +// +// - Parameter packets: The array of packets. +// - Returns: A stream made of the array of packets. +// */ +// public static func stream(from packets: [Data]) -> Data { +// var raw = Data() +// for payload in packets { +// raw.append(UInt16(payload.count).bigEndian) +// raw.append(payload) +// } +// return raw +// } +// +// private init() { +// } +//} /// :nodoc: extension ControlPacket { diff --git a/TunnelKit/Sources/Core/PacketStream.h b/TunnelKit/Sources/Core/PacketStream.h new file mode 100644 index 0000000..a7147e4 --- /dev/null +++ b/TunnelKit/Sources/Core/PacketStream.h @@ -0,0 +1,38 @@ +// +// PacketStream.h +// TunnelKit +// +// Created by Davide De Rosa on 4/25/19. +// Copyright (c) 2019 Davide De Rosa. All rights reserved. +// +// https://github.com/keeshux +// +// This file is part of TunnelKit. +// +// TunnelKit is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// TunnelKit is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with TunnelKit. If not, see . +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PacketStream : NSObject + ++ (NSArray *)packetsFromStream:(NSData *)stream until:(nullable NSInteger *)until; ++ (NSData *)streamFromPacket:(NSData *)packet; ++ (NSData *)streamFromPackets:(NSArray *)packets; + +@end + +NS_ASSUME_NONNULL_END diff --git a/TunnelKit/Sources/Core/PacketStream.m b/TunnelKit/Sources/Core/PacketStream.m new file mode 100644 index 0000000..f9b5e20 --- /dev/null +++ b/TunnelKit/Sources/Core/PacketStream.m @@ -0,0 +1,84 @@ +// +// PacketStream.m +// TunnelKit +// +// Created by Davide De Rosa on 4/25/19. +// Copyright (c) 2019 Davide De Rosa. All rights reserved. +// +// https://github.com/keeshux +// +// This file is part of TunnelKit. +// +// TunnelKit is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// TunnelKit is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with TunnelKit. If not, see . +// + +#import "PacketStream.h" + +static const NSInteger PacketStreamHeaderLength = sizeof(uint16_t); + +@implementation PacketStream + ++ (NSArray *)packetsFromStream:(NSData *)stream until:(NSInteger *)until +{ + NSInteger ni = 0; + NSMutableArray *parsed = [[NSMutableArray alloc] init]; + + while (ni + PacketStreamHeaderLength <= stream.length) { + const NSInteger packlen = CFSwapInt16BigToHost(*(uint16_t *)(stream.bytes + ni)); + const NSInteger start = ni + PacketStreamHeaderLength; + const NSInteger end = start + packlen; + if (end > stream.length) { + break; + } + NSData *packet = [stream subdataWithRange:NSMakeRange(start, packlen)]; + [parsed addObject:packet]; + ni = end; + } + if (until) { + *until = ni; + } + return parsed; +} + ++ (NSData *)streamFromPacket:(NSData *)packet +{ + NSMutableData *raw = [[NSMutableData alloc] initWithLength:(PacketStreamHeaderLength + packet.length)]; + + uint8_t *ptr = raw.mutableBytes; + *(uint16_t *)ptr = CFSwapInt16HostToBig(packet.length); + ptr += PacketStreamHeaderLength; + memcpy(ptr, packet.bytes, packet.length); + + return raw; +} + ++ (NSData *)streamFromPackets:(NSArray *)packets; +{ + NSInteger streamLength = 0; + for (NSData *p in packets) { + streamLength += PacketStreamHeaderLength + p.length; + } + + NSMutableData *raw = [[NSMutableData alloc] initWithLength:streamLength]; + uint8_t *ptr = raw.mutableBytes; + for (NSData *packet in packets) { + *(uint16_t *)ptr = CFSwapInt16HostToBig(packet.length); + ptr += PacketStreamHeaderLength; + memcpy(ptr, packet.bytes, packet.length); + ptr += packet.length; + } + return raw; +} + +@end diff --git a/TunnelKit/Sources/Core/module.modulemap b/TunnelKit/Sources/Core/module.modulemap index c17afba..b78163c 100644 --- a/TunnelKit/Sources/Core/module.modulemap +++ b/TunnelKit/Sources/Core/module.modulemap @@ -42,6 +42,7 @@ module __TunnelKitNative { header "CryptoBox.h" header "MSS.h" header "PacketMacros.h" + header "PacketStream.h" header "ControlPacket.h" header "ReplayProtector.h" header "CompressionFramingNative.h" diff --git a/TunnelKitTests/LinkTests.swift b/TunnelKitTests/LinkTests.swift index 3e4ec5f..4b0f385 100644 --- a/TunnelKitTests/LinkTests.swift +++ b/TunnelKitTests/LinkTests.swift @@ -37,6 +37,7 @@ import XCTest @testable import TunnelKit +@testable import __TunnelKitNative class LinkTests: XCTestCase { @@ -62,85 +63,85 @@ class LinkTests: XCTestCase { // TCP - func testPacketStream() { - var bytes: [UInt8] = [] - var until: Int - var packets: [Data] - - bytes.append(contentsOf: [0x00, 0x04]) - bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40]) - bytes.append(contentsOf: [0x00, 0x07]) - bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40, 0x50, 0x66, 0x77]) - bytes.append(contentsOf: [0x00, 0x01]) - bytes.append(contentsOf: [0xff]) - bytes.append(contentsOf: [0x00, 0x03]) - bytes.append(contentsOf: [0xaa]) - XCTAssertEqual(bytes.count, 21) - - (until, packets) = PacketStream.packets(from: Data(bytes)) - XCTAssertEqual(until, 18) - XCTAssertEqual(packets.count, 3) - - bytes.append(contentsOf: [0xbb, 0xcc]) - (until, packets) = PacketStream.packets(from: Data(bytes)) - XCTAssertEqual(until, 23) - XCTAssertEqual(packets.count, 4) - - bytes.append(contentsOf: [0x00, 0x05]) - (until, packets) = PacketStream.packets(from: Data(bytes)) - XCTAssertEqual(until, 23) - XCTAssertEqual(packets.count, 4) - - bytes.append(contentsOf: [0x11, 0x22, 0x33, 0x44]) - (until, packets) = PacketStream.packets(from: Data(bytes)) - XCTAssertEqual(until, 23) - XCTAssertEqual(packets.count, 4) - - bytes.append(contentsOf: [0x55]) - (until, packets) = PacketStream.packets(from: Data(bytes)) - XCTAssertEqual(until, 30) - XCTAssertEqual(packets.count, 5) - - // - - bytes.removeSubrange(0..