mirror of
https://github.com/ivre/masscanned.git
synced 2025-10-02 14:48:22 +00:00
Implement FSM for parsing SSH banner
This commit is contained in:
parent
1d4feb49ec
commit
87c95ff240
2 changed files with 369 additions and 12 deletions
|
@ -256,16 +256,16 @@ mod tests {
|
||||||
};
|
};
|
||||||
/***** TEST SSH *****/
|
/***** TEST SSH *****/
|
||||||
let payloads = [
|
let payloads = [
|
||||||
"SSH-2.0-PUTTY",
|
"SSH-2.0-PUTTY\r\n",
|
||||||
"SSH-2.0-Go",
|
"SSH-2.0-Go\r\n",
|
||||||
"SSH-2.0-libssh2_1.4.3",
|
"SSH-2.0-libssh2_1.4.3\r\n",
|
||||||
"SSH-2.0-PuTTY",
|
"SSH-2.0-PuTTY\r\n",
|
||||||
"SSH-2.0-AsyncSSH_2.1.0",
|
"SSH-2.0-AsyncSSH_2.1.0\r\n",
|
||||||
"SSH-2.0-libssh2_1.9.0",
|
"SSH-2.0-libssh2_1.9.0\r\n",
|
||||||
"SSH-2.0-libssh2_1.7.0",
|
"SSH-2.0-libssh2_1.7.0\r\n",
|
||||||
"SSH-2.0-8.35 FlowSsh: FlowSshNet_SftpStress54.38.116.473",
|
"SSH-2.0-8.35 FlowSsh: FlowSshNet_SftpStress54.38.116.473\r\n",
|
||||||
"SSH-2.0-libssh_0.9.5",
|
"SSH-2.0-libssh_0.9.5\r\n",
|
||||||
"SSH-2.0-OpenSSH_6.7p1 Raspbian-5+deb8u3",
|
"SSH-2.0-OpenSSH_6.7p1 Raspbian-5+deb8u3\r\n",
|
||||||
];
|
];
|
||||||
for payload in payloads.iter() {
|
for payload in payloads.iter() {
|
||||||
let _ssh_resp =
|
let _ssh_resp =
|
||||||
|
|
361
src/proto/ssh.rs
361
src/proto/ssh.rs
|
@ -23,6 +23,142 @@ use crate::Masscanned;
|
||||||
|
|
||||||
pub const SSH_PATTERN_CLIENT_PROTOCOL: &[u8; 7] = b"SSH-2.0";
|
pub const SSH_PATTERN_CLIENT_PROTOCOL: &[u8; 7] = b"SSH-2.0";
|
||||||
|
|
||||||
|
const SSH_STATE_START: usize = 0;
|
||||||
|
const SSH_STATE_S1: usize = 1;
|
||||||
|
const SSH_STATE_S2: usize = 2;
|
||||||
|
const SSH_STATE_H: usize = 3;
|
||||||
|
const SSH_STATE_DASH: usize = 4;
|
||||||
|
const SSH_STATE_VERSION: usize = 5;
|
||||||
|
const SSH_STATE_SOFTWARE: usize = 6;
|
||||||
|
const SSH_STATE_COMMENT: usize = 7;
|
||||||
|
const SSH_STATE_EOB: usize = 8;
|
||||||
|
const SSH_STATE_LF: usize = 9;
|
||||||
|
|
||||||
|
const SSH_STATE_FAIL: usize = 0xFFFF;
|
||||||
|
|
||||||
|
struct ProtocolState {
|
||||||
|
state: usize,
|
||||||
|
prev_state: usize,
|
||||||
|
ssh_version: Vec<u8>,
|
||||||
|
ssh_software: Vec<u8>,
|
||||||
|
ssh_comment: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProtocolState {
|
||||||
|
fn new() -> Self {
|
||||||
|
ProtocolState {
|
||||||
|
state: SSH_STATE_START,
|
||||||
|
prev_state: SSH_STATE_START,
|
||||||
|
ssh_version: Vec::<u8>::new(),
|
||||||
|
ssh_software: Vec::<u8>::new(),
|
||||||
|
ssh_comment: Vec::<u8>::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ssh_parse(pstate: &mut ProtocolState, data: &[u8]) {
|
||||||
|
/* RFC 4253:
|
||||||
|
*
|
||||||
|
* 4.2. Protocol Version Exchange
|
||||||
|
*
|
||||||
|
* When the connection has been established, both sides MUST send an
|
||||||
|
* identification string. This identification string MUST be
|
||||||
|
*
|
||||||
|
* SSH-protoversion-softwareversion SP comments CR LF
|
||||||
|
*
|
||||||
|
* Since the protocol being defined in this set of documents is version
|
||||||
|
* 2.0, the 'protoversion' MUST be "2.0". The 'comments' string is
|
||||||
|
* OPTIONAL. If the 'comments' string is included, a 'space' character
|
||||||
|
* (denoted above as SP, ASCII 32) MUST separate the 'softwareversion'
|
||||||
|
* and 'comments' strings. The identification MUST be terminated by a
|
||||||
|
* single Carriage Return (CR) and a single Line Feed (LF) character
|
||||||
|
* (ASCII 13 and 10, respectively). Implementers who wish to maintain
|
||||||
|
* compatibility with older, undocumented versions of this protocol may
|
||||||
|
* want to process the identification string without expecting the
|
||||||
|
* presence of the carriage return character for reasons described in
|
||||||
|
* Section 5 of this document. The null character MUST NOT be sent.
|
||||||
|
* The maximum length of the string is 255 characters, including the
|
||||||
|
* Carriage Return and Line Feed.
|
||||||
|
*/
|
||||||
|
let mut i = 0;
|
||||||
|
while i < data.len() {
|
||||||
|
match pstate.state {
|
||||||
|
SSH_STATE_START => {
|
||||||
|
pstate.state = SSH_STATE_S1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* first bytes should be "SSH-" */
|
||||||
|
SSH_STATE_S1 | SSH_STATE_S2 | SSH_STATE_H | SSH_STATE_DASH => {
|
||||||
|
if data[i] != b"SSH-"[pstate.state - SSH_STATE_S1] {
|
||||||
|
pstate.state = SSH_STATE_FAIL;
|
||||||
|
} else {
|
||||||
|
pstate.state += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* expect LF after a CR was read */
|
||||||
|
SSH_STATE_LF => {
|
||||||
|
if data[i] == b'\n' {
|
||||||
|
pstate.state = SSH_STATE_EOB;
|
||||||
|
} else {
|
||||||
|
if pstate.prev_state == SSH_STATE_SOFTWARE {
|
||||||
|
/* when reading software, \r can be followed by something else than \n */
|
||||||
|
pstate.state = pstate.prev_state;
|
||||||
|
/* cancel the read of this char */
|
||||||
|
i -= 1;
|
||||||
|
/* add the previously read \r to the software string */
|
||||||
|
pstate.ssh_software.push(b'\r');
|
||||||
|
} else if pstate.prev_state == SSH_STATE_COMMENT {
|
||||||
|
/* when reading comment, \r can be followed by something else than \n */
|
||||||
|
pstate.state = pstate.prev_state;
|
||||||
|
/* cancel the read of this char */
|
||||||
|
i -= 1;
|
||||||
|
/* add the previously read \r to the software string */
|
||||||
|
pstate.ssh_comment.push(b'\r');
|
||||||
|
} else {
|
||||||
|
/* in some other cases, it fails */
|
||||||
|
pstate.state = SSH_STATE_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SSH_STATE_VERSION => {
|
||||||
|
if data[i] == b'-' {
|
||||||
|
pstate.state = SSH_STATE_SOFTWARE;
|
||||||
|
} else if !data[i].is_ascii_digit() && data[i] != b'.' {
|
||||||
|
pstate.state = SSH_STATE_FAIL;
|
||||||
|
} else {
|
||||||
|
pstate.ssh_version.push(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SSH_STATE_SOFTWARE => {
|
||||||
|
if data[i] == b'\r' {
|
||||||
|
/* look for LF in the next char */
|
||||||
|
pstate.prev_state = pstate.state;
|
||||||
|
pstate.state = SSH_STATE_LF;
|
||||||
|
} else if data[i] == b' ' {
|
||||||
|
pstate.state = SSH_STATE_COMMENT;
|
||||||
|
} else {
|
||||||
|
pstate.ssh_software.push(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SSH_STATE_COMMENT => {
|
||||||
|
if data[i] == b'\r' {
|
||||||
|
/* look for LF in the next char */
|
||||||
|
pstate.prev_state = pstate.state;
|
||||||
|
pstate.state = SSH_STATE_LF;
|
||||||
|
} else {
|
||||||
|
pstate.ssh_comment.push(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SSH_STATE_FAIL => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SSH_STATE_EOB => { /* so far, do not parse after banner */ }
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn repl<'a>(
|
pub fn repl<'a>(
|
||||||
data: &'a [u8],
|
data: &'a [u8],
|
||||||
_masscanned: &Masscanned,
|
_masscanned: &Masscanned,
|
||||||
|
@ -30,10 +166,231 @@ pub fn repl<'a>(
|
||||||
_tcb: Option<&mut TCPControlBlock>,
|
_tcb: Option<&mut TCPControlBlock>,
|
||||||
) -> Option<Vec<u8>> {
|
) -> Option<Vec<u8>> {
|
||||||
debug!("receiving SSH data");
|
debug!("receiving SSH data");
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
ssh_parse(&mut pstate, data);
|
||||||
|
if pstate.state != SSH_STATE_EOB {
|
||||||
|
debug!("data in not correctly formatted - not responding");
|
||||||
|
debug!("pstate: {}", pstate.state);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let repl_data = b"SSH-2.0-1\r\n".to_vec();
|
let repl_data = b"SSH-2.0-1\r\n".to_vec();
|
||||||
debug!("sending SSH answer");
|
debug!("sending SSH answer");
|
||||||
warn!("SSH server banner to {}", byte2str(data));
|
warn!("SSH server banner to {}", str::from_utf8(&pstate.ssh_software).unwrap().trim_end());
|
||||||
return Some(repl_data);
|
Some(repl_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ssh_banner_parse() {
|
||||||
|
/* all at once */
|
||||||
|
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFTWARE");
|
||||||
|
assert!(pstate.ssh_comment == b"COMMENT");
|
||||||
|
/* byte by byte */
|
||||||
|
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
for i in 0..test_banner.len() {
|
||||||
|
if i == 0 {
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
} else if i > 0 && i < 4 {
|
||||||
|
assert!(pstate.state == SSH_STATE_S1 + i);
|
||||||
|
} else if i >= 4 && i < 8 {
|
||||||
|
assert!(pstate.state == SSH_STATE_VERSION);
|
||||||
|
} else if i >= 8 && i < 17 {
|
||||||
|
assert!(pstate.state == SSH_STATE_SOFTWARE);
|
||||||
|
} else if i >= 17 && i < test_banner.len() - 1 {
|
||||||
|
assert!(pstate.state == SSH_STATE_COMMENT);
|
||||||
|
} else {
|
||||||
|
assert!(pstate.state == SSH_STATE_LF);
|
||||||
|
}
|
||||||
|
ssh_parse(&mut pstate, &test_banner[i..i + 1]);
|
||||||
|
}
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFTWARE");
|
||||||
|
assert!(pstate.ssh_comment == b"COMMENT");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ssh_banner_space() {
|
||||||
|
/* space in SSH */
|
||||||
|
let test_banner = b"S SH-2.0-SOFTWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_FAIL);
|
||||||
|
/* space in VERSION */
|
||||||
|
let test_banner = b"SSH-2. 0-SOFTWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_FAIL);
|
||||||
|
/* space in software */
|
||||||
|
let test_banner = b"SSH-2.0-SOFT WARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFT");
|
||||||
|
assert!(pstate.ssh_comment == b"WARE COMMENT");
|
||||||
|
/* space in comment */
|
||||||
|
let test_banner = b"SSH-2.0-SOFTWARE COM MENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFTWARE");
|
||||||
|
assert!(pstate.ssh_comment == b"COM MENT");
|
||||||
|
/* double space */
|
||||||
|
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFTWARE");
|
||||||
|
assert!(pstate.ssh_comment == b" COMMENT");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ssh_banner_cr() {
|
||||||
|
/* CR in SSH */
|
||||||
|
let test_banner = b"S\rSH-2.0-SOFTWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_FAIL);
|
||||||
|
/* CR in VERSION */
|
||||||
|
let test_banner = b"SSH-2.\r0-SOFTWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_FAIL);
|
||||||
|
/* CR in SOFTWARE */
|
||||||
|
let test_banner = b"SSH-2.0-SOFT\rWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFT\rWARE");
|
||||||
|
assert!(pstate.ssh_comment == b"COMMENT");
|
||||||
|
/* CR in COMMENT */
|
||||||
|
let test_banner = b"SSH-2.0-SOFTWARE COM\rMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFTWARE");
|
||||||
|
assert!(pstate.ssh_comment == b"COM\rMENT");
|
||||||
|
/* CR at the end */
|
||||||
|
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\r\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFTWARE");
|
||||||
|
assert!(pstate.ssh_comment == b"COMMENT\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ssh_banner_lf() {
|
||||||
|
/* LF in SSH */
|
||||||
|
let test_banner = b"S\nSH-2.0-SOFTWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_FAIL);
|
||||||
|
/* LF in VERSION */
|
||||||
|
let test_banner = b"SSH-2.\n0-SOFTWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_FAIL);
|
||||||
|
/* LF in SOFTWARE */
|
||||||
|
let test_banner = b"SSH-2.0-SOFT\nWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFT\nWARE");
|
||||||
|
assert!(pstate.ssh_comment == b"COMMENT");
|
||||||
|
/* LF in COMMENT */
|
||||||
|
let test_banner = b"SSH-2.0-SOFTWARE COM\nMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFTWARE");
|
||||||
|
assert!(pstate.ssh_comment == b"COM\nMENT");
|
||||||
|
/* LF at the end */
|
||||||
|
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\n\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFTWARE");
|
||||||
|
assert!(pstate.ssh_comment == b"COMMENT\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ssh_banner_crlf() {
|
||||||
|
/* CRLF in SSH */
|
||||||
|
let test_banner = b"S\r\nSH-2.0-SOFTWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_FAIL);
|
||||||
|
/* CRLF in VERSION */
|
||||||
|
let test_banner = b"SSH-2.\r\n0-SOFTWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_FAIL);
|
||||||
|
/* CRLF in SOFTWARE */
|
||||||
|
let test_banner = b"SSH-2.0-SOFT\r\nWARE COMMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFT");
|
||||||
|
assert!(pstate.ssh_comment == b"");
|
||||||
|
/* CRLF in COMMENT */
|
||||||
|
let test_banner = b"SSH-2.0-SOFTWARE COM\r\nMENT\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFTWARE");
|
||||||
|
assert!(pstate.ssh_comment == b"COM");
|
||||||
|
/* CRLF at the end */
|
||||||
|
let test_banner = b"SSH-2.0-SOFTWARE COMMENT\r\n\r\n";
|
||||||
|
let mut pstate = ProtocolState::new();
|
||||||
|
assert!(pstate.state == SSH_STATE_START);
|
||||||
|
ssh_parse(&mut pstate, test_banner);
|
||||||
|
assert!(pstate.state == SSH_STATE_EOB);
|
||||||
|
assert!(pstate.ssh_version == b"2.0");
|
||||||
|
assert!(pstate.ssh_software == b"SOFTWARE");
|
||||||
|
assert!(pstate.ssh_comment == b"COMMENT");
|
||||||
|
}
|
||||||
|
>>>>>>> 29f9e75 (Implement FSM for parsing SSH banner)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue