remote(server): add automatic NAT configuration
This commit is contained in:
parent
ac5842c6a0
commit
dd4de861d9
|
@ -53,6 +53,10 @@ struct UTMServerView: View {
|
|||
ServerOverview()
|
||||
Divider()
|
||||
HStack {
|
||||
if let address = remoteServer.externalIPAddress, let port = remoteServer.externalPort {
|
||||
Text("Server IP: \(address), Port: \(String(port))")
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
Spacer()
|
||||
if remoteServer.isServerActive {
|
||||
Image(systemName: "circle.fill")
|
||||
|
|
|
@ -18,6 +18,7 @@ import Foundation
|
|||
import Combine
|
||||
import Network
|
||||
import SwiftConnect
|
||||
import SwiftPortmap
|
||||
import UserNotifications
|
||||
|
||||
let service = "_utm_server._tcp"
|
||||
|
@ -33,6 +34,7 @@ actor UTMRemoteServer {
|
|||
private var listener: Task<Void, Error>?
|
||||
private var pendingConnections: [State.ClientFingerprint: Connection] = [:]
|
||||
private var establishedConnections: [State.ClientFingerprint: Remote] = [:]
|
||||
private var natPort: SwiftPortmap.Port?
|
||||
|
||||
private func _replaceCancellables(with set: Set<AnyCancellable>) {
|
||||
cancellables = set
|
||||
|
@ -128,6 +130,21 @@ actor UTMRemoteServer {
|
|||
registerNotifications()
|
||||
listener = Task {
|
||||
await withErrorNotification {
|
||||
if isServerExternal && serverPort > 0 {
|
||||
natPort = Port.TCP(internalPort: UInt16(serverPort))
|
||||
natPort!.mappingChangedHandler = { port in
|
||||
Task {
|
||||
let address = try? await port.externalIpv4Address
|
||||
let port = try? await port.externalPort
|
||||
await self.state.setExternalAddress(address, port: port)
|
||||
}
|
||||
}
|
||||
await withErrorNotification {
|
||||
guard try await natPort!.externalPort == serverPort else {
|
||||
throw ServerError.natReservationMismatch(serverPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
let port = serverPort > 0 ? NWEndpoint.Port(integerLiteral: UInt16(serverPort)) : .any
|
||||
for try await connection in Connection.advertise(on: port, forServiceType: service, txtRecord: metadata, identity: keyManager.identity) {
|
||||
if let connection = try? await Connection(connection: connection) {
|
||||
|
@ -135,6 +152,7 @@ actor UTMRemoteServer {
|
|||
}
|
||||
}
|
||||
}
|
||||
natPort = nil
|
||||
await stop()
|
||||
}
|
||||
await state.setServerActive(true)
|
||||
|
@ -149,6 +167,7 @@ actor UTMRemoteServer {
|
|||
listener.cancel()
|
||||
_ = await listener.result
|
||||
}
|
||||
await state.setExternalAddress()
|
||||
await state.setServerActive(false)
|
||||
}
|
||||
|
||||
|
@ -462,6 +481,10 @@ extension UTMRemoteServer {
|
|||
}
|
||||
}
|
||||
|
||||
@Published private(set) var externalIPAddress: String?
|
||||
|
||||
@Published private(set) var externalPort: UInt16?
|
||||
|
||||
init() {
|
||||
var _approvedClients = Set<Client>()
|
||||
if let array = UserDefaults.standard.array(forKey: "TrustedClients") {
|
||||
|
@ -551,6 +574,11 @@ extension UTMRemoteServer {
|
|||
fileprivate func setServerFingerprint(_ fingerprint: ServerFingerprint) {
|
||||
serverFingerprint = fingerprint
|
||||
}
|
||||
|
||||
fileprivate func setExternalAddress(_ address: String? = nil, port: UInt16? = nil) {
|
||||
externalIPAddress = address
|
||||
externalPort = port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -783,6 +811,7 @@ extension UTMRemoteServer {
|
|||
extension UTMRemoteServer {
|
||||
enum ServerError: LocalizedError {
|
||||
case silentError(Error)
|
||||
case natReservationMismatch(Int)
|
||||
case notAuthenticated
|
||||
case versionMismatch
|
||||
case notFound(UUID)
|
||||
|
@ -793,6 +822,8 @@ extension UTMRemoteServer {
|
|||
switch self {
|
||||
case .silentError(let error):
|
||||
return error.localizedDescription
|
||||
case .natReservationMismatch(let port):
|
||||
return String.localizedStringWithFormat(NSLocalizedString("Cannot reserve port '%@' for external access from NAT. Make sure no other device on the network has reserved it.", comment: "UTMRemoteServer"), port)
|
||||
case .notAuthenticated:
|
||||
return NSLocalizedString("Not authenticated.", comment: "UTMRemoteServer")
|
||||
case .versionMismatch:
|
||||
|
|
|
@ -886,6 +886,7 @@
|
|||
CED8DF7528A120C100C34345 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = CED8DF7928A120C100C34345 /* Localizable.stringsdict */; };
|
||||
CED8DF7628A120C100C34345 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = CED8DF7928A120C100C34345 /* Localizable.stringsdict */; };
|
||||
CED8DF7728A120C100C34345 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = CED8DF7928A120C100C34345 /* Localizable.stringsdict */; };
|
||||
CEDD11C12B7C74D7004DDAC6 /* SwiftPortmap in Frameworks */ = {isa = PBXBuildFile; productRef = CEDD11C02B7C74D7004DDAC6 /* SwiftPortmap */; };
|
||||
CEDF83F9258AE24E0030E4AC /* UTMPasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDF83F8258AE24E0030E4AC /* UTMPasteboard.swift */; };
|
||||
CEDF83FA258AE24E0030E4AC /* UTMPasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDF83F8258AE24E0030E4AC /* UTMPasteboard.swift */; };
|
||||
CEE06B272B2FC89400A811AE /* UTMServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE06B262B2FC89400A811AE /* UTMServerView.swift */; };
|
||||
|
@ -2196,6 +2197,7 @@
|
|||
CE03D0CE24D9A30100F76B84 /* iconv.2.framework in Frameworks */,
|
||||
CE0B6EF124AD677200FE012D /* libgstplayback.a in Frameworks */,
|
||||
CE0B6EF424AD677200FE012D /* json-glib-1.0.0.framework in Frameworks */,
|
||||
CEDD11C12B7C74D7004DDAC6 /* SwiftPortmap in Frameworks */,
|
||||
CE0B6ED124AD677200FE012D /* phodav-2.0.0.framework in Frameworks */,
|
||||
CEF83F862500947D00557D15 /* gcrypt.20.framework in Frameworks */,
|
||||
CE0B6ECB24AD677200FE012D /* gstcheck-1.0.0.framework in Frameworks */,
|
||||
|
@ -3075,6 +3077,7 @@
|
|||
84B36D2127B3265400C22685 /* CocoaSpice */,
|
||||
84A0A8872A47D5C50038F329 /* QEMUKit */,
|
||||
CE9B15352B11A491003A32DD /* SwiftConnect */,
|
||||
CEDD11C02B7C74D7004DDAC6 /* SwiftPortmap */,
|
||||
);
|
||||
productName = UTM;
|
||||
productReference = CE2D951C24AD48BE0059923A /* UTM.app */;
|
||||
|
@ -3241,6 +3244,7 @@
|
|||
84E3A8FE293DBC290024A740 /* XCRemoteSwiftPackageReference "swift-argument-parser" */,
|
||||
84A0A8862A47D5C50038F329 /* XCRemoteSwiftPackageReference "QEMUKit" */,
|
||||
CE9B15342B11A491003A32DD /* XCRemoteSwiftPackageReference "SwiftConnect" */,
|
||||
CEDD11BF2B7C74D7004DDAC6 /* XCRemoteSwiftPackageReference "SwiftPortmap" */,
|
||||
);
|
||||
productRefGroup = CE550BCA225947990063E575 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -5120,6 +5124,14 @@
|
|||
minimumVersion = 6.5.6;
|
||||
};
|
||||
};
|
||||
CEDD11BF2B7C74D7004DDAC6 /* XCRemoteSwiftPackageReference "SwiftPortmap" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/osy/SwiftPortmap.git";
|
||||
requirement = {
|
||||
branch = main;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
CEF7F5852AEEDCC400E34952 /* XCRemoteSwiftPackageReference "swift-log" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/apple/swift-log";
|
||||
|
@ -5320,6 +5332,11 @@
|
|||
package = CEA45E23263519B5002FA97D /* XCRemoteSwiftPackageReference "IQKeyboardManager" */;
|
||||
productName = IQKeyboardManagerSwift;
|
||||
};
|
||||
CEDD11C02B7C74D7004DDAC6 /* SwiftPortmap */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = CEDD11BF2B7C74D7004DDAC6 /* XCRemoteSwiftPackageReference "SwiftPortmap" */;
|
||||
productName = SwiftPortmap;
|
||||
};
|
||||
CEF7F5842AEEDCC400E34952 /* Logging */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = CEF7F5852AEEDCC400E34952 /* XCRemoteSwiftPackageReference "swift-log" */;
|
||||
|
|
|
@ -81,6 +81,15 @@
|
|||
"revision" : "ac168e9e69e0dc4efbe88699e0fd712316348c55"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftportmap",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/osy/SwiftPortmap.git",
|
||||
"state" : {
|
||||
"branch" : "main",
|
||||
"revision" : "72782141ab6f6f6db58bd16bac96d4e7ce901e9a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftterm",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
|
Loading…
Reference in New Issue