// // IntercommOpenVPN.swift // io.privado.main.hive // // Created by Juraldinio on 6/21/20. // Copyright © 2020 Privado. All rights reserved. // import Foundation final class IntercommOpenVPN { enum Constants { static let sudoPath = "/usr/bin/sudo" } private var command: AsyncCommand? private weak var client: HiveIntercommConsole? init(client: HiveIntercommConsole?) { self.client = client } private static func authenticate(path: String, username: String, password: String, client: Socket? = nil) { do { guard let client = client else { let socket = try Socket.create(family: .unix, type: .stream, proto: .unix) try socket.connect(to: path) DispatchQueue.main.asyncAfter(deadline: .now() + 1) { Self.authenticate(path: path, username: username, password: password, client: socket) } return } if let data = try client.readString() , data.split(separator: "\r\n").map({String($0)}).first(where: {$0.contains(">PASSWORD:Need")}) != nil { try client.write(from: "username Auth \(username)\r\n") DispatchQueue.main.asyncAfter(deadline: .now() + 1) { _ = try? client.write(from: "password Auth \(password)\r\n") DispatchQueue.main.asyncAfter(deadline: .now() + 3) { client.close() } } } else { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { Self.authenticate(path: path, username: username, password: password, client: client) } } } catch { return } } } extension IntercommOpenVPN: IntercommOpenVPNType { // swiftlint:disable function_parameter_count func connect(type: Int, ovpnPath: String, configPath: String, username: String, password: String, completion: @escaping (Error?) -> Void) { guard self.command == nil else { let error = NSError(domain: PrivadoConstants.Hive.error, code: PrivadoConstants.Hive.ErrorCode.ovpnAlreadyConnected, userInfo: nil) completion(error) return } // Kill previously launched services main.run(bash: "\(Constants.sudoPath) killall openvpn") let socketPath = "/tmp/ovpn_privado_\(Int.random(in: 42..<255))" let arguments = [ovpnPath, "--config", configPath, "--management", socketPath, "unix", "--management-query-passwords", "--mute-replay-warnings"] let command = main.runAsync(Constants.sudoPath, arguments).onCompletion { [weak self] handler in // Clean command link for indicate that we already finished process self?.command = nil print(handler) } command.stdout.onStringOutput { [weak self] output in guard let client = self?.client else { return } if output.contains("Need password(s) from management interface, waiting") { Self.authenticate(path: socketPath, username: username, password: password) return } client.console(message: "\(PrivadoConstants.Hive.preffixConsole)\(output)") } command.stderror.onStringOutput { [weak self] output in guard let client = self?.client else { return } client.console(message: "\(PrivadoConstants.Hive.preffixError)\(output)") } self.command = command completion(nil) } // swiftlint:enable function_parameter_count func disconnect(completion: @escaping (Error?) -> Void) {// sudo killall openvpn guard let command = self.command else { let error = NSError(domain: PrivadoConstants.Hive.error, code: PrivadoConstants.Hive.ErrorCode.ovpnAlreadydisconnected, userInfo: nil) completion(error) return } command.interrupt() self.command = nil completion(nil) } func isConnected(completion: @escaping (Bool) -> Void) { completion(self.command != nil) } }