in Sources/NIOSSH/Connection State Machine/SSHConnectionStateMachine.swift [685:1027]
mutating func processOutboundMessage(_ message: SSHMessage,
buffer: inout ByteBuffer,
allocator: ByteBufferAllocator,
loop: EventLoop) throws {
switch self.state {
case .idle(var state):
switch message {
case .version:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentVersion(.init(idleState: state, allocator: allocator))
case .disconnect:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentDisconnect(state.role)
case .ignore, .debug, .unimplemented:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .idle(state)
default:
preconditionFailure("First message sent must be version, not \(message)")
}
case .sentVersion:
// We can't send anything else now.
// TODO(cory): We could refactor the key exchange state machine to accept the delayed version from the
// remote peer, and then we unlock the ability to remove another RTT to the remote peer.
preconditionFailure("Cannot send other messages before receiving version.")
case .keyExchange(var kex):
switch message {
case .keyExchange(let keyExchangeMessage):
try kex.writeKeyExchangeMessage(keyExchangeMessage, into: &buffer)
self.state = .keyExchange(kex)
case .keyExchangeInit(let kexInit):
try kex.writeKeyExchangeInitMessage(kexInit, into: &buffer)
self.state = .keyExchange(kex)
case .keyExchangeReply(let kexReply):
try kex.writeKeyExchangeReplyMessage(kexReply, into: &buffer)
self.state = .keyExchange(kex)
case .newKeys:
try kex.writeNewKeysMessage(into: &buffer)
let newState = SentNewKeysState(keyExchangeState: kex, loop: loop)
let possibleMessage = newState.userAuthStateMachine.beginAuthentication()
self.state = .sentNewKeys(newState)
// If we have a service request message, re-spin the state machine to process that too.
if let additionalMessage = possibleMessage {
try self.processOutboundMessage(.serviceRequest(additionalMessage), buffer: &buffer, allocator: allocator, loop: loop)
}
case .disconnect:
try kex.serializer.serialize(message: message, to: &buffer)
self.state = .sentDisconnect(kex.role)
case .ignore, .debug, .unimplemented:
try kex.serializer.serialize(message: message, to: &buffer)
self.state = .keyExchange(kex)
default:
throw NIOSSHError.protocolViolation(protocolName: "key exchange", violation: "Sent unexpected message type: \(message)")
}
case .receivedNewKeys(var kex):
switch message {
case .keyExchange(let keyExchangeMessage):
try kex.writeKeyExchangeMessage(keyExchangeMessage, into: &buffer)
self.state = .receivedNewKeys(kex)
case .keyExchangeInit(let kexInit):
try kex.writeKeyExchangeInitMessage(kexInit, into: &buffer)
self.state = .receivedNewKeys(kex)
case .keyExchangeReply(let kexReply):
try kex.writeKeyExchangeReplyMessage(kexReply, into: &buffer)
self.state = .receivedNewKeys(kex)
case .newKeys:
try kex.writeNewKeysMessage(into: &buffer)
let newState = UserAuthenticationState(receivedNewKeysState: kex)
let possibleMessage = newState.userAuthStateMachine.beginAuthentication()
self.state = .userAuthentication(newState)
// If we have a service request message, re-spin the state machine to process that too.
if let additionalMessage = possibleMessage {
try self.processOutboundMessage(.serviceRequest(additionalMessage), buffer: &buffer, allocator: allocator, loop: loop)
}
case .disconnect:
try kex.serializer.serialize(message: message, to: &buffer)
self.state = .sentDisconnect(kex.role)
case .ignore, .debug, .unimplemented:
try kex.serializer.serialize(message: message, to: &buffer)
self.state = .receivedNewKeys(kex)
default:
throw NIOSSHError.protocolViolation(protocolName: "key exchange", violation: "Sent unexpected message type: \(message)")
}
case .sentNewKeys(var state):
// In this state we tolerate sending service request. As we cannot have received any user auth messages
// (we're still waiting for newKeys), we cannot possibly send any other user auth message
switch message {
case .serviceRequest(let message):
try state.writeServiceRequest(message, into: &buffer)
self.state = .sentNewKeys(state)
case .serviceAccept, .userAuthRequest, .userAuthSuccess, .userAuthFailure:
throw NIOSSHError.protocolViolation(protocolName: "user auth", violation: "Cannot send \(message) before receiving newKeys")
case .disconnect:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentDisconnect(state.role)
case .ignore, .debug, .unimplemented:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentNewKeys(state)
default:
throw NIOSSHError.protocolViolation(protocolName: "user auth", violation: "Sent unexpected message type: \(message)")
}
case .userAuthentication(var state):
// In this state we tolerate sending user auth messages.
switch message {
case .serviceRequest(let message):
try state.writeServiceRequest(message, into: &buffer)
self.state = .userAuthentication(state)
case .serviceAccept(let message):
try state.writeServiceAccept(message, into: &buffer)
self.state = .userAuthentication(state)
case .userAuthBanner(let message):
try state.writeUserAuthBanner(message, into: &buffer)
self.state = .userAuthentication(state)
case .userAuthRequest(let message):
try state.writeUserAuthRequest(message, into: &buffer)
self.state = .userAuthentication(state)
case .userAuthSuccess:
try state.writeUserAuthSuccess(into: &buffer)
// Ok we're good to go!
self.state = .active(ActiveState(state))
case .userAuthFailure(let message):
try state.writeUserAuthFailure(message, into: &buffer)
self.state = .userAuthentication(state)
case .userAuthPKOK(let message):
try state.writeUserAuthPKOK(message, into: &buffer)
self.state = .userAuthentication(state)
case .disconnect:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentDisconnect(state.role)
case .ignore, .debug, .unimplemented:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .userAuthentication(state)
default:
throw NIOSSHError.protocolViolation(protocolName: "user auth", violation: "Sent unexpected message type: \(message)")
}
case .active(var state):
switch message {
case .channelOpen(let message):
try state.writeChannelOpen(message, into: &buffer)
case .channelOpenConfirmation(let message):
try state.writeChannelOpenConfirmation(message, into: &buffer)
case .channelOpenFailure(let message):
try state.writeChannelOpenFailure(message, into: &buffer)
case .channelEOF(let message):
try state.writeChannelEOF(message, into: &buffer)
case .channelClose(let message):
try state.writeChannelClose(message, into: &buffer)
case .channelWindowAdjust(let message):
try state.writeChannelWindowAdjust(message, into: &buffer)
case .channelData(let message):
try state.writeChannelData(message, into: &buffer)
case .channelExtendedData(let message):
try state.writeChannelExtendedData(message, into: &buffer)
case .channelRequest(let message):
try state.writeChannelRequest(message, into: &buffer)
case .channelSuccess(let message):
try state.writeChannelSuccess(message, into: &buffer)
case .channelFailure(let message):
try state.writeChannelFailure(message, into: &buffer)
case .globalRequest(let message):
try state.writeGlobalRequest(message, into: &buffer)
case .requestSuccess(let message):
try state.writeRequestSuccess(message, into: &buffer)
case .requestFailure:
try state.writeRequestFailure(into: &buffer)
case .disconnect:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentDisconnect(state.role)
return
case .ignore, .debug, .unimplemented:
try state.serializer.serialize(message: message, to: &buffer)
default:
throw NIOSSHError.protocolViolation(protocolName: "connection", violation: "Sent unexpected message type: \(message)")
}
self.state = .active(state)
case .receivedKexInitWhenActive(var state):
// In this state we only allow sending key exchange messages. In particular, the key exchange message is the only allowed one.
switch message {
case .keyExchange(let keyExchangeMessage):
try state.writeKeyExchangeMessage(keyExchangeMessage, into: &buffer)
self.state = .rekeying(.init(state))
case .disconnect:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentDisconnect(state.role)
case .ignore, .debug, .unimplemented:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .receivedKexInitWhenActive(state)
default:
throw NIOSSHError.protocolViolation(protocolName: "key exchange", violation: "Sent unexpected message type: \(message)")
}
case .sentKexInitWhenActive(var state):
// In this state we've send a key exchange init message, but not received one from the peer. We have nothing to send.
switch message {
case .disconnect:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentDisconnect(state.role)
case .ignore, .debug, .unimplemented:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentKexInitWhenActive(state)
default:
throw NIOSSHError.protocolViolation(protocolName: "key exchange", violation: "Sent unexpected message type: \(message)")
}
case .rekeying(var state):
// This is a full key exchange state.
switch message {
case .keyExchange(let keyExchangeMessage):
try state.writeKeyExchangeMessage(keyExchangeMessage, into: &buffer)
self.state = .rekeying(state)
case .keyExchangeInit(let kexInit):
try state.writeKeyExchangeInitMessage(kexInit, into: &buffer)
self.state = .rekeying(state)
case .keyExchangeReply(let kexReply):
try state.writeKeyExchangeReplyMessage(kexReply, into: &buffer)
self.state = .rekeying(state)
case .newKeys:
try state.writeNewKeysMessage(into: &buffer)
self.state = .rekeyingSentNewKeysState(.init(state))
case .disconnect:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentDisconnect(state.role)
case .ignore, .debug, .unimplemented:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .rekeying(state)
default:
throw NIOSSHError.protocolViolation(protocolName: "key exchange", violation: "Sent unexpected message type: \(message)")
}
case .rekeyingReceivedNewKeysState(var state):
// We may be doing any part of key exchange still.
switch message {
case .keyExchange(let keyExchangeMessage):
try state.writeKeyExchangeMessage(keyExchangeMessage, into: &buffer)
self.state = .rekeyingReceivedNewKeysState(state)
case .keyExchangeInit(let kexInit):
try state.writeKeyExchangeInitMessage(kexInit, into: &buffer)
self.state = .rekeyingReceivedNewKeysState(state)
case .keyExchangeReply(let kexReply):
try state.writeKeyExchangeReplyMessage(kexReply, into: &buffer)
self.state = .rekeyingReceivedNewKeysState(state)
case .newKeys:
try state.writeNewKeysMessage(into: &buffer)
self.state = .active(.init(state))
case .disconnect:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentDisconnect(state.role)
case .ignore, .debug, .unimplemented:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .rekeyingReceivedNewKeysState(state)
default:
throw NIOSSHError.protocolViolation(protocolName: "key exchange", violation: "Sent unexpected message type: \(message)")
}
case .rekeyingSentNewKeysState(var state):
// We can send channel messages again.
switch message {
case .channelOpen(let message):
try state.writeChannelOpen(message, into: &buffer)
case .channelOpenConfirmation(let message):
try state.writeChannelOpenConfirmation(message, into: &buffer)
case .channelOpenFailure(let message):
try state.writeChannelOpenFailure(message, into: &buffer)
case .channelEOF(let message):
try state.writeChannelEOF(message, into: &buffer)
case .channelClose(let message):
try state.writeChannelClose(message, into: &buffer)
case .channelWindowAdjust(let message):
try state.writeChannelWindowAdjust(message, into: &buffer)
case .channelData(let message):
try state.writeChannelData(message, into: &buffer)
case .channelExtendedData(let message):
try state.writeChannelExtendedData(message, into: &buffer)
case .channelRequest(let message):
try state.writeChannelRequest(message, into: &buffer)
case .channelSuccess(let message):
try state.writeChannelSuccess(message, into: &buffer)
case .channelFailure(let message):
try state.writeChannelFailure(message, into: &buffer)
case .globalRequest(let message):
try state.writeGlobalRequest(message, into: &buffer)
case .requestSuccess(let message):
try state.writeRequestSuccess(message, into: &buffer)
case .requestFailure:
try state.writeRequestFailure(into: &buffer)
case .disconnect:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .sentDisconnect(state.role)
return
case .ignore, .debug, .unimplemented:
try state.serializer.serialize(message: message, to: &buffer)
self.state = .rekeyingSentNewKeysState(state)
default:
throw NIOSSHError.protocolViolation(protocolName: "connection", violation: "Sent unexpected message type: \(message)")
}
self.state = .rekeyingSentNewKeysState(state)
case .sentDisconnect, .receivedDisconnect:
// We don't allow more messages once disconnect has occured
throw NIOSSHError.protocolViolation(protocolName: "transport", violation: "I/O after disconnect")
}
}