Move ControlPacket serialization to Obj-C
Additionally, make sessionId non-optional in control packets. They must have it, therefore treat a missing sessionId as a programming error instead. Reuse routines for acks to make PacketMacros the only point of packets serialization.
This commit is contained in:
parent
92dbb57666
commit
2bd9484a43
|
@ -29,8 +29,13 @@
|
|||
0E1108B11F77B9F900A92462 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108AF1F77B9F900A92462 /* Main.storyboard */; };
|
||||
0E1108B31F77B9F900A92462 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108B21F77B9F900A92462 /* Assets.xcassets */; };
|
||||
0E1108B61F77B9F900A92462 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108B41F77B9F900A92462 /* LaunchScreen.storyboard */; };
|
||||
0E12B2A32145341B00B4BAE9 /* PacketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E12B2A22145341B00B4BAE9 /* PacketTests.swift */; };
|
||||
0E245D692135972800B012A2 /* PushTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E245D682135972800B012A2 /* PushTests.swift */; };
|
||||
0E245D6C2137F73600B012A2 /* CompressionFramingNative.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E245D6B2137F73600B012A2 /* CompressionFramingNative.h */; };
|
||||
0E39BCE8214B2AB60035E9DE /* ControlPacket.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E39BCE6214B2AB60035E9DE /* ControlPacket.h */; };
|
||||
0E39BCE9214B2AB60035E9DE /* ControlPacket.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E39BCE6214B2AB60035E9DE /* ControlPacket.h */; };
|
||||
0E39BCEA214B2AB60035E9DE /* ControlPacket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E39BCE7214B2AB60035E9DE /* ControlPacket.m */; };
|
||||
0E39BCEB214B2AB60035E9DE /* ControlPacket.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E39BCE7214B2AB60035E9DE /* ControlPacket.m */; };
|
||||
0E3E0F212108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */; };
|
||||
0E3E0F222108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */; };
|
||||
0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58F12F2138AC2F00A49F27 /* DNSTests.swift */; };
|
||||
|
@ -189,10 +194,13 @@
|
|||
0E1108B21F77B9F900A92462 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
0E1108B51F77B9F900A92462 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
0E1108B71F77B9F900A92462 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0E12B2A22145341B00B4BAE9 /* PacketTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PacketTests.swift; sourceTree = "<group>"; };
|
||||
0E17D7F91F730D9F009EE129 /* TunnelKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TunnelKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0E245D682135972800B012A2 /* PushTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushTests.swift; sourceTree = "<group>"; };
|
||||
0E245D6B2137F73600B012A2 /* CompressionFramingNative.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompressionFramingNative.h; sourceTree = "<group>"; };
|
||||
0E3251C51F95770D00C108D9 /* TunnelKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TunnelKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0E39BCE6214B2AB60035E9DE /* ControlPacket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ControlPacket.h; sourceTree = "<group>"; };
|
||||
0E39BCE7214B2AB60035E9DE /* ControlPacket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ControlPacket.m; sourceTree = "<group>"; };
|
||||
0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+PushReply.swift"; sourceTree = "<group>"; };
|
||||
0E58F12F2138AC2F00A49F27 /* DNSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSTests.swift; sourceTree = "<group>"; };
|
||||
0E6479DD212EAC96008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
@ -318,6 +326,7 @@
|
|||
0EB2B45E20F0C098004233D7 /* EncryptionPerformanceTests.swift */,
|
||||
0EB2B45220F0BB44004233D7 /* EncryptionTests.swift */,
|
||||
0EB2B45820F0BD9A004233D7 /* LinkTests.swift */,
|
||||
0E12B2A22145341B00B4BAE9 /* PacketTests.swift */,
|
||||
0E245D682135972800B012A2 /* PushTests.swift */,
|
||||
0EB2B45620F0BD16004233D7 /* RandomTests.swift */,
|
||||
0EB2B45C20F0BF41004233D7 /* RawPerformanceTests.swift */,
|
||||
|
@ -416,6 +425,8 @@
|
|||
0EFEB42E2006D3C800F81029 /* Allocation.h */,
|
||||
0EFEB4462006D3C800F81029 /* Allocation.m */,
|
||||
0E245D6B2137F73600B012A2 /* CompressionFramingNative.h */,
|
||||
0E39BCE6214B2AB60035E9DE /* ControlPacket.h */,
|
||||
0E39BCE7214B2AB60035E9DE /* ControlPacket.m */,
|
||||
0EFEB44A2006D3C800F81029 /* CoreConfiguration.swift */,
|
||||
0E07597C20F0060E00F38FD8 /* CryptoAEAD.h */,
|
||||
0E07597D20F0060E00F38FD8 /* CryptoAEAD.m */,
|
||||
|
@ -522,6 +533,7 @@
|
|||
0EFEB4582006D3C800F81029 /* MSS.h in Headers */,
|
||||
0E245D6C2137F73600B012A2 /* CompressionFramingNative.h in Headers */,
|
||||
0EFEB4602006D3C800F81029 /* DataPath.h in Headers */,
|
||||
0E39BCE8214B2AB60035E9DE /* ControlPacket.h in Headers */,
|
||||
0E07597E20F0060E00F38FD8 /* CryptoAEAD.h in Headers */,
|
||||
0EFEB46C2006D3C800F81029 /* ZeroingData.h in Headers */,
|
||||
);
|
||||
|
@ -543,6 +555,7 @@
|
|||
0EF5CF282141E183004FF1BD /* CompressionFramingNative.h in Headers */,
|
||||
0EEC49E820B5F7F6008FEB91 /* ReplayProtector.h in Headers */,
|
||||
0EEC49E920B5F7F6008FEB91 /* TLSBox.h in Headers */,
|
||||
0E39BCE9214B2AB60035E9DE /* ControlPacket.h in Headers */,
|
||||
0E07597F20F0060E00F38FD8 /* CryptoAEAD.h in Headers */,
|
||||
0EEC49E220B5F7F6008FEB91 /* CryptoBox.h in Headers */,
|
||||
);
|
||||
|
@ -808,6 +821,7 @@
|
|||
0EB2B45320F0BB44004233D7 /* EncryptionTests.swift in Sources */,
|
||||
0EB2B45B20F0BE4C004233D7 /* TestUtils.swift in Sources */,
|
||||
0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */,
|
||||
0E12B2A32145341B00B4BAE9 /* PacketTests.swift in Sources */,
|
||||
0E245D692135972800B012A2 /* PushTests.swift in Sources */,
|
||||
0EB2B46120F0C0A4004233D7 /* DataPathPerformanceTests.swift in Sources */,
|
||||
0EB2B45F20F0C098004233D7 /* EncryptionPerformanceTests.swift in Sources */,
|
||||
|
@ -857,6 +871,7 @@
|
|||
0EC1BBA820D7D803007C4C7B /* ConnectionStrategy.swift in Sources */,
|
||||
0EFEB46F2006D3C800F81029 /* IOInterface.swift in Sources */,
|
||||
0E07598020F0060E00F38FD8 /* CryptoAEAD.m in Sources */,
|
||||
0E39BCEA214B2AB60035E9DE /* ControlPacket.m in Sources */,
|
||||
0EFEB4662006D3C800F81029 /* ZeroingData.swift in Sources */,
|
||||
0EBBF2F3208505D300E36B40 /* NEUDPInterface.swift in Sources */,
|
||||
0EFEB4682006D3C800F81029 /* MSS.m in Sources */,
|
||||
|
@ -908,6 +923,7 @@
|
|||
0E07596020EF6D1400F38FD8 /* CryptoCBC.m in Sources */,
|
||||
0EC1BBA920D7D803007C4C7B /* ConnectionStrategy.swift in Sources */,
|
||||
0EFEB4932006D7F300F81029 /* CryptoBox.m in Sources */,
|
||||
0E39BCEB214B2AB60035E9DE /* ControlPacket.m in Sources */,
|
||||
0E07598120F0060E00F38FD8 /* CryptoAEAD.m in Sources */,
|
||||
0EFEB49C2006D7F300F81029 /* Data+Manipulation.swift in Sources */,
|
||||
0EBBF2F4208505D400E36B40 /* NEUDPInterface.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// ControlPacket.h
|
||||
// TunnelKit
|
||||
//
|
||||
// Created by Davide De Rosa on 9/14/18.
|
||||
// Copyright (c) 2018 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>
|
||||
|
||||
#import "PacketMacros.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ControlPacket : NSObject
|
||||
|
||||
- (instancetype)initWithCode:(PacketCode)code
|
||||
key:(uint8_t)key
|
||||
sessionId:(NSData *)sessionId
|
||||
packetId:(uint32_t)packetId
|
||||
payload:(nullable NSData *)payload;
|
||||
|
||||
- (instancetype)initWithKey:(uint8_t)key
|
||||
sessionId:(NSData *)sessionId
|
||||
ackIds:(NSArray<NSNumber *> *)ackIds
|
||||
ackRemoteSessionId:(NSData *)ackRemoteSessionId;
|
||||
|
||||
@property (nonatomic, assign, readonly) PacketCode code;
|
||||
@property (nonatomic, readonly) BOOL isAck;
|
||||
@property (nonatomic, assign, readonly) uint8_t key;
|
||||
@property (nonatomic, strong, readonly) NSData *sessionId;
|
||||
@property (nonatomic, strong) NSArray<NSNumber *> *_Nullable ackIds; // uint32_t
|
||||
@property (nonatomic, strong) NSData *_Nullable ackRemoteSessionId;
|
||||
@property (nonatomic, assign, readonly) uint32_t packetId;
|
||||
@property (nonatomic, strong, readonly) NSData *_Nullable payload;
|
||||
@property (nonatomic, strong) NSDate *_Nullable sentDate;
|
||||
|
||||
- (NSInteger)serializeTo:(nullable uint8_t *)to;
|
||||
- (NSData *)serialized;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// ControlPacket.m
|
||||
// TunnelKit
|
||||
//
|
||||
// Created by Davide De Rosa on 9/14/18.
|
||||
// Copyright (c) 2018 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 "ControlPacket.h"
|
||||
|
||||
@implementation ControlPacket
|
||||
|
||||
- (instancetype)initWithCode:(PacketCode)code
|
||||
key:(uint8_t)key
|
||||
sessionId:(NSData *)sessionId
|
||||
packetId:(uint32_t)packetId
|
||||
payload:(nullable NSData *)payload
|
||||
{
|
||||
NSCParameterAssert(sessionId.length == PacketSessionIdLength);
|
||||
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
_code = code;
|
||||
_key = key;
|
||||
_sessionId = sessionId;
|
||||
_packetId = packetId;
|
||||
_payload = payload;
|
||||
self.sentDate = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithKey:(uint8_t)key
|
||||
sessionId:(NSData *)sessionId
|
||||
ackIds:(NSArray<NSNumber *> *)ackIds
|
||||
ackRemoteSessionId:(NSData *)ackRemoteSessionId
|
||||
{
|
||||
NSCParameterAssert(sessionId.length == PacketSessionIdLength);
|
||||
NSCParameterAssert(ackRemoteSessionId.length == PacketSessionIdLength);
|
||||
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
_packetId = UINT32_MAX; // bogus marker
|
||||
_code = PacketCodeAckV1;
|
||||
_key = key;
|
||||
_sessionId = sessionId;
|
||||
_ackIds = ackIds;
|
||||
_ackRemoteSessionId = ackRemoteSessionId;
|
||||
self.sentDate = nil;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isAck
|
||||
{
|
||||
return (self.packetId == UINT32_MAX);
|
||||
}
|
||||
|
||||
- (NSInteger)capacity
|
||||
{
|
||||
const BOOL isAck = self.isAck;
|
||||
const NSUInteger ackLength = self.ackIds.count;
|
||||
NSCAssert(!isAck || ackLength > 0, @"Ack packet must provide positive ackLength");
|
||||
NSInteger n = PacketHeaderLength + PacketSessionIdLength;
|
||||
n += PacketAckLengthLength;
|
||||
if (ackLength > 0) {
|
||||
n += ackLength * PacketIdLength + PacketSessionIdLength;
|
||||
}
|
||||
if (!isAck) {
|
||||
n += PacketIdLength;
|
||||
}
|
||||
n += self.payload.length;
|
||||
return n;
|
||||
}
|
||||
|
||||
// Ruby: send_ctrl
|
||||
- (NSInteger)serializeTo:(uint8_t *)to
|
||||
{
|
||||
if (!to) {
|
||||
return [self capacity];
|
||||
}
|
||||
uint8_t *ptr = to;
|
||||
ptr += PacketHeaderSet(ptr, self.code, self.key, self.sessionId.bytes);
|
||||
if (self.ackIds.count > 0) {
|
||||
NSCParameterAssert(self.ackRemoteSessionId.length == PacketSessionIdLength);
|
||||
*ptr = self.ackIds.count;
|
||||
ptr += PacketAckLengthLength;
|
||||
for (NSNumber *n in self.ackIds) {
|
||||
const uint32_t ackId = (uint32_t)n.unsignedIntegerValue;
|
||||
*(uint32_t *)ptr = CFSwapInt32HostToBig(ackId);
|
||||
ptr += PacketIdLength;
|
||||
}
|
||||
memcpy(ptr, self.ackRemoteSessionId.bytes, PacketSessionIdLength);
|
||||
ptr += PacketSessionIdLength;
|
||||
}
|
||||
else {
|
||||
*ptr = 0; // no acks
|
||||
ptr += PacketAckLengthLength;
|
||||
}
|
||||
if (self.code != PacketCodeAckV1) {
|
||||
*(uint32_t *)ptr = CFSwapInt32HostToBig(self.packetId);
|
||||
ptr += PacketIdLength;
|
||||
if (self.payload) {
|
||||
memcpy(ptr, self.payload.bytes, self.payload.length);
|
||||
ptr += self.payload.length;
|
||||
}
|
||||
}
|
||||
return (int)(ptr - to);
|
||||
}
|
||||
|
||||
- (NSData *)serialized
|
||||
{
|
||||
NSMutableData *data = [[NSMutableData alloc] initWithLength:self.capacity];
|
||||
[self serializeTo:data.mutableBytes];
|
||||
return data;
|
||||
}
|
||||
|
||||
@end
|
|
@ -290,7 +290,7 @@ const NSInteger CryptoAEADTagLength = 16;
|
|||
self.crypto.extraLength = PacketIdLength;
|
||||
self.crypto.extraPacketIdOffset = 0;
|
||||
self.setDataHeader = ^(uint8_t *to, uint8_t key) {
|
||||
PacketHeaderSet(to, PacketCodeDataV1, key);
|
||||
PacketHeaderSet(to, PacketCodeDataV1, key, nil);
|
||||
};
|
||||
self.checkPeerId = NULL;
|
||||
}
|
||||
|
|
|
@ -328,7 +328,7 @@ const NSInteger CryptoCBCMaxHMACLength = 100;
|
|||
if (peerId == PacketPeerIdDisabled) {
|
||||
self.headerLength = 1;
|
||||
self.setDataHeader = ^(uint8_t *to, uint8_t key) {
|
||||
PacketHeaderSet(to, PacketCodeDataV1, key);
|
||||
PacketHeaderSet(to, PacketCodeDataV1, key, nil);
|
||||
};
|
||||
self.checkPeerId = NULL;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
import Foundation
|
||||
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 {
|
||||
|
||||
|
@ -98,41 +100,6 @@ public class PacketStream {
|
|||
}
|
||||
}
|
||||
|
||||
class ControlPacket {
|
||||
let packetId: UInt32
|
||||
|
||||
let code: PacketCode
|
||||
|
||||
let key: UInt8
|
||||
|
||||
let sessionId: Data?
|
||||
|
||||
let payload: Data?
|
||||
|
||||
var sentDate: Date?
|
||||
|
||||
init(_ packetId: UInt32, _ code: PacketCode, _ key: UInt8, _ sessionId: Data?, _ payload: Data?) {
|
||||
self.packetId = packetId
|
||||
self.code = code
|
||||
self.key = key
|
||||
self.sessionId = sessionId
|
||||
self.payload = payload
|
||||
self.sentDate = nil
|
||||
}
|
||||
|
||||
// Ruby: send_ctrl
|
||||
func toBuffer() -> Data {
|
||||
var raw = PacketWithHeader(code, key, sessionId)
|
||||
// TODO: put HMAC here when tls-auth
|
||||
raw.append(UInt8(0)) // ackSize
|
||||
raw.append(UInt32(packetId).bigEndian)
|
||||
if let payload = payload {
|
||||
raw.append(payload)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
}
|
||||
|
||||
class DataPacket {
|
||||
static let pingString = Data(hex: "2a187bf3641eb4cb07ed2d0a981fc748")
|
||||
}
|
||||
|
|
|
@ -39,8 +39,12 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define PacketPeerIdDisabled 0xffffffu
|
||||
#define PacketIdLength 4
|
||||
#define PacketHeaderLength ((NSInteger)1)
|
||||
#define PacketIdLength ((NSInteger)4)
|
||||
#define PacketSessionIdLength ((NSInteger)8)
|
||||
#define PacketAckLengthLength ((NSInteger)1)
|
||||
#define PacketPeerIdLength ((NSInteger)3)
|
||||
#define PacketPeerIdDisabled ((uint32_t)0xffffffu)
|
||||
|
||||
typedef NS_ENUM(uint8_t, PacketCode) {
|
||||
PacketCodeSoftResetV1 = 0x03,
|
||||
|
@ -59,27 +63,21 @@ typedef NS_ENUM(uint8_t, PacketCode) {
|
|||
extern const uint8_t DataPacketPingData[16];
|
||||
|
||||
// Ruby: header
|
||||
static inline int PacketHeaderSet(uint8_t *to, PacketCode code, uint8_t key)
|
||||
static inline int PacketHeaderSet(uint8_t *to, PacketCode code, uint8_t key, const uint8_t *_Nullable sessionId)
|
||||
{
|
||||
*(uint8_t *)to = (code << 3) | (key & 0b111);
|
||||
return sizeof(uint8_t);
|
||||
}
|
||||
|
||||
// Ruby: header
|
||||
static inline NSData *PacketWithHeader(PacketCode code, uint8_t key, NSData *_Nullable sessionId)
|
||||
{
|
||||
NSMutableData *to = [[NSMutableData alloc] initWithLength:(sizeof(uint8_t) + (sessionId ? sessionId.length : 0))];
|
||||
const int offset = PacketHeaderSet(to.mutableBytes, code, key);
|
||||
int offset = PacketHeaderLength;
|
||||
if (sessionId) {
|
||||
memcpy(to.mutableBytes + offset, sessionId.bytes, sessionId.length);
|
||||
memcpy(to + offset, sessionId, PacketSessionIdLength);
|
||||
offset += PacketSessionIdLength;
|
||||
}
|
||||
return to;
|
||||
return offset;
|
||||
}
|
||||
|
||||
static inline int PacketHeaderSetDataV2(uint8_t *to, uint8_t key, uint32_t peerId)
|
||||
{
|
||||
*(uint32_t *)to = ((PacketCodeDataV2 << 3) | (key & 0b111)) | htonl(peerId & 0xffffff);
|
||||
return sizeof(uint32_t);
|
||||
return PacketHeaderLength + PacketPeerIdLength;
|
||||
}
|
||||
|
||||
static inline int PacketHeaderGetDataV2PeerId(const uint8_t *from)
|
||||
|
@ -87,14 +85,4 @@ static inline int PacketHeaderGetDataV2PeerId(const uint8_t *from)
|
|||
return ntohl(*(const uint32_t *)from & 0xffffff00);
|
||||
}
|
||||
|
||||
static inline NSData *PacketWithHeaderDataV2(uint8_t key, uint32_t peerId, NSData *sessionId)
|
||||
{
|
||||
NSMutableData *to = [[NSMutableData alloc] initWithLength:(sizeof(uint32_t) + (sessionId ? sessionId.length : 0))];
|
||||
const int offset = PacketHeaderSetDataV2(to.mutableBytes, key, peerId);
|
||||
if (sessionId) {
|
||||
memcpy(to.mutableBytes + offset, sessionId.bytes, sessionId.length);
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -36,14 +36,8 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import __TunnelKitNative
|
||||
|
||||
class ProtocolMacros {
|
||||
static let peerIdLength = 3
|
||||
|
||||
static let sessionIdLength = 8
|
||||
|
||||
static let packetIdLength = 4
|
||||
|
||||
// UInt32(0) + UInt8(KeyMethod = 2)
|
||||
static let tlsPrefix = Data(hex: "0000000002")
|
||||
|
|
|
@ -455,11 +455,11 @@ public class SessionProxy {
|
|||
|
||||
var offset = 1
|
||||
if (code == .dataV2) {
|
||||
guard packet.count >= offset + ProtocolMacros.peerIdLength else {
|
||||
guard packet.count >= offset + PacketPeerIdLength else {
|
||||
log.warning("Dropped malformed packet (missing peerId)")
|
||||
continue
|
||||
}
|
||||
offset += ProtocolMacros.peerIdLength
|
||||
offset += PacketPeerIdLength
|
||||
}
|
||||
|
||||
if (code == .dataV1) || (code == .dataV2) {
|
||||
|
@ -477,12 +477,12 @@ public class SessionProxy {
|
|||
continue
|
||||
}
|
||||
|
||||
guard packet.count >= offset + ProtocolMacros.sessionIdLength else {
|
||||
guard packet.count >= offset + PacketSessionIdLength else {
|
||||
log.warning("Dropped malformed packet (missing sessionId)")
|
||||
continue
|
||||
}
|
||||
let sessionId = packet.subdata(offset: offset, count: ProtocolMacros.sessionIdLength)
|
||||
offset += ProtocolMacros.sessionIdLength
|
||||
let sessionId = packet.subdata(offset: offset, count: PacketSessionIdLength)
|
||||
offset += PacketSessionIdLength
|
||||
|
||||
guard packet.count >= offset + 1 else {
|
||||
log.warning("Dropped malformed packet (missing ackSize)")
|
||||
|
@ -494,7 +494,7 @@ public class SessionProxy {
|
|||
log.debug("Packet has code \(code.rawValue), key \(key), sessionId \(sessionId.toHex()) and \(ackSize) acks entries")
|
||||
|
||||
if (ackSize > 0) {
|
||||
guard packet.count >= (offset + Int(ackSize) * ProtocolMacros.packetIdLength) else {
|
||||
guard packet.count >= (offset + Int(ackSize) * PacketIdLength) else {
|
||||
log.warning("Dropped malformed packet (missing acks)")
|
||||
continue
|
||||
}
|
||||
|
@ -502,15 +502,15 @@ public class SessionProxy {
|
|||
for _ in 0..<ackSize {
|
||||
let ackedPacketId = packet.networkUInt32Value(from: offset)
|
||||
ackedPacketIds.append(ackedPacketId)
|
||||
offset += ProtocolMacros.packetIdLength
|
||||
offset += PacketIdLength
|
||||
}
|
||||
|
||||
guard packet.count >= offset + ProtocolMacros.sessionIdLength else {
|
||||
guard packet.count >= offset + PacketSessionIdLength else {
|
||||
log.warning("Dropped malformed packet (missing remoteSessionId)")
|
||||
continue
|
||||
}
|
||||
let remoteSessionId = packet.subdata(offset: offset, count: ProtocolMacros.sessionIdLength)
|
||||
offset += ProtocolMacros.sessionIdLength
|
||||
let remoteSessionId = packet.subdata(offset: offset, count: PacketSessionIdLength)
|
||||
offset += PacketSessionIdLength
|
||||
|
||||
log.debug("Server acked packetIds \(ackedPacketIds) with remoteSessionId \(remoteSessionId.toHex())")
|
||||
|
||||
|
@ -521,13 +521,13 @@ public class SessionProxy {
|
|||
continue
|
||||
}
|
||||
|
||||
guard packet.count >= offset + ProtocolMacros.packetIdLength else {
|
||||
guard packet.count >= offset + PacketIdLength else {
|
||||
log.warning("Dropped malformed packet (missing packetId)")
|
||||
continue
|
||||
}
|
||||
let packetId = packet.networkUInt32Value(from: offset)
|
||||
log.debug("Control packet has packetId \(packetId)")
|
||||
offset += ProtocolMacros.packetIdLength
|
||||
offset += PacketIdLength
|
||||
|
||||
sendAck(key: key, packetId: packetId, remoteSessionId: sessionId)
|
||||
|
||||
|
@ -544,7 +544,7 @@ public class SessionProxy {
|
|||
}
|
||||
}
|
||||
|
||||
let controlPacket = ControlPacket(packetId, code, key, sessionId, payload)
|
||||
let controlPacket = ControlPacket(code: code, key: key, sessionId: sessionId, packetId: packetId, payload: payload)
|
||||
controlQueueIn.append(controlPacket)
|
||||
controlQueueIn.sort { $0.packetId < $1.packetId }
|
||||
|
||||
|
@ -643,7 +643,7 @@ public class SessionProxy {
|
|||
resetControlChannel()
|
||||
pushReply = nil
|
||||
do {
|
||||
try sessionId = SecureRandom.data(length: ProtocolMacros.sessionIdLength)
|
||||
try sessionId = SecureRandom.data(length: PacketSessionIdLength)
|
||||
} catch let e {
|
||||
deferStop(.shutdown, e)
|
||||
return
|
||||
|
@ -762,22 +762,16 @@ public class SessionProxy {
|
|||
if (((packet.code == .hardResetServerV2) && (negotiationKey.state == .hardReset)) ||
|
||||
((packet.code == .softResetV1) && (negotiationKey.state == .softReset))) {
|
||||
|
||||
if (negotiationKey.state == .hardReset) {
|
||||
guard let sessionId = packet.sessionId else {
|
||||
deferStop(.shutdown, SessionError.missingSessionId)
|
||||
return
|
||||
}
|
||||
remoteSessionId = sessionId
|
||||
if negotiationKey.state == .hardReset {
|
||||
remoteSessionId = packet.sessionId
|
||||
}
|
||||
guard let remoteSessionId = remoteSessionId else {
|
||||
log.error("No remote session id")
|
||||
deferStop(.shutdown, SessionError.missingSessionId)
|
||||
return
|
||||
}
|
||||
guard (packet.sessionId == remoteSessionId) else {
|
||||
if let packetSessionId = packet.sessionId {
|
||||
log.error("Packet session mismatch (\(packetSessionId.toHex()) != \(remoteSessionId.toHex()))")
|
||||
}
|
||||
guard packet.sessionId == remoteSessionId else {
|
||||
log.error("Packet session mismatch (\(packet.sessionId.toHex()) != \(remoteSessionId.toHex()))")
|
||||
deferStop(.shutdown, SessionError.sessionMismatch)
|
||||
return
|
||||
}
|
||||
|
@ -812,10 +806,8 @@ public class SessionProxy {
|
|||
deferStop(.shutdown, SessionError.missingSessionId)
|
||||
return
|
||||
}
|
||||
guard (packet.sessionId == remoteSessionId) else {
|
||||
if let packetSessionId = packet.sessionId {
|
||||
log.error("Packet session mismatch (\(packetSessionId.toHex()) != \(remoteSessionId.toHex()))")
|
||||
}
|
||||
guard packet.sessionId == remoteSessionId else {
|
||||
log.error("Packet session mismatch (\(packet.sessionId.toHex()) != \(remoteSessionId.toHex()))")
|
||||
deferStop(.shutdown, SessionError.sessionMismatch)
|
||||
return
|
||||
}
|
||||
|
@ -946,6 +938,9 @@ public class SessionProxy {
|
|||
log.warning("Not writing to LINK, interface is down")
|
||||
return
|
||||
}
|
||||
guard let sessionId = sessionId else {
|
||||
fatalError("Missing sessionId, do hardReset() first")
|
||||
}
|
||||
|
||||
let oldIdOut = controlPacketIdOut
|
||||
let maxCount = link.mtu
|
||||
|
@ -955,7 +950,7 @@ public class SessionProxy {
|
|||
repeat {
|
||||
let subPayloadLength = min(maxCount, payload.count - offset)
|
||||
let subPayloadData = payload.subdata(offset: offset, count: subPayloadLength)
|
||||
let packet = ControlPacket(controlPacketIdOut, code, key, sessionId, subPayloadData)
|
||||
let packet = ControlPacket(code: code, key: key, sessionId: sessionId, packetId: controlPacketIdOut, payload: subPayloadData)
|
||||
|
||||
controlQueueOut.append(packet)
|
||||
controlPacketIdOut += 1
|
||||
|
@ -996,7 +991,7 @@ public class SessionProxy {
|
|||
}
|
||||
}
|
||||
|
||||
let raw = controlPacket.toBuffer()
|
||||
let raw = controlPacket.serialized()
|
||||
log.debug("Send control packet (\(raw.count) bytes): \(raw.toHex())")
|
||||
|
||||
// track pending acks for sent packets
|
||||
|
@ -1176,10 +1171,14 @@ public class SessionProxy {
|
|||
private func sendAck(key: UInt8, packetId: UInt32, remoteSessionId: Data) {
|
||||
log.debug("Send ack for received packetId \(packetId)")
|
||||
|
||||
var raw = PacketWithHeader(.ackV1, key, sessionId)
|
||||
raw.append(UInt8(1)) // ackSize
|
||||
raw.append(UInt32(packetId).bigEndian)
|
||||
raw.append(remoteSessionId)
|
||||
guard let sessionId = sessionId else {
|
||||
log.warning("Sending ack without a sessionId?")
|
||||
deferStop(.shutdown, SessionError.missingSessionId)
|
||||
return
|
||||
}
|
||||
|
||||
let ackPacket = ControlPacket(key: key, sessionId: sessionId, ackIds: [packetId as NSNumber], ackRemoteSessionId: remoteSessionId)
|
||||
let raw = ackPacket.serialized()
|
||||
|
||||
// WARNING: runs in Network.framework queue
|
||||
link?.writePacket(raw) { [weak self] (error) in
|
||||
|
|
|
@ -42,6 +42,7 @@ module __TunnelKitNative {
|
|||
header "Encryption.h"
|
||||
header "MSS.h"
|
||||
header "PacketMacros.h"
|
||||
header "ControlPacket.h"
|
||||
header "ReplayProtector.h"
|
||||
header "CompressionFramingNative.h"
|
||||
header "DataPath.h"
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// PacketTests.swift
|
||||
// TunnelKitTests
|
||||
//
|
||||
// Created by Davide De Rosa on 9/9/18.
|
||||
// Copyright (c) 2018 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 XCTest
|
||||
@testable import TunnelKit
|
||||
import __TunnelKitNative
|
||||
|
||||
class PacketTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testControlPacket() {
|
||||
let id: UInt32 = 0x1456
|
||||
let code: PacketCode = .controlV1
|
||||
let key: UInt8 = 3
|
||||
let sessionId = Data(hex: "1122334455667788")
|
||||
let payload = Data(hex: "932748238742397591704891")
|
||||
|
||||
let serialized = ControlPacket(code: code, key: key, sessionId: sessionId, packetId: id, payload: payload).serialized()
|
||||
let expected = Data(hex: "2311223344556677880000001456932748238742397591704891")
|
||||
print("Serialized: \(serialized.toHex())")
|
||||
print("Expected : \(expected.toHex())")
|
||||
|
||||
XCTAssertEqual(serialized, expected)
|
||||
}
|
||||
|
||||
func testAckPacket() {
|
||||
let acks: [UInt32] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee]
|
||||
let key: UInt8 = 3
|
||||
let sessionId = Data(hex: "1122334455667788")
|
||||
let remoteSessionId = Data(hex: "a639328cbf03490e")
|
||||
|
||||
let serialized = ControlPacket(key: key, sessionId: sessionId, ackIds: acks as [NSNumber], ackRemoteSessionId: remoteSessionId).serialized()
|
||||
let expected = Data(hex: "2b112233445566778805000000aa000000bb000000cc000000dd000000eea639328cbf03490e")
|
||||
print("Serialized: \(serialized.toHex())")
|
||||
print("Expected : \(expected.toHex())")
|
||||
|
||||
XCTAssertEqual(serialized, expected)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue