2022-04-14 14:55:45 -07:00
|
|
|
use std::io::Read;
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
use aes::cipher::{AsyncStreamCipher, NewCipher};
|
2022-04-14 14:55:45 -07:00
|
|
|
use aes::Aes128;
|
|
|
|
use anyhow::{bail, ensure, Context};
|
2022-11-01 03:11:51 -07:00
|
|
|
use bytes::{Buf, BufMut, BytesMut};
|
2022-04-14 14:55:45 -07:00
|
|
|
use cfb8::Cfb8;
|
2022-11-01 03:11:51 -07:00
|
|
|
use flate2::bufread::ZlibDecoder;
|
|
|
|
use flate2::write::ZlibEncoder;
|
2022-04-14 14:55:45 -07:00
|
|
|
use flate2::Compression;
|
2022-11-01 03:11:51 -07:00
|
|
|
use log::log_enabled;
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
use crate::protocol::packets::{DecodePacket, EncodePacket};
|
|
|
|
use crate::protocol::var_int::VarIntDecodeError;
|
2022-08-31 19:20:49 -07:00
|
|
|
use crate::protocol::{Decode, Encode, VarInt, MAX_PACKET_SIZE};
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
/// The AES block cipher with a 128 bit key, using the CFB-8 mode of
|
|
|
|
/// operation.
|
|
|
|
type Cipher = Cfb8<Aes128>;
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct PacketEncoder {
|
|
|
|
buf: BytesMut,
|
2022-04-14 14:55:45 -07:00
|
|
|
compress_buf: Vec<u8>,
|
|
|
|
compression_threshold: Option<u32>,
|
|
|
|
cipher: Option<Cipher>,
|
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
impl PacketEncoder {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self::default()
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
pub fn append_packet<P>(&mut self, pkt: &P) -> anyhow::Result<()>
|
|
|
|
where
|
|
|
|
P: EncodePacket + ?Sized,
|
|
|
|
{
|
|
|
|
self.append_or_prepend_packet::<true>(pkt)
|
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
pub fn prepend_packet<P>(&mut self, pkt: &P) -> anyhow::Result<()>
|
|
|
|
where
|
|
|
|
P: EncodePacket + ?Sized,
|
|
|
|
{
|
|
|
|
self.append_or_prepend_packet::<false>(pkt)
|
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
fn append_or_prepend_packet<const APPEND: bool>(
|
|
|
|
&mut self,
|
|
|
|
pkt: &(impl EncodePacket + ?Sized),
|
|
|
|
) -> anyhow::Result<()> {
|
|
|
|
let data_len = pkt.encoded_packet_len();
|
2022-04-14 14:55:45 -07:00
|
|
|
|
|
|
|
if let Some(threshold) = self.compression_threshold {
|
|
|
|
if data_len >= threshold as usize {
|
2022-11-01 20:27:08 -07:00
|
|
|
let mut z = ZlibEncoder::new(&mut self.compress_buf, Compression::new(4));
|
2022-11-01 03:11:51 -07:00
|
|
|
pkt.encode_packet(&mut z)?;
|
|
|
|
drop(z);
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
let packet_len = VarInt(data_len as i32).encoded_len() + self.compress_buf.len();
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
ensure!(
|
|
|
|
packet_len <= MAX_PACKET_SIZE as usize,
|
|
|
|
"packet exceeds maximum length"
|
|
|
|
);
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
// BytesMut doesn't implement io::Write for some reason.
|
|
|
|
let mut writer = (&mut self.buf).writer();
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
if APPEND {
|
|
|
|
VarInt(packet_len as i32).encode(&mut writer)?;
|
|
|
|
VarInt(data_len as i32).encode(&mut writer)?;
|
|
|
|
self.buf.extend_from_slice(&self.compress_buf);
|
|
|
|
} else {
|
|
|
|
let mut slice = move_forward_by(
|
|
|
|
&mut self.buf,
|
|
|
|
VarInt(packet_len as i32).encoded_len() + packet_len,
|
|
|
|
);
|
|
|
|
|
|
|
|
VarInt(packet_len as i32).encode(&mut slice)?;
|
|
|
|
VarInt(data_len as i32).encode(&mut slice)?;
|
|
|
|
slice.copy_from_slice(&self.compress_buf);
|
|
|
|
}
|
2022-08-05 23:10:25 -07:00
|
|
|
|
|
|
|
self.compress_buf.clear();
|
2022-04-14 14:55:45 -07:00
|
|
|
} else {
|
2022-10-19 01:52:02 -07:00
|
|
|
let packet_len = VarInt(0).encoded_len() + data_len;
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
ensure!(
|
|
|
|
packet_len <= MAX_PACKET_SIZE as usize,
|
|
|
|
"packet exceeds maximum length"
|
|
|
|
);
|
2022-08-05 23:10:25 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
let mut writer = (&mut self.buf).writer();
|
|
|
|
|
|
|
|
if APPEND {
|
|
|
|
VarInt(packet_len as i32).encode(&mut writer)?;
|
|
|
|
VarInt(0).encode(&mut writer)?; // 0 for no compression on this packet.
|
|
|
|
pkt.encode_packet(&mut writer)?;
|
|
|
|
} else {
|
|
|
|
let mut slice = move_forward_by(
|
|
|
|
&mut self.buf,
|
|
|
|
VarInt(packet_len as i32).encoded_len() + packet_len,
|
|
|
|
);
|
|
|
|
|
|
|
|
VarInt(packet_len as i32).encode(&mut slice)?;
|
|
|
|
VarInt(0).encode(&mut slice)?;
|
|
|
|
pkt.encode_packet(&mut slice)?;
|
|
|
|
|
|
|
|
debug_assert!(
|
|
|
|
slice.is_empty(),
|
|
|
|
"actual size of {} packet differs from reported size (actual = {}, \
|
|
|
|
reported = {})",
|
|
|
|
pkt.packet_name(),
|
|
|
|
data_len - slice.len(),
|
|
|
|
data_len,
|
|
|
|
);
|
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let packet_len = data_len;
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
ensure!(
|
|
|
|
packet_len <= MAX_PACKET_SIZE as usize,
|
|
|
|
"packet exceeds maximum length"
|
|
|
|
);
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
if APPEND {
|
|
|
|
let mut writer = (&mut self.buf).writer();
|
|
|
|
VarInt(packet_len as i32).encode(&mut writer)?;
|
|
|
|
pkt.encode_packet(&mut writer)?;
|
|
|
|
} else {
|
|
|
|
let mut slice = move_forward_by(
|
|
|
|
&mut self.buf,
|
|
|
|
VarInt(packet_len as i32).encoded_len() + packet_len,
|
|
|
|
);
|
|
|
|
|
|
|
|
VarInt(packet_len as i32).encode(&mut slice)?;
|
|
|
|
pkt.encode_packet(&mut slice)?;
|
2022-08-05 23:10:25 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
debug_assert!(
|
|
|
|
slice.is_empty(),
|
|
|
|
"actual size of {} packet differs from reported size (actual = {}, reported = \
|
|
|
|
{})",
|
|
|
|
pkt.packet_name(),
|
|
|
|
data_len - slice.len(),
|
|
|
|
data_len,
|
|
|
|
);
|
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-08-05 23:10:25 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
/// Takes all the packets written so far and encrypts them if encryption is
|
|
|
|
/// enabled.
|
|
|
|
pub fn take(&mut self) -> BytesMut {
|
|
|
|
if let Some(cipher) = &mut self.cipher {
|
|
|
|
cipher.encrypt(&mut self.buf);
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
self.buf.split()
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
pub fn set_compression(&mut self, threshold: Option<u32>) {
|
|
|
|
self.compression_threshold = threshold;
|
2022-08-05 23:10:25 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
/// Enables encryption for all future packets **and any packets that have
|
|
|
|
/// not been [taken] yet.**
|
|
|
|
///
|
|
|
|
/// [taken]: Self::take
|
2022-04-14 14:55:45 -07:00
|
|
|
pub fn enable_encryption(&mut self, key: &[u8; 16]) {
|
2022-11-01 03:11:51 -07:00
|
|
|
assert!(self.cipher.is_none(), "encryption is already enabled");
|
2022-04-14 14:55:45 -07:00
|
|
|
self.cipher = Some(NewCipher::new(key.into(), key.into()));
|
|
|
|
}
|
2022-11-01 03:11:51 -07:00
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
/// Move the bytes in `bytes` forward by `count` bytes and return a
|
|
|
|
/// mutable reference to the new space at the front.
|
|
|
|
fn move_forward_by(bytes: &mut BytesMut, count: usize) -> &mut [u8] {
|
|
|
|
let len = bytes.len();
|
|
|
|
bytes.put_bytes(0, count);
|
|
|
|
bytes.copy_within(..len, count);
|
|
|
|
&mut bytes[..count]
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct PacketDecoder {
|
|
|
|
buf: BytesMut,
|
2022-04-14 14:55:45 -07:00
|
|
|
decompress_buf: Vec<u8>,
|
2022-11-01 03:11:51 -07:00
|
|
|
compression: bool,
|
2022-04-14 14:55:45 -07:00
|
|
|
cipher: Option<Cipher>,
|
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
impl PacketDecoder {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self::default()
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
pub fn try_next_packet<P>(&mut self) -> anyhow::Result<Option<P>>
|
|
|
|
where
|
|
|
|
P: DecodePacket,
|
|
|
|
{
|
|
|
|
let mut r = &self.buf[..];
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
let packet_len = match VarInt::decode_partial(&mut r) {
|
|
|
|
Ok(len) => len,
|
|
|
|
Err(VarIntDecodeError::Incomplete) => return Ok(None),
|
|
|
|
Err(VarIntDecodeError::TooLarge) => bail!("malformed packet length VarInt"),
|
|
|
|
};
|
2022-04-14 14:55:45 -07:00
|
|
|
|
|
|
|
ensure!(
|
2022-11-01 03:11:51 -07:00
|
|
|
packet_len <= MAX_PACKET_SIZE,
|
|
|
|
"packet length of {packet_len} is out of bounds"
|
2022-04-14 14:55:45 -07:00
|
|
|
);
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
if r.len() < packet_len as usize {
|
|
|
|
return Ok(None);
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
r = &r[..packet_len as usize];
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
let packet = if self.compression {
|
|
|
|
let data_len = VarInt::decode(&mut r)?.0;
|
2022-04-14 14:55:45 -07:00
|
|
|
|
|
|
|
ensure!(
|
2022-11-01 03:11:51 -07:00
|
|
|
(0..MAX_PACKET_SIZE).contains(&data_len),
|
|
|
|
"decompressed packet length of {data_len} is out of bounds"
|
2022-04-14 14:55:45 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
if data_len != 0 {
|
2022-11-01 03:11:51 -07:00
|
|
|
self.decompress_buf.clear();
|
|
|
|
self.decompress_buf.reserve_exact(data_len as usize);
|
|
|
|
let mut z = ZlibDecoder::new(r).take(data_len as u64);
|
|
|
|
|
|
|
|
z.read_to_end(&mut self.decompress_buf)
|
|
|
|
.context("decompressing packet")?;
|
|
|
|
|
|
|
|
r = &self.decompress_buf;
|
|
|
|
P::decode_packet(&mut r)?
|
2022-04-14 14:55:45 -07:00
|
|
|
} else {
|
2022-11-01 03:11:51 -07:00
|
|
|
P::decode_packet(&mut r)?
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
} else {
|
2022-11-01 03:11:51 -07:00
|
|
|
P::decode_packet(&mut r)?
|
2022-04-14 14:55:45 -07:00
|
|
|
};
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
if !r.is_empty() {
|
|
|
|
if log_enabled!(log::Level::Debug) {
|
|
|
|
log::debug!("packet after partial decode: {packet:?}");
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bail!(
|
2022-11-01 03:11:51 -07:00
|
|
|
"packet contents were not read completely ({} bytes remain)",
|
|
|
|
r.len()
|
2022-04-14 14:55:45 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
let total_packet_len = VarInt(packet_len).encoded_len() + packet_len as usize;
|
|
|
|
|
|
|
|
self.buf.advance(total_packet_len);
|
|
|
|
|
|
|
|
Ok(Some(packet))
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
pub fn set_compression(&mut self, compression: bool) {
|
|
|
|
self.compression = compression;
|
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
pub fn enable_encryption(&mut self, key: &[u8; 16]) {
|
|
|
|
assert!(self.cipher.is_none(), "encryption is already enabled");
|
|
|
|
|
|
|
|
let mut cipher = Cipher::new(key.into(), key.into());
|
|
|
|
// Don't forget to decrypt the data we already have.
|
|
|
|
cipher.decrypt(&mut self.buf);
|
|
|
|
self.cipher = Some(cipher);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn queue_bytes(&mut self, mut bytes: BytesMut) {
|
|
|
|
if let Some(cipher) = &mut self.cipher {
|
|
|
|
cipher.decrypt(&mut bytes);
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
2022-11-01 03:11:51 -07:00
|
|
|
|
|
|
|
self.buf.unsplit(bytes);
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
pub fn queue_slice(&mut self, bytes: &[u8]) {
|
|
|
|
let len = self.buf.len();
|
|
|
|
self.buf.extend_from_slice(bytes);
|
|
|
|
if let Some(cipher) = &mut self.cipher {
|
|
|
|
cipher.decrypt(&mut self.buf[len..]);
|
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
pub fn queued_bytes(&self) -> &[u8] {
|
|
|
|
self.buf.as_ref()
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
2022-07-01 15:29:31 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
pub fn take_capacity(&mut self) -> BytesMut {
|
|
|
|
self.buf.split_off(self.buf.len())
|
2022-07-04 18:22:29 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
pub fn reserve(&mut self, additional: usize) {
|
|
|
|
self.buf.reserve(additional);
|
2022-07-01 15:29:31 -07:00
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2022-11-01 03:11:51 -07:00
|
|
|
use std::io::Write;
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
use anyhow::Context;
|
2022-04-14 14:55:45 -07:00
|
|
|
|
|
|
|
use super::*;
|
2022-11-01 03:11:51 -07:00
|
|
|
use crate::protocol::packets::{DecodePacket, EncodePacket, PacketName};
|
|
|
|
use crate::protocol::{Decode, Encode};
|
2022-04-14 14:55:45 -07:00
|
|
|
|
|
|
|
const CRYPT_KEY: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
struct TestPacket {
|
|
|
|
string: String,
|
|
|
|
vec_of_u16: Vec<u16>,
|
|
|
|
u64: u64,
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
impl PacketName for TestPacket {
|
|
|
|
fn packet_name(&self) -> &'static str {
|
|
|
|
"TestPacket"
|
|
|
|
}
|
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
impl EncodePacket for TestPacket {
|
|
|
|
fn encode_packet(&self, w: &mut impl Write) -> anyhow::Result<()> {
|
|
|
|
self.string.encode(w)?;
|
|
|
|
self.vec_of_u16.encode(w)?;
|
|
|
|
self.u64.encode(w)
|
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
fn encoded_packet_len(&self) -> usize {
|
|
|
|
self.string.encoded_len() + self.vec_of_u16.encoded_len() + self.u64.encoded_len()
|
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
impl DecodePacket for TestPacket {
|
|
|
|
fn decode_packet(r: &mut &[u8]) -> anyhow::Result<Self> {
|
|
|
|
Ok(TestPacket {
|
|
|
|
string: String::decode(r).context("decoding string field")?,
|
|
|
|
vec_of_u16: Vec::decode(r).context("decoding vec of u16 field")?,
|
|
|
|
u64: u64::decode(r).context("decoding u64 field")?,
|
|
|
|
})
|
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
impl TestPacket {
|
|
|
|
fn new(s: impl Into<String>) -> Self {
|
|
|
|
Self {
|
|
|
|
string: s.into(),
|
|
|
|
vec_of_u16: vec![0x1234, 0xabcd],
|
|
|
|
u64: 0x1122334455667788,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check(&self, s: impl AsRef<str>) {
|
|
|
|
assert_eq!(&self.string, s.as_ref());
|
|
|
|
assert_eq!(&self.vec_of_u16, &[0x1234, 0xabcd]);
|
|
|
|
assert_eq!(self.u64, 0x1122334455667788);
|
|
|
|
}
|
|
|
|
}
|
2022-04-14 14:55:45 -07:00
|
|
|
|
2022-11-01 03:11:51 -07:00
|
|
|
#[test]
|
|
|
|
fn packets_round_trip() {
|
|
|
|
let mut buf = BytesMut::new();
|
|
|
|
|
|
|
|
let mut enc = PacketEncoder::new();
|
|
|
|
|
|
|
|
enc.append_packet(&TestPacket::new("first")).unwrap();
|
|
|
|
enc.set_compression(Some(0));
|
|
|
|
enc.append_packet(&TestPacket::new("second")).unwrap();
|
|
|
|
buf.unsplit(enc.take());
|
|
|
|
enc.enable_encryption(&CRYPT_KEY);
|
|
|
|
enc.append_packet(&TestPacket::new("third")).unwrap();
|
|
|
|
enc.prepend_packet(&TestPacket::new("fourth")).unwrap();
|
|
|
|
buf.unsplit(enc.take());
|
|
|
|
|
|
|
|
let mut dec = PacketDecoder::new();
|
|
|
|
|
|
|
|
dec.queue_bytes(buf);
|
|
|
|
dec.try_next_packet::<TestPacket>()
|
|
|
|
.unwrap()
|
|
|
|
.unwrap()
|
|
|
|
.check("first");
|
|
|
|
dec.set_compression(true);
|
|
|
|
dec.try_next_packet::<TestPacket>()
|
|
|
|
.unwrap()
|
|
|
|
.unwrap()
|
|
|
|
.check("second");
|
|
|
|
dec.enable_encryption(&CRYPT_KEY);
|
|
|
|
dec.try_next_packet::<TestPacket>()
|
|
|
|
.unwrap()
|
|
|
|
.unwrap()
|
|
|
|
.check("fourth");
|
|
|
|
dec.try_next_packet::<TestPacket>()
|
|
|
|
.unwrap()
|
|
|
|
.unwrap()
|
|
|
|
.check("third");
|
2022-04-14 14:55:45 -07:00
|
|
|
}
|
|
|
|
}
|