#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TcpState {
Closed,
SynSent,
SynReceived,
Established,
FinWait1,
FinWait2,
CloseWait,
Closing,
LastAck,
TimeWait,
Unknown,
}
impl TcpState {
pub fn color(&self) -> &str {
match self {
TcpState::Established => "green",
TcpState::SynSent | TcpState::SynReceived => "cyan",
TcpState::FinWait1 | TcpState::FinWait2 | TcpState::Closing => "yellow",
TcpState::TimeWait => "blue",
TcpState::CloseWait | TcpState::LastAck => "magenta",
TcpState::Closed => "dark_gray",
TcpState::Unknown => "gray",
}
}
pub fn short_name(&self) -> &str {
match self {
TcpState::Closed => "CLOSED",
TcpState::SynSent => "SYN_SENT",
TcpState::SynReceived => "SYN_RECV",
TcpState::Established => "ESTAB",
TcpState::FinWait1 => "FIN_W1",
TcpState::FinWait2 => "FIN_W2",
TcpState::CloseWait => "CLOSE_W",
TcpState::Closing => "CLOSING",
TcpState::LastAck => "LAST_ACK",
TcpState::TimeWait => "TIME_WT",
TcpState::Unknown => "UNKNOWN",
}
}
pub fn short_name_3char(&self) -> &str {
match self {
TcpState::Closed => "CLS",
TcpState::SynSent => "SYN",
TcpState::SynReceived => "SYR",
TcpState::Established => "EST",
TcpState::FinWait1 => "FW1",
TcpState::FinWait2 => "FW2",
TcpState::CloseWait => "CW",
TcpState::Closing => "CLO",
TcpState::LastAck => "LA",
TcpState::TimeWait => "TW",
TcpState::Unknown => "UNK",
}
}
pub fn transition(&self, syn: bool, ack: bool, fin: bool, rst: bool) -> Self {
if rst {
return TcpState::Closed;
}
match self {
TcpState::Closed | TcpState::Unknown => {
if syn && !ack {
TcpState::SynSent
} else if ack && !syn && !fin {
TcpState::Established
} else {
*self
}
}
TcpState::SynSent => {
if syn && ack {
TcpState::SynReceived
} else if ack && !syn {
TcpState::Established
} else {
*self
}
}
TcpState::SynReceived => {
if ack && !syn {
TcpState::Established
} else if fin {
TcpState::FinWait1
} else {
*self
}
}
TcpState::Established => {
if fin {
TcpState::FinWait1
} else {
*self
}
}
TcpState::FinWait1 => {
if ack && !fin {
TcpState::FinWait2
} else if fin && ack {
TcpState::TimeWait
} else if fin {
TcpState::Closing
} else {
*self
}
}
TcpState::FinWait2 => {
if fin {
TcpState::TimeWait
} else {
*self
}
}
TcpState::CloseWait => {
if fin {
TcpState::LastAck
} else {
*self
}
}
TcpState::Closing => {
if ack && !fin {
TcpState::TimeWait
} else {
*self
}
}
TcpState::LastAck => {
if ack && !fin {
TcpState::Closed
} else {
*self
}
}
TcpState::TimeWait => {
*self
}
}
}
pub fn from_flags(syn: bool, ack: bool, fin: bool, rst: bool) -> Self {
if rst {
return TcpState::Closed;
}
match (syn, ack, fin) {
(true, false, false) => TcpState::SynSent,
(true, true, false) => TcpState::SynReceived,
(false, true, false) => TcpState::Established,
(false, _, true) => TcpState::FinWait1,
(false, false, false) => TcpState::Unknown,
_ => TcpState::Unknown,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tcp_state_closed() {
let state = TcpState::Closed;
assert_eq!(state.color(), "dark_gray");
assert_eq!(state.short_name(), "CLOSED");
}
#[test]
fn test_tcp_state_established() {
let state = TcpState::Established;
assert_eq!(state.color(), "green");
assert_eq!(state.short_name(), "ESTAB");
}
#[test]
fn test_tcp_state_from_flags() {
assert_eq!(
TcpState::from_flags(true, false, false, false),
TcpState::SynSent
);
assert_eq!(
TcpState::from_flags(true, true, false, false),
TcpState::SynReceived
);
assert_eq!(
TcpState::from_flags(false, true, false, false),
TcpState::Established
);
assert_eq!(
TcpState::from_flags(false, false, true, false),
TcpState::FinWait1
);
assert_eq!(
TcpState::from_flags(false, false, false, true),
TcpState::Closed
);
}
#[test]
fn test_tcp_state_transition_syn_to_established() {
let state = TcpState::Closed;
let state = state.transition(true, false, false, false);
assert_eq!(state, TcpState::SynSent);
let state = state.transition(false, true, false, false);
assert_eq!(state, TcpState::Established);
}
#[test]
fn test_tcp_state_rst_closes() {
let state = TcpState::Established;
let state = state.transition(false, false, false, true);
assert_eq!(state, TcpState::Closed);
}
#[test]
fn test_tcp_state_mid_connection_monitoring() {
let state = TcpState::Closed;
let state = state.transition(false, true, false, false);
assert_eq!(state, TcpState::Established);
}
#[test]
fn test_tcp_state_from_flags_ack_only() {
assert_eq!(
TcpState::from_flags(false, true, false, false),
TcpState::Established
);
}
}