Skip to content

Commit f1ac25e

Browse files
committed
Reduce size of ReadSshIdBuffer, add unit tests
1 parent 359d708 commit f1ac25e

1 file changed

Lines changed: 114 additions & 3 deletions

File tree

russh/src/ssh_read.rs

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, ReadBuf};
66

77
use crate::Error;
88

9+
const SSH_ID_BUF_SIZE: usize = 256;
10+
911
/// The buffer to read the identification string (first line in the
1012
/// protocol). Not sensitive data — just protocol version exchange.
1113
struct ReadSshIdBuffer {
12-
pub buf: Vec<u8>,
14+
pub buf: Box<[u8; SSH_ID_BUF_SIZE]>,
1315
pub total: usize,
1416
pub bytes_read: usize,
1517
pub sshid_len: usize,
@@ -22,9 +24,8 @@ impl ReadSshIdBuffer {
2224
}
2325

2426
pub fn new() -> ReadSshIdBuffer {
25-
let buf = vec![0; 256];
2627
ReadSshIdBuffer {
27-
buf,
28+
buf: Box::new([0; SSH_ID_BUF_SIZE]),
2829
sshid_len: 0,
2930
bytes_read: 0,
3031
total: 0,
@@ -172,3 +173,113 @@ impl<R: AsyncRead + Unpin> SshRead<R> {
172173
}
173174
}
174175
}
176+
177+
#[cfg(test)]
178+
mod tests {
179+
use super::*;
180+
use std::iter;
181+
182+
#[tokio::test]
183+
async fn test_ssh_id_openssh() {
184+
let data = "SSH-2.0-OpenSSH_10.2\r\n";
185+
let mut read = SshRead::new(data.as_bytes());
186+
187+
let received = read.read_ssh_id().await.unwrap();
188+
assert_eq!(received, b"SSH-2.0-OpenSSH_10.2");
189+
}
190+
191+
#[tokio::test]
192+
async fn test_ssh_id_openssh_7_4() {
193+
let data = "SSH-2.0-OpenSSH_7.4\n";
194+
let mut read = SshRead::new(data.as_bytes());
195+
196+
let received = read.read_ssh_id().await.unwrap();
197+
assert_eq!(received, b"SSH-2.0-OpenSSH_7.4");
198+
}
199+
200+
#[tokio::test]
201+
async fn test_ssh_id_too_long() {
202+
let data = String::from_iter(iter::once("SSH-2.0-").chain(
203+
iter::repeat("A").take(500)));
204+
let mut read = SshRead::new(data.as_bytes());
205+
206+
let received = read.read_ssh_id().await;
207+
assert!(matches!(received.err(), Some(Error::Disconnect)));
208+
}
209+
210+
#[tokio::test]
211+
async fn test_ssh_id_empty() {
212+
let data = "";
213+
let mut read = SshRead::new(data.as_bytes());
214+
215+
let received = read.read_ssh_id().await;
216+
assert!(matches!(received.err(), Some(Error::Disconnect)));
217+
}
218+
219+
#[tokio::test]
220+
async fn test_ssh_id_almost_empty_cr_nl() {
221+
let data = "SSH-2.0-\n";
222+
let mut read = SshRead::new(data.as_bytes());
223+
224+
let received = read.read_ssh_id().await.unwrap();
225+
assert_eq!(received, b"SSH-2.0-");
226+
}
227+
228+
#[tokio::test]
229+
async fn test_ssh_id_almost_empty_nl() {
230+
let data = "SSH-2.0-\n";
231+
let mut read = SshRead::new(data.as_bytes());
232+
233+
let received = read.read_ssh_id().await.unwrap();
234+
assert_eq!(received, b"SSH-2.0-");
235+
}
236+
237+
#[tokio::test]
238+
async fn test_ssh_id_newline() {
239+
let data = "\n";
240+
let mut read = SshRead::new(data.as_bytes());
241+
242+
let received = read.read_ssh_id().await;
243+
assert!(matches!(received.err(), Some(Error::Disconnect)));
244+
}
245+
246+
#[tokio::test]
247+
async fn test_ssh_id_contains_cr() {
248+
// A \r that isn't followed by \n has no special meaning
249+
let data = "SSH-2.0-OpenSSH\r10.2\n";
250+
let mut read = SshRead::new(data.as_bytes());
251+
252+
let received = read.read_ssh_id().await.unwrap();
253+
assert_eq!(received, b"SSH-2.0-OpenSSH\r10.2");
254+
}
255+
256+
#[tokio::test]
257+
async fn test_ssh_id_trailing_cr() {
258+
// Verify this doesn't cause an out-of-bounds access when testing for \r\n
259+
let data = "SSH-2.0-OpenSSH_10.2\r";
260+
let mut read = SshRead::new(data.as_bytes());
261+
262+
let received = read.read_ssh_id().await;
263+
assert!(matches!(received.err(), Some(Error::Disconnect)));
264+
}
265+
266+
#[tokio::test]
267+
async fn test_ssh_id_nl_cr() {
268+
// Like \r\n but backwards
269+
let data = "SSH-2.0-OpenSSH_10.2\n\r";
270+
let mut read = SshRead::new(data.as_bytes());
271+
272+
let received = read.read_ssh_id().await.unwrap();
273+
assert_eq!(received, b"SSH-2.0-OpenSSH_10.2");
274+
}
275+
276+
#[tokio::test]
277+
async fn test_ssh_id_nl_cr_nl() {
278+
// Like \r\n but backwards, but also part of \r\n
279+
let data = "SSH-2.0-OpenSSH_10.2\n\r\n";
280+
let mut read = SshRead::new(data.as_bytes());
281+
282+
let received = read.read_ssh_id().await.unwrap();
283+
assert_eq!(received, b"SSH-2.0-OpenSSH_10.2");
284+
}
285+
}

0 commit comments

Comments
 (0)