mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-10-28 08:20:49 +08:00
refactor: 将 DTLS 本地依赖迁移到 libs 目录
This commit is contained in:
111
libs/dtls/src/record_layer/mod.rs
Normal file
111
libs/dtls/src/record_layer/mod.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
pub mod record_layer_header;
|
||||
|
||||
#[cfg(test)]
|
||||
mod record_layer_test;
|
||||
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use record_layer_header::*;
|
||||
|
||||
use super::content::*;
|
||||
use super::error::*;
|
||||
use crate::alert::Alert;
|
||||
use crate::application_data::ApplicationData;
|
||||
use crate::change_cipher_spec::ChangeCipherSpec;
|
||||
use crate::handshake::Handshake;
|
||||
|
||||
/*
|
||||
The TLS Record Layer which handles all data transport.
|
||||
The record layer is assumed to sit directly on top of some
|
||||
reliable transport such as TCP. The record layer can carry four types of content:
|
||||
|
||||
1. Handshake messages—used for algorithm negotiation and key establishment.
|
||||
2. ChangeCipherSpec messages—really part of the handshake but technically a separate kind of message.
|
||||
3. Alert messages—used to signal that errors have occurred
|
||||
4. Application layer data
|
||||
|
||||
The DTLS record layer is extremely similar to that of TLS 1.1. The
|
||||
only change is the inclusion of an explicit sequence number in the
|
||||
record. This sequence number allows the recipient to correctly
|
||||
verify the TLS MAC.
|
||||
*/
|
||||
/// ## Specifications
|
||||
///
|
||||
/// * [RFC 4347 §4.1]
|
||||
///
|
||||
/// [RFC 4347 §4.1]: https://tools.ietf.org/html/rfc4347#section-4.1
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct RecordLayer {
|
||||
pub record_layer_header: RecordLayerHeader,
|
||||
pub content: Content,
|
||||
}
|
||||
|
||||
impl RecordLayer {
|
||||
pub fn new(protocol_version: ProtocolVersion, epoch: u16, content: Content) -> Self {
|
||||
RecordLayer {
|
||||
record_layer_header: RecordLayerHeader {
|
||||
content_type: content.content_type(),
|
||||
protocol_version,
|
||||
epoch,
|
||||
sequence_number: 0,
|
||||
content_len: content.size() as u16,
|
||||
},
|
||||
content,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
|
||||
self.record_layer_header.marshal(writer)?;
|
||||
self.content.marshal(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
|
||||
let record_layer_header = RecordLayerHeader::unmarshal(reader)?;
|
||||
let content = match record_layer_header.content_type {
|
||||
ContentType::Alert => Content::Alert(Alert::unmarshal(reader)?),
|
||||
ContentType::ApplicationData => {
|
||||
Content::ApplicationData(ApplicationData::unmarshal(reader)?)
|
||||
}
|
||||
ContentType::ChangeCipherSpec => {
|
||||
Content::ChangeCipherSpec(ChangeCipherSpec::unmarshal(reader)?)
|
||||
}
|
||||
ContentType::Handshake => Content::Handshake(Handshake::unmarshal(reader)?),
|
||||
_ => return Err(Error::Other("Invalid Content Type".to_owned())),
|
||||
};
|
||||
|
||||
Ok(RecordLayer {
|
||||
record_layer_header,
|
||||
content,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Note that as with TLS, multiple handshake messages may be placed in
|
||||
// the same DTLS record, provided that there is room and that they are
|
||||
// part of the same flight. Thus, there are two acceptable ways to pack
|
||||
// two DTLS messages into the same datagram: in the same record or in
|
||||
// separate records.
|
||||
// https://tools.ietf.org/html/rfc6347#section-4.2.3
|
||||
pub(crate) fn unpack_datagram(buf: &[u8]) -> Result<Vec<Vec<u8>>> {
|
||||
let mut out = vec![];
|
||||
|
||||
let mut offset = 0;
|
||||
while buf.len() != offset {
|
||||
if buf.len() - offset <= RECORD_LAYER_HEADER_SIZE {
|
||||
return Err(Error::ErrInvalidPacketLength);
|
||||
}
|
||||
|
||||
let pkt_len = RECORD_LAYER_HEADER_SIZE
|
||||
+ (((buf[offset + RECORD_LAYER_HEADER_SIZE - 2] as usize) << 8)
|
||||
| buf[offset + RECORD_LAYER_HEADER_SIZE - 1] as usize);
|
||||
if offset + pkt_len > buf.len() {
|
||||
return Err(Error::ErrInvalidPacketLength);
|
||||
}
|
||||
|
||||
out.push(buf[offset..offset + pkt_len].to_vec());
|
||||
offset += pkt_len
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
94
libs/dtls/src/record_layer/record_layer_header.rs
Normal file
94
libs/dtls/src/record_layer/record_layer_header.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
use crate::content::*;
|
||||
use crate::error::*;
|
||||
|
||||
pub const RECORD_LAYER_HEADER_SIZE: usize = 13;
|
||||
pub const MAX_SEQUENCE_NUMBER: u64 = 0x0000FFFFFFFFFFFF;
|
||||
|
||||
pub const DTLS1_2MAJOR: u8 = 0xfe;
|
||||
pub const DTLS1_2MINOR: u8 = 0xfd;
|
||||
|
||||
pub const DTLS1_0MAJOR: u8 = 0xfe;
|
||||
pub const DTLS1_0MINOR: u8 = 0xff;
|
||||
|
||||
// VERSION_DTLS12 is the DTLS version in the same style as
|
||||
// VersionTLSXX from crypto/tls
|
||||
pub const VERSION_DTLS12: u16 = 0xfefd;
|
||||
|
||||
pub const PROTOCOL_VERSION1_0: ProtocolVersion = ProtocolVersion {
|
||||
major: DTLS1_0MAJOR,
|
||||
minor: DTLS1_0MINOR,
|
||||
};
|
||||
pub const PROTOCOL_VERSION1_2: ProtocolVersion = ProtocolVersion {
|
||||
major: DTLS1_2MAJOR,
|
||||
minor: DTLS1_2MINOR,
|
||||
};
|
||||
|
||||
/// ## Specifications
|
||||
///
|
||||
/// * [RFC 4346 §6.2.1]
|
||||
///
|
||||
/// [RFC 4346 §6.2.1]: https://tools.ietf.org/html/rfc4346#section-6.2.1
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
|
||||
pub struct ProtocolVersion {
|
||||
pub major: u8,
|
||||
pub minor: u8,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
|
||||
pub struct RecordLayerHeader {
|
||||
pub content_type: ContentType,
|
||||
pub protocol_version: ProtocolVersion,
|
||||
pub epoch: u16,
|
||||
pub sequence_number: u64, // uint48 in spec
|
||||
pub content_len: u16,
|
||||
}
|
||||
|
||||
impl RecordLayerHeader {
|
||||
pub fn marshal<W: Write>(&self, writer: &mut W) -> Result<()> {
|
||||
if self.sequence_number > MAX_SEQUENCE_NUMBER {
|
||||
return Err(Error::ErrSequenceNumberOverflow);
|
||||
}
|
||||
|
||||
writer.write_u8(self.content_type as u8)?;
|
||||
writer.write_u8(self.protocol_version.major)?;
|
||||
writer.write_u8(self.protocol_version.minor)?;
|
||||
writer.write_u16::<BigEndian>(self.epoch)?;
|
||||
|
||||
let be: [u8; 8] = self.sequence_number.to_be_bytes();
|
||||
writer.write_all(&be[2..])?; // uint48 in spec
|
||||
|
||||
writer.write_u16::<BigEndian>(self.content_len)?;
|
||||
|
||||
Ok(writer.flush()?)
|
||||
}
|
||||
|
||||
pub fn unmarshal<R: Read>(reader: &mut R) -> Result<Self> {
|
||||
let content_type = reader.read_u8()?.into();
|
||||
let major = reader.read_u8()?;
|
||||
let minor = reader.read_u8()?;
|
||||
let epoch = reader.read_u16::<BigEndian>()?;
|
||||
|
||||
// SequenceNumber is stored as uint48, make into uint64
|
||||
let mut be: [u8; 8] = [0u8; 8];
|
||||
reader.read_exact(&mut be[2..])?;
|
||||
let sequence_number = u64::from_be_bytes(be);
|
||||
|
||||
let protocol_version = ProtocolVersion { major, minor };
|
||||
if protocol_version != PROTOCOL_VERSION1_0 && protocol_version != PROTOCOL_VERSION1_2 {
|
||||
return Err(Error::ErrUnsupportedProtocolVersion);
|
||||
}
|
||||
let content_len = reader.read_u16::<BigEndian>()?;
|
||||
|
||||
Ok(RecordLayerHeader {
|
||||
content_type,
|
||||
protocol_version,
|
||||
epoch,
|
||||
sequence_number,
|
||||
content_len,
|
||||
})
|
||||
}
|
||||
}
|
||||
118
libs/dtls/src/record_layer/record_layer_test.rs
Normal file
118
libs/dtls/src/record_layer/record_layer_test.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
use std::io::{BufReader, BufWriter};
|
||||
|
||||
use super::record_layer_header::*;
|
||||
use super::*;
|
||||
use crate::change_cipher_spec::ChangeCipherSpec;
|
||||
|
||||
#[test]
|
||||
fn test_udp_decode() -> Result<()> {
|
||||
let tests = vec![
|
||||
(
|
||||
"Change Cipher Spec, single packet",
|
||||
vec![
|
||||
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01,
|
||||
],
|
||||
vec![vec![
|
||||
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01,
|
||||
]],
|
||||
None,
|
||||
),
|
||||
(
|
||||
"Change Cipher Spec, multi packet",
|
||||
vec![
|
||||
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01,
|
||||
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x01,
|
||||
],
|
||||
vec![
|
||||
vec![
|
||||
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01,
|
||||
0x01,
|
||||
],
|
||||
vec![
|
||||
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01,
|
||||
0x01,
|
||||
],
|
||||
],
|
||||
None,
|
||||
),
|
||||
(
|
||||
"Invalid packet length",
|
||||
vec![0x14, 0xfe],
|
||||
vec![],
|
||||
Some(Error::ErrInvalidPacketLength),
|
||||
),
|
||||
(
|
||||
"Packet declared invalid length",
|
||||
vec![
|
||||
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xFF, 0x01,
|
||||
],
|
||||
vec![],
|
||||
Some(Error::ErrInvalidPacketLength),
|
||||
),
|
||||
];
|
||||
|
||||
for (name, data, wanted, wanted_err) in tests {
|
||||
let dtls_pkts = unpack_datagram(&data);
|
||||
if let Some(err) = wanted_err {
|
||||
if let Err(dtls) = dtls_pkts {
|
||||
assert_eq!(err.to_string(), dtls.to_string());
|
||||
} else {
|
||||
panic!("something wrong for {name} when wanted_err is Some");
|
||||
}
|
||||
} else if let Ok(pkts) = dtls_pkts {
|
||||
assert_eq!(
|
||||
wanted, pkts,
|
||||
"{name} UDP decode: got {pkts:?}, want {wanted:?}",
|
||||
);
|
||||
} else {
|
||||
panic!("something wrong for {name} when wanted_err is None");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_record_layer_round_trip() -> Result<()> {
|
||||
let tests = vec![(
|
||||
"Change Cipher Spec, single packet",
|
||||
vec![
|
||||
0x14, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x01,
|
||||
],
|
||||
RecordLayer {
|
||||
record_layer_header: RecordLayerHeader {
|
||||
content_type: ContentType::ChangeCipherSpec,
|
||||
protocol_version: ProtocolVersion {
|
||||
major: 0xfe,
|
||||
minor: 0xff,
|
||||
},
|
||||
epoch: 0,
|
||||
sequence_number: 18,
|
||||
content_len: 1,
|
||||
},
|
||||
content: Content::ChangeCipherSpec(ChangeCipherSpec {}),
|
||||
},
|
||||
)];
|
||||
|
||||
for (name, data, want) in tests {
|
||||
let mut reader = BufReader::new(data.as_slice());
|
||||
let r = RecordLayer::unmarshal(&mut reader)?;
|
||||
|
||||
assert_eq!(
|
||||
want, r,
|
||||
"{name} recordLayer.unmarshal: got {r:?}, want {want:?}"
|
||||
);
|
||||
|
||||
let mut data2 = vec![];
|
||||
{
|
||||
let mut writer = BufWriter::<&mut Vec<u8>>::new(data2.as_mut());
|
||||
r.marshal(&mut writer)?;
|
||||
}
|
||||
assert_eq!(
|
||||
data, data2,
|
||||
"{name} recordLayer.marshal: got {data2:?}, want {data:?}"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user