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:
Davide De Rosa 2018-09-09 11:40:58 +02:00
parent 92dbb57666
commit 2bd9484a43
11 changed files with 333 additions and 101 deletions

View File

@ -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 */,

View File

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

View File

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

View File

@ -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;
}

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
}
}