Convert PacketSteram to Obj-C

For better TCP efficiency.
This commit is contained in:
Davide De Rosa 2019-04-25 11:15:25 +02:00
parent 3d914f72c4
commit 1b8647bcac
8 changed files with 279 additions and 142 deletions

View File

@ -17,7 +17,6 @@ custom_categories:
- IOInterface
- LinkInterface
- TunnelInterface
- PacketStream
- BidirectionalState
- StaticKey
- SessionProxy

View File

@ -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 = "<group>"; };
0E3B65712249247E00EFF4DA /* tunnelbear.key */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.key; sourceTree = "<group>"; };
0E3E0F202108A8CC00B371C1 /* SessionProxy+SessionReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+SessionReply.swift"; sourceTree = "<group>"; };
0E48AC622271ADA8009B1A98 /* PacketStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PacketStream.h; sourceTree = "<group>"; };
0E48AC632271ADA9009B1A98 /* PacketStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PacketStream.m; sourceTree = "<group>"; };
0E500EA522493B5B00CAE560 /* tunnelbear.enc.1.key */ = {isa = PBXFileReference; lastKnownFileType = text; path = tunnelbear.enc.1.key; sourceTree = "<group>"; };
0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlChannelTests.swift; sourceTree = "<group>"; };
0E58BF2F22405410006FB157 /* lzoconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzoconf.h; sourceTree = "<group>"; };
@ -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 */,

View File

@ -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..<newBuffer.count)
self?.loopReadPackets(queue, newBuffer, handler)
@ -233,14 +235,14 @@ class NETCPLink: LinkInterface {
}
func writePacket(_ packet: Data, completionHandler: ((Error?) -> 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)
}

View File

@ -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 {

View File

@ -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 <http://www.gnu.org/licenses/>.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface PacketStream : NSObject
+ (NSArray<NSData *> *)packetsFromStream:(NSData *)stream until:(nullable NSInteger *)until;
+ (NSData *)streamFromPacket:(NSData *)packet;
+ (NSData *)streamFromPackets:(NSArray<NSData *> *)packets;
@end
NS_ASSUME_NONNULL_END

View File

@ -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 <http://www.gnu.org/licenses/>.
//
#import "PacketStream.h"
static const NSInteger PacketStreamHeaderLength = sizeof(uint16_t);
@implementation PacketStream
+ (NSArray<NSData *> *)packetsFromStream:(NSData *)stream until:(NSInteger *)until
{
NSInteger ni = 0;
NSMutableArray<NSData *> *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<NSData *> *)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

View File

@ -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"

View File

@ -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..<until)
XCTAssertEqual(bytes.count, 0)
bytes.append(contentsOf: [0x00, 0x04])
bytes.append(contentsOf: [0x10, 0x20])
(until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 0)
XCTAssertEqual(packets.count, 0)
bytes.removeSubrange(0..<until)
XCTAssertEqual(bytes.count, 4)
bytes.append(contentsOf: [0x30, 0x40])
bytes.append(contentsOf: [0x00, 0x07])
bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40])
(until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 6)
XCTAssertEqual(packets.count, 1)
bytes.removeSubrange(0..<until)
XCTAssertEqual(bytes.count, 6)
bytes.append(contentsOf: [0x50, 0x66, 0x77])
bytes.append(contentsOf: [0x00, 0x01])
bytes.append(contentsOf: [0xff])
bytes.append(contentsOf: [0x00, 0x03])
bytes.append(contentsOf: [0xaa])
(until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 12)
XCTAssertEqual(packets.count, 2)
bytes.removeSubrange(0..<until)
XCTAssertEqual(bytes.count, 3)
bytes.append(contentsOf: [0xbb, 0xcc])
(until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 5)
XCTAssertEqual(packets.count, 1)
bytes.removeSubrange(0..<until)
XCTAssertEqual(bytes.count, 0)
}
// private 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..<until)
// XCTAssertEqual(bytes.count, 0)
//
// bytes.append(contentsOf: [0x00, 0x04])
// bytes.append(contentsOf: [0x10, 0x20])
// (until, packets) = PacketStream.packets(from: Data(bytes))
// XCTAssertEqual(until, 0)
// XCTAssertEqual(packets.count, 0)
// bytes.removeSubrange(0..<until)
// XCTAssertEqual(bytes.count, 4)
//
// bytes.append(contentsOf: [0x30, 0x40])
// bytes.append(contentsOf: [0x00, 0x07])
// bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40])
// (until, packets) = PacketStream.packets(from: Data(bytes))
// XCTAssertEqual(until, 6)
// XCTAssertEqual(packets.count, 1)
// bytes.removeSubrange(0..<until)
// XCTAssertEqual(bytes.count, 6)
//
// bytes.append(contentsOf: [0x50, 0x66, 0x77])
// bytes.append(contentsOf: [0x00, 0x01])
// bytes.append(contentsOf: [0xff])
// bytes.append(contentsOf: [0x00, 0x03])
// bytes.append(contentsOf: [0xaa])
// (until, packets) = PacketStream.packets(from: Data(bytes))
// XCTAssertEqual(until, 12)
// XCTAssertEqual(packets.count, 2)
// bytes.removeSubrange(0..<until)
// XCTAssertEqual(bytes.count, 3)
//
// bytes.append(contentsOf: [0xbb, 0xcc])
// (until, packets) = PacketStream.packets(from: Data(bytes))
// XCTAssertEqual(until, 5)
// XCTAssertEqual(packets.count, 1)
// bytes.removeSubrange(0..<until)
// XCTAssertEqual(bytes.count, 0)
// }
// helpers