mirror of
https://github.com/passepartoutvpn/passepartout-apple.git
synced 2025-01-12 11:39:04 +00:00
7c27125dd7
Move the following dependencies: - OpenVPN/OpenSSL - WireGuard/Go up the chain until the main App/Tunnel targets, so that UILibrary and CommonLibrary can abstract from these unnecessary details. Instead, give module views access to generic implementations via Registry. Incidentally, this fixes an issue preventing TV previews from working due to OpenSSL linkage.
191 lines
5.8 KiB
Swift
191 lines
5.8 KiB
Swift
//
|
|
// WireGuardView.swift
|
|
// Passepartout
|
|
//
|
|
// Created by Davide De Rosa on 7/31/24.
|
|
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
|
|
//
|
|
// https://github.com/passepartoutvpn
|
|
//
|
|
// This file is part of Passepartout.
|
|
//
|
|
// Passepartout 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.
|
|
//
|
|
// Passepartout 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 Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
import CommonLibrary
|
|
import PassepartoutKit
|
|
import SwiftUI
|
|
|
|
struct WireGuardView: View, ModuleDraftEditing {
|
|
|
|
@ObservedObject
|
|
var editor: ProfileEditor
|
|
|
|
let module: WireGuardModule.Builder
|
|
|
|
let impl: WireGuardModule.Implementation?
|
|
|
|
var body: some View {
|
|
contentView
|
|
.moduleView(editor: editor, draft: draft.wrappedValue)
|
|
}
|
|
}
|
|
|
|
// MARK: - Content
|
|
|
|
private extension WireGuardView {
|
|
var configuration: WireGuard.Configuration.Builder {
|
|
guard let impl else {
|
|
fatalError("Requires WireGuardModule implementation")
|
|
}
|
|
return draft.wrappedValue.configurationBuilder ?? .init(keyGenerator: impl.keyGenerator)
|
|
}
|
|
|
|
@ViewBuilder
|
|
var contentView: some View {
|
|
moduleSection(for: interfaceRows, header: Strings.Modules.Wireguard.interface)
|
|
moduleSection(for: dnsRows, header: Strings.Unlocalized.dns)
|
|
ForEach(Array(zip(configuration.peers.indices, configuration.peers)), id: \.1.publicKey) { index, peer in
|
|
moduleSection(for: peersRows(for: peer), header: Strings.Modules.Wireguard.peer(index + 1))
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Subviews
|
|
|
|
private extension WireGuardView {
|
|
var interfaceRows: [ModuleRow]? {
|
|
var rows: [ModuleRow] = []
|
|
rows.append(.longContent(caption: Strings.Global.privateKey, value: configuration.interface.privateKey))
|
|
configuration.interface.addresses
|
|
.nilIfEmpty
|
|
.map {
|
|
rows.append(.textList(
|
|
caption: Strings.Global.addresses,
|
|
values: $0
|
|
))
|
|
}
|
|
configuration.interface.mtu.map {
|
|
rows.append(.text(caption: Strings.Unlocalized.mtu, value: $0.description))
|
|
}
|
|
return rows.nilIfEmpty
|
|
}
|
|
|
|
var dnsRows: [ModuleRow]? {
|
|
var rows: [ModuleRow] = []
|
|
|
|
configuration.interface.dns.servers
|
|
.nilIfEmpty
|
|
.map {
|
|
rows.append(.textList(
|
|
caption: Strings.Global.servers,
|
|
values: $0
|
|
))
|
|
}
|
|
|
|
configuration.interface.dns.domainName.map {
|
|
rows.append(.text(
|
|
caption: Strings.Global.domain,
|
|
value: $0
|
|
))
|
|
}
|
|
|
|
configuration.interface.dns.searchDomains?
|
|
.nilIfEmpty
|
|
.map {
|
|
rows.append(.textList(
|
|
caption: Strings.Entities.Dns.searchDomains,
|
|
values: $0
|
|
))
|
|
}
|
|
|
|
return rows.nilIfEmpty
|
|
}
|
|
|
|
func peersRows(for peer: WireGuard.RemoteInterface.Builder) -> [ModuleRow]? {
|
|
var rows: [ModuleRow] = []
|
|
rows.append(.longContent(caption: Strings.Global.publicKey, value: peer.publicKey))
|
|
peer.preSharedKey.map {
|
|
rows.append(.longContent(caption: Strings.Modules.Wireguard.presharedKey, value: $0))
|
|
}
|
|
peer.endpoint.map {
|
|
rows.append(.copiableText(caption: Strings.Global.endpoint, value: $0))
|
|
}
|
|
peer.allowedIPs
|
|
.nilIfEmpty
|
|
.map {
|
|
rows.append(.textList(
|
|
caption: Strings.Modules.Wireguard.allowedIps,
|
|
values: $0
|
|
))
|
|
}
|
|
peer.keepAlive.map {
|
|
rows.append(.text(caption: Strings.Global.keepAlive, value: TimeInterval($0).localizedDescription(style: .timeString)))
|
|
}
|
|
return rows.nilIfEmpty
|
|
}
|
|
}
|
|
|
|
private extension WireGuardView {
|
|
func importConfiguration(from url: URL) {
|
|
// TODO: #657, import draft from external URL
|
|
}
|
|
}
|
|
|
|
// MARK: - Previews
|
|
|
|
// swiftlint: disable force_try
|
|
#Preview {
|
|
let gen = MockGenerator()
|
|
|
|
var builder = WireGuard.Configuration.Builder(keyGenerator: gen)
|
|
builder.interface.addresses = ["1.1.1.1", "2.2.2.2"]
|
|
builder.interface.mtu = 1200
|
|
builder.interface.dns.protocolType = .cleartext
|
|
builder.interface.dns.servers = ["8.8.8.8", "4.4.4.4"]
|
|
builder.interface.dns.domainName = "domain.com"
|
|
builder.interface.dns.searchDomains = ["search1.com", "search2.net"]
|
|
|
|
builder.peers = (0..<3).map { _ in
|
|
var peer = WireGuard.RemoteInterface.Builder(publicKey: try! gen.publicKey(for: gen.newPrivateKey()))
|
|
peer.preSharedKey = gen.newPrivateKey()
|
|
peer.allowedIPs = ["1.1.1.1/8", "2.2.2.2/12"]
|
|
peer.endpoint = "8.8.8.8:12345"
|
|
peer.keepAlive = 30
|
|
return peer
|
|
}
|
|
|
|
let module = WireGuardModule.Builder(configurationBuilder: builder)
|
|
return module.preview()
|
|
}
|
|
// swiftlint: enable force_try
|
|
|
|
private final class MockGenerator: WireGuardKeyGenerator {
|
|
func newPrivateKey() -> String {
|
|
"private-key"
|
|
}
|
|
|
|
func privateKey(from string: String) throws -> String {
|
|
"private-key"
|
|
}
|
|
|
|
func publicKey(from string: String) throws -> String {
|
|
"public-key"
|
|
}
|
|
|
|
func publicKey(for privateKey: String) throws -> String {
|
|
"public-key"
|
|
}
|
|
}
|