Skip to content

Commit b0e891b

Browse files
author
Carlos Cabanero
committed
Mosh with IPv6 address family and other swift client improvements
- When selecting local resolution, now you can choose between IPv4 and IPv6. - Connected logging. - Improved error messages.
1 parent fccd78f commit b0e891b

File tree

2 files changed

+62
-16
lines changed

2 files changed

+62
-16
lines changed

Blink/Commands/mosh/MoshCommand.swift

+34-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ struct MoshCommand: ParsableCommand {
6262
@Flag var verbose: Bool = false
6363

6464
@Flag (
65-
name: [.customShort("T")]
65+
name: [.customShort("T")],
66+
help: "Do not start a TTY"
6667
)
6768
var noSshPty: Bool = false
6869

@@ -73,6 +74,9 @@ struct MoshCommand: ParsableCommand {
7374
)
7475
var experimentalRemoteIP: BKMoshExperimentalIP?
7576

77+
@Flag(exclusivity: .exclusive)
78+
var addressFamily: AddressFamily?
79+
7680
// Mosh Key
7781
@Option(
7882
name: [.customShort("k")],
@@ -134,6 +138,12 @@ struct MoshCommand: ParsableCommand {
134138
}
135139
}
136140
}
141+
142+
func validate() throws {
143+
if addressFamily != nil && experimentalRemoteIP != BKMoshExperimentalIPLocal {
144+
throw ValidationError("Address Family can only be used with 'local' IP resolution (-R).")
145+
}
146+
}
137147
}
138148

139149
extension MoshCommand {
@@ -208,3 +218,26 @@ extension BKMoshExperimentalIP {
208218
}
209219
}
210220
}
221+
222+
enum AddressFamily: String, EnumerableFlag {
223+
case IPv4
224+
case IPv6
225+
226+
static func name(for value: AddressFamily) -> NameSpecification {
227+
switch value {
228+
case .IPv4:
229+
return NameSpecification([.customShort(Character("4")), .customLong("inet4")])
230+
case .IPv6:
231+
return NameSpecification([.customShort(Character("6")), .customLong("inet6")])
232+
}
233+
}
234+
235+
static func help(for value: AddressFamily) -> ArgumentHelp? {
236+
switch value {
237+
case .IPv4:
238+
return "Use IPv4 only on 'local' IP resolution"
239+
case .IPv6:
240+
return "Use IPv6 only on 'local' IP resolution"
241+
}
242+
}
243+
}

Blink/Commands/mosh/mosh.swift

+28-15
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,17 @@ import ios_system
4949
// return cmd.start(argc, argv: argv.args(count: argc))
5050
// }
5151

52-
enum MoshError: Error {
52+
enum MoshError: Error, LocalizedError {
5353
case NoBinaryAvailable
5454
case NoBinaryExecFlag
5555
case NoChecksumMatch
5656
case UserCancelled
5757
case NoMoshServerArgs
5858
case NoRemoteServerIP
5959
case AddressInfo(String)
60-
case StartMoshServerError(String)
60+
case MissingArguments(String)
6161

62-
public var description: String {
62+
public var errorDescription: String? {
6363
switch self {
6464
case .NoBinaryAvailable:
6565
return "Could not find static binary for the remote platform and architecture."
@@ -70,13 +70,13 @@ enum MoshError: Error {
7070
case .UserCancelled:
7171
return "User cancelled the operation"
7272
case .NoMoshServerArgs:
73-
return "Error connecting to mosh-server. Could not parse arguments for client connection."
73+
return "Did not find mosh server startup message. (Have you installed mosh on your server?)"
7474
case .NoRemoteServerIP:
75-
return "ExperimentalIPRemote error. IP not found."
75+
return "Bad Mosh SSH_CONNECTION String."
7676
case .AddressInfo(let error):
7777
return "Address resolution failed - \(error)"
78-
case .StartMoshServerError(let error):
79-
return "Error starting remote mosh-server: \(error)"
78+
case .MissingArguments(let message):
79+
return "\(message)"
8080
}
8181
}
8282
}
@@ -165,7 +165,7 @@ enum MoshError: Error {
165165
let moshServerParams: MoshServerParams
166166
if let customKey = command.customKey {
167167
guard let customUDPPort = moshClientParams.customUDPPort else {
168-
throw MoshError.StartMoshServerError("If MOSH_KEY is set, port is required. (-p)")
168+
throw MoshError.MissingArguments("If MOSH_KEY is set, port is required. (-p)")
169169
}
170170

171171
// Resolved as part of the host info or explicit on params.
@@ -200,6 +200,7 @@ enum MoshError: Error {
200200
.flatMap { self.bootstrapMoshServer(on: $0,
201201
sequence: sequence,
202202
experimentalRemoteIP: moshClientParams.experimentalRemoteIP,
203+
family: command.addressFamily,
203204
args: moshServerStartupArgs,
204205
withPTY: pty) }
205206
//.print()
@@ -298,10 +299,11 @@ enum MoshError: Error {
298299
private func bootstrapMoshServer(on client: SSHClient,
299300
sequence: [MoshBootstrap],
300301
experimentalRemoteIP: BKMoshExperimentalIP,
302+
family: AddressFamily?,
301303
args: String,
302304
withPTY pty: SSH.SSHClient.PTY? = nil) -> AnyPublisher<MoshServerParams, Error> {
303305
let log = logger.log("bootstrapMoshServer")
304-
log.info("Trying bootstrap with sequence: \(sequence), experimental: \(experimentalRemoteIP), args: \(args)")
306+
log.info("Trying bootstrap with sequence: \(sequence), experimental: \(experimentalRemoteIP), family: \(family), args: \(args)")
305307

306308
if sequence.isEmpty {
307309
return Fail(error: MoshError.NoBinaryAvailable).eraseToAnyPublisher()
@@ -345,8 +347,7 @@ enum MoshError: Error {
345347
return try MoshServerParams(parsing: output, remoteIP: nil)
346348
case BKMoshExperimentalIPLocal:
347349
// local - resolve address on its own.
348-
// TODO Or to INET6 from CLI flag
349-
let remoteIP = try self.resolveAddress(host: client.host, port: client.options.port, family: nil)
350+
let remoteIP = try self.resolveAddress(host: client.host, port: client.options.port, family: family)
350351
return try MoshServerParams(parsing: output, remoteIP: remoteIP)
351352
default:
352353
// default - get it from the established SSH Connection.
@@ -380,13 +381,25 @@ enum MoshError: Error {
380381
// https://stackoverflow.com/questions/39857435/swift-getaddrinfo
381382
// getnameinfo
382383
// https://stackoverflow.com/questions/44478074/swift-getnameinfo-unreliable-results-for-ipv6
383-
private func resolveAddress(host: String, port: String?, family: Int32?) throws -> String {
384+
private func resolveAddress(host: String, port: String?, family: AddressFamily?) throws -> String {
384385
guard let port = (port ?? "22").cString(using: .utf8) else {
385386
throw MoshError.AddressInfo("Invalid port")
386387
}
388+
389+
let ai_family = {
390+
switch family {
391+
case .IPv4:
392+
AF_INET
393+
case .IPv6:
394+
AF_INET6
395+
default:
396+
AF_UNSPEC
397+
}
398+
}()
399+
387400
var hints = addrinfo(
388401
ai_flags: 0,
389-
ai_family: family ?? AF_UNSPEC,
402+
ai_family: ai_family,
390403
ai_socktype: SOCK_STREAM,
391404
ai_protocol: IPPROTO_TCP,
392405
ai_addrlen: 0,
@@ -503,8 +516,8 @@ struct MoshLogger {
503516
.format { [ ($0[.component] as? String)?.appending(":") ?? "global:",
504517
$0[.message] as? String ?? ""
505518
].joined(separator: " ") }
506-
.sink(receiveValue: { print($0[.message]) })
507-
//.sinkToStream(output)
519+
// .sink(receiveValue: { print($0[.message]) })
520+
.sinkToStream(output)
508521
}
509522
)
510523
}

0 commit comments

Comments
 (0)