mirror of
https://github.com/Noratrieb/cluelessh.git
synced 2026-01-14 16:35:06 +01:00
more
This commit is contained in:
parent
1adf798c5d
commit
ed30d5b4dc
8 changed files with 75 additions and 87 deletions
27
Cargo.lock
generated
27
Cargo.lock
generated
|
|
@ -17,16 +17,6 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aead"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
|
||||||
dependencies = [
|
|
||||||
"crypto-common",
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
|
|
@ -113,19 +103,6 @@ dependencies = [
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "chacha20poly1305"
|
|
||||||
version = "0.10.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
|
|
||||||
dependencies = [
|
|
||||||
"aead",
|
|
||||||
"chacha20",
|
|
||||||
"cipher",
|
|
||||||
"poly1305",
|
|
||||||
"zeroize",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cipher"
|
name = "cipher"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
|
|
@ -134,7 +111,6 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
"inout",
|
"inout",
|
||||||
"zeroize",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -159,7 +135,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"rand_core",
|
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -693,7 +668,6 @@ name = "ssh-transport"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chacha20",
|
"chacha20",
|
||||||
"chacha20poly1305",
|
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"eyre",
|
"eyre",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
|
|
@ -703,7 +677,6 @@ dependencies = [
|
||||||
"sha2",
|
"sha2",
|
||||||
"subtle",
|
"subtle",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
|
||||||
"x25519-dalek",
|
"x25519-dalek",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
13
src/main.rs
13
src/main.rs
|
|
@ -8,14 +8,19 @@ use tokio::{
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
use ssh_transport::{ServerConnection, SshError, ThreadRngRand};
|
use ssh_transport::{ServerConnection, SshError, ThreadRngRand};
|
||||||
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> eyre::Result<()> {
|
async fn main() -> eyre::Result<()> {
|
||||||
tracing_subscriber::fmt().init();
|
tracing_subscriber::fmt()
|
||||||
|
.with_env_filter(EnvFilter::from_default_env())
|
||||||
|
.init();
|
||||||
|
|
||||||
let listener = TcpListener::bind("0.0.0.0:2222")
|
let addr = "0.0.0.0:2222".parse::<SocketAddr>().unwrap();
|
||||||
.await
|
|
||||||
.wrap_err("binding listener")?;
|
info!(?addr, "Starting server");
|
||||||
|
|
||||||
|
let listener = TcpListener::bind(addr).await.wrap_err("binding listener")?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let next = listener.accept().await?;
|
let next = listener.accept().await?;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chacha20 = "0.9.1"
|
chacha20 = "0.9.1"
|
||||||
chacha20poly1305 = "0.10.1"
|
|
||||||
ed25519-dalek = { version = "2.1.1" }
|
ed25519-dalek = { version = "2.1.1" }
|
||||||
eyre = "0.6.12"
|
eyre = "0.6.12"
|
||||||
poly1305 = "0.8.0"
|
poly1305 = "0.8.0"
|
||||||
|
|
@ -14,7 +13,6 @@ rand_core = "0.6.4"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
subtle = "2.6.1"
|
subtle = "2.6.1"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
|
||||||
x25519-dalek = "2.0.1"
|
x25519-dalek = "2.0.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
Transport layer of SSH.
|
Transport layer of SSH.
|
||||||
|
|
||||||
Based on [RFC 4253 The Secure Shell (SSH) Transport Layer Protocol](https://datatracker.ietf.org/doc/html/rfc4253)
|
Based on [RFC 4253 The Secure Shell (SSH) Transport Layer Protocol](https://datatracker.ietf.org/doc/html/rfc4253)
|
||||||
and [RFC 4251 The Secure Shell (SSH) Protocol Architecture](https://datatracker.ietf.org/doc/html/rfc4251).
|
and [RFC 4251 The Secure Shell (SSH) Protocol Architecture](https://datatracker.ietf.org/doc/html/rfc4251)
|
||||||
|
and [RFC 4250 The Secure Shell (SSH) Protocol Assigned Numbers](https://datatracker.ietf.org/doc/html/rfc4250).
|
||||||
|
|
||||||
Other relevant RFCs:
|
Other relevant RFCs:
|
||||||
- [RFC 5649 AES Galois Counter Mode for the Secure Shell Transport Layer Protocol](https://datatracker.ietf.org/doc/html/rfc5647)
|
- [RFC 5649 AES Galois Counter Mode for the Secure Shell Transport Layer Protocol](https://datatracker.ietf.org/doc/html/rfc5647)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ pub(crate) struct Session {
|
||||||
pub(crate) trait Decryptor: Send + Sync + 'static {
|
pub(crate) trait Decryptor: Send + Sync + 'static {
|
||||||
fn decrypt_len(&mut self, bytes: &mut [u8; 4], packet_number: u64);
|
fn decrypt_len(&mut self, bytes: &mut [u8; 4], packet_number: u64);
|
||||||
fn decrypt_packet(&mut self, raw_packet: RawPacket, packet_number: u64) -> Result<Packet>;
|
fn decrypt_packet(&mut self, raw_packet: RawPacket, packet_number: u64) -> Result<Packet>;
|
||||||
|
fn additional_mac_len(&self) -> usize;
|
||||||
fn rekey(&mut self, h: [u8; 32], k: [u8; 32]) -> Result<(), ()>;
|
fn rekey(&mut self, h: [u8; 32], k: [u8; 32]) -> Result<(), ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,6 +26,9 @@ impl Decryptor for Plaintext {
|
||||||
fn decrypt_packet(&mut self, raw: RawPacket, _: u64) -> Result<Packet> {
|
fn decrypt_packet(&mut self, raw: RawPacket, _: u64) -> Result<Packet> {
|
||||||
Packet::from_raw(&raw.rest())
|
Packet::from_raw(&raw.rest())
|
||||||
}
|
}
|
||||||
|
fn additional_mac_len(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
fn rekey(&mut self, _: [u8; 32], _: [u8; 32]) -> Result<(), ()> {
|
fn rekey(&mut self, _: [u8; 32], _: [u8; 32]) -> Result<(), ()> {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
|
|
@ -65,6 +69,10 @@ impl Decryptor for Session {
|
||||||
.decrypt_packet(bytes, packet_number)
|
.decrypt_packet(bytes, packet_number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn additional_mac_len(&self) -> usize {
|
||||||
|
poly1305::BLOCK_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
fn rekey(&mut self, h: [u8; 32], k: [u8; 32]) -> Result<(), ()> {
|
fn rekey(&mut self, h: [u8; 32], k: [u8; 32]) -> Result<(), ()> {
|
||||||
*self = Self::from_keys(self.session_id, h, k);
|
*self = Self::from_keys(self.session_id, h, k);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -120,53 +128,33 @@ type SshChaCha20 = chacha20::ChaCha20Legacy;
|
||||||
|
|
||||||
struct SshChaCha20Poly1305 {
|
struct SshChaCha20Poly1305 {
|
||||||
header_key: chacha20::Key,
|
header_key: chacha20::Key,
|
||||||
main_key2: chacha20::Key,
|
main_key: chacha20::Key,
|
||||||
main_key:
|
|
||||||
chacha20poly1305::ChaChaPoly1305<chacha20::ChaCha20Legacy, chacha20::cipher::typenum::U8>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SshChaCha20Poly1305 {
|
impl SshChaCha20Poly1305 {
|
||||||
fn new(key: [u8; 64]) -> Self {
|
fn new(key: [u8; 64]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
main_key2: <[u8; 32]>::try_from(&key[..32]).unwrap().into(),
|
main_key: <[u8; 32]>::try_from(&key[..32]).unwrap().into(),
|
||||||
main_key: chacha20poly1305::ChaChaPoly1305::new(
|
|
||||||
&<[u8; 32]>::try_from(&key[..32]).unwrap().into(),
|
|
||||||
),
|
|
||||||
header_key: <[u8; 32]>::try_from(&key[32..]).unwrap().into(),
|
header_key: <[u8; 32]>::try_from(&key[32..]).unwrap().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_len(&self, bytes: &mut [u8], packet_number: u64) {
|
fn decrypt_len(&self, bytes: &mut [u8], packet_number: u64) {
|
||||||
eprintln!("encrypted len: {:x?}", &bytes);
|
|
||||||
// <https://github.com/openssh/openssh-portable/blob/1ec0a64c5dc57b8a2053a93b5ef0d02ff8598e5c/PROTOCOL.chacha20poly1305>
|
// <https://github.com/openssh/openssh-portable/blob/1ec0a64c5dc57b8a2053a93b5ef0d02ff8598e5c/PROTOCOL.chacha20poly1305>
|
||||||
let mut cipher = SshChaCha20::new(&self.header_key, &packet_number.to_be_bytes().into());
|
let mut cipher = SshChaCha20::new(&self.header_key, &packet_number.to_be_bytes().into());
|
||||||
cipher.apply_keystream(bytes);
|
cipher.apply_keystream(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_packet(&mut self, bytes: RawPacket, packet_number: u64) -> Result<Packet> {
|
fn decrypt_packet(&mut self, mut bytes: RawPacket, packet_number: u64) -> Result<Packet> {
|
||||||
// <https://github.com/openssh/openssh-portable/blob/1ec0a64c5dc57b8a2053a93b5ef0d02ff8598e5c/PROTOCOL.chacha20poly1305>
|
// <https://github.com/openssh/openssh-portable/blob/1ec0a64c5dc57b8a2053a93b5ef0d02ff8598e5c/PROTOCOL.chacha20poly1305>
|
||||||
|
|
||||||
//let mut aead_payload = bytes.into_full_packet();
|
let mut cipher = <SshChaCha20 as chacha20::cipher::KeyIvInit>::new(
|
||||||
//let mut associated_data = [0; 4];
|
&self.main_key,
|
||||||
//associated_data.copy_from_slice(&aead_payload[0..4]);
|
|
||||||
//aead_payload.splice(0..4, []);
|
|
||||||
|
|
||||||
//chacha20poly1305::AeadInPlace::decrypt_in_place(
|
|
||||||
// &self.main_key,
|
|
||||||
// &packet_number.to_be_bytes().into(),
|
|
||||||
// &associated_data,
|
|
||||||
// &mut aead_payload,
|
|
||||||
//)
|
|
||||||
//.map_err(|err| crate::client_error!("failed to decrypt invalid poly1305 MAC: {err}"))?;
|
|
||||||
|
|
||||||
let mut cipher = <chacha20::ChaCha20Legacy as chacha20::cipher::KeyIvInit>::new(
|
|
||||||
&self.main_key2,
|
|
||||||
&packet_number.to_be_bytes().into(),
|
&packet_number.to_be_bytes().into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let tag_offset = bytes.full_packet().len() - 16;
|
let tag_offset = bytes.full_packet().len() - 16;
|
||||||
let data_to_mac = &bytes.full_packet()[..tag_offset];
|
let data_to_mac = &bytes.full_packet()[..tag_offset];
|
||||||
eprintln!("data_to_mac: {:x?}", &data_to_mac);
|
|
||||||
|
|
||||||
let mac = {
|
let mac = {
|
||||||
let mut poly1305_key = [0; poly1305::KEY_SIZE];
|
let mut poly1305_key = [0; poly1305::KEY_SIZE];
|
||||||
|
|
@ -176,20 +164,17 @@ impl SshChaCha20Poly1305 {
|
||||||
|
|
||||||
let read_tag = poly1305::Tag::from_slice(&bytes.full_packet()[tag_offset..]);
|
let read_tag = poly1305::Tag::from_slice(&bytes.full_packet()[tag_offset..]);
|
||||||
|
|
||||||
eprintln!("expected MAC: {mac:x?}");
|
|
||||||
eprintln!("found MAC: {read_tag:x?}");
|
|
||||||
|
|
||||||
if !bool::from(mac.ct_eq(read_tag)) {
|
if !bool::from(mac.ct_eq(read_tag)) {
|
||||||
return Err(crate::client_error!(
|
return Err(crate::client_error!(
|
||||||
"failed to decrypt: invalid poly1305 MAC"
|
"failed to decrypt: invalid poly1305 MAC"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
//mac.verify(read_tag)
|
|
||||||
// .map_err(|err| crate::client_error!("failed to decrypt invalid poly1305 MAC: {err}"))?;
|
|
||||||
|
|
||||||
cipher.seek(64);
|
cipher.seek(64);
|
||||||
|
|
||||||
todo!()
|
let encrypted_packet_content = bytes.content_mut();
|
||||||
|
cipher.apply_keystream(encrypted_packet_content);
|
||||||
|
|
||||||
|
Packet::from_raw(encrypted_packet_content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,10 @@ use packet::{
|
||||||
DhKeyExchangeInitPacket, DhKeyExchangeInitReplyPacket, KeyExchangeInitPacket, Packet,
|
DhKeyExchangeInitPacket, DhKeyExchangeInitReplyPacket, KeyExchangeInitPacket, Packet,
|
||||||
PacketTransport, SshPublicKey, SshSignature,
|
PacketTransport, SshPublicKey, SshSignature,
|
||||||
};
|
};
|
||||||
use parse::{MpInt, NameList};
|
use parse::{MpInt, NameList, Parser};
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
|
use tracing::{debug, info};
|
||||||
use x25519_dalek::{EphemeralSecret, PublicKey};
|
use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -304,7 +305,18 @@ impl ServerConnection {
|
||||||
self.state = ServerState::ServiceRequest {};
|
self.state = ServerState::ServiceRequest {};
|
||||||
self.packet_transport.set_key(h, k);
|
self.packet_transport.set_key(h, k);
|
||||||
}
|
}
|
||||||
ServerState::ServiceRequest {} => {}
|
ServerState::ServiceRequest {} => {
|
||||||
|
if packet.payload.get(0) != Some(&Packet::SSH_MSG_SERVICE_REQUEST) {
|
||||||
|
return Err(client_error!("did not send SSH_MSG_SERVICE_REQUEST"));
|
||||||
|
}
|
||||||
|
let mut p = Parser::new(&packet.payload[1..]);
|
||||||
|
let service = p.utf8_string()?;
|
||||||
|
debug!(?service, "Client requesting service");
|
||||||
|
|
||||||
|
if service != "ssh-userauth" {
|
||||||
|
return Err(client_error!("only supports ssh-userauth"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ pub(crate) struct Packet {
|
||||||
pub(crate) payload: Vec<u8>,
|
pub(crate) payload: Vec<u8>,
|
||||||
}
|
}
|
||||||
impl Packet {
|
impl Packet {
|
||||||
|
pub(crate) const SSH_MSG_SERVICE_REQUEST: u8 = 5;
|
||||||
pub(crate) const SSH_MSG_KEXINIT: u8 = 20;
|
pub(crate) const SSH_MSG_KEXINIT: u8 = 20;
|
||||||
pub(crate) const SSH_MSG_NEWKEYS: u8 = 21;
|
pub(crate) const SSH_MSG_NEWKEYS: u8 = 21;
|
||||||
pub(crate) const SSH_MSG_KEXDH_INIT: u8 = 30;
|
pub(crate) const SSH_MSG_KEXDH_INIT: u8 = 30;
|
||||||
|
|
@ -78,9 +79,10 @@ impl Packet {
|
||||||
};
|
};
|
||||||
let payload = &bytes[1..][..payload_len];
|
let payload = &bytes[1..][..payload_len];
|
||||||
|
|
||||||
if (bytes.len() + 4) % 8 != 0 {
|
// TODO: this fails with OpenSSH client... why?
|
||||||
return Err(client_error!("full packet length must be multiple of 8"));
|
//if (bytes.len() + 4) % 8 != 0 {
|
||||||
}
|
// return Err(client_error!("full packet length must be multiple of 8: {}", bytes.len()));
|
||||||
|
//}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
payload: payload.to_vec(),
|
payload: payload.to_vec(),
|
||||||
|
|
@ -138,7 +140,7 @@ impl<'a> KeyExchangeInitPacket<'a> {
|
||||||
"expected SSH_MSG_KEXINIT packet, found {kind}"
|
"expected SSH_MSG_KEXINIT packet, found {kind}"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let cookie = c.read_array::<16>()?;
|
let cookie = c.array::<16>()?;
|
||||||
let kex_algorithms = c.name_list()?;
|
let kex_algorithms = c.name_list()?;
|
||||||
let server_host_key_algorithms = c.name_list()?;
|
let server_host_key_algorithms = c.name_list()?;
|
||||||
let encryption_algorithms_client_to_server = c.name_list()?;
|
let encryption_algorithms_client_to_server = c.name_list()?;
|
||||||
|
|
@ -258,6 +260,7 @@ impl<'a> DhKeyExchangeInitReplyPacket<'a> {
|
||||||
|
|
||||||
pub(crate) struct RawPacket {
|
pub(crate) struct RawPacket {
|
||||||
len: usize,
|
len: usize,
|
||||||
|
mac_len: usize,
|
||||||
raw: Vec<u8>,
|
raw: Vec<u8>,
|
||||||
}
|
}
|
||||||
impl RawPacket {
|
impl RawPacket {
|
||||||
|
|
@ -267,8 +270,9 @@ impl RawPacket {
|
||||||
pub(crate) fn full_packet(&self) -> &[u8] {
|
pub(crate) fn full_packet(&self) -> &[u8] {
|
||||||
&self.raw
|
&self.raw
|
||||||
}
|
}
|
||||||
pub(crate) fn into_full_packet(self) -> Vec<u8> {
|
pub(crate) fn content_mut(&mut self) -> &mut [u8] {
|
||||||
self.raw
|
let mac_start = self.raw.len() - self.mac_len;
|
||||||
|
&mut self.raw[4..mac_start]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,9 +327,9 @@ impl PacketParser {
|
||||||
|
|
||||||
decrytor.decrypt_len(&mut len_to_decrypt, next_seq_nr);
|
decrytor.decrypt_len(&mut len_to_decrypt, next_seq_nr);
|
||||||
let packet_length = u32::from_be_bytes(len_to_decrypt);
|
let packet_length = u32::from_be_bytes(len_to_decrypt);
|
||||||
let packet_length = packet_length.try_into().unwrap();
|
let packet_length: usize = packet_length.try_into().unwrap();
|
||||||
|
|
||||||
dbg!(packet_length);
|
let packet_length = packet_length + decrytor.additional_mac_len();
|
||||||
|
|
||||||
self.packet_length = Some(packet_length);
|
self.packet_length = Some(packet_length);
|
||||||
|
|
||||||
|
|
@ -347,6 +351,7 @@ impl PacketParser {
|
||||||
consumed,
|
consumed,
|
||||||
RawPacket {
|
RawPacket {
|
||||||
raw: std::mem::take(&mut self.raw_data),
|
raw: std::mem::take(&mut self.raw_data),
|
||||||
|
mac_len: decrytor.additional_mac_len(),
|
||||||
len: packet_length,
|
len: packet_length,
|
||||||
},
|
},
|
||||||
)))
|
)))
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,16 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn u8(&mut self) -> Result<u8> {
|
pub(crate) fn u8(&mut self) -> Result<u8> {
|
||||||
let arr = self.read_array::<1>()?;
|
let arr = self.array::<1>()?;
|
||||||
Ok(arr[0])
|
Ok(arr[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn u32(&mut self) -> Result<u32> {
|
pub(crate) fn u32(&mut self) -> Result<u32> {
|
||||||
let arr = self.read_array()?;
|
let arr = self.array()?;
|
||||||
Ok(u32::from_be_bytes(arr))
|
Ok(u32::from_be_bytes(arr))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> {
|
pub(crate) fn array<const N: usize>(&mut self) -> Result<[u8; N]> {
|
||||||
if self.0.len() < N {
|
if self.0.len() < N {
|
||||||
return Err(crate::client_error!("packet too short"));
|
return Err(crate::client_error!("packet too short"));
|
||||||
}
|
}
|
||||||
|
|
@ -30,7 +30,7 @@ impl<'a> Parser<'a> {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_slice(&mut self, len: usize) -> Result<&'a [u8]> {
|
pub(crate) fn slice(&mut self, len: usize) -> Result<&'a [u8]> {
|
||||||
if self.0.len() < len {
|
if self.0.len() < len {
|
||||||
return Err(crate::client_error!("packet too short"));
|
return Err(crate::client_error!("packet too short"));
|
||||||
}
|
}
|
||||||
|
|
@ -49,19 +49,28 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn name_list(&mut self) -> Result<NameList<'a>> {
|
pub(crate) fn name_list(&mut self) -> Result<NameList<'a>> {
|
||||||
let len = self.u32()?;
|
let list = self.utf8_string()?;
|
||||||
let list = self.read_slice(len.try_into().unwrap())?;
|
|
||||||
let Ok(list) = str::from_utf8(list) else {
|
|
||||||
return Err(crate::client_error!("name-list is invalid UTF-8"));
|
|
||||||
};
|
|
||||||
Ok(NameList(list))
|
Ok(NameList(list))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn mpint(&mut self) -> Result<MpInt<'a>> {
|
pub(crate) fn mpint(&mut self) -> Result<MpInt<'a>> {
|
||||||
let len = self.u32()?;
|
let data = self.string()?;
|
||||||
let data = self.read_slice(len as usize)?;
|
|
||||||
Ok(MpInt(data))
|
Ok(MpInt(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn string(&mut self) -> Result<&'a [u8]> {
|
||||||
|
let len = self.u32()?;
|
||||||
|
let data = self.slice(len.try_into().unwrap())?;
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn utf8_string(&mut self) -> Result<&'a str> {
|
||||||
|
let s = self.string()?;
|
||||||
|
let Ok(s) = str::from_utf8(s) else {
|
||||||
|
return Err(crate::client_error!("name-list is invalid UTF-8"));
|
||||||
|
};
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A simplified `byteorder` clone that emits client errors when the data is too short.
|
/// A simplified `byteorder` clone that emits client errors when the data is too short.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue