use std::borrow::Cow;
use std::ffi::CStr;
use std::fmt;
use std::io::{Cursor, Read, Write};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::ops::RangeInclusive;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::net;
use std::str::FromStr;
use bitflags::bitflags;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::{common, map_error, return_error};
use crate::error::{HaProxErr, HaProxRes};
use super::protocol_raw::{self, HEADER_UNIX_ADDR_LEN};
pub const PP2_TYPE_MIN_CUSTOM: u8 = 0xE0;
pub const PP2_TYPE_MAX_CUSTOM: u8 = 0xEF;
pub const PP2_TYPE_MIN_EXPERIMENT: u8 = 0xF0;
pub const PP2_TYPE_MAX_EXPERIMENT: u8 = 0xF7;
pub const PP2_TYPE_MIN_FUTURE: u8 = 0xF8;
pub const PP2_TYPE_MAX_FUTURE: u8 = 0xFF;
pub const MAX_UNIQ_ID_LEN_BYTES: u16 = 128;
pub const TLV_HEADER_LEN: u16 = 3;
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum HdrV2Command
{
LOCAL = 0,
PROXY = 1,
UNKNOWN,
}
impl From<HdrV2Command> for u8
{
fn from(value: HdrV2Command) -> Self
{
if value == HdrV2Command::UNKNOWN
{
panic!("can not encode the unknown command");
}
return value as u8;
}
}
impl HdrV2Command
{
pub(crate)
fn decode(raw: u8) -> Self
{
match raw & protocol_raw::ProxyHdrV2::COMMAND_MASK
{
r if r == HdrV2Command::LOCAL.into() => return Self::LOCAL,
r if r == HdrV2Command::PROXY.into() => return Self::PROXY,
_ => return Self::UNKNOWN,
}
}
}
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ProtocolVersion
{
V1 = 1,
V2 = 2,
UNKNOWN,
}
impl From<ProtocolVersion> for u8
{
fn from(value: ProtocolVersion) -> Self
{
if value == ProtocolVersion::UNKNOWN
{
panic!("can not encode the unknown version");
}
return (value as u8) << 4;
}
}
impl ProtocolVersion
{
pub(crate)
fn decode(raw: u8) -> Self
{
match raw >> 4
{
1 => return Self::V1,
2 => return Self::V2,
_ => return Self::UNKNOWN,
}
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PP2TlvClient: u8
{
const PP2_CLIENT_SSL = 0x01;
const PP2_CLIENT_CERT_CONN = 0x02;
const PP2_CLIENT_CERT_SESS = 0x04;
}
}
#[test]
fn ttt()
{
let bi: u8 = 7;
println!("{} {:?}", PP2TlvClient::all().bits(), PP2TlvClient::from_bits(bi));
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PP2TlvsTypeSsl
{
pub(crate) client: PP2TlvClient,
pub(crate) verify: u32,
pub(crate) sub_tlv: Vec<PP2Tlvs>
}
impl fmt::Display for PP2TlvsTypeSsl
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "CLIENT: {:?}, VERIFY: {}, TLVs: {}",
self.client, self.verify,
self.sub_tlv.iter().map(|t| t.to_string()).collect::<Vec<String>>().join(", "))
}
}
#[repr(u8)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PP2Tlvs
{
TypeAlpn(Vec<Vec<u8>>) = Self::TYPE_ALPN,
TypeAuthority(String) = Self::TYPE_AUTHORITY,
TypeCrc32c(u32) = Self::TYPE_CRC32C,
TypeNoop = Self::TYPE_NOOP,
TypeUniqId(Vec<u8>) = Self::TYPE_UNIQID,
TypeSsl
{
client: PP2TlvClient,
verify: u32,
} = Self::TYPE_SSL,
TypeSubtypeSslVersion(Cow<'static, str>) = Self::TYPE_SUBTYPE_SSL_VERSION,
TypeSubtypeSslCn(Cow<'static, str>) = Self::TYPE_SUBTYPE_SSL_CN,
TypeSubtypeSslCipher(Cow<'static, str>) = Self::TYPE_SUBTYPE_SSL_CIPHER,
TypeSubtypeSslSigAlg(Cow<'static, str>) = Self::TYPE_SUBTYPE_SSL_SIGALG,
TypeSubtypeSslKeyAlg(Cow<'static, str>) = Self::TYPE_SUBTYPE_SSL_KEYALG,
TypeNetNs(String) = Self::TYPE_NETNS,
}
impl fmt::Display for PP2Tlvs
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
let id : u8= self.into();
match self
{
PP2Tlvs::TypeAlpn(alpns) =>
{
let alpns_dec =
alpns
.iter()
.map(
|a|
std
::str
::from_utf8(a)
.map_err(|e| map_error!(MalformedData, "{}", e))
)
.collect::<HaProxRes<Vec<&str>>>()
.map_err(|_e| fmt::Error)?
.join(",");
write!(f, "ALPNS({:02X}): {}", id, alpns_dec)
},
PP2Tlvs::TypeAuthority(sni) =>
write!(f, "SNI({:02X}): {}", id, sni),
PP2Tlvs::TypeCrc32c(crc) =>
write!(f, "CRC({:02X}): {}", id, crc),
PP2Tlvs::TypeNoop =>
write!(f, "NOOP({:02X})", id),
PP2Tlvs::TypeUniqId(items) =>
write!(f, "UNIQID({:02X}): {:02X?}", id, items),
PP2Tlvs::TypeSsl{client, verify} =>
write!(f, "SSL({:02X}): client: {:?}, verify: {}", id, client, verify),
PP2Tlvs::TypeSubtypeSslVersion(ver) =>
write!(f, "SSL VERSION({:02X}): {}", id, ver),
PP2Tlvs::TypeSubtypeSslCn(cn) =>
write!(f, "SSL CN({:02X}): {}", id, cn),
PP2Tlvs::TypeSubtypeSslCipher(c) =>
write!(f, "SSL CIPHER({:02X}): {}", id, c),
PP2Tlvs::TypeSubtypeSslSigAlg(sa) =>
write!(f, "SSL SIGALG({:02X}): {}", id, sa),
PP2Tlvs::TypeSubtypeSslKeyAlg(ka) =>
write!(f, "SSL KEYALG({:02X}): {}", id, ka),
PP2Tlvs::TypeNetNs(ns) =>
write!(f, "NETNS({:02X}): {}", id, ns),
}
}
}
impl From<PP2Tlvs> for u8
{
fn from(value: PP2Tlvs) -> Self
{
return (&value).into();
}
}
impl From<&PP2Tlvs> for u8
{
fn from(value: &PP2Tlvs) -> Self
{
return unsafe { *<*const _>::from(value).cast::<Self>() };
}
}
impl PP2Tlvs
{
pub const TLV_TYPE_MAIN_RANGES: &'static [RangeInclusive<u8>] =
&[
Self::TYPE_ALPN..=Self::TYPE_SSL,
Self::TYPE_NETNS ..= Self::TYPE_NETNS
];
pub const TLV_TYPE_SSL_SUB_RANGE: &'static [RangeInclusive<u8>] =
&[Self::TYPE_SUBTYPE_SSL_VERSION ..= Self::TYPE_SUBTYPE_SSL_KEYALG];
pub const TYPE_ALPN: u8 = 0x01;
pub const TYPE_AUTHORITY: u8 = 0x02;
pub const TYPE_CRC32C: u8 = 0x03;
pub const TYPE_NOOP: u8 = 0x04;
pub const TYPE_UNIQID: u8 = 0x05;
pub const TYPE_SSL: u8 = 0x20;
pub const TYPE_SUBTYPE_SSL_VERSION: u8 = 0x21;
pub const TYPE_SUBTYPE_SSL_CN: u8 = 0x22;
pub const TYPE_SUBTYPE_SSL_CIPHER: u8 = 0x23;
pub const TYPE_SUBTYPE_SSL_SIGALG: u8 = 0x24;
pub const TYPE_SUBTYPE_SSL_KEYALG: u8 = 0x25;
pub const TYPE_NETNS: u8 = 0x30;
pub
fn contains_subtype(&self) -> bool
{
let Self::TypeSsl{ .. } = self else { return false };
return true;
}
pub
fn conntains_subtype_discr(discr: u8) -> bool
{
return discr == Self::TYPE_SSL;
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProxyV2AddrType
{
AfUnspec = 0x00,
AfInet = 0x01,
AfInet6 = 0x02,
AfUnix = 0x03,
}
impl From<ProxyV2AddrType> for u8
{
fn from(value: ProxyV2AddrType) -> Self
{
return value as u8;
}
}
impl ProxyV2AddrType
{
pub const DEF_IPV4_ADDR_LEN: u16 = 12;
pub const DEF_IPV6_ADDR_LEN: u16 = 36;
pub const DEF_UNIX_ADDR_LEN: u16 = 216;
pub(crate)
fn decode(raw: u8) -> HaProxRes<Self>
{
match raw >> 4
{
r if r == ProxyV2AddrType::AfUnspec.into() =>
return Ok(Self::AfUnspec),
r if r == ProxyV2AddrType::AfInet.into() =>
return Ok(Self::AfInet),
r if r == ProxyV2AddrType::AfInet6.into() =>
return Ok(Self::AfInet6),
r if r == ProxyV2AddrType::AfUnix.into() =>
return Ok(Self::AfUnix),
r =>
return_error!(ProtocolUnknownData, "can not decode address type: '{:02X}'", r),
}
}
pub
fn get_size_by_addr_family(&self) -> Option<u16>
{
match self
{
Self::AfUnspec =>
return None,
Self::AfInet =>
return Some(Self::DEF_IPV4_ADDR_LEN),
Self::AfInet6 =>
return Some(Self::DEF_IPV6_ADDR_LEN),
Self::AfUnix =>
return Some(Self::DEF_UNIX_ADDR_LEN),
}
}
}
#[derive(Clone, Debug)]
pub enum ProxyV2Addr
{
Ip
{
src: SocketAddr,
dst: SocketAddr,
},
Unix
{
src: net::SocketAddr,
dst: net::SocketAddr,
}
}
impl Eq for ProxyV2Addr {}
impl PartialEq for ProxyV2Addr
{
fn eq(&self, other: &Self) -> bool
{
match (self, other)
{
(
Self::Ip { src: l_src, dst: l_dst },
Self::Ip { src: r_src, dst: r_dst }
) =>
l_src == r_src && l_dst == r_dst,
(
Self::Unix { src: l_src, dst: l_dst },
Self::Unix { src: r_src, dst: r_dst }
) =>
l_src.as_pathname() == r_src.as_pathname() && l_dst.as_pathname() == r_dst.as_pathname(),
_ => false,
}
}
}
impl fmt::Display for ProxyV2Addr
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self
{
ProxyV2Addr::Ip{ src, dst } =>
write!(f, "SRC: {}, DST: {}", src, dst),
ProxyV2Addr::Unix{ src, dst } =>
write!(f, "SRC: {:?}, DST: {:?}", src, dst)
}
}
}
impl TryFrom<(IpAddr, u16, IpAddr, u16)> for ProxyV2Addr
{
type Error = HaProxErr;
fn try_from(value: (IpAddr, u16, IpAddr, u16)) -> Result<Self, Self::Error>
{
let src = SocketAddr::new(value.0, value.1);
let dst = SocketAddr::new(value.2, value.3);
return Ok(Self::Ip{ src: src, dst: dst });
}
}
impl TryFrom<(SocketAddr, SocketAddr)> for ProxyV2Addr
{
type Error = HaProxErr;
fn try_from(value: (SocketAddr, SocketAddr)) -> Result<Self, Self::Error>
{
return Ok(Self::Ip{ src: value.0, dst: value.1 });
}
}
impl TryFrom<(net::SocketAddr, net::SocketAddr)> for ProxyV2Addr
{
type Error = HaProxErr;
fn try_from(value: (net::SocketAddr, net::SocketAddr)) -> Result<Self, Self::Error>
{
return Ok(Self::Unix{ src: value.0, dst: value.1 });
}
}
impl TryFrom<(&str, &str)> for ProxyV2Addr
{
type Error = HaProxErr;
fn try_from(value: (&str, &str)) -> Result<Self, Self::Error>
{
if let Ok(src) = SocketAddr::from_str(value.0)
{
let Ok(dst) = SocketAddr::from_str(value.1)
else
{
return_error!(ArgumentEinval, "can not convert '{}' to SocketAddr",
common::sanitize_str_unicode(value.1));
};
return Ok(Self::Ip{ src: src, dst: dst });
}
else if let Ok(src) = net::SocketAddr::from_pathname(value.0)
{
let Ok(dst) = net::SocketAddr::from_pathname(value.1)
else
{
return_error!(ArgumentEinval, "can not convert '{}' to net::SocketAddr",
common::sanitize_str_unicode(value.1));
};
return Ok(Self::Unix{ src: src, dst: dst });
}
else
{
return_error!(ArgumentEinval, "can not convert '{}' to either SocketAddr or net::SocketAddr",
common::sanitize_str_unicode(value.0));
}
}
}
impl ProxyV2Addr
{
#[inline]
pub
fn get_len(&self) -> u16
{
return self.as_addr_family().get_size_by_addr_family().unwrap();
}
#[inline]
pub
fn as_addr_family(&self) -> ProxyV2AddrType
{
match self
{
ProxyV2Addr::Ip{ src, .. } =>
{
if src.is_ipv4() == true
{
return ProxyV2AddrType::AfInet;
}
else
{
return ProxyV2AddrType::AfInet6;
}
}
ProxyV2Addr::Unix{ .. } =>
return ProxyV2AddrType::AfUnix
}
}
pub
fn read(addr_fam: ProxyV2AddrType, cur: &mut Cursor<&[u8]>) -> HaProxRes<Option<Self>>
{
match addr_fam
{
ProxyV2AddrType::AfUnspec => Ok(None),
ProxyV2AddrType::AfInet =>
{
let src = IpAddr::from(Ipv4Addr::from_bits(cur.read_u32::<BigEndian>().map_err(common::map_io_err)?));
let dst = IpAddr::from(Ipv4Addr::from_bits(cur.read_u32::<BigEndian>().map_err(common::map_io_err)?));
let src_port = cur.read_u16::<BigEndian>().map_err(common::map_io_err)?;
let dst_port = cur.read_u16::<BigEndian>().map_err(common::map_io_err)?;
return Ok(Some(Self::try_from((src, src_port, dst, dst_port))?));
},
ProxyV2AddrType::AfInet6 =>
{
let src = IpAddr::from(Ipv6Addr::from_bits(cur.read_u128::<BigEndian>().map_err(common::map_io_err)?));
let dst = IpAddr::from(Ipv6Addr::from_bits(cur.read_u128::<BigEndian>().map_err(common::map_io_err)?));
let src_port = cur.read_u16::<BigEndian>().map_err(common::map_io_err)?;
let dst_port = cur.read_u16::<BigEndian>().map_err(common::map_io_err)?;
return Ok(Some(Self::try_from((src, src_port, dst, dst_port))?));
},
ProxyV2AddrType::AfUnix =>
{
let mut n_src: [u8; HEADER_UNIX_ADDR_LEN] = [0_u8; HEADER_UNIX_ADDR_LEN];
cur.read(&mut n_src).map_err(common::map_io_err)?;
let mut n_dst: [u8; HEADER_UNIX_ADDR_LEN] = [0_u8; HEADER_UNIX_ADDR_LEN];
cur.read(&mut n_dst).map_err(common::map_io_err)?;
let src_s =
net::SocketAddr::from_pathname(
CStr::from_bytes_until_nul(&n_src)
.map_err(|e|
map_error!(MalformedData, "cannot read unix path, error: {}", e)
)?
.to_str()
.map_err(|e|
map_error!(MalformedData, "cannot read unix path, error: {}", e)
)?
)
.map_err(|e|
map_error!(MalformedData, "cannot read unix path, error: {}", e)
)?;
let dst_s =
net::SocketAddr::from_pathname(
CStr::from_bytes_until_nul(&n_dst)
.map_err(|e|
map_error!(MalformedData, "cannot read unix path, error: {}", e)
)?
.to_str()
.map_err(|e|
map_error!(MalformedData, "cannot read unix path, error: {}", e)
)?
)
.map_err(|e|
map_error!(MalformedData, "cannot read unix path, error: {}", e)
)?;
return Ok(Some(Self::try_from((src_s, dst_s))?));
},
}
}
pub
fn write(&self, cur: &mut Cursor<Vec<u8>>) -> HaProxRes<()>
{
match self
{
ProxyV2Addr::Ip{ src, dst } =>
{
match src.ip()
{
IpAddr::V4(ipv4_addr) =>
cur.write_u32::<BigEndian>(ipv4_addr.to_bits()).map_err(common::map_io_err)?,
IpAddr::V6(ipv6_addr) =>
cur.write_u128::<BigEndian>(ipv6_addr.to_bits()).map_err(common::map_io_err)?,
}
match dst.ip()
{
IpAddr::V4(ipv4_addr) =>
cur.write_u32::<BigEndian>(ipv4_addr.to_bits()).map_err(common::map_io_err)?,
IpAddr::V6(ipv6_addr) =>
cur.write_u128::<BigEndian>(ipv6_addr.to_bits()).map_err(common::map_io_err)?,
}
cur.write_u16::<BigEndian>(src.port()).map_err(common::map_io_err)?;
cur.write_u16::<BigEndian>(dst.port()).map_err(common::map_io_err)?;
},
ProxyV2Addr::Unix { src, dst } =>
{
let src_p =
src.as_pathname().ok_or_else(|| map_error!(ArgumentEinval, "UNIX src socket addr is not path"))?;
let dst_p =
dst.as_pathname().ok_or_else(|| map_error!(ArgumentEinval, "UNIX src socket addr is not path"))?;
let src_b = src_p.as_os_str().as_bytes();
let dst_b = dst_p.as_os_str().as_bytes();
if src_b.len() > HEADER_UNIX_ADDR_LEN
{
return_error!(ArgumentEinval, "socket path: '{}' longer than: '{}'",
src_p.display(), HEADER_UNIX_ADDR_LEN);
}
else if dst_b.len() > HEADER_UNIX_ADDR_LEN
{
return_error!(ArgumentEinval, "socket path: '{}' longer than: '{}'",
dst_p.display(), HEADER_UNIX_ADDR_LEN);
}
let mut n_src: [u8; HEADER_UNIX_ADDR_LEN] = [0_u8; HEADER_UNIX_ADDR_LEN];
n_src[0..src_b.len()].copy_from_slice(src_b);
cur.write_all(&n_src).map_err(common::map_io_err)?;
let mut n_dst: [u8; HEADER_UNIX_ADDR_LEN] = [0_u8; HEADER_UNIX_ADDR_LEN];
n_dst[0..dst_b.len()].copy_from_slice(dst_b);
cur.write_all(&n_dst).map_err(common::map_io_err)?;
}
}
return Ok(());
}
}
#[repr(u8)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ProxyTransportFam
{
UNSPEC = 0x00,
STREAM,
DGRAM,
}
impl fmt::Display for ProxyTransportFam
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self
{
Self::UNSPEC => write!(f, "UNSPEC"),
Self::STREAM => write!(f, "STREAM"),
Self::DGRAM => write!(f, "DGRAM"),
}
}
}
impl From<ProxyTransportFam> for u8
{
fn from(value: ProxyTransportFam) -> Self
{
return value as u8;
}
}
impl From<&ProxyTransportFam> for u8
{
fn from(value: &ProxyTransportFam) -> Self
{
return unsafe { *<*const _>::from(value).cast::<Self>() };
}
}
impl ProxyTransportFam
{
pub(crate)
fn decode(value: u8) -> HaProxRes<Self>
{
match value & protocol_raw::ProxyHdrV2::TRANSPT_MASK
{
v if ProxyTransportFam::UNSPEC as u8 == v => return Ok(Self::UNSPEC),
v if ProxyTransportFam::STREAM as u8 == v => return Ok(Self::STREAM),
v if ProxyTransportFam::DGRAM as u8 == v => return Ok(Self::DGRAM),
_ => return_error!(ProtocolUnknownData, "unknown transport {:02X}", value)
}
}
}
#[cfg(test)]
mod tests
{
use core::fmt;
use std::{slice, time::Instant};
use crate::protocol::{protocol::{PP2TlvClient, ProxyTransportFam, ProxyV2Addr}, protocol_composer::{HdrV2OpLocal, HdrV2OpProxy, ProxyHdrV2}, protocol_raw::{self, HEADER_MAGIC_V2, HEADER_MAGINC_LEN}, PP2TlvUniqId};
#[test]
fn test_hdr1()
{
for _ in 0..10
{
let s = Instant::now();
let _local = ProxyHdrV2::<HdrV2OpLocal>::new();
let e = s.elapsed();
println!("{:?}", e);
}
let buf = ProxyHdrV2::<HdrV2OpLocal>::new();
let mut sign: [u8; HEADER_MAGINC_LEN] = [0_u8; HEADER_MAGINC_LEN];
sign.copy_from_slice(HEADER_MAGIC_V2);
let ctrl =
vec![
protocol_raw::ProxyHdrV2
{
signature: sign,
ver_cmd: 0x20,
fam: 0,
len: 0,
address: [],
}
];
println!("{} {}", buf.len(), size_of::<protocol_raw::ProxyHdrV2>());
let ctrl_buf = unsafe { slice::from_raw_parts(ctrl.as_ptr() as *const _ as *const u8, size_of::<protocol_raw::ProxyHdrV2>()) };
assert_eq!(buf.as_slice(), ctrl_buf);
}
#[test]
fn test_proxy_compose()
{
struct UniqIdHolder;
impl UniqIdHolder
{
const ID: &'static [u8] = b"ABCD12345678901234567890";
}
impl fmt::Display for UniqIdHolder
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
write!(f, "")
}
}
impl PP2TlvUniqId for UniqIdHolder
{
fn into_bytes(&self) -> Vec<u8>
{
Self::ID.to_vec()
}
fn get_len(&self) -> u16
{
Self::ID.len() as u16
}
}
for _ in 0..10
{
let addr: ProxyV2Addr = ProxyV2Addr::try_from(("127.0.0.1:4567", "127.0.0.1:443")).unwrap();
let id = UniqIdHolder;
let s = Instant::now();
let mut proxy =
ProxyHdrV2::<HdrV2OpProxy>::new(ProxyTransportFam::STREAM, addr).unwrap();
let mut plts = proxy.set_plts();
plts.add_crc32().unwrap();
plts.add_authority("www.example.com").unwrap();
plts.add_uniq_id(id).unwrap();
drop(plts);
let e = s.elapsed();
println!("prep: {:?}", e);
let s = Instant::now();
let _buf: Vec<u8> = proxy.try_into().unwrap();
let e = s.elapsed();
println!("{:?}", e);
}
return;
}
#[test]
fn test_hdr2()
{
let addr: ProxyV2Addr = ProxyV2Addr::try_from(("127.0.0.1:39754", "127.0.0.67:11883")).unwrap();
let s = Instant::now();
let mut proxy =
ProxyHdrV2::<HdrV2OpProxy>::new(ProxyTransportFam::STREAM, addr).unwrap();
let plts = proxy.set_plts();
let mut sub_ssl = plts.add_ssl(PP2TlvClient::PP2_CLIENT_SSL, 0).unwrap();
sub_ssl.add_ssl_sub_version("TLSv1.2").unwrap();
sub_ssl.done().unwrap();
let e = s.elapsed();
println!("prep: {:?}", e);
let s = Instant::now();
let buf: Vec<u8> = proxy.try_into().unwrap();
let e = s.elapsed();
println!("{:?}", e);
let reference =
b"\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a\x21\x11\x00\x1e\
\x7f\x00\x00\x01\x7f\x00\x00\x43\x9b\x4a\x2e\x6b\x20\x00\x0f\x01\
\x00\x00\x00\x00\x21\x00\x07\x54\x4c\x53\x76\x31\x2e\x32";
assert_eq!(buf.as_slice(), reference.as_slice());
}
}