15 march works with app

This commit is contained in:
2026-03-15 23:47:15 +07:00
parent 102099e1cd
commit 7dbfaec60d
38 changed files with 659 additions and 542 deletions
+5 -5
View File
@@ -58,7 +58,7 @@ impl ChaChaCipher {
self.encrypt_state = NonceState::new(w_iv);
self.decrypt_state = NonceState::new(r_iv);
tracing::debug!("Cipher keys and IVs updated for both directions");
netrunner_logger::debug!("Cipher keys and IVs updated for both directions");
}
}
impl AeadPacker for ChaChaCipher {
@@ -69,7 +69,7 @@ impl AeadPacker for ChaChaCipher {
match self.encrypt_cipher.encrypt_in_place(&nonce, &nonce, data) {
Ok(_) => {
tracing::trace!(
netrunner_logger::trace!(
counter = current_counter,
nonce = %hex::encode(nonce),
len = data_len,
@@ -78,7 +78,7 @@ impl AeadPacker for ChaChaCipher {
Ok(data.split().freeze())
}
Err(e) => {
tracing::error!(
netrunner_logger::error!(
counter = current_counter,
nonce = %hex::encode(nonce),
len = data_len,
@@ -97,7 +97,7 @@ impl AeadPacker for ChaChaCipher {
match self.decrypt_cipher.decrypt_in_place(&nonce, &nonce, data) {
Ok(_) => {
tracing::trace!(
netrunner_logger::trace!(
counter = current_counter,
nonce = %hex::encode(nonce),
len = data_len,
@@ -111,7 +111,7 @@ impl AeadPacker for ChaChaCipher {
} else {
hex::encode(data.as_ref())
};
tracing::error!(
netrunner_logger::error!(
counter = current_counter,
nonce = %hex::encode(nonce),
len = data_len,
+6 -6
View File
@@ -74,7 +74,7 @@ impl SessionKeys {
) -> Result<([u8; 32], [u8; 12], [u8; 32], [u8; 12]), String> {
self.salt.set_remote_salt(salt);
tracing::debug!(
netrunner_logger::debug!(
remote_salt = %hex::encode(&salt[..8]),
local_salt = %hex::encode(&self.salt.get_local()[..8]),
total_salt = %hex::encode(&self.salt.get_total()[28..36]),
@@ -120,7 +120,7 @@ impl SessionKeys {
let public_key = PublicKey::from(key_bytes);
tracing::debug!(
netrunner_logger::debug!(
remote_pub = %hex::encode(&key_bytes[..4]),
role = if is_server { "Server" } else { "Client" },
"Key exchange successful, deriving material..."
@@ -141,7 +141,7 @@ impl SessionKeys {
.get_shared(public_key)
.ok_or_else(|| "No shared secret".to_string())?;
tracing::debug!(
netrunner_logger::debug!(
shared_prefix = %hex::encode(&shared_key[..8]),
"DH Shared secret derived"
);
@@ -155,7 +155,7 @@ impl SessionKeys {
let auth_secret = HKDF::expand_key::<32>(&hkdf, b"auth_key").map_err(|e| e.to_string())?;
self.auth_key = auth_secret;
tracing::info!(
netrunner_logger::info!(
client_key_short = %hex::encode(&c_key[..4]),
server_key_short = %hex::encode(&s_key[..4]),
"HKDF expansion complete"
@@ -197,13 +197,13 @@ impl SessionKeys {
for step in (current_step.saturating_sub(2))..=(current_step.saturating_add(2)) {
if &Self::compute_tag(&self.auth_key, step) == received_tag {
if step != current_step {
tracing::debug!(expected = %current_step, matched = %step, "Auth tag valid with time offset");
netrunner_logger::debug!(expected = %current_step, matched = %step, "Auth tag valid with time offset");
}
return true;
}
}
tracing::warn!(
netrunner_logger::warn!(
current_step = %current_step,
"AUTH MISMATCH: All tags rejected for current window"
);
-3
View File
@@ -1,8 +1,5 @@
mod crypto;
mod logger;
pub mod protocol;
pub mod proxy;
mod tlseng;
mod utils;
pub use logger::logger_init;
-36
View File
@@ -1,36 +0,0 @@
use tracing_log::LogTracer;
use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry};
pub fn logger_init() {
eprintln!("--- [DEBUG] logger_init start ---");
let _ = LogTracer::init();
let filter_layer =
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("trace"));
let registry = tracing_subscriber::registry().with(filter_layer);
#[cfg(target_os = "android")]
{
let android_layer =
tracing_android::layer("NETRUNNER_RUST").expect("Failed to create android layer");
let subscriber = registry.with(android_layer);
if let Err(e) = subscriber.try_init() {
eprintln!("Subscriber already set: {:?}", e);
}
}
#[cfg(not(target_os = "android"))]
{
let fmt_layer = fmt::layer()
.with_target(true)
.with_line_number(true)
.with_ansi(false);
let subscriber = registry.with(fmt_layer);
if let Err(e) = subscriber.try_init() {
eprintln!("Subscriber already set: {:?}", e);
}
}
}
+5 -5
View File
@@ -60,7 +60,7 @@ impl Codec {
.session_keys
.update_keys(client_msg.random(), client_msg.extensions(), true)
.map_err(|e| {
tracing::error!(error = %e, "Server failed to update keys from ClientHello");
netrunner_logger::error!(error = %e, "Server failed to update keys from ClientHello");
TlsError::new(
ErrorStage::Handshake("Key Err"),
ErrorAction::Drop,
@@ -87,7 +87,7 @@ impl Codec {
.session_keys
.update_keys(mes.random(), mes.extensions(), false)
.map_err(|e| {
tracing::error!(error = %e, "Client failed to update keys from ServerHello");
netrunner_logger::error!(error = %e, "Client failed to update keys from ServerHello");
TlsError::new(
ErrorStage::Handshake("Keys update error"),
ErrorAction::Drop,
@@ -116,7 +116,7 @@ impl Codec {
let padding = Padding::generate_padding();
let tag = self.session_keys.generate_auth_tag();
tracing::debug!(
netrunner_logger::debug!(
step = %(std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() / 60),
auth_key_hash = %hex::encode(&self.session_keys.auth_key[..4]),
generated_tag = %hex::encode(&tag[..4]),
@@ -139,7 +139,7 @@ impl Codec {
let mut frame_bytes = frame.into_bytes(&tag);
let encrypted_payload = self.crypto.encrypt(&mut frame_bytes).map_err(|e| {
tracing::error!("Encryption failed: {:?}", e);
netrunner_logger::error!("Encryption failed: {:?}", e);
TlsError::new(
ErrorStage::Tls("Encryption failed"),
ErrorAction::Drop,
@@ -189,7 +189,7 @@ impl Codec {
received_tag.copy_from_slice(&decrypted[..16]);
if !self.session_keys.verify_auth_tag(&received_tag) {
tracing::error!(
netrunner_logger::error!(
expected_hash = %hex::encode(&self.session_keys.auth_key[..4]),
received = %hex::encode(&received_tag[..4]),
"AUTH MISMATCH: Potential replay or MITM attack. Dropping connection."
+1 -1
View File
@@ -1,5 +1,5 @@
use bytes::Bytes;
use tracing::{error, trace, warn};
use netrunner_logger::{error, trace, warn};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorAction {
+1 -1
View File
@@ -57,7 +57,7 @@ impl Parser for Frame {
let p_len = u16::from_be_bytes([bytes[21], bytes[22]]) as usize;
let pad_len = u16::from_be_bytes([bytes[23], bytes[24]]) as usize;
tracing::debug!(
netrunner_logger::debug!(
"CAN_PARSE: p_len={}, pad_len={}, total_needed={}, have={}",
p_len,
pad_len,
+1 -1
View File
@@ -2,8 +2,8 @@ use crate::protocol::codec::frame::FrameType;
use crate::proxy::connection::connection::BUF_SIZE;
use crate::proxy::connection::muxer::{MuxMessage, Muxer};
use bytes::{Bytes, BytesMut};
use netrunner_logger::{debug, error};
use tokio::sync::mpsc;
use tracing::{debug, error};
pub async fn run_proxy_bridge<R, W>(
stream_id: u32,
mut reader: R,
+80 -83
View File
@@ -1,6 +1,6 @@
use bytes::{Bytes, BytesMut};
use tracing::{instrument, info, debug, error, trace, warn};
use std::{net::SocketAddr};
use netrunner_logger::{debug, error, info, instrument, trace, warn};
use std::net::SocketAddr;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{
@@ -9,6 +9,7 @@ use tokio::{
},
sync::mpsc::{self},
};
use tokio_util::sync::CancellationToken;
use crate::{
protocol::{
@@ -21,13 +22,15 @@ use crate::{
parser::parser::Parser,
},
proxy::connection::{
bridge::run_proxy_bridge, engine::TunnelEngine, handler::StreamHandler, muxer::{MuxMessage, Muxer}
bridge::run_proxy_bridge,
engine::TunnelEngine,
handler::StreamHandler,
muxer::{MuxMessage, Muxer},
},
};
pub const BUF_SIZE: usize = 16384;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ConnectionRole {
Client,
@@ -43,11 +46,7 @@ pub struct Connection {
}
impl Connection {
pub fn new(
stream: TcpStream,
addr: SocketAddr,
init: bool,
) -> Self {
pub fn new(stream: TcpStream, addr: SocketAddr, init: bool) -> Self {
let (inbound, outbound) = stream.into_split();
Self {
addr,
@@ -58,27 +57,22 @@ impl Connection {
}
}
async fn read_socks_request(&mut self) -> Result<SocksRequest, String> {
loop {
match SocksRequest::parse(&mut self.read_buf) {
Ok(Some(req)) => {
info!(client = %self.addr, request = ?req, "SOCKS request successfully parsed");
return Ok(req);
}
Ok(None) => {
trace!(client = %self.addr, buffer_len = self.read_buf.len(), "SOCKS parse: need more data");
}
}
Err(e) => {
error!(client = %self.addr, error = %e, "SOCKS protocol violation");
return Err(format!("Socks parse error: {}", e));
}
}
let n = self
.inbound
.read_buf(&mut self.read_buf)
@@ -92,49 +86,36 @@ impl Connection {
warn!(client = %self.addr, "Client closed connection prematurely during SOCKS handshake");
return Err("Client closed connection during SOCKS handshake".into());
}
trace!(client = %self.addr, read_bytes = n, "Read data from client for SOCKS handshake");
}
}
async fn send_socks_reply(&mut self, reply: SocksReply) -> Result<(), String> {
let mut buf = BytesMut::with_capacity(24);
debug!(client = %self.addr, reply = ?reply, "Sending SOCKS reply to client");
reply.write_to(&mut buf);
self.outbound
.write_all(&buf)
.await
.map_err(|e| {
error!(client = %self.addr, error = %e, "Failed to send SOCKS reply");
e.to_string()
})?;
self.outbound.write_all(&buf).await.map_err(|e| {
error!(client = %self.addr, error = %e, "Failed to send SOCKS reply");
e.to_string()
})?;
Ok(())
}
#[instrument(
name = "socks_handler",
skip(self, muxer),
fields(addr = %self.addr)
)]
pub async fn handle_socks_client(mut self, muxer: Muxer) -> Result<(), String> {
info!("Starting SOCKS multiplexed handling");
debug!("Reading SOCKS handshake request");
let _ = self.read_socks_request().await.map_err(|e| {
error!("SOCKS handshake failed: {}", e);
e
})?;
self.send_socks_reply(SocksReply::HandshakeSelect { method: 0x00 }).await?;
self.send_socks_reply(SocksReply::HandshakeSelect { method: 0x00 })
.await?;
let req = self.read_socks_request().await?;
let target = if let SocksRequest::Connect { target, .. } = req {
target
@@ -145,46 +126,61 @@ impl Connection {
let stream_id = muxer.next_id();
let target_str = target.to_string();
let (v_tx, mut v_rx) = mpsc::channel::<Bytes>(1024);
muxer.register_stream(stream_id, v_tx).await;
muxer.to_network.send(MuxMessage {
stream_id,
frame_type: FrameType::Connect,
data: Bytes::from(target_str),
}).await.map_err(|e| e.to_string())?;
muxer
.to_network
.send(MuxMessage {
stream_id,
frame_type: FrameType::Connect,
data: Bytes::from(target_str),
})
.await
.map_err(|e| e.to_string())?;
let first_payload = match tokio::time::timeout(std::time::Duration::from_secs(10), v_rx.recv()).await {
Ok(Some(data)) => data,
_ => {
error!(stream_id, "Server timeout or failed to send Connect confirmation");
self.send_socks_reply(SocksReply::ConnectResult {
reply_code: 0x01, atyp: 0x01, addr: [0, 0, 0, 0], port: 0,
}).await.ok();
return Err("Target connection failed".into());
}
};
let first_payload =
match tokio::time::timeout(std::time::Duration::from_secs(10), v_rx.recv()).await {
Ok(Some(data)) => data,
_ => {
error!(
stream_id,
"Server timeout or failed to send Connect confirmation"
);
self.send_socks_reply(SocksReply::ConnectResult {
reply_code: 0x01,
atyp: 0x01,
addr: [0, 0, 0, 0],
port: 0,
})
.await
.ok();
return Err("Target connection failed".into());
}
};
if first_payload.len() >= 2 && first_payload[1] == 0x00 {
debug!(stream_id, "Server confirmed connection, forwarding SOCKS reply to browser");
self.outbound.write_all(&first_payload).await.map_err(|e| e.to_string())?;
debug!(
stream_id,
"Server confirmed connection, forwarding SOCKS reply to browser"
);
self.outbound
.write_all(&first_payload)
.await
.map_err(|e| e.to_string())?;
} else {
self.outbound.write_all(&first_payload).await.ok();
return Err("Server rejected connection".into());
}
let Self { inbound: browser_in, outbound: browser_out, .. } = self;
let Self {
inbound: browser_in,
outbound: browser_out,
..
} = self;
let muxer_clone = muxer.clone();
tokio::spawn(async move {
run_proxy_bridge(stream_id, browser_in, browser_out, muxer_clone, v_rx).await;
@@ -193,49 +189,51 @@ impl Connection {
Ok(())
}
#[instrument(
name = "server_tunnel",
skip(self),
fields(addr = %self.addr)
)]
pub async fn handle_server_tunnel(mut self) -> Result<(), String> {
pub async fn handle_server_tunnel(mut self, token: CancellationToken) -> Result<(), String> {
info!("Acting as TLS Server, waiting for ClientHello");
let (mux_tx, mux_rx) = mpsc::channel(BUF_SIZE);
let muxer = Muxer::new(mux_tx.clone(), false);
let muxer = Muxer::new(mux_tx.clone(), false);
let server_hello_bytes = loop {
match self.codec.make_server_handshake(&mut self.read_buf) {
Ok(bytes) => {
info!("ClientHello received, sending ServerHello");
break bytes;
},
}
Err(e) if e.action == ErrorAction::Wait => {
let n = self.inbound.read_buf(&mut self.read_buf).await
let n = self
.inbound
.read_buf(&mut self.read_buf)
.await
.map_err(|err| format!("Read error: {}", err))?;
if n == 0 { return Err("Client closed connection".into()); }
if n == 0 {
return Err("Client closed connection".into());
}
}
Err(e) => return Err(format!("TLS error: {:?}", e)),
}
};
self.outbound.write_all(&server_hello_bytes).await.map_err(|e| e.to_string())?;
self.outbound
.write_all(&server_hello_bytes)
.await
.map_err(|e| e.to_string())?;
info!("TLS Tunnel established as server");
let handler = std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Server));
let handler =
std::sync::Arc::new(StreamHandler::new(muxer.clone(), ConnectionRole::Server));
debug!("Handover to TunnelEngine");
let engine = TunnelEngine {
inbound: self.inbound,
outbound: self.outbound,
outbound: self.outbound,
codec: self.codec,
read_buf: self.read_buf,
mux_rx,
handler
handler,
token,
};
engine.run().await.map_err(|e| {
@@ -243,5 +241,4 @@ impl Connection {
e
})
}
}
+28 -5
View File
@@ -1,15 +1,19 @@
use std::sync::Arc;
use bytes::BytesMut;
use bytes::{Bytes, BytesMut};
use netrunner_logger::{debug, error, info};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::tcp::{OwnedReadHalf, OwnedWriteHalf},
sync::mpsc::Receiver,
};
use tracing::{debug, error};
use tokio_util::sync::CancellationToken;
use crate::{
protocol::{codec::codec::Codec, errors::ErrorAction},
protocol::{
codec::{codec::Codec, frame::FrameType},
errors::ErrorAction,
},
proxy::connection::{handler::StreamHandler, muxer::MuxMessage},
};
@@ -20,6 +24,7 @@ pub struct TunnelEngine {
pub read_buf: BytesMut,
pub mux_rx: Receiver<MuxMessage>,
pub handler: Arc<StreamHandler>,
pub token: CancellationToken,
}
impl TunnelEngine {
@@ -30,18 +35,35 @@ impl TunnelEngine {
let mut read_buf = self.read_buf;
let mut mux_rx = self.mux_rx;
let handler = self.handler;
let token = self.token;
let mut heartbeat = tokio::time::interval(std::time::Duration::from_secs(15));
loop {
tokio::select! {
_ = token.cancelled() => {
info!("TunnelEngine: Shutdown signal received. Closing...");
return Ok(());
}
res = Self::process_inbound(&mut inbound, &mut codec, &mut read_buf, &handler) => {
res?
}
_ = heartbeat.tick() => {
let msg = MuxMessage { stream_id: 0, frame_type: FrameType::Heartbeat, data: Bytes::new() };
Self::handle_outbound(&mut outbound, &mut codec, msg).await?;
}
Some(msg) = mux_rx.recv() => {
Self::handle_outbound( &mut outbound, &mut codec, msg).await?;
msg_opt = mux_rx.recv() => {
if let Some(msg) = msg_opt {
Self::handle_outbound(&mut outbound, &mut codec, msg).await?;
} else {
break;
}
}
}
}
Ok(())
}
async fn process_inbound(
@@ -56,6 +78,7 @@ impl TunnelEngine {
.map_err(|e| e.to_string())?;
if n == 0 && read_buf.is_empty() {
netrunner_logger::error!("Connection closed by peer (EOF detected)");
return Err("EOF".into());
}
+1 -1
View File
@@ -1,5 +1,5 @@
use bytes::{Bytes, BytesMut};
use tracing::{debug, error, info};
use netrunner_logger::{debug, error, info};
use crate::{
protocol::codec::{
+6 -6
View File
@@ -1,11 +1,11 @@
use crate::protocol::codec::frame::FrameType;
use bytes::Bytes;
use netrunner_logger::{debug, error};
use std::collections::HashMap;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use tokio::sync::mpsc::Sender;
use tokio::sync::RwLock;
use tracing::{debug, error};
pub struct IdGenerator {
counter: AtomicU32,
@@ -53,7 +53,7 @@ impl Muxer {
pub async fn register_stream(&self, stream_id: u32, tx: Sender<Bytes>) {
let mut lock = self.streams.write().await;
lock.insert(stream_id, tx);
tracing::debug!(
netrunner_logger::debug!(
stream_id,
total_active = lock.len(),
"MUXER: [REGISTER] Stream added"
@@ -89,9 +89,9 @@ impl Muxer {
if let Some(tx) = tx {
if data.is_empty() {
tracing::debug!(stream_id, "MUXER: [EOF] Forwarding EOF to local handler");
netrunner_logger::debug!(stream_id, "MUXER: [EOF] Forwarding EOF to local handler");
} else {
tracing::trace!(
netrunner_logger::trace!(
stream_id,
len = data.len(),
"MUXER: [DISPATCH] Sending data"
@@ -99,14 +99,14 @@ impl Muxer {
}
if let Err(_e) = tx.send(data).await {
tracing::debug!(
netrunner_logger::debug!(
stream_id,
"MUXER: [WARN] Local channel closed, dropping packet"
);
self.remove_stream(stream_id).await;
}
} else {
tracing::trace!(
netrunner_logger::trace!(
stream_id,
"MUXER: [IGNORE] Packet for already closed stream"
);
+43 -17
View File
@@ -8,11 +8,12 @@ use crate::{
tlseng::profile::BrowserProfile,
};
use bytes::BytesMut;
use netrunner_logger::{error, info, instrument};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{TcpListener, TcpStream},
};
use tracing::{error, info, instrument};
use tokio_util::sync::CancellationToken;
pub struct Network {
host: String,
@@ -36,15 +37,14 @@ impl Network {
}
}
#[instrument(skip(self), fields(role = ?self.role, port = self.port))]
pub async fn run(&self) {
pub async fn run(&self, token: CancellationToken) {
let addr = format!("{}:{}", self.host, self.port);
match self.role {
ConnectionRole::Client => {
info!("Starting Client mode: Initializing persistent tunnel to proxy...");
let muxer = match self.initialize_client_tunnel().await {
let muxer = match self.initialize_client_tunnel(token.clone()).await {
Ok(m) => m,
Err(e) => {
error!(error = %e, "Global tunnel failed. Exit.");
@@ -56,12 +56,20 @@ impl Network {
info!(socks_addr = %addr, "SOCKS5 ready");
loop {
if let Ok((stream, client_addr)) = listener.accept().await {
let current_muxer = muxer.clone();
tokio::spawn(async move {
let connection = Connection::new(stream, client_addr, false);
let _ = connection.handle_socks_client(current_muxer).await;
});
tokio::select! {
_ = token.cancelled() => {
info!("SOCKS listener: Shutting down...");
break;
}
res = listener.accept() => {
if let Ok((stream, client_addr)) = listener.accept().await {
let current_muxer = muxer.clone();
tokio::spawn(async move {
let connection = Connection::new(stream, client_addr, false);
let _ = connection.handle_socks_client(current_muxer).await;
});
}
}
}
}
}
@@ -73,20 +81,37 @@ impl Network {
info!(listen_addr = %addr, "Proxy Server listening for incoming tunnels");
loop {
if let Ok((stream, client_addr)) = listener.accept().await {
tokio::spawn(async move {
let connection = Connection::new(stream, client_addr, true);
if let Err(e) = connection.handle_server_tunnel().await {
error!(client = %client_addr, error = %e, "Tunnel error");
tokio::select! {
_ = token.cancelled() => {
info!("Server listener: Shutting down...");
break;
}
res = listener.accept() => {
match res {
Ok((stream, client_addr)) => {
let connection = Connection::new(stream, client_addr, true);
let connection_token = token.clone();
tokio::spawn(async move {
if let Err(e) = connection.handle_server_tunnel(connection_token).await {
error!(client = %client_addr, error = %e, "Tunnel error");
}
});
}
Err(e) => {
error!(error = %e, "Failed to accept connection");
}
}
});
}
}
}
}
}
}
pub async fn initialize_client_tunnel(&self) -> Result<Muxer, String> {
pub async fn initialize_client_tunnel(
&self,
token: CancellationToken,
) -> Result<Muxer, String> {
let server_addr = self.remote_proxy_addr.as_ref().ok_or("No proxy addr")?;
let stream = TcpStream::connect(server_addr)
@@ -133,6 +158,7 @@ impl Network {
read_buf: sh_buf,
mux_rx,
handler,
token,
};
tokio::spawn(async move { engine.run().await });
+2 -2
View File
@@ -62,7 +62,7 @@ impl ClientHello {
let ext_len = self.extensions.len();
let total_handshake_len = (buf.len() - length_pos - 3) as u32;
tracing::debug!(
netrunner_logger::debug!(
handshake_type = "ClientHello",
body_len = total_handshake_len,
extensions_len = ext_len,
@@ -193,7 +193,7 @@ impl ServerHello {
let total_handshake_len = (buf.len() - length_pos - 3) as u32;
let ext_len = self.extensions.len();
tracing::debug!(
netrunner_logger::debug!(
handshake_type = "ServerHello",
body_len = total_handshake_len,
extensions_len = ext_len,
+1 -1
View File
@@ -35,7 +35,7 @@ impl TlsRecord {
}
pub fn build_application_data(payload: Bytes) -> Bytes {
tracing::trace!(payload_len = payload.len(), "Building TlsRecord from Bytes");
netrunner_logger::trace!(payload_len = payload.len(), "Building TlsRecord from Bytes");
let record = Self::new(
ContentType::ApplicationData,