// // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import Foundation import Network import SignalCoreKit extension SignalProxy { /// Establishes a connection to a Signal TLS Proxy and relays transmitted data via the provided `RelayClient` class ProxyClient { @Atomic private(set) var isStarted = false let id: UUID var didStopCallback: ((Error?) -> Void)? private weak var relayClient: RelayClient? private var connection: NWConnection? private lazy var queue = DispatchQueue(label: "org.signal.proxy.client", attributes: .concurrent) init(relayClient: RelayClient) { self.id = relayClient.id self.relayClient = relayClient } static func parseHost(_ host: String) -> (String, UInt16)? { let proxyHostComponents = host.components(separatedBy: ":") guard let proxyHost = proxyHostComponents[safe: 0] else { return nil } let proxyPort: UInt16 if let portString = proxyHostComponents[safe: 1], let port = UInt16(portString) { proxyPort = port } else { proxyPort = 443 } return (proxyHost, proxyPort) } func start() { guard !isStarted else { return } isStarted = true guard let (proxyHost, proxyPort) = SignalProxy.host.flatMap({ Self.parseHost($0) }) else { return stop(error: OWSAssertionError("Unexpectedly missing proxy host!")) } // Special case for testing: UNENCRYPTED_FOR_TESTING@foo.bar connects over TCP instead of TLS. // (This is only the "outer" layer of the proxy; the inner connection to the Signal servers // still uses TLS with a pinned root cert.) let actualHost: String let networkParams: NWParameters if let atSign = proxyHost.firstIndex(of: "@"), proxyHost[..