From 381cfd09a296ce34155fd613459358623f984dcf Mon Sep 17 00:00:00 2001 From: consuelita Date: Fri, 6 Aug 2021 15:17:40 -0500 Subject: [PATCH] Removed Replicant and Shadow as they are now standalone transport libraries --- Package.swift | 63 -- .../Replicant/NWEndpoint.Host+Codable.swift | 107 ---- .../Replicant/NWEndpoint.Port+Codable.swift | 86 --- Sources/Replicant/ReplicantConnection.swift | 603 ------------------ .../ReplicantConnectionFactory.swift | 78 --- Sources/Shadow/AddressReader.swift | 147 ----- Sources/Shadow/Cipher.swift | 341 ---------- Sources/Shadow/ShadowConfig.swift | 63 -- Sources/Shadow/ShadowConnection.swift | 463 -------------- Sources/Shadow/ShadowConnectionFactory.swift | 93 --- Sources/Shadow/ShadowErrors.swift | 16 - Sources/Shadow/Socks.swift | 41 -- Sources/Shadow/Socks5Addr.swift | 38 -- Tests/OptimizerTests/OptimizerTests.swift | 12 +- 14 files changed, 1 insertion(+), 2150 deletions(-) delete mode 100644 Sources/Replicant/NWEndpoint.Host+Codable.swift delete mode 100644 Sources/Replicant/NWEndpoint.Port+Codable.swift delete mode 100644 Sources/Replicant/ReplicantConnection.swift delete mode 100644 Sources/Replicant/ReplicantConnectionFactory.swift delete mode 100644 Sources/Shadow/AddressReader.swift delete mode 100644 Sources/Shadow/Cipher.swift delete mode 100644 Sources/Shadow/ShadowConfig.swift delete mode 100644 Sources/Shadow/ShadowConnection.swift delete mode 100644 Sources/Shadow/ShadowConnectionFactory.swift delete mode 100644 Sources/Shadow/ShadowErrors.swift delete mode 100644 Sources/Shadow/Socks.swift delete mode 100644 Sources/Shadow/Socks5Addr.swift diff --git a/Package.swift b/Package.swift index 8d85c5c..2c93459 100644 --- a/Package.swift +++ b/Package.swift @@ -11,34 +11,19 @@ let package = Package( .iOS(.v14)], products: [ - .library(name: "Shadow", targets: ["Shadow"]), .library(name: "Protean", targets: ["Protean"]), .library(name: "Optimizer", targets: ["Optimizer"]), - .library(name: "Replicant", targets: ["Replicant"]), .library(name: "LoggerQueue", targets: ["LoggerQueue"]), .library(name: "ExampleTransports", targets: ["ExampleTransports"])], dependencies: [ - .package(url: "https://github.com/OperatorFoundation/Chord.git", from: "0.0.12"), .package(url: "https://github.com/OperatorFoundation/Datable.git", from: "3.0.4"), .package(url: "https://github.com/OperatorFoundation/ProteanSwift.git", from: "1.2.0"), - .package(url: "https://github.com/OperatorFoundation/ReplicantSwift.git", from: "0.8.6"), .package(url: "https://github.com/apple/swift-log.git", from: "1.4.2"), - .package(url: "https://github.com/OperatorFoundation/SwiftHexTools.git", from: "1.2.2"), .package(url: "https://github.com/OperatorFoundation/SwiftQueue.git", from: "0.1.0"), - .package(url: "https://github.com/OperatorFoundation/Transmission.git", from: "0.2.3"), .package(url: "https://github.com/OperatorFoundation/Transport.git", from: "2.3.5")], targets: [ - .target( - name: "Shadow", - dependencies: [ - "Chord", - "Datable", - "Transmission", - "Transport", - .product(name: "Logging", package: "swift-log")]), - .target( name: "Protean", dependencies: [ @@ -56,12 +41,6 @@ let package = Package( .product(name: "Logging", package: "swift-log")], exclude: ["Info.plist", "README.md"]), - .target( - name:"Replicant", - dependencies:[ - "ReplicantSwift", - .product(name: "Transmission", package: "Transmission")]), - .target( name: "LoggerQueue", dependencies: [ @@ -75,15 +54,6 @@ let package = Package( "Transport", .product(name: "Logging", package: "swift-log")]), - .testTarget( - name: "ShadowTests", - dependencies: [ - "Datable", - "Shadow", - "SwiftHexTools", - .product(name: "Logging", package: "swift-log")], - exclude: ["Info.plist"]), - .testTarget( name: "ProteanTests", dependencies: [ @@ -97,7 +67,6 @@ let package = Package( "Datable", "Optimizer", "Protean", - "Replicant", .product(name: "Logging", package: "swift-log"),], exclude: ["Info.plist"]), @@ -114,17 +83,13 @@ let package = Package( let package = Package( name: "Shapeshifter-Swift-Transports", products: [ - .library(name: "Shadow", targets: ["Shadow"]), .library(name: "Optimizer", targets: ["Optimizer"]), - .library(name: "Replicant", targets: ["Replicant"]), .library(name: "LoggerQueue", targets: ["LoggerQueue"]), .library(name: "ExampleTransports", targets: ["ExampleTransports"])], dependencies: [ - .package(url: "https://github.com/OperatorFoundation/Chord.git", from: "0.0.12"), .package(url: "https://github.com/OperatorFoundation/Datable.git", from: "3.0.4"), .package(url: "https://github.com/OperatorFoundation/NetworkLinux.git", from: "0.2.4"), - .package(url: "https://github.com/OperatorFoundation/ReplicantSwift.git", from: "0.8.3"), .package(url: "https://github.com/apple/swift-crypto.git", from: "1.1.2"), .package(url: "https://github.com/apple/swift-log.git", from: "1.4.0"), .package(url: "https://github.com/OperatorFoundation/SwiftHexTools.git", from: "1.2.2"), @@ -133,17 +98,6 @@ let package = Package( .package(url: "https://github.com/OperatorFoundation/Transport.git", from: "2.3.3")], targets: [ - .target( - name: "Shadow", - dependencies: [ - "Chord", - "Datable", - "Transport", - .product(name: "Crypto", package: "swift-crypto"), - .product(name: "Logging", package: "swift-log"), - .product(name: "NetworkLinux", package: "NetworkLinux"), - .product(name: "TransmissionLinux", package: "TransmissionLinux")]), - .target( name: "Optimizer", dependencies: [ @@ -154,13 +108,6 @@ let package = Package( .product(name: "TransmissionLinux", package: "TransmissionLinux")], exclude: ["Info.plist", "README.md"]), - .target( - name:"Replicant", - dependencies:[ - "ReplicantSwift", - .product(name: "Crypto", package: "swift-crypto"), - .product(name: "TransmissionLinux", package: "TransmissionLinux")]), - .target( name: "LoggerQueue", dependencies: [ @@ -178,21 +125,11 @@ let package = Package( .product(name: "NetworkLinux", package: "NetworkLinux"), .product(name: "TransmissionLinux", package: "TransmissionLinux")]), - .testTarget( - name: "ShadowTests", - dependencies: [ - "Datable", - "Shadow", - "SwiftHexTools", - .product(name: "Logging", package: "swift-log")], - exclude: ["Info.plist"]), - .testTarget( name: "OptimizerTests", dependencies: [ "Datable", "Optimizer", - "Replicant", .product(name: "Logging", package: "swift-log")], exclude: ["Info.plist"]), diff --git a/Sources/Replicant/NWEndpoint.Host+Codable.swift b/Sources/Replicant/NWEndpoint.Host+Codable.swift deleted file mode 100644 index 7690d58..0000000 --- a/Sources/Replicant/NWEndpoint.Host+Codable.swift +++ /dev/null @@ -1,107 +0,0 @@ -//// -//// NWEnpoint.Host+Codable.swift -//// Replicant -//// -//// Created by Mafalda on 1/29/19. -//// MIT License -//// -//// Copyright (c) 2020 Operator Foundation -//// -//// Permission is hereby granted, free of charge, to any person obtaining a copy -//// of this software and associated documentation files (the "Software"), to deal -//// in the Software without restriction, including without limitation the rights -//// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -//// copies of the Software, and to permit persons to whom the Software is -//// furnished to do so, subject to the following conditions: -//// -//// The above copyright notice and this permission notice shall be included in all -//// copies or substantial portions of the Software. -//// -//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -//// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -//// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE -//// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -//// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -//// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -//// SOFTWARE. -// -//import Foundation -// -//#if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) -//import Network -//#else -//import NetworkLinux -//#endif -// -//extension NWEndpoint.Host: Encodable -//{ -// public func encode(to encoder: Encoder) throws -// { -// var container = encoder.singleValueContainer() -// -// switch self -// { -// case .ipv4(let ipv4Address): -// do -// { -// let addressString = "\(ipv4Address)" -// try container.encode(addressString) -// } -// catch let error -// { -// throw error -// } -// case .ipv6(let ipv6Address): -// do -// { -// let addressString = "\(ipv6Address)" -// try container.encode(addressString) -// } -// catch let error -// { -// throw error -// } -// case .name(let nameString, _): -// do -// { -// try container.encode(nameString) -// } -// catch let error -// { -// throw error -// } -// default: -// throw HostError.invalidIP -// } -// } -//} -// -//enum HostError: Error -//{ -// case invalidIP -//} -// -//extension NWEndpoint.Host: Decodable -//{ -// public init(from decoder: Decoder) throws -// { -// do -// { -// let container = try decoder.singleValueContainer() -// -// do -// { -// let addressString = try container.decode(String.self) -// self.init(addressString) -// } -// catch let error -// { -// throw error -// } -// } -// catch let error -// { -// throw error -// } -// } -//} diff --git a/Sources/Replicant/NWEndpoint.Port+Codable.swift b/Sources/Replicant/NWEndpoint.Port+Codable.swift deleted file mode 100644 index 7db2486..0000000 --- a/Sources/Replicant/NWEndpoint.Port+Codable.swift +++ /dev/null @@ -1,86 +0,0 @@ -//// -//// NWEndpoint.Port+Codable.swift -//// Replicant -//// -//// Created by Mafalda on 1/29/19. -//// MIT License -//// -//// Copyright (c) 2020 Operator Foundation -//// -//// Permission is hereby granted, free of charge, to any person obtaining a copy -//// of this software and associated documentation files (the "Software"), to deal -//// in the Software without restriction, including without limitation the rights -//// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -//// copies of the Software, and to permit persons to whom the Software is -//// furnished to do so, subject to the following conditions: -//// -//// The above copyright notice and this permission notice shall be included in all -//// copies or substantial portions of the Software. -//// -//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -//// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -//// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE -//// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -//// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -//// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -//// SOFTWARE. -// -//import Foundation -// -//import ReplicantSwift -// -//#if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) -//import Network -//#else -//import NetworkLinux -//#endif -// -//extension NWEndpoint.Port: Encodable -//{ -// public func encode(to encoder: Encoder) throws -// { -// let portInt = self.rawValue -// var container = encoder.singleValueContainer() -// -// do -// { -// try container.encode(portInt) -// } -// catch let error -// { -// throw error -// } -// } -//} -// -//extension NWEndpoint.Port: Decodable -//{ -// public init(from decoder: Decoder) throws -// { -// do -// { -// let container = try decoder.singleValueContainer() -// -// do -// { -// let portInt = try container.decode(UInt16.self) -// guard let port = NWEndpoint.Port(rawValue: portInt) -// else -// { -// throw ReplicantError.invalidPort -// } -// -// self = port -// } -// catch let error -// { -// throw error -// } -// } -// catch let error -// { -// throw error -// } -// } -//} -// diff --git a/Sources/Replicant/ReplicantConnection.swift b/Sources/Replicant/ReplicantConnection.swift deleted file mode 100644 index 9a78179..0000000 --- a/Sources/Replicant/ReplicantConnection.swift +++ /dev/null @@ -1,603 +0,0 @@ -// -// ReplicantConnection.swift -// Shapeshifter-Swift-Transports -// -// Created by Adelita Schule on 11/21/18. -// MIT License -// -// Copyright (c) 2020 Operator Foundation -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation -import Logging - -import ReplicantSwift -import Transport - -#if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) -import CryptoKit -import Network -import Transmission -#else -import Crypto -import NetworkLinux -import TransmissionLinux -#endif - - -open class ReplicantConnection: Transport.Connection -{ - public let aesOverheadSize = 113 - public let payloadLengthOverhead = 2 - public var stateUpdateHandler: ((NWConnection.State) -> Void)? - public var viabilityUpdateHandler: ((Bool) -> Void)? - public var config: ReplicantConfig - public var replicantClientModel: ReplicantClientModel - public var log: Logger - - var unencryptedChunkSize: UInt16 = 400 // FIXME: unencrypted chunk size for non-polish - var sendTimer: Timer? - var networkQueue = DispatchQueue(label: "Replicant Queue") - var sendBufferQueue = DispatchQueue(label: "SendBuffer Queue") - var bufferLock = DispatchGroup() - var decryptedReceiveBuffer: Data - var sendBuffer: Data - - #if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) - var network: Transmission.Connection - #else - var network: TransmissionLinux.Connection - #endif - - public convenience init?(host: NWEndpoint.Host, - port: NWEndpoint.Port, - parameters: NWParameters, - config: ReplicantConfig, - logger: Logger) - { - logger.debug("Initialized a Replicant Client Connection") - - #if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) - guard let newConnection = Transmission.Connection(host: "\(host)", port: Int(port.rawValue)) - else - { - logger.error("Failed to create replicant connection. NetworkConnectionFactory.connect returned nil.") - return nil - } - #else - guard let newConnection = TransmissionLinux.Connection(host: "\(host)", port: Int(port.rawValue)) - else - { - logger.error("Failed to create replicant connection. NetworkConnectionFactory.connect returned nil.") - return nil - } - #endif - - self.init(connection: newConnection, parameters: parameters, config: config, logger: logger) - } - - #if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) - public init?(connection: Transmission.Connection, - parameters: NWParameters, - config: ReplicantConfig, - logger: Logger) - { - let newReplicant = ReplicantClientModel(withConfig: config, logger: logger) - - self.network = connection - self.config = config - self.replicantClientModel = newReplicant - self.decryptedReceiveBuffer = Data() - self.sendBuffer = Data() - self.log = logger - - if let polishConnection = replicantClientModel.polish - { - self.unencryptedChunkSize = polishConnection.chunkSize - UInt16(payloadLengthOverhead) - } - - introductions - { - (maybeIntroError) in - - guard maybeIntroError == nil - else - { - logger.error("Error attempting to meet the server during Replicant Connection Init.") - return - } - - logger.debug("\nNew Replicant connection is ready. šŸŽ‰ \n") - } - } - #else - public init?(connection: TransmissionLinux.Connection, - parameters: NWParameters, - config: ReplicantConfig, - logger: Logger) - { - let newReplicant = ReplicantClientModel(withConfig: config, logger: logger) - - self.network = connection - self.config = config - self.replicantClientModel = newReplicant - self.decryptedReceiveBuffer = Data() - self.sendBuffer = Data() - self.log = logger - - if let polishConnection = replicantClientModel.polish - { - self.unencryptedChunkSize = polishConnection.chunkSize - UInt16(payloadLengthOverhead) - } - - introductions - { - (maybeIntroError) in - - guard maybeIntroError == nil - else - { - logger.error("Error attempting to meet the server during Replicant Connection Init.") - return - } - - logger.debug("\nNew Replicant connection is ready. šŸŽ‰ \n") - } - } - #endif - - public func start(queue: DispatchQueue) - { - log.debug("\nšŸ Start called on Replicant connection.") - - guard let updateHandler = stateUpdateHandler - else - { - log.info("Called start when there is no stateUpdateHandler.") - return - } - - updateHandler(.ready) - } - - public func send(content: Data?, contentContext: NWConnection.ContentContext, isComplete: Bool, completion: NWConnection.SendCompletion) - { - log.debug("\nšŸ’Œ Send called on Replicant connection.") - if let polishConnection = replicantClientModel.polish - { - // Lock so that the timer cannot fire and change the buffer. Unlock in the network send() callback. - bufferLock.enter() - - guard let someData = content - else - { - log.error("Received a send command with no content.") - switch completion - { - case .contentProcessed(let handler): - handler(nil) - bufferLock.leave() - return - default: - bufferLock.leave() - return - } - } - - self.sendBuffer.append(someData) - sendBufferChunks(polishConnection: polishConnection, contentContext: contentContext, isComplete: isComplete, completion: completion) - } - else // No Polish needed, send the data if it's provided - { - guard let someData = content - else - { - log.error("Received a send command with no content.") - switch completion - { - case .contentProcessed(let handler): - handler(nil) - bufferLock.leave() - return - default: - bufferLock.leave() - return - } - } - - let written = network.write(data: someData) - - switch completion - { - case .contentProcessed(let handler): - if written { handler(nil) } - else { handler(NWError.posix(.EIO)) } - return - default: - return - } - } - } - - func sendBufferChunks(polishConnection: PolishConnection, contentContext: NWConnection.ContentContext, isComplete: Bool, completion: NWConnection.SendCompletion) - { - // Only encrypt and send over network when chunk size is available, leftovers to the buffer - guard self.sendBuffer.count >= (unencryptedChunkSize) - else - { - log.error("Received a send command with content less than chunk size.") - switch completion - { - case .contentProcessed(let handler): - handler(nil) - bufferLock.leave() - return - default: - bufferLock.leave() - return - } - } - - let payloadData = self.sendBuffer[0 ..< unencryptedChunkSize] - let payloadSize = UInt16(unencryptedChunkSize) - let dataChunk = payloadSize.data + payloadData - guard let polishedData = polishConnection.polish(inputData: dataChunk) - else - { - log.error("sendBufferChunks: Failed to polish data. Giving up.") - bufferLock.leave() - return - } - - // Buffer should only contain unsent data - self.sendBuffer = self.sendBuffer[unencryptedChunkSize...] - - // Turn off the timer - if self.sendTimer != nil - { - self.sendTimer!.invalidate() - self.sendTimer = nil - } - - // Keep calling network.write if the leftover data is at least chunk size - guard network.write(data: polishedData) - else - { - self.log.error("Replicant write failed.") - if self.sendTimer != nil - { - self.sendTimer!.invalidate() - self.sendTimer = nil - } - - switch completion - { - case .contentProcessed(let handler): - handler(NWError.posix(.EIO)) - self.bufferLock.leave() - return - default: - self.bufferLock.leave() - return - } - } - - if self.sendBuffer.count >= (self.unencryptedChunkSize) - { - // Play it again Sam - self.sendBufferChunks(polishConnection: polishConnection, contentContext: contentContext, isComplete: isComplete, completion: completion) - } - else - { - // Start the timer - if self.sendBuffer.count > 0 - { - self.sendTimer = Timer(timeInterval: TimeInterval(polishConnection.chunkTimeout), repeats: true) - { - (timer) in - - self.chunkTimeout() - } - } - - switch completion - { - // FIXME: There might be data in the buffer - case .contentProcessed(let handler): - handler(nil) - self.bufferLock.leave() - return - default: - self.bufferLock.leave() - return - } - } - } - - public func receive(completion: @escaping (Data?, NWConnection.ContentContext?, Bool, NWError?) -> Void) - { - self.receive(minimumIncompleteLength: 1, maximumLength: 1000000, completion: completion) - } - - public func receive(minimumIncompleteLength: Int, maximumLength: Int, completion: @escaping (Data?, NWConnection.ContentContext?, Bool, NWError?) -> Void) - { - log.debug("\nšŸ™‹ā€ā™€ļø Replicant connection receive called.\n") - - if let polishConnection = replicantClientModel.polish - { - bufferLock.enter() - - // Check to see if we have min length data in decrypted buffer before calling network receive. Skip the call if we do. - if decryptedReceiveBuffer.count >= minimumIncompleteLength - { - // Make sure that the slice we get isn't bigger than the available data count or the maximum requested. - let sliceLength = decryptedReceiveBuffer.count < maximumLength ? decryptedReceiveBuffer.count : maximumLength - - // Return the requested amount - let returnData = self.decryptedReceiveBuffer[0 ..< sliceLength] - - // Remove what was delivered from the buffer - self.decryptedReceiveBuffer = self.decryptedReceiveBuffer[sliceLength...] - - completion(returnData, NWConnection.ContentContext.defaultMessage, false, nil) - bufferLock.leave() - return - } - else - { - // Read ChunkSize amount of data - // Check to see if we got data, and that it is the right size - guard let someData = network.read(size: Int(polishConnection.chunkSize)), someData.count == polishConnection.chunkSize - else - { - self.log.error("\nšŸ™‹ā€ā™€ļø Read called but no data was receieved.\n") - completion(nil, .defaultMessage, false, NWError.posix(.ENODATA)) - return - } - - let maybeReturnData = self.handleReceivedData(polishConnection: polishConnection, minimumIncompleteLength: minimumIncompleteLength, maximumLength: maximumLength, encryptedData: someData) - - completion(maybeReturnData, .defaultMessage, false, nil) - self.bufferLock.leave() - return - } - } - else - { - // Check to see if we got data - guard let someData = network.read(size: minimumIncompleteLength) - else - { - self.log.error("\nšŸ™‹ā€ā™€ļø Read called but no data was receieved.\n") - completion(nil, .defaultMessage, false, NWError.posix(.ENODATA)) - return - } - - completion(someData, .defaultMessage, false, nil) - return - } - } - - public func cancel() - { - // TODO: network.cancel() - - if let stateUpdate = self.stateUpdateHandler - { - stateUpdate(NWConnection.State.cancelled) - } - - if let viabilityUpdate = self.viabilityUpdateHandler - { - viabilityUpdate(false) - } - } - - /// This takes an optional data and adds it to the buffer before acting on min/max lengths - func handleReceivedData(polishConnection: PolishConnection, minimumIncompleteLength: Int, maximumLength: Int, encryptedData: Data) -> Data? - { - // Try to decrypt the entire contents of the encrypted buffer - guard let decryptedData = polishConnection.unpolish(polishedData: encryptedData) - else - { - log.error("Unable to decrypt encrypted receive buffer") - return nil - } - - // The first two bytes simply lets us know the actual size of the payload - // This helps account for cases when the payload must be smaller than chunk size - guard let uintPayloadSize = decryptedData[..= minimumIncompleteLength - else - { - // Not enough data return nothing - return nil - } - - // Make sure that the slice we get isn't bigger than the available data count or the maximum requested. - let sliceLength = decryptedReceiveBuffer.count < maximumLength ? decryptedReceiveBuffer.count : maximumLength - - // Return the requested amount - let returnData = self.decryptedReceiveBuffer[0 ..< sliceLength] - - // Remove what was delivered from the buffer - self.decryptedReceiveBuffer = self.decryptedReceiveBuffer[sliceLength...] - - return returnData - } - - func voightKampffTest(completion: @escaping (Error?) -> Void) - { - // Tone Burst - if var toneBurst = self.replicantClientModel.toneBurst - { - toneBurst.play(connection: self) - { - maybeError in - - completion(maybeError) - } - } - else - { - completion(nil) - } - } - - func introductions(completion: @escaping (Error?) -> Void) - { - voightKampffTest - { - (maybeVKError) in - - guard maybeVKError == nil - else - { - self.stateUpdateHandler?(NWConnection.State.cancelled) - self.log.error("Toneburst error: \(maybeVKError!)") - completion(maybeVKError) - return - } - - if var polishConnection = self.replicantClientModel.polish - { - polishConnection.handshake(connection: self) - { - (maybeHandshakeError) in - - if let handshakeError = maybeHandshakeError - { - self.log.error("Received a handshake error: \(handshakeError)") - self.stateUpdateHandler?(NWConnection.State.cancelled) - completion(handshakeError) - return - } - else - { - self.log.debug("\nšŸ¤ Client successfully completed handshake. šŸ‘šŸ‘šŸ‘šŸ‘\n") - self.stateUpdateHandler?(NWConnection.State.ready) - completion(nil) - } - } - } - else - { - self.log.debug("No need to perform the Replicant handshake with the server, the Polish connection is nil.") - completion(nil) - } - } - } - - @objc func chunkTimeout() - { - // Lock so that send isn't called while we're working - bufferLock.enter() - - self.sendTimer = nil - - // Double check the buffer to be sure that there is still data in there. - self.log.debug("\nā° Chunk Timeout Reached\n ā°") - - let payloadSize = sendBuffer.count - - if let polishConnection = replicantClientModel.polish - { - guard payloadSize > 0, payloadSize < polishConnection.chunkSize - else - { - bufferLock.leave() - return - } - - let payloadData = self.sendBuffer - let paddingSize = Int(unencryptedChunkSize) - payloadSize - let padding = Data(repeating: 0, count: paddingSize) - let dataChunk = UInt16(payloadSize).data + payloadData + padding - let maybeEncryptedData = polishConnection.polish(inputData: dataChunk) - - // Buffer should only contain unsent data - self.sendBuffer = Data() - - // Keep calling network.send if the leftover data is at least chunk size - if let encryptedData = maybeEncryptedData - { - guard network.write(data: encryptedData) - else - { - self.log.error("Replicant Connection write failed.") - self.bufferLock.leave() - return - } - - self.bufferLock.leave() - return - } - } - else // Replicant without polish - { - guard payloadSize > 0 - else - { - bufferLock.leave() - return - } - - let payloadData = self.sendBuffer - let paddingSize = Int(unencryptedChunkSize) - payloadSize - let padding = Data(repeating: 0, count: paddingSize) - let dataChunk = UInt16(payloadSize).data + payloadData + padding - - // Buffer should only contain unsent data - self.sendBuffer = Data() - - // Keep calling network.send if the leftover data is at least chunk size - guard network.write(data: dataChunk) - else - { - self.log.error("Replicant Connection write failed.") - self.bufferLock.leave() - return - } - - self.bufferLock.leave() - return - } - } - -} - -enum ToneBurstError: Error -{ - case generateFailure - case removeFailure -} - -enum IntroductionsError: Error -{ - case nilStateHandler -} diff --git a/Sources/Replicant/ReplicantConnectionFactory.swift b/Sources/Replicant/ReplicantConnectionFactory.swift deleted file mode 100644 index feef451..0000000 --- a/Sources/Replicant/ReplicantConnectionFactory.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// ReplicantConnectionFactory.swift -// Replicant -// -// Created by Adelita Schule on 11/21/18. -// MIT License -// -// Copyright (c) 2020 Operator Foundation -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation -import Logging - -import ReplicantSwift -import SwiftQueue -import Transport - -#if os(Linux) -import NetworkLinux -#else -import Network -#endif - -open class ReplicantConnectionFactory: ConnectionFactory -{ - public var name: String = "Replicant" - public var connection: Connection? - public var host: NWEndpoint.Host? - public var port: NWEndpoint.Port? - public var config: ReplicantConfig - - let log: Logger - - public init?(ipString: String, portInt: UInt16, config: ReplicantConfig, logger: Logger) - { - self.host = NWEndpoint.Host(ipString) - self.port = NWEndpoint.Port(integerLiteral: portInt) - self.config = config - self.log = logger - } - - public init(host: String, port: UInt16, config: ReplicantConfig, log: Logger) - { - self.host = NWEndpoint.Host(host) - self.port = NWEndpoint.Port(integerLiteral: port) - self.config = config - self.log = log - } - - public func connect(using parameters: NWParameters) -> Connection? - { - guard let currentHost = host, let currentPort = port - else - { - log.error("Unable to connect, host or port is nil.") - return nil - } - - return ReplicantConnection(host: currentHost, port: currentPort, parameters: parameters, config: config, logger: log) - } -} diff --git a/Sources/Shadow/AddressReader.swift b/Sources/Shadow/AddressReader.swift deleted file mode 100644 index bc12228..0000000 --- a/Sources/Shadow/AddressReader.swift +++ /dev/null @@ -1,147 +0,0 @@ -// -// AddressReader.swift -// Shadow -// -// Created by Mafalda on 8/27/20. -// - -import Foundation - -// FIXME: Logger - -class AddressReader -{ - func createAddr() -> Data - { - let type: [UInt8] = [0x01] - let host: [UInt8] = [0x00, 0x00, 0x00, 0x00] - let port: [UInt8] = [0x00, 0x00] - - return Data(array: type + host + port) - } - - func getAddr(from buffer: Data) -> (address: Socks5Addr, remaining: Data)? - { - let typeLength = 1 - let portLength = 2 - - guard let addrType = AddrType(rawValue: Int(buffer[0])) - else - { - print("Failed to initialize Socks5Addr, AddrType is unknown: \(Int(buffer[0]))") - return nil - } - - switch addrType - { - case .domainName: - // The second byte indicates the length of the domain name - let hostLength = Int(buffer[1]) - let addrLength = typeLength + 1 + hostLength + portLength - - // Quality check to make sure that our address data is the right size - guard buffer.count >= addrLength - else - { - print("Unable to initialize a Socks5Addr. Total data size \(buffer.count) is incorrect for a domain length of \(hostLength)") - return nil - } - - // Get the address data from the beginning of the buffer - let addressData = buffer[..= addressLength - else - { - print("Received an IPv4 address with an incorrect length: \(buffer.count)") - return nil - } - - let addressData = buffer[..= addressLength - else - { - print("Received an IPv6 address with an incorrect length: \(buffer.count)") - return nil - } - - let addressData = buffer[.. Data? - { - // Salt size shoud be 16 bytes for aes128 - let saltSize: Int - - switch mode - { - case .AES_128_GCM: - saltSize = 16 - case .AES_256_GCM: - saltSize = 32 - case .CHACHA20_IETF_POLY1305: - saltSize = 32 - } - - return generateRandomBytes(count: saltSize) - } - - /* - var b, prev []byte - h := md5.New() - for len(b) < keyLen { - h.Write(prev) - h.Write([]byte(password)) - b = h.Sum(b) - prev = b[len(b)-h.Size():] - h.Reset() - } - return b[:keyLen] - */ - static func kdf(shadowConfig: ShadowConfig) -> Data - { - var keyLength: Int - var md5Hash = Insecure.MD5() - var keyBuffer = Data() - var previous = Data() - - switch shadowConfig.mode - { - case .AES_128_GCM: - keyLength = 16 - case .AES_256_GCM: - keyLength = 32 - case .CHACHA20_IETF_POLY1305: - keyLength = 32 - } - - while keyBuffer.count < keyLength - { - md5Hash.update(data: previous) - md5Hash.update(data: shadowConfig.password.data) - keyBuffer += Data(md5Hash.finalize()) - previous = keyBuffer[(keyBuffer.count - Insecure.MD5.byteCount)...] - - md5Hash = Insecure.MD5() - } - - return keyBuffer[.. Data? - { - print("HKDFSHA1") - print("\nsecret") - print(secret.array) - print("\nsalt") - print(salt.array) - let info = Data(string: "ss-subkey") - let outputSize = secret.count - - let iterations = UInt8(ceil(Double(outputSize) / Double(Insecure.SHA1.byteCount))) - guard iterations <= 255 - else - { - print("Key derviation failure: Too many iterations - \(iterations)") - return nil - } - - let prk = HMAC.authenticationCode(for: secret, using: SymmetricKey(data: salt)) - let key = SymmetricKey(data: prk) - var hkdf = Data() - var value = Data() - - for i in 1...iterations - { - value.append(info) - value.append(i) - - let code = HMAC.authenticationCode(for: value, using: key) - hkdf.append(contentsOf: code) - - value = Data(code) - } - - return hkdf.prefix(outputSize) - } - - /// [encrypted payload length][length tag][encrypted payload][payload tag] - func pack(plaintext: Data) -> Data? - { - let payloadLength = UInt16(plaintext.count) - DatableConfig.endianess = .big - - guard payloadLength <= Cipher.maxPayloadSize - else - { - log.error("Requested payload size \(plaintext.count) is greater than the maximum allowed \(Cipher.maxPayloadSize). Unable to send payload.") - return nil - } - - guard let (encryptedPayloadLength, lengthTag) = encrypt(plaintext: payloadLength.data) - else { return nil } - guard let (encryptedPayload, payloadTag) = encrypt(plaintext: plaintext) - else { return nil } - - return encryptedPayloadLength + lengthTag + encryptedPayload + payloadTag - } - - /// Returns [encrypted][payload] - private func encrypt(plaintext: Data) -> (cipherText: Data, tag: Data)? - { - var cipherText = Data() - var tag = Data() - - switch mode - { - case .AES_128_GCM: - do - { - let aesGCMNonce = try AES.GCM.Nonce(data: nonce()) - let sealedBox = try AES.GCM.seal(plaintext, using: key, nonce: aesGCMNonce) - cipherText = sealedBox.ciphertext - tag = sealedBox.tag - } - catch let encryptError - { - print("Error running AESGCM encryption: \(encryptError)") - } - - case .AES_256_GCM: - do - { - let aesGCMNonce = try AES.GCM.Nonce(data: nonce()) - let sealedBox = try AES.GCM.seal(plaintext, using: key, nonce: aesGCMNonce) - cipherText = sealedBox.ciphertext - tag = sealedBox.tag - } - catch let encryptError - { - print("Error running AESGCM encryption: \(encryptError)") - } - - case .CHACHA20_IETF_POLY1305: - do - { - let chachaPolyNonce = try ChaChaPoly.Nonce(data: nonce()) - let sealedBox = try ChaChaPoly.seal(plaintext, using: key, nonce: chachaPolyNonce) - cipherText = sealedBox.ciphertext - tag = sealedBox.tag - } - catch let encryptError - { - print("Error running ChaChaPoly encryption: \(encryptError)") - } - } - - return (cipherText, tag) - } - - func unpack(encrypted: Data, expectedCiphertextLength: Int) -> Data? - { - let ciphertext = encrypted[0.. Data? - { - switch mode - { - case .AES_128_GCM: - do - { - let sealedBox = try AES.GCM.SealedBox(nonce: AES.GCM.Nonce(data: nonce()), ciphertext: encrypted, tag: tag) - return try AES.GCM.open(sealedBox, using: key) - } - catch let decryptError - { - print("Error running AESGCM decryption: \(decryptError)") - return nil - } - case .AES_256_GCM: - do - { - let sealedBox = try AES.GCM.SealedBox(nonce: AES.GCM.Nonce(data: nonce()), ciphertext: encrypted, tag: tag) - return try AES.GCM.open(sealedBox, using: key) - } - catch let decryptError - { - print("Error running AESGCM decryption: \(decryptError)") - return nil - } - case .CHACHA20_IETF_POLY1305: - do - { - let sealedBox = try ChaChaPoly.SealedBox(nonce: ChaChaPoly.Nonce(data: nonce()), ciphertext: encrypted, tag: tag) - return try ChaChaPoly.open(sealedBox, using: key) - } - catch let decryptError - { - print("Error running ChaChaPoly decryption: \(decryptError)") - return nil - } - } - } - - func nonce() -> Data - { - DatableConfig.endianess = .little - var counterData = counter.data - - // We have 8 bytes, nonce should be 12 - counterData.append(contentsOf: [0, 0, 0, 0]) - print("Nonce counter data size: \(counterData.count)") - print("Nonce counter data: \(counterData[0]), \(counterData[11])") - - // We increment our counter every time nonce is used (encrypt/decrypt) - counter += 1 - - return counterData - } - - static func generateRandomBytes(count: Int) -> Data - { - var bytes = [UInt8]() - for _ in 1...count - { - bytes.append(UInt8.random(in: 0...255)) - } - - return Data(bytes) - } -} - -public enum CipherMode: String, Codable -{ - // AES 196 is not currently supported by go-shadowsocks2. - // We are not supporting it at this time either. - case AES_128_GCM = "AES-128-GCM" - case AES_256_GCM = "AES-256-GCM" - case CHACHA20_IETF_POLY1305 = "CHACHA20-IETF-POLY1305" -} - diff --git a/Sources/Shadow/ShadowConfig.swift b/Sources/Shadow/ShadowConfig.swift deleted file mode 100644 index cbc44e8..0000000 --- a/Sources/Shadow/ShadowConfig.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// ShadowConfig.swift -// Shadow -// -// Created by Mafalda on 8/18/20. -// - -import Foundation - -#if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) -import CryptoKit -#else -import Crypto -#endif - -public struct ShadowConfig: Codable -{ - public let password: String - public let mode: CipherMode - - private enum CodingKeys : String, CodingKey - { - case password, mode = "cipherName" - } - - public init(password: String, mode: CipherMode) - { - self.password = password - self.mode = mode - } - - init?(from data: Data) - { - let decoder = JSONDecoder() - do - { - let decoded = try decoder.decode(ShadowConfig.self, from: data) - self = decoded - } - catch let decodeError - { - print("Error decoding Shadow Config data: \(decodeError)") - return nil - } - } - - public init?(path: String) - { - let url = URL(fileURLWithPath: path) - - do - { - let data = try Data(contentsOf: url) - self.init(from: data) - } - catch (let error) - { - print("Failed to get data from path \(url.path). \nError: \(error)") - return nil - } - } - -} diff --git a/Sources/Shadow/ShadowConnection.swift b/Sources/Shadow/ShadowConnection.swift deleted file mode 100644 index e323710..0000000 --- a/Sources/Shadow/ShadowConnection.swift +++ /dev/null @@ -1,463 +0,0 @@ -// -// ShadowConnection.swift -// Shadow -// -// Created by Mafalda on 8/3/20. -// MIT License -// -// Copyright (c) 2020 Operator Foundation -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation -import Logging - -import Chord -import Datable -import Transport - -#if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) -import CryptoKit -import Network -import Transmission -#else -import Crypto -import NetworkLinux -import TransmissionLinux -#endif - -open class ShadowConnection: Transport.Connection -{ - public var stateUpdateHandler: ((NWConnection.State) -> Void)? - public var viabilityUpdateHandler: ((Bool) -> Void)? - public var log: Logger - public var config: ShadowConfig - - let networkQueue = DispatchQueue(label: "ShadowNetworkQueue") - let salt: Data - let encryptingCipher: Cipher - var decryptingCipher: Cipher? - - #if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) - var network: Transmission.Connection - #else - var network: TransmissionLinux.Connection - #endif - - public convenience init?(host: NWEndpoint.Host, - port: NWEndpoint.Port, - parameters: NWParameters, - config: ShadowConfig, - logger: Logger) - { - #if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) - guard let newConnection = Transmission.Connection(host: "\(host)", port: Int(port.rawValue)) - else - { - logger.error("Failed to initialize a ShadowConnection because we could not create a Network Connection using host \(host) and port \(Int(port.rawValue)).") - return nil - } - #else - guard let newConnection = TransmissionLinux.Connection(host: "\(host)", port: Int(port.rawValue)) - else - { - logger.error("Failed to initialize a ShadowConnection because we could not create a Network Connection using host \(host) and port \(Int(port.rawValue)).") - return nil - } - #endif - - self.init(connection: newConnection, parameters: parameters, config: config, logger: logger) - } - - #if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) - public init?(connection: Transmission.Connection, parameters: NWParameters, config: ShadowConfig, logger: Logger) - { - guard let actualSalt = Cipher.createSalt(mode: config.mode) - else - { - if let updateHandler = stateUpdateHandler - { - updateHandler(.failed(NWError.posix(.EIO))) - } - - return nil - } - - guard let eCipher = Cipher(config: config, salt: actualSalt, logger: logger) - else - { - if let updateHandler = stateUpdateHandler - { - updateHandler(.failed(NWError.posix(.EIO))) - } - - return nil - } - - self.salt = actualSalt - self.encryptingCipher = eCipher - self.network = connection - self.log = logger - self.config = config - - handshake() - } - #else - public init?(connection: TransmissionLinux.Connection, parameters: NWParameters, config: ShadowConfig, logger: Logger) - { - guard let actualSalt = Cipher.createSalt(mode: config.mode) - else - { - if let updateHandler = stateUpdateHandler - { - updateHandler(.failed(NWError.posix(.EIO))) - } - - return nil - } - - guard let eCipher = Cipher(config: config, salt: actualSalt, logger: logger) - else - { - if let updateHandler = stateUpdateHandler - { - updateHandler(.failed(NWError.posix(.EIO))) - } - - return nil - } - - self.salt = actualSalt - self.encryptingCipher = eCipher - self.network = connection - self.log = logger - self.config = config - - handshake() - } - #endif - - // MARK: Connection Protocol - - public func start(queue: DispatchQueue) - { - guard let updateHandler = stateUpdateHandler - else - { - log.info("Called start when there is no stateUpdateHandler.") - return - } - - updateHandler(.ready) - } - - public func cancel() - { - // FIXME: Need to add Connection.close() to Transmission library - // network.close() - - if let stateUpdate = self.stateUpdateHandler - { - stateUpdate(NWConnection.State.cancelled) - } - - if let viabilityUpdate = self.viabilityUpdateHandler - { - viabilityUpdate(false) - } - } - - /// Gets content and encrypts it before passing it along to the network - public func send(content: Data?, contentContext: NWConnection.ContentContext, isComplete: Bool, completion: NWConnection.SendCompletion) - { - guard let someData = content - else - { - log.debug("Shadow connection received a send command with no content.") - switch completion - { - case .contentProcessed(let handler): - handler(nil) - return - default: - return - } - } - - guard let encrypted = encryptingCipher.pack(plaintext: someData) - else - { - log.error("Failed to encrypt shadow send content.") - return - } - - let written = network.write(data: encrypted) - - switch completion - { - case .contentProcessed(let handler): - if written { handler(nil) } - else { handler(NWError.posix(.EIO)) } - return - default: - return - } - } - - // Decrypts the received content before passing it along - public func receive(completion: @escaping (Data?, NWConnection.ContentContext?, Bool, NWError?) -> Void) - { - self.receive(minimumIncompleteLength: 1, maximumLength: Cipher.maxPayloadSize, completion: completion) - } - - - // TODO: Introduce buffer to honor the requested read size from the application - // Decrypts the received content before passing it along - public func receive(minimumIncompleteLength: Int, - maximumLength: Int, - completion: @escaping (Data?, NWConnection.ContentContext?, Bool, NWError?) -> Void) - { - // Get our encrypted length first - let encryptedLengthSize = Cipher.lengthSize + Cipher.tagSize - let maybeData = network.read(size: encryptedLengthSize) - - // Nothing to decrypt - guard let someData = maybeData - else - { - self.log.debug("Shadow receive called, but there was no data.") - completion(nil, .defaultMessage, false, NWError.posix(.ENODATA)) - return - } - - guard let dCipher = self.decryptingCipher - else - { - self.log.error("Unable to decrypt received data. Decrypting cipher is nil.") - completion(nil, .defaultMessage, false, NWError.posix(POSIXErrorCode.EFAULT)) - return - } - - guard let lengthData = dCipher.unpack(encrypted: someData, expectedCiphertextLength: Cipher.lengthSize) - else - { - completion(maybeData, .defaultMessage, false, NWError.posix(POSIXErrorCode.EINVAL)) - return - } - - DatableConfig.endianess = .big - - guard let lengthUInt16 = lengthData.uint16 - else - { - self.log.error("Failed to get encrypted data's expected length. Length data could not be converted to UInt16") - completion(maybeData, .defaultMessage, false, NWError.posix(POSIXErrorCode.EINVAL)) - return - } - - // Read data of payloadLength + tagSize - let payloadLength = Int(lengthUInt16) - let expectedLength = payloadLength + Cipher.tagSize - let nextMaybeData = network.read(size: expectedLength) - - self.shadowReceive(payloadLength: payloadLength, maybeData: nextMaybeData, maybeContext: .defaultMessage, connectionComplete: false, maybeError: nil, completion: completion) - } - - func shadowReceive(payloadLength: Int, - maybeData: Data?, - maybeContext: NWConnection.ContentContext?, - connectionComplete: Bool, - maybeError: NWError?, - completion: @escaping (Data?, NWConnection.ContentContext?, Bool, NWError?) -> Void) - { - // Something went wrong - if let error = maybeError - { - self.log.error("Shadow receive called, but we got an error: \(error)") - completion(maybeData, maybeContext, connectionComplete, error) - return - } - - // Nothing to decrypt - guard let someData = maybeData - else - { - self.log.debug("Shadow receive called, but there was no data.") - completion(nil, maybeContext, connectionComplete, maybeError) - return - } - - guard let dCipher = self.decryptingCipher - else - { - self.log.error("Unable to decrypt received data. Decrypting cipher is nil.") - completion(nil, maybeContext, connectionComplete, NWError.posix(POSIXErrorCode.EFAULT)) - return - } - - // Attempt tp decrypt the data we received before passing it along - guard let decrypted = dCipher.unpack(encrypted: someData, expectedCiphertextLength: payloadLength) - else - { - self.log.error("Shadow failed to decrypt received data.") - completion(someData, maybeContext, connectionComplete, NWError.posix(POSIXErrorCode.EBADMSG)) - return - } - - completion(decrypted, maybeContext, connectionComplete, maybeError) - } - - // End of Connection Protocol - - func handshake() - { - let saltSent = sendSalt() - let saltReceived = receiveSalt() - - if saltSent, saltReceived - { - if let actualStateUpdateHandler = self.stateUpdateHandler - { - actualStateUpdateHandler(.ready) - } - - if let actualViabilityUpdateHandler = self.viabilityUpdateHandler - { - actualViabilityUpdateHandler(true) - } - } - else - { - // FIXME: Add a Connection.close() function to the Transmission library - // self.network.cancel() - - if let actualStateUpdateHandler = self.stateUpdateHandler - { - actualStateUpdateHandler(.cancelled) - } - - if let actualViabilityUpdateHandler = self.viabilityUpdateHandler - { - actualViabilityUpdateHandler(false) - } - } - } - - func sendSalt() -> Bool - { - print("Sending Salt : \(salt.array)") - guard network.write(data: salt) - else - { - self.log.error("Failed to send the shadow handshake.") - return false - } - - return true - } - - func receiveSalt() -> Bool - { - let maybeSalt = network.read(size: salt.count) - - guard let serverSalt = maybeSalt - else - { - print("We did not receive salt from the server.") - return false - } - - self.log.debug("Received salt from the server: \(serverSalt.array)") - - guard serverSalt.count == self.salt.count - else - { - self.log.error("Received a salt with the wrong size. \nGot \(serverSalt.count), expected \(self.salt.count)") - - return false - } - - guard let dCipher = Cipher(config: self.config, salt: serverSalt, logger: self.log) - else - { - log.error("Unable to receive the servers salt, we were not able to construct the cipher.") - return false - } - - self.decryptingCipher = dCipher - - return true - } - - func sendAddress() - { - let address = AddressReader().createAddr() - guard let encryptedAddress = encryptingCipher.pack(plaintext: address) - else - { - self.log.error("Failed to encrypt our address. Cancelling connection.") - - // FIXME: Add Connection.close() to the Transmission library - // self.network.cancel() - - if let actualStateUpdateHandler = self.stateUpdateHandler - { - actualStateUpdateHandler(.cancelled) - } - - if let actualViabilityUpdateHandler = self.viabilityUpdateHandler - { - actualViabilityUpdateHandler(false) - } - - return - } - - print("Sending address: \(encryptedAddress.count)") - - let written = network.write(data: encryptedAddress) - if written - { - if let actualStateUpdateHandler = self.stateUpdateHandler - { - actualStateUpdateHandler(.ready) - } - - if let actualViabilityUpdateHandler = self.viabilityUpdateHandler - { - actualViabilityUpdateHandler(true) - } - } - else - { - // FIXME: Add Connection.close() to the Transmission library - // self.network.cancel() - - if let actualStateUpdateHandler = self.stateUpdateHandler - { - actualStateUpdateHandler(.cancelled) - } - - if let actualViabilityUpdateHandler = self.viabilityUpdateHandler - { - actualViabilityUpdateHandler(false) - } - } - } -} diff --git a/Sources/Shadow/ShadowConnectionFactory.swift b/Sources/Shadow/ShadowConnectionFactory.swift deleted file mode 100644 index fe0ca3f..0000000 --- a/Sources/Shadow/ShadowConnectionFactory.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// ShadowConnectionFactory.swift -// Shapeshifter-Swift-Transports -// -// Created by Mafalda on 8/3/20. -// MIT License -// -// Copyright (c) 2020 Operator Foundation -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation -import Logging - -#if os(Linux) -import NetworkLinux -#else -import Network -#endif - -import Transport - -open class ShadowConnectionFactory: ConnectionFactory -{ - var log: Logger - - public var name = "Shadow" - public var config: ShadowConfig - public var connection: Connection? - public var host: NWEndpoint.Host? - public var port: NWEndpoint.Port? - - public init(host: NWEndpoint.Host, port: NWEndpoint.Port, config: ShadowConfig, logger: Logger) - { - self.host = host - self.port = port - self.config = config - self.log = logger - } - - public func connect(using parameters: NWParameters) -> Connection? - { - guard let currentHost = host, let currentPort = port - else - { - return nil - } - - return ShadowConnection(host: currentHost, port: currentPort, parameters: parameters, config: config, logger: log) - } - -// public init(connection: Connection, config: ShadowConfig, logger: Logger) -// { -// self.connection = connection -// self.config = config -// self.log = logger -// } - -// public func connect(using parameters: NWParameters) -> Connection? -// { -// if let currentConnection = connection -// { -// return ShadowConnection(connection: currentConnection, parameters: parameters, config: config, logger: log) -// } -// else -// { -// guard let currentHost = host, let currentPort = port -// else -// { -// return nil -// } -// -// return ShadowConnection(host: currentHost, port: currentPort, parameters: parameters, config: config, logger: log) -// } -// } - -} diff --git a/Sources/Shadow/ShadowErrors.swift b/Sources/Shadow/ShadowErrors.swift deleted file mode 100644 index b0c8a66..0000000 --- a/Sources/Shadow/ShadowErrors.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// ShadowErrors.swift -// Shadow -// -// Created by Mafalda on 8/26/20. -// - -import Foundation - -enum ShadowError: Error -{ - case failedToUnpackLength - case failedToDecodeLenght - case failedToEncrypt - case failedToDecrypt -} diff --git a/Sources/Shadow/Socks.swift b/Sources/Shadow/Socks.swift deleted file mode 100644 index 40cb770..0000000 --- a/Sources/Shadow/Socks.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// Socks.swift -// Shadow -// -// Created by Mafalda on 8/26/20. -// - -import Foundation -import Logging - -import Datable - -struct Socks -{ - // MaxAddrLen is the maximum size of SOCKS address in bytes. - let maxAddrLen = 1 + 1 + 255 + 2 - let address: Socks5Addr - let log: Logger - - var udpEnabled = false -// -// func readAddr(buffer: Data) -> (addr: Socks5Addr, remaining: Data)? -// { -// guard let addrType = AddrType(rawValue: Int(buffer[0])) -// else -// { -// log.error("Failed to read Socks5Addr, AddrType is unknown: \(Int(buffer[0]))") -// return nil -// } -// -// switch addrType -// { -// case .domainName: -// <#code#> -// default: -// return nil -// } -// } -} - - diff --git a/Sources/Shadow/Socks5Addr.swift b/Sources/Shadow/Socks5Addr.swift deleted file mode 100644 index 8aa3b0a..0000000 --- a/Sources/Shadow/Socks5Addr.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// Socks5Addr.swift -// Shadow -// -// Created by Mafalda on 8/27/20. -// - -import Foundation -import Logging - -/* - Addresses used in Shadowsocks follow the SOCKS5 address format: - [1-byte type][variable-length host][2-byte port] - The port number is a 2-byte big-endian unsigned integer. - */ -/// Addr represents a SOCKS address as defined in RFC 1928 section 5. -/// [1-byte type][variable-length host][2-byte port] -struct Socks5Addr -{ - let type: AddrType - let data: Data - var string: String - -} - -/** - SOCKS address types as defined in RFC 1928 section 5. - The following address types are defined: - 0x01: host is a 4-byte IPv4 address. - 0x03: host is a variable length string, starting with a 1-byte length, followed by up to 255-byte domain name. - 0x04: host is a 16-byte IPv6 address. - */ -enum AddrType: Int -{ - case ipV4 = 1 - case domainName = 3 - case ipV6 = 4 -} diff --git a/Tests/OptimizerTests/OptimizerTests.swift b/Tests/OptimizerTests/OptimizerTests.swift index f6c1e2b..66f73c7 100644 --- a/Tests/OptimizerTests/OptimizerTests.swift +++ b/Tests/OptimizerTests/OptimizerTests.swift @@ -29,10 +29,7 @@ import XCTest import Transport import Protean import ProteanSwift -import ReplicantSwift -import Replicant import SwiftQueue -//import ExampleTransports import Logging #if os(Linux) @@ -134,12 +131,6 @@ class OptimizerTests: XCTestCase let proteanConfig = Protean.Config(byteSequenceConfig: sampleSequenceConfig(), encryptionConfig: sampleEncryptionConfig(), headerConfig: sampleHeaderConfig()) - guard let replicantClientConfig = ReplicantConfig(polish: nil, toneBurst: nil) else - { - print("\nUnable to create ReplicantClient config.\n") - XCTFail() - return - } guard let portUInt = UInt16(portString), let port = NWEndpoint.Port(rawValue: portUInt) else @@ -159,11 +150,10 @@ class OptimizerTests: XCTestCase let logger = Logger(label: "test") let host = NWEndpoint.Host.ipv4(ipv4Address) - let replicantTransport = ReplicantConnectionFactory(host: "\(host)", port: port.rawValue, config: replicantClientConfig, log: logger) let proteanTransport = ProteanConnectionFactory(host: host, port: port, config: proteanConfig, logger: logger) //let passthroughTransport = PassthroughConnectionFactory(host: host, port: port, logger: logger) //let rot13Transport = Rot13ConnectionFactory(host: host, port: port, logger: logger) - let possibleTransports:[ConnectionFactory] = [replicantTransport, proteanTransport] + let possibleTransports:[ConnectionFactory] = [ proteanTransport] let strategy = CoreMLStrategy(transports: possibleTransports, logger: logger) let connected1 = expectation(description: "Connected 1.")