Merge branch 'refactor-control-channel'
This commit is contained in:
commit
9d6c7c846f
@ -17,8 +17,13 @@ custom_categories:
|
|||||||
- IOInterface
|
- IOInterface
|
||||||
- LinkInterface
|
- LinkInterface
|
||||||
- TunnelInterface
|
- TunnelInterface
|
||||||
|
- PacketStream
|
||||||
|
- BidirectionalState
|
||||||
- SessionProxy
|
- SessionProxy
|
||||||
- SessionProxyDelegate
|
- SessionProxyDelegate
|
||||||
|
- SessionReply
|
||||||
|
- IPv4Settings
|
||||||
|
- IPv6Settings
|
||||||
- SessionError
|
- SessionError
|
||||||
- name: AppExtension
|
- name: AppExtension
|
||||||
children:
|
children:
|
||||||
|
@ -29,8 +29,19 @@
|
|||||||
0E1108B11F77B9F900A92462 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108AF1F77B9F900A92462 /* Main.storyboard */; };
|
0E1108B11F77B9F900A92462 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108AF1F77B9F900A92462 /* Main.storyboard */; };
|
||||||
0E1108B31F77B9F900A92462 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108B21F77B9F900A92462 /* Assets.xcassets */; };
|
0E1108B31F77B9F900A92462 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108B21F77B9F900A92462 /* Assets.xcassets */; };
|
||||||
0E1108B61F77B9F900A92462 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108B41F77B9F900A92462 /* LaunchScreen.storyboard */; };
|
0E1108B61F77B9F900A92462 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E1108B41F77B9F900A92462 /* LaunchScreen.storyboard */; };
|
||||||
|
0E12B2A32145341B00B4BAE9 /* PacketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E12B2A22145341B00B4BAE9 /* PacketTests.swift */; };
|
||||||
|
0E12B2A521454F7F00B4BAE9 /* BidirectionalState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E12B2A421454F7F00B4BAE9 /* BidirectionalState.swift */; };
|
||||||
|
0E12B2A621454F7F00B4BAE9 /* BidirectionalState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E12B2A421454F7F00B4BAE9 /* BidirectionalState.swift */; };
|
||||||
|
0E12B2A821456C0200B4BAE9 /* ControlChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E12B2A721456C0200B4BAE9 /* ControlChannel.swift */; };
|
||||||
|
0E12B2A921456C0200B4BAE9 /* ControlChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E12B2A721456C0200B4BAE9 /* ControlChannel.swift */; };
|
||||||
|
0E12B2AB2145E01700B4BAE9 /* ControlChannelSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E12B2AA2145E01700B4BAE9 /* ControlChannelSerializer.swift */; };
|
||||||
|
0E12B2AC2145E01700B4BAE9 /* ControlChannelSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E12B2AA2145E01700B4BAE9 /* ControlChannelSerializer.swift */; };
|
||||||
0E245D692135972800B012A2 /* PushTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E245D682135972800B012A2 /* PushTests.swift */; };
|
0E245D692135972800B012A2 /* PushTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E245D682135972800B012A2 /* PushTests.swift */; };
|
||||||
0E245D6C2137F73600B012A2 /* CompressionFramingNative.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E245D6B2137F73600B012A2 /* CompressionFramingNative.h */; };
|
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 */; };
|
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 */; };
|
0E3E0F222108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+PushReply.swift */; };
|
||||||
0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58F12F2138AC2F00A49F27 /* DNSTests.swift */; };
|
0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E58F12F2138AC2F00A49F27 /* DNSTests.swift */; };
|
||||||
@ -189,10 +200,16 @@
|
|||||||
0E1108B21F77B9F900A92462 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
||||||
|
0E12B2A421454F7F00B4BAE9 /* BidirectionalState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BidirectionalState.swift; sourceTree = "<group>"; };
|
||||||
|
0E12B2A721456C0200B4BAE9 /* ControlChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlChannel.swift; sourceTree = "<group>"; };
|
||||||
|
0E12B2AA2145E01700B4BAE9 /* ControlChannelSerializer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlChannelSerializer.swift; sourceTree = "<group>"; };
|
||||||
0E17D7F91F730D9F009EE129 /* TunnelKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TunnelKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
0E6479DD212EAC96008E6888 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
@ -318,6 +335,7 @@
|
|||||||
0EB2B45E20F0C098004233D7 /* EncryptionPerformanceTests.swift */,
|
0EB2B45E20F0C098004233D7 /* EncryptionPerformanceTests.swift */,
|
||||||
0EB2B45220F0BB44004233D7 /* EncryptionTests.swift */,
|
0EB2B45220F0BB44004233D7 /* EncryptionTests.swift */,
|
||||||
0EB2B45820F0BD9A004233D7 /* LinkTests.swift */,
|
0EB2B45820F0BD9A004233D7 /* LinkTests.swift */,
|
||||||
|
0E12B2A22145341B00B4BAE9 /* PacketTests.swift */,
|
||||||
0E245D682135972800B012A2 /* PushTests.swift */,
|
0E245D682135972800B012A2 /* PushTests.swift */,
|
||||||
0EB2B45620F0BD16004233D7 /* RandomTests.swift */,
|
0EB2B45620F0BD16004233D7 /* RandomTests.swift */,
|
||||||
0EB2B45C20F0BF41004233D7 /* RawPerformanceTests.swift */,
|
0EB2B45C20F0BF41004233D7 /* RawPerformanceTests.swift */,
|
||||||
@ -415,7 +433,12 @@
|
|||||||
children = (
|
children = (
|
||||||
0EFEB42E2006D3C800F81029 /* Allocation.h */,
|
0EFEB42E2006D3C800F81029 /* Allocation.h */,
|
||||||
0EFEB4462006D3C800F81029 /* Allocation.m */,
|
0EFEB4462006D3C800F81029 /* Allocation.m */,
|
||||||
|
0E12B2A421454F7F00B4BAE9 /* BidirectionalState.swift */,
|
||||||
0E245D6B2137F73600B012A2 /* CompressionFramingNative.h */,
|
0E245D6B2137F73600B012A2 /* CompressionFramingNative.h */,
|
||||||
|
0E39BCE6214B2AB60035E9DE /* ControlPacket.h */,
|
||||||
|
0E39BCE7214B2AB60035E9DE /* ControlPacket.m */,
|
||||||
|
0E12B2A721456C0200B4BAE9 /* ControlChannel.swift */,
|
||||||
|
0E12B2AA2145E01700B4BAE9 /* ControlChannelSerializer.swift */,
|
||||||
0EFEB44A2006D3C800F81029 /* CoreConfiguration.swift */,
|
0EFEB44A2006D3C800F81029 /* CoreConfiguration.swift */,
|
||||||
0E07597C20F0060E00F38FD8 /* CryptoAEAD.h */,
|
0E07597C20F0060E00F38FD8 /* CryptoAEAD.h */,
|
||||||
0E07597D20F0060E00F38FD8 /* CryptoAEAD.m */,
|
0E07597D20F0060E00F38FD8 /* CryptoAEAD.m */,
|
||||||
@ -522,6 +545,7 @@
|
|||||||
0EFEB4582006D3C800F81029 /* MSS.h in Headers */,
|
0EFEB4582006D3C800F81029 /* MSS.h in Headers */,
|
||||||
0E245D6C2137F73600B012A2 /* CompressionFramingNative.h in Headers */,
|
0E245D6C2137F73600B012A2 /* CompressionFramingNative.h in Headers */,
|
||||||
0EFEB4602006D3C800F81029 /* DataPath.h in Headers */,
|
0EFEB4602006D3C800F81029 /* DataPath.h in Headers */,
|
||||||
|
0E39BCE8214B2AB60035E9DE /* ControlPacket.h in Headers */,
|
||||||
0E07597E20F0060E00F38FD8 /* CryptoAEAD.h in Headers */,
|
0E07597E20F0060E00F38FD8 /* CryptoAEAD.h in Headers */,
|
||||||
0EFEB46C2006D3C800F81029 /* ZeroingData.h in Headers */,
|
0EFEB46C2006D3C800F81029 /* ZeroingData.h in Headers */,
|
||||||
);
|
);
|
||||||
@ -543,6 +567,7 @@
|
|||||||
0EF5CF282141E183004FF1BD /* CompressionFramingNative.h in Headers */,
|
0EF5CF282141E183004FF1BD /* CompressionFramingNative.h in Headers */,
|
||||||
0EEC49E820B5F7F6008FEB91 /* ReplayProtector.h in Headers */,
|
0EEC49E820B5F7F6008FEB91 /* ReplayProtector.h in Headers */,
|
||||||
0EEC49E920B5F7F6008FEB91 /* TLSBox.h in Headers */,
|
0EEC49E920B5F7F6008FEB91 /* TLSBox.h in Headers */,
|
||||||
|
0E39BCE9214B2AB60035E9DE /* ControlPacket.h in Headers */,
|
||||||
0E07597F20F0060E00F38FD8 /* CryptoAEAD.h in Headers */,
|
0E07597F20F0060E00F38FD8 /* CryptoAEAD.h in Headers */,
|
||||||
0EEC49E220B5F7F6008FEB91 /* CryptoBox.h in Headers */,
|
0EEC49E220B5F7F6008FEB91 /* CryptoBox.h in Headers */,
|
||||||
);
|
);
|
||||||
@ -808,6 +833,7 @@
|
|||||||
0EB2B45320F0BB44004233D7 /* EncryptionTests.swift in Sources */,
|
0EB2B45320F0BB44004233D7 /* EncryptionTests.swift in Sources */,
|
||||||
0EB2B45B20F0BE4C004233D7 /* TestUtils.swift in Sources */,
|
0EB2B45B20F0BE4C004233D7 /* TestUtils.swift in Sources */,
|
||||||
0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */,
|
0E58F1302138AC2F00A49F27 /* DNSTests.swift in Sources */,
|
||||||
|
0E12B2A32145341B00B4BAE9 /* PacketTests.swift in Sources */,
|
||||||
0E245D692135972800B012A2 /* PushTests.swift in Sources */,
|
0E245D692135972800B012A2 /* PushTests.swift in Sources */,
|
||||||
0EB2B46120F0C0A4004233D7 /* DataPathPerformanceTests.swift in Sources */,
|
0EB2B46120F0C0A4004233D7 /* DataPathPerformanceTests.swift in Sources */,
|
||||||
0EB2B45F20F0C098004233D7 /* EncryptionPerformanceTests.swift in Sources */,
|
0EB2B45F20F0C098004233D7 /* EncryptionPerformanceTests.swift in Sources */,
|
||||||
@ -845,6 +871,7 @@
|
|||||||
0EFEB45D2006D3C800F81029 /* CryptoBox.m in Sources */,
|
0EFEB45D2006D3C800F81029 /* CryptoBox.m in Sources */,
|
||||||
0EBBF2FA2085061600E36B40 /* NETCPInterface.swift in Sources */,
|
0EBBF2FA2085061600E36B40 /* NETCPInterface.swift in Sources */,
|
||||||
0E0C2125212ED29D008AB282 /* SessionError.swift in Sources */,
|
0E0C2125212ED29D008AB282 /* SessionError.swift in Sources */,
|
||||||
|
0E12B2A821456C0200B4BAE9 /* ControlChannel.swift in Sources */,
|
||||||
0EFEB4552006D3C800F81029 /* SessionProxy+EncryptionBridge.swift in Sources */,
|
0EFEB4552006D3C800F81029 /* SessionProxy+EncryptionBridge.swift in Sources */,
|
||||||
0EFEB45C2006D3C800F81029 /* ZeroingData.m in Sources */,
|
0EFEB45C2006D3C800F81029 /* ZeroingData.m in Sources */,
|
||||||
0EFEB4632006D3C800F81029 /* ProtocolMacros.swift in Sources */,
|
0EFEB4632006D3C800F81029 /* ProtocolMacros.swift in Sources */,
|
||||||
@ -857,6 +884,8 @@
|
|||||||
0EC1BBA820D7D803007C4C7B /* ConnectionStrategy.swift in Sources */,
|
0EC1BBA820D7D803007C4C7B /* ConnectionStrategy.swift in Sources */,
|
||||||
0EFEB46F2006D3C800F81029 /* IOInterface.swift in Sources */,
|
0EFEB46F2006D3C800F81029 /* IOInterface.swift in Sources */,
|
||||||
0E07598020F0060E00F38FD8 /* CryptoAEAD.m in Sources */,
|
0E07598020F0060E00F38FD8 /* CryptoAEAD.m in Sources */,
|
||||||
|
0E39BCEA214B2AB60035E9DE /* ControlPacket.m in Sources */,
|
||||||
|
0E12B2AB2145E01700B4BAE9 /* ControlChannelSerializer.swift in Sources */,
|
||||||
0EFEB4662006D3C800F81029 /* ZeroingData.swift in Sources */,
|
0EFEB4662006D3C800F81029 /* ZeroingData.swift in Sources */,
|
||||||
0EBBF2F3208505D300E36B40 /* NEUDPInterface.swift in Sources */,
|
0EBBF2F3208505D300E36B40 /* NEUDPInterface.swift in Sources */,
|
||||||
0EFEB4682006D3C800F81029 /* MSS.m in Sources */,
|
0EFEB4682006D3C800F81029 /* MSS.m in Sources */,
|
||||||
@ -869,6 +898,7 @@
|
|||||||
0EFEB4782006D3C800F81029 /* TunnelKitProvider+Configuration.swift in Sources */,
|
0EFEB4782006D3C800F81029 /* TunnelKitProvider+Configuration.swift in Sources */,
|
||||||
0E3E0F212108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */,
|
0E3E0F212108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */,
|
||||||
0EFEB4752006D3C800F81029 /* Errors.m in Sources */,
|
0EFEB4752006D3C800F81029 /* Errors.m in Sources */,
|
||||||
|
0E12B2A521454F7F00B4BAE9 /* BidirectionalState.swift in Sources */,
|
||||||
0EBBF2E52084FE6F00E36B40 /* GenericSocket.swift in Sources */,
|
0EBBF2E52084FE6F00E36B40 /* GenericSocket.swift in Sources */,
|
||||||
0EFEB4762006D3C800F81029 /* DataPath.m in Sources */,
|
0EFEB4762006D3C800F81029 /* DataPath.m in Sources */,
|
||||||
0E0C2127212ED29D008AB282 /* SessionProxy+Configuration.swift in Sources */,
|
0E0C2127212ED29D008AB282 /* SessionProxy+Configuration.swift in Sources */,
|
||||||
@ -896,6 +926,7 @@
|
|||||||
0EFEB4962006D7F300F81029 /* ProtocolMacros.swift in Sources */,
|
0EFEB4962006D7F300F81029 /* ProtocolMacros.swift in Sources */,
|
||||||
0EFEB48A2006D7C400F81029 /* TunnelKitProvider.swift in Sources */,
|
0EFEB48A2006D7C400F81029 /* TunnelKitProvider.swift in Sources */,
|
||||||
0E0C2126212ED29D008AB282 /* SessionError.swift in Sources */,
|
0E0C2126212ED29D008AB282 /* SessionError.swift in Sources */,
|
||||||
|
0E12B2A921456C0200B4BAE9 /* ControlChannel.swift in Sources */,
|
||||||
0EBBF2FB2085061600E36B40 /* NETCPInterface.swift in Sources */,
|
0EBBF2FB2085061600E36B40 /* NETCPInterface.swift in Sources */,
|
||||||
0EFEB4982006D7F300F81029 /* ZeroingData.swift in Sources */,
|
0EFEB4982006D7F300F81029 /* ZeroingData.swift in Sources */,
|
||||||
0EFEB4A32006D7F300F81029 /* Errors.m in Sources */,
|
0EFEB4A32006D7F300F81029 /* Errors.m in Sources */,
|
||||||
@ -908,6 +939,8 @@
|
|||||||
0E07596020EF6D1400F38FD8 /* CryptoCBC.m in Sources */,
|
0E07596020EF6D1400F38FD8 /* CryptoCBC.m in Sources */,
|
||||||
0EC1BBA920D7D803007C4C7B /* ConnectionStrategy.swift in Sources */,
|
0EC1BBA920D7D803007C4C7B /* ConnectionStrategy.swift in Sources */,
|
||||||
0EFEB4932006D7F300F81029 /* CryptoBox.m in Sources */,
|
0EFEB4932006D7F300F81029 /* CryptoBox.m in Sources */,
|
||||||
|
0E39BCEB214B2AB60035E9DE /* ControlPacket.m in Sources */,
|
||||||
|
0E12B2AC2145E01700B4BAE9 /* ControlChannelSerializer.swift in Sources */,
|
||||||
0E07598120F0060E00F38FD8 /* CryptoAEAD.m in Sources */,
|
0E07598120F0060E00F38FD8 /* CryptoAEAD.m in Sources */,
|
||||||
0EFEB49C2006D7F300F81029 /* Data+Manipulation.swift in Sources */,
|
0EFEB49C2006D7F300F81029 /* Data+Manipulation.swift in Sources */,
|
||||||
0EBBF2F4208505D400E36B40 /* NEUDPInterface.swift in Sources */,
|
0EBBF2F4208505D400E36B40 /* NEUDPInterface.swift in Sources */,
|
||||||
@ -920,6 +953,7 @@
|
|||||||
0EFEB4A42006D7F300F81029 /* DataPath.m in Sources */,
|
0EFEB4A42006D7F300F81029 /* DataPath.m in Sources */,
|
||||||
0EBBF2E62084FE6F00E36B40 /* GenericSocket.swift in Sources */,
|
0EBBF2E62084FE6F00E36B40 /* GenericSocket.swift in Sources */,
|
||||||
0E3E0F222108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */,
|
0E3E0F222108A8CC00B371C1 /* SessionProxy+PushReply.swift in Sources */,
|
||||||
|
0E12B2A621454F7F00B4BAE9 /* BidirectionalState.swift in Sources */,
|
||||||
0EFEB4912006D7F300F81029 /* TLSBox.m in Sources */,
|
0EFEB4912006D7F300F81029 /* TLSBox.m in Sources */,
|
||||||
0EFEB49D2006D7F300F81029 /* IOInterface.swift in Sources */,
|
0EFEB49D2006D7F300F81029 /* IOInterface.swift in Sources */,
|
||||||
0E0C2128212ED29D008AB282 /* SessionProxy+Configuration.swift in Sources */,
|
0E0C2128212ED29D008AB282 /* SessionProxy+Configuration.swift in Sources */,
|
||||||
|
@ -224,7 +224,7 @@ class NETCPLink: LinkInterface {
|
|||||||
|
|
||||||
var newBuffer = buffer
|
var newBuffer = buffer
|
||||||
newBuffer.append(contentsOf: data)
|
newBuffer.append(contentsOf: data)
|
||||||
let (until, packets) = CommonPacket.parsed(newBuffer)
|
let (until, packets) = PacketStream.packets(from: newBuffer)
|
||||||
newBuffer = newBuffer.subdata(in: until..<newBuffer.count)
|
newBuffer = newBuffer.subdata(in: until..<newBuffer.count)
|
||||||
self?.loopReadPackets(queue, newBuffer, handler)
|
self?.loopReadPackets(queue, newBuffer, handler)
|
||||||
|
|
||||||
@ -234,14 +234,14 @@ class NETCPLink: LinkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
|
||||||
let stream = CommonPacket.stream(packet)
|
let stream = PacketStream.stream(from: packet)
|
||||||
impl.write(stream) { (error) in
|
impl.write(stream) { (error) in
|
||||||
completionHandler?(error)
|
completionHandler?(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
|
||||||
let stream = CommonPacket.stream(packets)
|
let stream = PacketStream.stream(from: packets)
|
||||||
impl.write(stream) { (error) in
|
impl.write(stream) { (error) in
|
||||||
completionHandler?(error)
|
completionHandler?(error)
|
||||||
}
|
}
|
||||||
|
@ -280,9 +280,10 @@ open class TunnelKitProvider: NEPacketTunnelProvider {
|
|||||||
|
|
||||||
case .dataCount:
|
case .dataCount:
|
||||||
if let proxy = proxy {
|
if let proxy = proxy {
|
||||||
|
let dataCount = proxy.dataCount()
|
||||||
response = Data()
|
response = Data()
|
||||||
response?.append(UInt64(proxy.bytesIn))
|
response?.append(UInt64(dataCount.0)) // inbound
|
||||||
response?.append(UInt64(proxy.bytesOut))
|
response?.append(UInt64(dataCount.1)) // outbound
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
65
TunnelKit/Sources/Core/BidirectionalState.swift
Normal file
65
TunnelKit/Sources/Core/BidirectionalState.swift
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// BidirectionalState.swift
|
||||||
|
// TunnelKit
|
||||||
|
//
|
||||||
|
// 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 Foundation
|
||||||
|
|
||||||
|
/// A generic structure holding a pair of inbound/outbound states.
|
||||||
|
public class BidirectionalState<T> {
|
||||||
|
private let resetValue: T
|
||||||
|
|
||||||
|
/// The inbound state.
|
||||||
|
public var inbound: T
|
||||||
|
|
||||||
|
/// The outbound state.
|
||||||
|
public var outbound: T
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns current state as a pair.
|
||||||
|
|
||||||
|
- Returns: Current state as a pair, inbound first.
|
||||||
|
*/
|
||||||
|
public var pair: (T, T) {
|
||||||
|
return (inbound, outbound)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Inits state with a value that will later be reused by `reset()`.
|
||||||
|
|
||||||
|
- Parameter value: The value to initialize with and reset to.
|
||||||
|
*/
|
||||||
|
public init(withResetValue value: T) {
|
||||||
|
inbound = value
|
||||||
|
outbound = value
|
||||||
|
resetValue = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Resets state to the value provided with `init(withResetValue:)`.
|
||||||
|
*/
|
||||||
|
public func reset() {
|
||||||
|
inbound = resetValue
|
||||||
|
outbound = resetValue
|
||||||
|
}
|
||||||
|
}
|
230
TunnelKit/Sources/Core/ControlChannel.swift
Normal file
230
TunnelKit/Sources/Core/ControlChannel.swift
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
//
|
||||||
|
// ControlChannel.swift
|
||||||
|
// TunnelKit
|
||||||
|
//
|
||||||
|
// 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 Foundation
|
||||||
|
import __TunnelKitNative
|
||||||
|
import SwiftyBeaver
|
||||||
|
|
||||||
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
|
class ControlChannelError: Error, CustomStringConvertible {
|
||||||
|
let description: String
|
||||||
|
|
||||||
|
init(_ message: String) {
|
||||||
|
description = "\(String(describing: ControlChannelError.self))(\(message))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ControlChannel {
|
||||||
|
private let serializer: ControlChannelSerializer
|
||||||
|
|
||||||
|
private(set) var sessionId: Data?
|
||||||
|
|
||||||
|
var remoteSessionId: Data? {
|
||||||
|
didSet {
|
||||||
|
if let id = remoteSessionId {
|
||||||
|
log.debug("Control: Remote sessionId is \(id.toHex())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var queue: BidirectionalState<[ControlPacket]>
|
||||||
|
|
||||||
|
private var currentPacketId: BidirectionalState<UInt32>
|
||||||
|
|
||||||
|
private var pendingAcks: Set<UInt32>
|
||||||
|
|
||||||
|
private var plainBuffer: ZeroingData
|
||||||
|
|
||||||
|
private var dataCount: BidirectionalState<Int>
|
||||||
|
|
||||||
|
convenience init() {
|
||||||
|
self.init(serializer: PlainSerializer())
|
||||||
|
}
|
||||||
|
|
||||||
|
private init(serializer: ControlChannelSerializer) {
|
||||||
|
self.serializer = serializer
|
||||||
|
sessionId = nil
|
||||||
|
remoteSessionId = nil
|
||||||
|
queue = BidirectionalState(withResetValue: [])
|
||||||
|
currentPacketId = BidirectionalState(withResetValue: 0)
|
||||||
|
pendingAcks = []
|
||||||
|
plainBuffer = Z(count: TLSBoxMaxBufferLength)
|
||||||
|
dataCount = BidirectionalState(withResetValue: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset(forNewSession: Bool) throws {
|
||||||
|
if forNewSession {
|
||||||
|
try sessionId = SecureRandom.data(length: PacketSessionIdLength)
|
||||||
|
remoteSessionId = nil
|
||||||
|
}
|
||||||
|
queue.reset()
|
||||||
|
currentPacketId.reset()
|
||||||
|
pendingAcks.removeAll()
|
||||||
|
plainBuffer.zero()
|
||||||
|
dataCount.reset()
|
||||||
|
serializer.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func readInboundPacket(withData data: Data, offset: Int) throws -> ControlPacket {
|
||||||
|
let packet = try serializer.deserialize(data: data, start: offset, end: nil)
|
||||||
|
log.debug("Control: Read packet \(packet)")
|
||||||
|
if let ackIds = packet.ackIds as? [UInt32], let ackRemoteSessionId = packet.ackRemoteSessionId {
|
||||||
|
try readAcks(ackIds, acksRemoteSessionId: ackRemoteSessionId)
|
||||||
|
}
|
||||||
|
return packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func enqueueInboundPacket(packet: ControlPacket) -> [ControlPacket] {
|
||||||
|
queue.inbound.append(packet)
|
||||||
|
queue.inbound.sort { $0.packetId < $1.packetId }
|
||||||
|
|
||||||
|
var toHandle: [ControlPacket] = []
|
||||||
|
for queuedPacket in queue.inbound {
|
||||||
|
if queuedPacket.packetId < currentPacketId.inbound {
|
||||||
|
queue.inbound.removeFirst()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if queuedPacket.packetId != currentPacketId.inbound {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
toHandle.append(queuedPacket)
|
||||||
|
|
||||||
|
currentPacketId.inbound += 1
|
||||||
|
queue.inbound.removeFirst()
|
||||||
|
}
|
||||||
|
return toHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
func enqueueOutboundPackets(withCode code: PacketCode, key: UInt8, payload: Data, maxPacketSize: Int) {
|
||||||
|
guard let sessionId = sessionId else {
|
||||||
|
fatalError("Missing sessionId, do reset(forNewSession: true) first")
|
||||||
|
}
|
||||||
|
|
||||||
|
let oldIdOut = currentPacketId.outbound
|
||||||
|
var queuedCount = 0
|
||||||
|
var offset = 0
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
let subPayloadLength = min(maxPacketSize, payload.count - offset)
|
||||||
|
let subPayloadData = payload.subdata(offset: offset, count: subPayloadLength)
|
||||||
|
let packet = ControlPacket(code: code, key: key, sessionId: sessionId, packetId: currentPacketId.outbound, payload: subPayloadData)
|
||||||
|
|
||||||
|
queue.outbound.append(packet)
|
||||||
|
currentPacketId.outbound += 1
|
||||||
|
offset += maxPacketSize
|
||||||
|
queuedCount += subPayloadLength
|
||||||
|
} while (offset < payload.count)
|
||||||
|
|
||||||
|
assert(queuedCount == payload.count)
|
||||||
|
|
||||||
|
// packet count
|
||||||
|
let packetCount = currentPacketId.outbound - oldIdOut
|
||||||
|
if (packetCount > 1) {
|
||||||
|
log.debug("Control: Enqueued \(packetCount) packets [\(oldIdOut)-\(currentPacketId.outbound - 1)]")
|
||||||
|
} else {
|
||||||
|
log.debug("Control: Enqueued 1 packet [\(oldIdOut)]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeOutboundPackets() throws -> [Data] {
|
||||||
|
var rawList: [Data] = []
|
||||||
|
for packet in queue.outbound {
|
||||||
|
if let sentDate = packet.sentDate {
|
||||||
|
let timeAgo = -sentDate.timeIntervalSinceNow
|
||||||
|
guard (timeAgo >= CoreConfiguration.retransmissionLimit) else {
|
||||||
|
log.debug("Control: Skip writing packet with packetId \(packet.packetId) (sent on \(sentDate), \(timeAgo) seconds ago)")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Control: Write control packet \(packet)")
|
||||||
|
|
||||||
|
let raw = try serializer.serialize(packet: packet)
|
||||||
|
rawList.append(raw)
|
||||||
|
packet.sentDate = Date()
|
||||||
|
|
||||||
|
// track pending acks for sent packets
|
||||||
|
pendingAcks.insert(packet.packetId)
|
||||||
|
}
|
||||||
|
// log.verbose("Packets now pending ack: \(pendingAcks)")
|
||||||
|
return rawList
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasPendingAcks() -> Bool {
|
||||||
|
return !pendingAcks.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ruby: handle_acks
|
||||||
|
private func readAcks(_ packetIds: [UInt32], acksRemoteSessionId: Data) throws {
|
||||||
|
guard let sessionId = sessionId else {
|
||||||
|
throw SessionError.missingSessionId
|
||||||
|
}
|
||||||
|
guard acksRemoteSessionId == sessionId else {
|
||||||
|
log.error("Control: Ack session mismatch (\(acksRemoteSessionId.toHex()) != \(sessionId.toHex()))")
|
||||||
|
throw SessionError.sessionMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop queued out packets if ack-ed
|
||||||
|
for (i, packet) in queue.outbound.enumerated() {
|
||||||
|
if packetIds.contains(packet.packetId) {
|
||||||
|
queue.outbound.remove(at: i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove ack-ed packets from pending
|
||||||
|
pendingAcks.subtract(packetIds)
|
||||||
|
|
||||||
|
// log.verbose("Packets still pending ack: \(pendingAcks)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeAcks(withKey key: UInt8, ackPacketIds: [UInt32], ackRemoteSessionId: Data) throws -> Data {
|
||||||
|
guard let sessionId = sessionId else {
|
||||||
|
throw SessionError.missingSessionId
|
||||||
|
}
|
||||||
|
let packet = ControlPacket(key: key, sessionId: sessionId, ackIds: ackPacketIds as [NSNumber], ackRemoteSessionId: ackRemoteSessionId)
|
||||||
|
log.debug("Control: Write ack packet \(packet)")
|
||||||
|
return try serializer.serialize(packet: packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentControlData(withTLS tls: TLSBox) throws -> ZeroingData {
|
||||||
|
var length = 0
|
||||||
|
try tls.pullRawPlainText(plainBuffer.mutableBytes, length: &length)
|
||||||
|
return plainBuffer.withOffset(0, count: length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addReceivedDataCount(_ count: Int) {
|
||||||
|
dataCount.inbound += count
|
||||||
|
}
|
||||||
|
|
||||||
|
func addSentDataCount(_ count: Int) {
|
||||||
|
dataCount.outbound += count
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentDataCount() -> (Int, Int) {
|
||||||
|
return dataCount.pair
|
||||||
|
}
|
||||||
|
}
|
129
TunnelKit/Sources/Core/ControlChannelSerializer.swift
Normal file
129
TunnelKit/Sources/Core/ControlChannelSerializer.swift
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
//
|
||||||
|
// ControlChannelSerializer.swift
|
||||||
|
// TunnelKit
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 9/10/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
|
||||||
|
import __TunnelKitNative
|
||||||
|
import SwiftyBeaver
|
||||||
|
|
||||||
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
|
protocol ControlChannelSerializer {
|
||||||
|
func reset()
|
||||||
|
|
||||||
|
func serialize(packet: ControlPacket) throws -> Data
|
||||||
|
|
||||||
|
func deserialize(data: Data, start: Int, end: Int?) throws -> ControlPacket
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ControlChannel {
|
||||||
|
class PlainSerializer: ControlChannelSerializer {
|
||||||
|
func reset() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func serialize(packet: ControlPacket) throws -> Data {
|
||||||
|
return packet.serialized()
|
||||||
|
}
|
||||||
|
|
||||||
|
func deserialize(data packet: Data, start: Int, end: Int?) throws -> ControlPacket {
|
||||||
|
var offset = start
|
||||||
|
let end = end ?? packet.count
|
||||||
|
|
||||||
|
guard end >= offset + PacketHeaderLength else {
|
||||||
|
throw ControlChannelError("Missing header")
|
||||||
|
}
|
||||||
|
let codeValue = packet[offset] >> 3
|
||||||
|
guard let code = PacketCode(rawValue: codeValue) else {
|
||||||
|
throw ControlChannelError("Unknown code: \(codeValue))")
|
||||||
|
}
|
||||||
|
let key = packet[offset] & 0b111
|
||||||
|
offset += PacketHeaderLength
|
||||||
|
|
||||||
|
log.debug("Control: Try read packet with code \(code) and key \(key)")
|
||||||
|
|
||||||
|
guard end >= offset + PacketSessionIdLength else {
|
||||||
|
throw ControlChannelError("Missing sessionId")
|
||||||
|
}
|
||||||
|
let sessionId = packet.subdata(offset: offset, count: PacketSessionIdLength)
|
||||||
|
offset += PacketSessionIdLength
|
||||||
|
|
||||||
|
guard end >= offset + 1 else {
|
||||||
|
throw ControlChannelError("Missing ackSize")
|
||||||
|
}
|
||||||
|
let ackSize = packet[offset]
|
||||||
|
offset += 1
|
||||||
|
|
||||||
|
var ackIds: [UInt32]?
|
||||||
|
var ackRemoteSessionId: Data?
|
||||||
|
if ackSize > 0 {
|
||||||
|
guard end >= (offset + Int(ackSize) * PacketIdLength) else {
|
||||||
|
throw ControlChannelError("Missing acks")
|
||||||
|
}
|
||||||
|
var ids: [UInt32] = []
|
||||||
|
for _ in 0..<ackSize {
|
||||||
|
let id = packet.networkUInt32Value(from: offset)
|
||||||
|
ids.append(id)
|
||||||
|
offset += PacketIdLength
|
||||||
|
}
|
||||||
|
|
||||||
|
guard end >= offset + PacketSessionIdLength else {
|
||||||
|
throw ControlChannelError("Missing remoteSessionId")
|
||||||
|
}
|
||||||
|
let remoteSessionId = packet.subdata(offset: offset, count: PacketSessionIdLength)
|
||||||
|
offset += PacketSessionIdLength
|
||||||
|
|
||||||
|
ackIds = ids
|
||||||
|
ackRemoteSessionId = remoteSessionId
|
||||||
|
}
|
||||||
|
|
||||||
|
if code == .ackV1 {
|
||||||
|
guard let ackIds = ackIds else {
|
||||||
|
throw ControlChannelError("Ack packet without ids")
|
||||||
|
}
|
||||||
|
guard let ackRemoteSessionId = ackRemoteSessionId else {
|
||||||
|
throw ControlChannelError("Ack packet without remoteSessionId")
|
||||||
|
}
|
||||||
|
return ControlPacket(key: key, sessionId: sessionId, ackIds: ackIds as [NSNumber], ackRemoteSessionId: ackRemoteSessionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard end >= offset + PacketIdLength else {
|
||||||
|
throw ControlChannelError("Missing packetId")
|
||||||
|
}
|
||||||
|
let packetId = packet.networkUInt32Value(from: offset)
|
||||||
|
offset += PacketIdLength
|
||||||
|
|
||||||
|
var payload: Data?
|
||||||
|
if offset < end {
|
||||||
|
payload = packet.subdata(in: offset..<end)
|
||||||
|
}
|
||||||
|
|
||||||
|
let controlPacket = ControlPacket(code: code, key: key, sessionId: sessionId, packetId: packetId, payload: payload)
|
||||||
|
if let ackIds = ackIds {
|
||||||
|
controlPacket.ackIds = ackIds as [NSNumber]
|
||||||
|
controlPacket.ackRemoteSessionId = ackRemoteSessionId
|
||||||
|
}
|
||||||
|
return controlPacket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
TunnelKit/Sources/Core/ControlPacket.h
Normal file
60
TunnelKit/Sources/Core/ControlPacket.h
Normal 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
|
137
TunnelKit/Sources/Core/ControlPacket.m
Normal file
137
TunnelKit/Sources/Core/ControlPacket.m
Normal 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
|
@ -290,7 +290,7 @@ const NSInteger CryptoAEADTagLength = 16;
|
|||||||
self.crypto.extraLength = PacketIdLength;
|
self.crypto.extraLength = PacketIdLength;
|
||||||
self.crypto.extraPacketIdOffset = 0;
|
self.crypto.extraPacketIdOffset = 0;
|
||||||
self.setDataHeader = ^(uint8_t *to, uint8_t key) {
|
self.setDataHeader = ^(uint8_t *to, uint8_t key) {
|
||||||
PacketHeaderSet(to, PacketCodeDataV1, key);
|
PacketHeaderSet(to, PacketCodeDataV1, key, nil);
|
||||||
};
|
};
|
||||||
self.checkPeerId = NULL;
|
self.checkPeerId = NULL;
|
||||||
}
|
}
|
||||||
|
@ -328,7 +328,7 @@ const NSInteger CryptoCBCMaxHMACLength = 100;
|
|||||||
if (peerId == PacketPeerIdDisabled) {
|
if (peerId == PacketPeerIdDisabled) {
|
||||||
self.headerLength = 1;
|
self.headerLength = 1;
|
||||||
self.setDataHeader = ^(uint8_t *to, uint8_t key) {
|
self.setDataHeader = ^(uint8_t *to, uint8_t key) {
|
||||||
PacketHeaderSet(to, PacketCodeDataV1, key);
|
PacketHeaderSet(to, PacketCodeDataV1, key, nil);
|
||||||
};
|
};
|
||||||
self.checkPeerId = NULL;
|
self.checkPeerId = NULL;
|
||||||
}
|
}
|
||||||
|
@ -38,20 +38,20 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import __TunnelKitNative
|
import __TunnelKitNative
|
||||||
|
|
||||||
class CommonPacket {
|
// TODO: convert to C for efficiency
|
||||||
let packetId: UInt32
|
|
||||||
|
|
||||||
let code: PacketCode
|
/// Reads and writes packets as a stream. Useful for stream-oriented links (e.g TCP/IP).
|
||||||
|
public class PacketStream {
|
||||||
|
|
||||||
let key: UInt8
|
/**
|
||||||
|
Parses packets from a stream.
|
||||||
|
|
||||||
let sessionId: Data?
|
- Parameter stream: The data stream.
|
||||||
|
- Returns: A pair where the first value is the `Int` offset up to which
|
||||||
let payload: Data?
|
the stream could be parsed, and the second value is an array containing
|
||||||
|
the parsed packets up to such offset.
|
||||||
var sentDate: Date?
|
*/
|
||||||
|
public static func packets(from stream: Data) -> (Int, [Data]) {
|
||||||
static func parsed(_ stream: Data) -> (Int, [Data]) {
|
|
||||||
var ni = 0
|
var ni = 0
|
||||||
var parsed: [Data] = []
|
var parsed: [Data] = []
|
||||||
while (ni + 2 <= stream.count) {
|
while (ni + 2 <= stream.count) {
|
||||||
@ -68,14 +68,26 @@ class CommonPacket {
|
|||||||
return (ni, parsed)
|
return (ni, parsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func stream(_ packet: Data) -> Data {
|
/**
|
||||||
var stream = Data(capacity: 2 + packet.count)
|
Creates a contiguous stream of packets.
|
||||||
stream.append(UInt16(packet.count).bigEndian)
|
|
||||||
stream.append(contentsOf: packet)
|
- Parameter packet: The packet.
|
||||||
return stream
|
- 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
|
||||||
}
|
}
|
||||||
|
|
||||||
static func stream(_ packets: [Data]) -> Data {
|
/**
|
||||||
|
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()
|
var raw = Data()
|
||||||
for payload in packets {
|
for payload in packets {
|
||||||
raw.append(UInt16(payload.count).bigEndian)
|
raw.append(UInt16(payload.count).bigEndian)
|
||||||
@ -84,27 +96,50 @@ class CommonPacket {
|
|||||||
return raw
|
return raw
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_ packetId: UInt32, _ code: PacketCode, _ key: UInt8, _ sessionId: Data?, _ payload: Data?) {
|
private init() {
|
||||||
self.packetId = packetId
|
|
||||||
self.code = code
|
|
||||||
self.key = key
|
|
||||||
self.sessionId = sessionId
|
|
||||||
self.payload = payload
|
|
||||||
self.sentDate = nil
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ruby: send_ctrl
|
/// :nodoc:
|
||||||
func toBuffer() -> Data {
|
extension ControlPacket {
|
||||||
var raw = PacketWithHeader(code, key, sessionId)
|
|
||||||
raw.append(UInt8(0))
|
/// :nodoc:
|
||||||
raw.append(UInt32(packetId).bigEndian)
|
open override var description: String {
|
||||||
if let payload = payload {
|
var msg: [String] = ["\(code) | \(key)"]
|
||||||
raw.append(payload)
|
msg.append("sid: \(sessionId.toHex())")
|
||||||
|
if let ackIds = ackIds, let ackRemoteSessionId = ackRemoteSessionId {
|
||||||
|
msg.append("acks: {\(ackIds), \(ackRemoteSessionId.toHex())}")
|
||||||
}
|
}
|
||||||
return raw
|
if !isAck {
|
||||||
|
msg.append("pid: \(packetId)")
|
||||||
|
}
|
||||||
|
if let payload = payload {
|
||||||
|
if CoreConfiguration.logsSensitiveData {
|
||||||
|
msg.append("[\(payload.count) bytes] -> \(payload.toHex())")
|
||||||
|
} else {
|
||||||
|
msg.append("[\(payload.count) bytes]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "{\(msg.joined(separator: ", "))}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DataPacket {
|
class DataPacket {
|
||||||
static let pingString = Data(hex: "2a187bf3641eb4cb07ed2d0a981fc748")
|
static let pingString = Data(hex: "2a187bf3641eb4cb07ed2d0a981fc748")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
|
extension PacketCode: CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
switch self {
|
||||||
|
case .softResetV1: return "SOFT_RESET_V1"
|
||||||
|
case .controlV1: return "CONTROL_V1"
|
||||||
|
case .ackV1: return "ACK_V1"
|
||||||
|
case .dataV1: return "DATA_V1"
|
||||||
|
case .hardResetClientV2: return "HARD_RESET_CLIENT_V2"
|
||||||
|
case .hardResetServerV2: return "HARD_RESET_SERVER_V2"
|
||||||
|
case .dataV2: return "DATA_V2"
|
||||||
|
case .unknown: return "UNKNOWN"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -39,8 +39,12 @@
|
|||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
#define PacketPeerIdDisabled 0xffffffu
|
#define PacketHeaderLength ((NSInteger)1)
|
||||||
#define PacketIdLength 4
|
#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) {
|
typedef NS_ENUM(uint8_t, PacketCode) {
|
||||||
PacketCodeSoftResetV1 = 0x03,
|
PacketCodeSoftResetV1 = 0x03,
|
||||||
@ -59,27 +63,21 @@ typedef NS_ENUM(uint8_t, PacketCode) {
|
|||||||
extern const uint8_t DataPacketPingData[16];
|
extern const uint8_t DataPacketPingData[16];
|
||||||
|
|
||||||
// Ruby: header
|
// 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);
|
*(uint8_t *)to = (code << 3) | (key & 0b111);
|
||||||
return sizeof(uint8_t);
|
int offset = PacketHeaderLength;
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
if (sessionId) {
|
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)
|
static inline int PacketHeaderSetDataV2(uint8_t *to, uint8_t key, uint32_t peerId)
|
||||||
{
|
{
|
||||||
*(uint32_t *)to = ((PacketCodeDataV2 << 3) | (key & 0b111)) | htonl(peerId & 0xffffff);
|
*(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)
|
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);
|
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
|
NS_ASSUME_NONNULL_END
|
||||||
|
@ -36,14 +36,8 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import __TunnelKitNative
|
|
||||||
|
|
||||||
class ProtocolMacros {
|
class ProtocolMacros {
|
||||||
static let peerIdLength = 3
|
|
||||||
|
|
||||||
static let sessionIdLength = 8
|
|
||||||
|
|
||||||
static let packetIdLength = 4
|
|
||||||
|
|
||||||
// UInt32(0) + UInt8(KeyMethod = 2)
|
// UInt32(0) + UInt8(KeyMethod = 2)
|
||||||
static let tlsPrefix = Data(hex: "0000000002")
|
static let tlsPrefix = Data(hex: "0000000002")
|
||||||
|
@ -127,15 +127,15 @@ extension SessionProxy {
|
|||||||
/// The path to the optional CA for TLS negotiation (PEM format).
|
/// The path to the optional CA for TLS negotiation (PEM format).
|
||||||
public var caPath: String?
|
public var caPath: String?
|
||||||
|
|
||||||
/// Sets compression framing, disabled by default.
|
|
||||||
public var compressionFraming: CompressionFraming
|
|
||||||
|
|
||||||
/// The path to the optional client certificate for TLS negotiation (PEM format).
|
/// The path to the optional client certificate for TLS negotiation (PEM format).
|
||||||
public var clientCertificatePath: String?
|
public var clientCertificatePath: String?
|
||||||
|
|
||||||
/// The path to the private key for the certificate at `clientCertificatePath` (PEM format).
|
/// The path to the private key for the certificate at `clientCertificatePath` (PEM format).
|
||||||
public var clientKeyPath: String?
|
public var clientKeyPath: String?
|
||||||
|
|
||||||
|
/// Sets compression framing, disabled by default.
|
||||||
|
public var compressionFraming: CompressionFraming
|
||||||
|
|
||||||
/// Sends periodical keep-alive packets if set.
|
/// Sends periodical keep-alive packets if set.
|
||||||
public var keepAliveInterval: TimeInterval?
|
public var keepAliveInterval: TimeInterval?
|
||||||
|
|
||||||
|
@ -136,19 +136,13 @@ public class SessionProxy {
|
|||||||
return link?.isReliable ?? false
|
return link?.isReliable ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
private var sessionId: Data?
|
|
||||||
|
|
||||||
private var remoteSessionId: Data?
|
|
||||||
|
|
||||||
private var pushReply: SessionReply?
|
private var pushReply: SessionReply?
|
||||||
|
|
||||||
private var nextPushRequestDate: Date?
|
private var nextPushRequestDate: Date?
|
||||||
|
|
||||||
private var connectedDate: Date?
|
private var connectedDate: Date?
|
||||||
|
|
||||||
private var lastPingOut: Date
|
private var lastPing: BidirectionalState<Date>
|
||||||
|
|
||||||
private var lastPingIn: Date
|
|
||||||
|
|
||||||
private var isStopping: Bool
|
private var isStopping: Bool
|
||||||
|
|
||||||
@ -157,26 +151,10 @@ public class SessionProxy {
|
|||||||
|
|
||||||
// MARK: Control
|
// MARK: Control
|
||||||
|
|
||||||
private let controlPlainBuffer: ZeroingData
|
private var controlChannel: ControlChannel
|
||||||
|
|
||||||
private var controlQueueOut: [CommonPacket]
|
|
||||||
|
|
||||||
private var controlQueueIn: [CommonPacket]
|
|
||||||
|
|
||||||
private var controlPendingAcks: Set<UInt32>
|
|
||||||
|
|
||||||
private var controlPacketIdOut: UInt32
|
|
||||||
|
|
||||||
private var controlPacketIdIn: UInt32
|
|
||||||
|
|
||||||
private var authenticator: Authenticator?
|
private var authenticator: Authenticator?
|
||||||
|
|
||||||
// MARK: Data
|
|
||||||
|
|
||||||
private(set) var bytesIn: Int
|
|
||||||
|
|
||||||
private(set) var bytesOut: Int
|
|
||||||
|
|
||||||
// MARK: Init
|
// MARK: Init
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -192,18 +170,10 @@ public class SessionProxy {
|
|||||||
keys = [:]
|
keys = [:]
|
||||||
oldKeys = []
|
oldKeys = []
|
||||||
negotiationKeyIdx = 0
|
negotiationKeyIdx = 0
|
||||||
lastPingOut = Date.distantPast
|
lastPing = BidirectionalState(withResetValue: Date.distantPast)
|
||||||
lastPingIn = Date.distantPast
|
|
||||||
isStopping = false
|
isStopping = false
|
||||||
|
|
||||||
controlPlainBuffer = Z(count: TLSBoxMaxBufferLength)
|
controlChannel = ControlChannel()
|
||||||
controlQueueOut = []
|
|
||||||
controlQueueIn = []
|
|
||||||
controlPendingAcks = []
|
|
||||||
controlPacketIdOut = 0
|
|
||||||
controlPacketIdIn = 0
|
|
||||||
bytesIn = 0
|
|
||||||
bytesOut = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -285,6 +255,15 @@ public class SessionProxy {
|
|||||||
loopTunnel()
|
loopTunnel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the current data bytes count.
|
||||||
|
|
||||||
|
- Returns: The current data bytes count as a pair, inbound first.
|
||||||
|
*/
|
||||||
|
public func dataCount() -> (Int, Int) {
|
||||||
|
return controlChannel.currentDataCount()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Shuts down the session with an optional `Error` reason. Does nothing if the session is already stopped or about to stop.
|
Shuts down the session with an optional `Error` reason. Does nothing if the session is already stopped or about to stop.
|
||||||
|
|
||||||
@ -329,8 +308,6 @@ public class SessionProxy {
|
|||||||
negotiationKeyIdx = 0
|
negotiationKeyIdx = 0
|
||||||
currentKeyIdx = nil
|
currentKeyIdx = nil
|
||||||
|
|
||||||
sessionId = nil
|
|
||||||
remoteSessionId = nil
|
|
||||||
nextPushRequestDate = nil
|
nextPushRequestDate = nil
|
||||||
connectedDate = nil
|
connectedDate = nil
|
||||||
authenticator = nil
|
authenticator = nil
|
||||||
@ -433,7 +410,7 @@ public class SessionProxy {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lastPingIn = Date()
|
lastPing.inbound = Date()
|
||||||
|
|
||||||
var dataPacketsByKey = [UInt8: [Data]]()
|
var dataPacketsByKey = [UInt8: [Data]]()
|
||||||
|
|
||||||
@ -441,7 +418,7 @@ public class SessionProxy {
|
|||||||
// log.verbose("Received data from LINK (\(packet.count) bytes): \(packet.toHex())")
|
// log.verbose("Received data from LINK (\(packet.count) bytes): \(packet.toHex())")
|
||||||
|
|
||||||
guard let firstByte = packet.first else {
|
guard let firstByte = packet.first else {
|
||||||
log.warning("Dropped malformed packet (missing header)")
|
log.warning("Dropped malformed packet (missing opcode)")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let codeValue = firstByte >> 3
|
let codeValue = firstByte >> 3
|
||||||
@ -449,20 +426,19 @@ public class SessionProxy {
|
|||||||
log.warning("Dropped malformed packet (unknown code: \(codeValue))")
|
log.warning("Dropped malformed packet (unknown code: \(codeValue))")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let key = firstByte & 0b111
|
// log.verbose("Parsed packet with code \(code)")
|
||||||
|
|
||||||
// log.verbose("Parsed packet with (code, key) = (\(code.rawValue), \(key))")
|
|
||||||
|
|
||||||
var offset = 1
|
var offset = 1
|
||||||
if (code == .dataV2) {
|
if (code == .dataV2) {
|
||||||
guard packet.count >= offset + ProtocolMacros.peerIdLength else {
|
guard packet.count >= offset + PacketPeerIdLength else {
|
||||||
log.warning("Dropped malformed packet (missing peerId)")
|
log.warning("Dropped malformed packet (missing peerId)")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
offset += ProtocolMacros.peerIdLength
|
offset += PacketPeerIdLength
|
||||||
}
|
}
|
||||||
|
|
||||||
if (code == .dataV1) || (code == .dataV2) {
|
if (code == .dataV1) || (code == .dataV2) {
|
||||||
|
let key = firstByte & 0b111
|
||||||
guard let _ = keys[key] else {
|
guard let _ = keys[key] else {
|
||||||
log.error("Key with id \(key) not found")
|
log.error("Key with id \(key) not found")
|
||||||
deferStop(.shutdown, SessionError.badKey)
|
deferStop(.shutdown, SessionError.badKey)
|
||||||
@ -477,90 +453,26 @@ public class SessionProxy {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
guard packet.count >= offset + ProtocolMacros.sessionIdLength else {
|
let controlPacket: ControlPacket
|
||||||
log.warning("Dropped malformed packet (missing sessionId)")
|
do {
|
||||||
|
let parsedPacket = try controlChannel.readInboundPacket(withData: packet, offset: 0)
|
||||||
|
handleAcks()
|
||||||
|
if parsedPacket.code == .ackV1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let sessionId = packet.subdata(offset: offset, count: ProtocolMacros.sessionIdLength)
|
controlPacket = parsedPacket
|
||||||
offset += ProtocolMacros.sessionIdLength
|
} catch let e {
|
||||||
|
log.warning("Dropped malformed packet: \(e)")
|
||||||
guard packet.count >= offset + 1 else {
|
|
||||||
log.warning("Dropped malformed packet (missing ackSize)")
|
|
||||||
continue
|
continue
|
||||||
}
|
// deferStop(.shutdown, e)
|
||||||
let ackSize = packet[offset]
|
// return
|
||||||
offset += 1
|
|
||||||
|
|
||||||
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 {
|
|
||||||
log.warning("Dropped malformed packet (missing acks)")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var ackedPacketIds = [UInt32]()
|
|
||||||
for _ in 0..<ackSize {
|
|
||||||
let ackedPacketId = packet.networkUInt32Value(from: offset)
|
|
||||||
ackedPacketIds.append(ackedPacketId)
|
|
||||||
offset += ProtocolMacros.packetIdLength
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guard packet.count >= offset + ProtocolMacros.sessionIdLength else {
|
sendAck(for: controlPacket)
|
||||||
log.warning("Dropped malformed packet (missing remoteSessionId)")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
let remoteSessionId = packet.subdata(offset: offset, count: ProtocolMacros.sessionIdLength)
|
|
||||||
offset += ProtocolMacros.sessionIdLength
|
|
||||||
|
|
||||||
log.debug("Server acked packetIds \(ackedPacketIds) with remoteSessionId \(remoteSessionId.toHex())")
|
let pendingInboundQueue = controlChannel.enqueueInboundPacket(packet: controlPacket)
|
||||||
|
for inboundPacket in pendingInboundQueue {
|
||||||
handleAcks(ackedPacketIds, remoteSessionId: remoteSessionId)
|
handleControlPacket(inboundPacket)
|
||||||
}
|
|
||||||
|
|
||||||
if (code == .ackV1) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
guard packet.count >= offset + ProtocolMacros.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
|
|
||||||
|
|
||||||
sendAck(key: key, packetId: packetId, remoteSessionId: sessionId)
|
|
||||||
|
|
||||||
var payload: Data?
|
|
||||||
if (offset < packet.count) {
|
|
||||||
payload = packet.subdata(in: offset..<packet.count)
|
|
||||||
|
|
||||||
if let payload = payload {
|
|
||||||
if CoreConfiguration.logsSensitiveData {
|
|
||||||
log.debug("Control packet payload (\(payload.count) bytes): \(payload.toHex())")
|
|
||||||
} else {
|
|
||||||
log.debug("Control packet payload (\(payload.count) bytes)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let controlPacket = CommonPacket(packetId, code, key, sessionId, payload)
|
|
||||||
controlQueueIn.append(controlPacket)
|
|
||||||
controlQueueIn.sort { $0.packetId < $1.packetId }
|
|
||||||
|
|
||||||
for queuedControlPacket in controlQueueIn {
|
|
||||||
if (queuedControlPacket.packetId < controlPacketIdIn) {
|
|
||||||
controlQueueIn.removeFirst()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (queuedControlPacket.packetId != controlPacketIdIn) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
handleControlPacket(queuedControlPacket)
|
|
||||||
|
|
||||||
controlPacketIdIn += 1
|
|
||||||
controlQueueIn.removeFirst()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,7 +492,7 @@ public class SessionProxy {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
sendDataPackets(packets)
|
sendDataPackets(packets)
|
||||||
lastPingOut = Date()
|
lastPing.outbound = Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: ping
|
// Ruby: ping
|
||||||
@ -590,14 +502,14 @@ public class SessionProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let now = Date()
|
let now = Date()
|
||||||
guard (now.timeIntervalSince(lastPingIn) <= CoreConfiguration.pingTimeout) else {
|
guard (now.timeIntervalSince(lastPing.inbound) <= CoreConfiguration.pingTimeout) else {
|
||||||
deferStop(.shutdown, SessionError.pingTimeout)
|
deferStop(.shutdown, SessionError.pingTimeout)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// postpone ping if elapsed less than keep-alive
|
// postpone ping if elapsed less than keep-alive
|
||||||
if let interval = keepAliveInterval {
|
if let interval = keepAliveInterval {
|
||||||
let elapsed = now.timeIntervalSince(lastPingOut)
|
let elapsed = now.timeIntervalSince(lastPing.outbound)
|
||||||
guard (elapsed >= interval) else {
|
guard (elapsed >= interval) else {
|
||||||
scheduleNextPing(elapsed: elapsed)
|
scheduleNextPing(elapsed: elapsed)
|
||||||
return
|
return
|
||||||
@ -606,7 +518,7 @@ public class SessionProxy {
|
|||||||
|
|
||||||
log.debug("Send ping")
|
log.debug("Send ping")
|
||||||
sendDataPackets([DataPacket.pingString])
|
sendDataPackets([DataPacket.pingString])
|
||||||
lastPingOut = Date()
|
lastPing.outbound = Date()
|
||||||
|
|
||||||
scheduleNextPing()
|
scheduleNextPing()
|
||||||
}
|
}
|
||||||
@ -624,30 +536,21 @@ public class SessionProxy {
|
|||||||
// MARK: Handshake
|
// MARK: Handshake
|
||||||
|
|
||||||
// Ruby: reset_ctrl
|
// Ruby: reset_ctrl
|
||||||
private func resetControlChannel() {
|
private func resetControlChannel(forNewSession: Bool) {
|
||||||
controlPlainBuffer.zero()
|
|
||||||
controlQueueOut.removeAll()
|
|
||||||
controlQueueIn.removeAll()
|
|
||||||
controlPendingAcks.removeAll()
|
|
||||||
controlPacketIdOut = 0
|
|
||||||
controlPacketIdIn = 0
|
|
||||||
authenticator = nil
|
authenticator = nil
|
||||||
bytesIn = 0
|
do {
|
||||||
bytesOut = 0
|
try controlChannel.reset(forNewSession: forNewSession)
|
||||||
|
} catch let e {
|
||||||
|
deferStop(.shutdown, e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: hard_reset
|
// Ruby: hard_reset
|
||||||
private func hardReset() {
|
private func hardReset() {
|
||||||
log.debug("Send hard reset")
|
log.debug("Send hard reset")
|
||||||
|
|
||||||
resetControlChannel()
|
resetControlChannel(forNewSession: true)
|
||||||
pushReply = nil
|
pushReply = nil
|
||||||
do {
|
|
||||||
try sessionId = SecureRandom.data(length: ProtocolMacros.sessionIdLength)
|
|
||||||
} catch let e {
|
|
||||||
deferStop(.shutdown, e)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
negotiationKeyIdx = 0
|
negotiationKeyIdx = 0
|
||||||
let newKey = SessionKey(id: UInt8(negotiationKeyIdx))
|
let newKey = SessionKey(id: UInt8(negotiationKeyIdx))
|
||||||
keys[negotiationKeyIdx] = newKey
|
keys[negotiationKeyIdx] = newKey
|
||||||
@ -661,7 +564,7 @@ public class SessionProxy {
|
|||||||
private func softReset() {
|
private func softReset() {
|
||||||
log.debug("Send soft reset")
|
log.debug("Send soft reset")
|
||||||
|
|
||||||
resetControlChannel()
|
resetControlChannel(forNewSession: false)
|
||||||
negotiationKeyIdx = max(1, (negotiationKeyIdx + 1) % ProtocolMacros.numberOfKeys)
|
negotiationKeyIdx = max(1, (negotiationKeyIdx + 1) % ProtocolMacros.numberOfKeys)
|
||||||
let newKey = SessionKey(id: UInt8(negotiationKeyIdx))
|
let newKey = SessionKey(id: UInt8(negotiationKeyIdx))
|
||||||
keys[negotiationKeyIdx] = newKey
|
keys[negotiationKeyIdx] = newKey
|
||||||
@ -750,41 +653,32 @@ public class SessionProxy {
|
|||||||
// MARK: Control
|
// MARK: Control
|
||||||
|
|
||||||
// Ruby: handle_ctrl_pkt
|
// Ruby: handle_ctrl_pkt
|
||||||
private func handleControlPacket(_ packet: CommonPacket) {
|
private func handleControlPacket(_ packet: ControlPacket) {
|
||||||
guard (packet.key == negotiationKey.id) else {
|
guard (packet.key == negotiationKey.id) else {
|
||||||
log.error("Bad key in control packet (\(packet.key) != \(negotiationKey.id))")
|
log.error("Bad key in control packet (\(packet.key) != \(negotiationKey.id))")
|
||||||
// deferStop(.shutdown, SessionError.badKey)
|
// deferStop(.shutdown, SessionError.badKey)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("Handle control packet with code \(packet.code.rawValue) and id \(packet.packetId)")
|
|
||||||
|
|
||||||
if (((packet.code == .hardResetServerV2) && (negotiationKey.state == .hardReset)) ||
|
if (((packet.code == .hardResetServerV2) && (negotiationKey.state == .hardReset)) ||
|
||||||
((packet.code == .softResetV1) && (negotiationKey.state == .softReset))) {
|
((packet.code == .softResetV1) && (negotiationKey.state == .softReset))) {
|
||||||
|
|
||||||
if (negotiationKey.state == .hardReset) {
|
if negotiationKey.state == .hardReset {
|
||||||
guard let sessionId = packet.sessionId else {
|
controlChannel.remoteSessionId = packet.sessionId
|
||||||
|
}
|
||||||
|
guard let remoteSessionId = controlChannel.remoteSessionId else {
|
||||||
|
log.error("No remote sessionId (never set)")
|
||||||
deferStop(.shutdown, SessionError.missingSessionId)
|
deferStop(.shutdown, SessionError.missingSessionId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
remoteSessionId = sessionId
|
guard packet.sessionId == remoteSessionId else {
|
||||||
}
|
log.error("Packet session mismatch (\(packet.sessionId.toHex()) != \(remoteSessionId.toHex()))")
|
||||||
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()))")
|
|
||||||
}
|
|
||||||
deferStop(.shutdown, SessionError.sessionMismatch)
|
deferStop(.shutdown, SessionError.sessionMismatch)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
negotiationKey.state = .tls
|
negotiationKey.state = .tls
|
||||||
|
|
||||||
log.debug("Remote sessionId is \(remoteSessionId.toHex())")
|
|
||||||
log.debug("Start TLS handshake")
|
log.debug("Start TLS handshake")
|
||||||
|
|
||||||
negotiationKey.tlsOptional = TLSBox(
|
negotiationKey.tlsOptional = TLSBox(
|
||||||
@ -808,14 +702,13 @@ public class SessionProxy {
|
|||||||
enqueueControlPackets(code: .controlV1, key: negotiationKey.id, payload: cipherTextOut)
|
enqueueControlPackets(code: .controlV1, key: negotiationKey.id, payload: cipherTextOut)
|
||||||
}
|
}
|
||||||
else if ((packet.code == .controlV1) && (negotiationKey.state == .tls)) {
|
else if ((packet.code == .controlV1) && (negotiationKey.state == .tls)) {
|
||||||
guard let remoteSessionId = remoteSessionId else {
|
guard let remoteSessionId = controlChannel.remoteSessionId else {
|
||||||
|
log.error("No remote sessionId found in packet (control packets before server HARD_RESET)")
|
||||||
deferStop(.shutdown, SessionError.missingSessionId)
|
deferStop(.shutdown, SessionError.missingSessionId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard (packet.sessionId == remoteSessionId) else {
|
guard packet.sessionId == remoteSessionId else {
|
||||||
if let packetSessionId = packet.sessionId {
|
log.error("Packet session mismatch (\(packet.sessionId.toHex()) != \(remoteSessionId.toHex()))")
|
||||||
log.error("Packet session mismatch (\(packetSessionId.toHex()) != \(remoteSessionId.toHex()))")
|
|
||||||
}
|
|
||||||
deferStop(.shutdown, SessionError.sessionMismatch)
|
deferStop(.shutdown, SessionError.sessionMismatch)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -838,10 +731,7 @@ public class SessionProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
var length = 0
|
let controlData = try controlChannel.currentControlData(withTLS: negotiationKey.tls)
|
||||||
try negotiationKey.tls.pullRawPlainText(controlPlainBuffer.mutableBytes, length: &length)
|
|
||||||
|
|
||||||
let controlData = controlPlainBuffer.withOffset(0, count: length)
|
|
||||||
handleControlData(controlData)
|
handleControlData(controlData)
|
||||||
} catch _ {
|
} catch _ {
|
||||||
}
|
}
|
||||||
@ -850,7 +740,9 @@ public class SessionProxy {
|
|||||||
|
|
||||||
// Ruby: handle_ctrl_data
|
// Ruby: handle_ctrl_data
|
||||||
private func handleControlData(_ data: ZeroingData) {
|
private func handleControlData(_ data: ZeroingData) {
|
||||||
guard let auth = authenticator else { return }
|
guard let auth = authenticator else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if CoreConfiguration.logsSensitiveData {
|
if CoreConfiguration.logsSensitiveData {
|
||||||
log.debug("Pulled plain control data (\(data.count) bytes): \(data.toHex())")
|
log.debug("Pulled plain control data (\(data.count) bytes): \(data.toHex())")
|
||||||
@ -947,63 +839,26 @@ public class SessionProxy {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let oldIdOut = controlPacketIdOut
|
controlChannel.enqueueOutboundPackets(withCode: code, key: key, payload: payload, maxPacketSize: link.mtu)
|
||||||
let maxCount = link.mtu
|
|
||||||
var queuedCount = 0
|
|
||||||
var offset = 0
|
|
||||||
|
|
||||||
repeat {
|
|
||||||
let subPayloadLength = min(maxCount, payload.count - offset)
|
|
||||||
let subPayloadData = payload.subdata(offset: offset, count: subPayloadLength)
|
|
||||||
let packet = CommonPacket(controlPacketIdOut, code, key, sessionId, subPayloadData)
|
|
||||||
|
|
||||||
controlQueueOut.append(packet)
|
|
||||||
controlPacketIdOut += 1
|
|
||||||
offset += maxCount
|
|
||||||
queuedCount += subPayloadLength
|
|
||||||
} while (offset < payload.count)
|
|
||||||
|
|
||||||
assert(queuedCount == payload.count)
|
|
||||||
|
|
||||||
let packetCount = controlPacketIdOut - oldIdOut
|
|
||||||
if (packetCount > 1) {
|
|
||||||
log.debug("Enqueued \(packetCount) control packets [\(oldIdOut)-\(controlPacketIdOut - 1)]")
|
|
||||||
} else {
|
|
||||||
log.debug("Enqueued 1 control packet [\(oldIdOut)]")
|
|
||||||
}
|
|
||||||
|
|
||||||
flushControlQueue()
|
flushControlQueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: flush_ctrl_q_out
|
// Ruby: flush_ctrl_q_out
|
||||||
private func flushControlQueue() {
|
private func flushControlQueue() {
|
||||||
for controlPacket in controlQueueOut {
|
let rawList: [Data]
|
||||||
if let sentDate = controlPacket.sentDate {
|
do {
|
||||||
let timeAgo = -sentDate.timeIntervalSinceNow
|
rawList = try controlChannel.writeOutboundPackets()
|
||||||
guard (timeAgo >= CoreConfiguration.retransmissionLimit) else {
|
} catch let e {
|
||||||
log.debug("Skip control packet with id \(controlPacket.packetId) (sent on \(sentDate), \(timeAgo) seconds ago)")
|
log.warning("Failed control packet serialization: \(e)")
|
||||||
continue
|
deferStop(.shutdown, e)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
for raw in rawList {
|
||||||
|
|
||||||
log.debug("Send control packet with code \(controlPacket.code.rawValue)")
|
|
||||||
|
|
||||||
if let payload = controlPacket.payload {
|
|
||||||
if CoreConfiguration.logsSensitiveData {
|
|
||||||
log.debug("Control packet has payload (\(payload.count) bytes): \(payload.toHex())")
|
|
||||||
} else {
|
|
||||||
log.debug("Control packet has payload (\(payload.count) bytes)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let raw = controlPacket.toBuffer()
|
|
||||||
log.debug("Send control packet (\(raw.count) bytes): \(raw.toHex())")
|
log.debug("Send control packet (\(raw.count) bytes): \(raw.toHex())")
|
||||||
|
}
|
||||||
// track pending acks for sent packets
|
|
||||||
controlPendingAcks.insert(controlPacket.packetId)
|
|
||||||
|
|
||||||
// WARNING: runs in Network.framework queue
|
// WARNING: runs in Network.framework queue
|
||||||
link?.writePacket(raw) { [weak self] (error) in
|
link?.writePackets(rawList) { [weak self] (error) in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
self?.queue.sync {
|
self?.queue.sync {
|
||||||
log.error("Failed LINK write during control flush: \(error)")
|
log.error("Failed LINK write during control flush: \(error)")
|
||||||
@ -1012,9 +867,6 @@ public class SessionProxy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
controlPacket.sentDate = Date()
|
|
||||||
}
|
|
||||||
// log.verbose("Packets now pending ack: \(controlPendingAcks)")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: setup_keys
|
// Ruby: setup_keys
|
||||||
@ -1022,10 +874,10 @@ public class SessionProxy {
|
|||||||
guard let auth = authenticator else {
|
guard let auth = authenticator else {
|
||||||
fatalError("Setting up encryption without having authenticated")
|
fatalError("Setting up encryption without having authenticated")
|
||||||
}
|
}
|
||||||
guard let sessionId = sessionId else {
|
guard let sessionId = controlChannel.sessionId else {
|
||||||
fatalError("Setting up encryption without a local sessionId")
|
fatalError("Setting up encryption without a local sessionId")
|
||||||
}
|
}
|
||||||
guard let remoteSessionId = remoteSessionId else {
|
guard let remoteSessionId = controlChannel.remoteSessionId else {
|
||||||
fatalError("Setting up encryption without a remote sessionId")
|
fatalError("Setting up encryption without a remote sessionId")
|
||||||
}
|
}
|
||||||
guard let serverRandom1 = auth.serverRandom1, let serverRandom2 = auth.serverRandom2 else {
|
guard let serverRandom1 = auth.serverRandom1, let serverRandom2 = auth.serverRandom2 else {
|
||||||
@ -1088,7 +940,7 @@ public class SessionProxy {
|
|||||||
|
|
||||||
// Ruby: handle_data_pkt
|
// Ruby: handle_data_pkt
|
||||||
private func handleDataPackets(_ packets: [Data], key: SessionKey) {
|
private func handleDataPackets(_ packets: [Data], key: SessionKey) {
|
||||||
bytesIn += packets.flatCount
|
controlChannel.addReceivedDataCount(packets.flatCount)
|
||||||
do {
|
do {
|
||||||
guard let decryptedPackets = try key.decrypt(packets: packets) else {
|
guard let decryptedPackets = try key.decrypt(packets: packets) else {
|
||||||
log.warning("Could not decrypt packets, is SessionKey properly configured (dataPath, peerId)?")
|
log.warning("Could not decrypt packets, is SessionKey properly configured (dataPath, peerId)?")
|
||||||
@ -1123,7 +975,7 @@ public class SessionProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: runs in Network.framework queue
|
// WARNING: runs in Network.framework queue
|
||||||
bytesOut += encryptedPackets.flatCount
|
controlChannel.addSentDataCount(encryptedPackets.flatCount)
|
||||||
link?.writePackets(encryptedPackets) { [weak self] (error) in
|
link?.writePackets(encryptedPackets) { [weak self] (error) in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
self?.queue.sync {
|
self?.queue.sync {
|
||||||
@ -1145,52 +997,40 @@ public class SessionProxy {
|
|||||||
|
|
||||||
// MARK: Acks
|
// MARK: Acks
|
||||||
|
|
||||||
// Ruby: handle_acks
|
private func handleAcks() {
|
||||||
private func handleAcks(_ packetIds: [UInt32], remoteSessionId: Data) {
|
|
||||||
guard (remoteSessionId == sessionId) else {
|
|
||||||
if let sessionId = sessionId {
|
|
||||||
log.error("Ack session mismatch (\(remoteSessionId.toHex()) != \(sessionId.toHex()))")
|
|
||||||
}
|
|
||||||
deferStop(.shutdown, SessionError.sessionMismatch)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// drop queued out packets if ack-ed
|
|
||||||
for (i, controlPacket) in controlQueueOut.enumerated() {
|
|
||||||
if packetIds.contains(controlPacket.packetId) {
|
|
||||||
controlQueueOut.remove(at: i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove ack-ed packets from pending
|
|
||||||
controlPendingAcks.subtract(packetIds)
|
|
||||||
// log.verbose("Packets still pending ack: \(controlPendingAcks)")
|
|
||||||
|
|
||||||
// retry PUSH_REQUEST if ack queue is empty (all sent packets were ack'ed)
|
// retry PUSH_REQUEST if ack queue is empty (all sent packets were ack'ed)
|
||||||
if (isReliableLink && controlPendingAcks.isEmpty) {
|
if isReliableLink && !controlChannel.hasPendingAcks() {
|
||||||
pushRequest()
|
pushRequest()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ruby: send_ack
|
// Ruby: send_ack
|
||||||
private func sendAck(key: UInt8, packetId: UInt32, remoteSessionId: Data) {
|
private func sendAck(for controlPacket: ControlPacket) {
|
||||||
log.debug("Send ack for received packetId \(packetId)")
|
log.debug("Send ack for received packetId \(controlPacket.packetId)")
|
||||||
|
|
||||||
var raw = PacketWithHeader(.ackV1, key, sessionId)
|
let raw: Data
|
||||||
raw.append(UInt8(1)) // ackSize
|
do {
|
||||||
raw.append(UInt32(packetId).bigEndian)
|
raw = try controlChannel.writeAcks(
|
||||||
raw.append(remoteSessionId)
|
withKey: controlPacket.key,
|
||||||
|
ackPacketIds: [controlPacket.packetId],
|
||||||
|
ackRemoteSessionId: controlPacket.sessionId
|
||||||
|
)
|
||||||
|
} catch let e {
|
||||||
|
deferStop(.shutdown, e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// WARNING: runs in Network.framework queue
|
// WARNING: runs in Network.framework queue
|
||||||
link?.writePacket(raw) { [weak self] (error) in
|
link?.writePacket(raw) { [weak self] (error) in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
self?.queue.sync {
|
self?.queue.sync {
|
||||||
log.error("Failed LINK write during send ack for packetId \(packetId): \(error)")
|
log.error("Failed LINK write during send ack for packetId \(controlPacket.packetId): \(error)")
|
||||||
self?.deferStop(.reconnect, SessionError.failedLinkWrite)
|
self?.deferStop(.reconnect, SessionError.failedLinkWrite)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.debug("Ack successfully written to LINK for packetId \(packetId)")
|
log.debug("Ack successfully written to LINK for packetId \(controlPacket.packetId)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ module __TunnelKitNative {
|
|||||||
header "Encryption.h"
|
header "Encryption.h"
|
||||||
header "MSS.h"
|
header "MSS.h"
|
||||||
header "PacketMacros.h"
|
header "PacketMacros.h"
|
||||||
|
header "ControlPacket.h"
|
||||||
header "ReplayProtector.h"
|
header "ReplayProtector.h"
|
||||||
header "CompressionFramingNative.h"
|
header "CompressionFramingNative.h"
|
||||||
header "DataPath.h"
|
header "DataPath.h"
|
||||||
|
@ -77,27 +77,27 @@ class LinkTests: XCTestCase {
|
|||||||
bytes.append(contentsOf: [0xaa])
|
bytes.append(contentsOf: [0xaa])
|
||||||
XCTAssertEqual(bytes.count, 21)
|
XCTAssertEqual(bytes.count, 21)
|
||||||
|
|
||||||
(until, packets) = CommonPacket.parsed(Data(bytes: bytes))
|
(until, packets) = PacketStream.packets(from: Data(bytes: bytes))
|
||||||
XCTAssertEqual(until, 18)
|
XCTAssertEqual(until, 18)
|
||||||
XCTAssertEqual(packets.count, 3)
|
XCTAssertEqual(packets.count, 3)
|
||||||
|
|
||||||
bytes.append(contentsOf: [0xbb, 0xcc])
|
bytes.append(contentsOf: [0xbb, 0xcc])
|
||||||
(until, packets) = CommonPacket.parsed(Data(bytes: bytes))
|
(until, packets) = PacketStream.packets(from: Data(bytes: bytes))
|
||||||
XCTAssertEqual(until, 23)
|
XCTAssertEqual(until, 23)
|
||||||
XCTAssertEqual(packets.count, 4)
|
XCTAssertEqual(packets.count, 4)
|
||||||
|
|
||||||
bytes.append(contentsOf: [0x00, 0x05])
|
bytes.append(contentsOf: [0x00, 0x05])
|
||||||
(until, packets) = CommonPacket.parsed(Data(bytes: bytes))
|
(until, packets) = PacketStream.packets(from: Data(bytes: bytes))
|
||||||
XCTAssertEqual(until, 23)
|
XCTAssertEqual(until, 23)
|
||||||
XCTAssertEqual(packets.count, 4)
|
XCTAssertEqual(packets.count, 4)
|
||||||
|
|
||||||
bytes.append(contentsOf: [0x11, 0x22, 0x33, 0x44])
|
bytes.append(contentsOf: [0x11, 0x22, 0x33, 0x44])
|
||||||
(until, packets) = CommonPacket.parsed(Data(bytes: bytes))
|
(until, packets) = PacketStream.packets(from: Data(bytes: bytes))
|
||||||
XCTAssertEqual(until, 23)
|
XCTAssertEqual(until, 23)
|
||||||
XCTAssertEqual(packets.count, 4)
|
XCTAssertEqual(packets.count, 4)
|
||||||
|
|
||||||
bytes.append(contentsOf: [0x55])
|
bytes.append(contentsOf: [0x55])
|
||||||
(until, packets) = CommonPacket.parsed(Data(bytes: bytes))
|
(until, packets) = PacketStream.packets(from: Data(bytes: bytes))
|
||||||
XCTAssertEqual(until, 30)
|
XCTAssertEqual(until, 30)
|
||||||
XCTAssertEqual(packets.count, 5)
|
XCTAssertEqual(packets.count, 5)
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ class LinkTests: XCTestCase {
|
|||||||
|
|
||||||
bytes.append(contentsOf: [0x00, 0x04])
|
bytes.append(contentsOf: [0x00, 0x04])
|
||||||
bytes.append(contentsOf: [0x10, 0x20])
|
bytes.append(contentsOf: [0x10, 0x20])
|
||||||
(until, packets) = CommonPacket.parsed(Data(bytes: bytes))
|
(until, packets) = PacketStream.packets(from: Data(bytes: bytes))
|
||||||
XCTAssertEqual(until, 0)
|
XCTAssertEqual(until, 0)
|
||||||
XCTAssertEqual(packets.count, 0)
|
XCTAssertEqual(packets.count, 0)
|
||||||
bytes.removeSubrange(0..<until)
|
bytes.removeSubrange(0..<until)
|
||||||
@ -117,7 +117,7 @@ class LinkTests: XCTestCase {
|
|||||||
bytes.append(contentsOf: [0x30, 0x40])
|
bytes.append(contentsOf: [0x30, 0x40])
|
||||||
bytes.append(contentsOf: [0x00, 0x07])
|
bytes.append(contentsOf: [0x00, 0x07])
|
||||||
bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40])
|
bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40])
|
||||||
(until, packets) = CommonPacket.parsed(Data(bytes: bytes))
|
(until, packets) = PacketStream.packets(from: Data(bytes: bytes))
|
||||||
XCTAssertEqual(until, 6)
|
XCTAssertEqual(until, 6)
|
||||||
XCTAssertEqual(packets.count, 1)
|
XCTAssertEqual(packets.count, 1)
|
||||||
bytes.removeSubrange(0..<until)
|
bytes.removeSubrange(0..<until)
|
||||||
@ -128,14 +128,14 @@ class LinkTests: XCTestCase {
|
|||||||
bytes.append(contentsOf: [0xff])
|
bytes.append(contentsOf: [0xff])
|
||||||
bytes.append(contentsOf: [0x00, 0x03])
|
bytes.append(contentsOf: [0x00, 0x03])
|
||||||
bytes.append(contentsOf: [0xaa])
|
bytes.append(contentsOf: [0xaa])
|
||||||
(until, packets) = CommonPacket.parsed(Data(bytes: bytes))
|
(until, packets) = PacketStream.packets(from: Data(bytes: bytes))
|
||||||
XCTAssertEqual(until, 12)
|
XCTAssertEqual(until, 12)
|
||||||
XCTAssertEqual(packets.count, 2)
|
XCTAssertEqual(packets.count, 2)
|
||||||
bytes.removeSubrange(0..<until)
|
bytes.removeSubrange(0..<until)
|
||||||
XCTAssertEqual(bytes.count, 3)
|
XCTAssertEqual(bytes.count, 3)
|
||||||
|
|
||||||
bytes.append(contentsOf: [0xbb, 0xcc])
|
bytes.append(contentsOf: [0xbb, 0xcc])
|
||||||
(until, packets) = CommonPacket.parsed(Data(bytes: bytes))
|
(until, packets) = PacketStream.packets(from: Data(bytes: bytes))
|
||||||
XCTAssertEqual(until, 5)
|
XCTAssertEqual(until, 5)
|
||||||
XCTAssertEqual(packets.count, 1)
|
XCTAssertEqual(packets.count, 1)
|
||||||
bytes.removeSubrange(0..<until)
|
bytes.removeSubrange(0..<until)
|
||||||
|
70
TunnelKitTests/PacketTests.swift
Normal file
70
TunnelKitTests/PacketTests.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user