refactor: 将 DTLS 本地依赖迁移到 libs 目录

This commit is contained in:
mofeng-git
2026-10-01 22:46:36 +08:00
parent 2f60e61dd5
commit 4f59e78179
99 changed files with 2 additions and 2 deletions

View 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)
}

View 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,
})
}
}

View 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(())
}