mirror of
https://github.com/TrustTunnel/TrustTunnel.git
synced 2026-04-21 02:11:45 +00:00
Squashed commit of the following:
commit 6eae1e962a27b2c3bcb6362f53bb1d7d92a66983
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Fri Dec 26 11:46:12 2025 +0400
Run lint on both macos and linux
commit 94254caec3ea166db80c6b3f4004b4126605a1b7
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:44:10 2025 +0400
Fix note again by adding lint hint
commit 5a67ae358a5676a22e85798683674607d2788a51
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:42:02 2025 +0400
Fix note
commit 937b178302244fe237d06b6f38ba0f29db6e0d7e
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:39:45 2025 +0400
Fix README
commit 769c5d9ebdc03e8500f9fc00d7f2b6f316924557
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:39:15 2025 +0400
Cargo update
commit 1e932e4037c2b9ffc4b12f398f1ef14c32b5481e
Merge: dcf6a53 2041edc
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:37:24 2025 +0400
Merge remote-tracking branch 'origin/master' into feature/TRUST-235
commit dcf6a53410e59411a3e05f798ed4be7f7c9994ce
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:24:56 2025 +0400
Get rid of rustls-pemfile and update sentry
commit cb2e26e47d4612d65ae990ec887875bb1ac94456
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 19:08:23 2025 +0400
Fix tests
commit a3cde3fdf16edfe2e2a574b8d729c2b9d59daf84
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 18:33:46 2025 +0400
Fix vulnerabilities
commit 35cb9c699a0ddf2eb344c7c475be3c36a26dbf83
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 17:13:18 2025 +0400
Don't install cargo-audit manually
commit 71a5411ac4fe31fc08c3bacb83d327bf6b7ab8c3
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 17:07:30 2025 +0400
Install stable rust for cargo-audit
commit b7f38a90054cda39d72760b0ebc3ce295fba95d2
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:54:13 2025 +0400
Fix yaml
commit fbbe78f68b2987280874f23d4ed05ef75ed42f46
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:53:15 2025 +0400
Try to lock cargo-audit version
commit 08f31734b49c70d9dc03c7977ac6182198d1cbde
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:46:06 2025 +0400
Update audit workflow
commit c202f186cd1610439a13928fc1fabac88e83097b
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:30:55 2025 +0400
Install rust tools and better rust cache
commit eccf2fa91efcc4c6e5684960e368892bc68e67cd
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:30:25 2025 +0400
Name for job
commit dccc19f13180e767b8390c8ea32fde4285c0cab8
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:30:17 2025 +0400
Update checkout step version
commit edbb4404bf6fc1927f0184433df9982767a9c762
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:30:02 2025 +0400
Run lint only on linux
commit b59ed893fa55edf030f9ffee2e442c8b947fa28f
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:09:43 2025 +0400
Lint in the same workflow as testing to avoid rebuilds
commit 8d8ecd51859c825d0437361f8c51bde6b46994bc
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 15:27:23 2025 +0400
More clippy fixes
... and 6 more commits
550 lines
18 KiB
Rust
550 lines
18 KiB
Rust
use bytes::BufMut;
|
|
use futures::{future, FutureExt, StreamExt};
|
|
use http::Request;
|
|
use log::info;
|
|
use std::future::Future;
|
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
|
use std::pin::Pin;
|
|
use std::thread;
|
|
use std::time::Duration;
|
|
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
|
use tokio::net::{TcpListener, UdpSocket};
|
|
use trusttunnel::net_utils;
|
|
|
|
#[allow(dead_code)]
|
|
mod common;
|
|
|
|
const TCP_CONTENT_SIZE: usize = 2 * 1024 * 1024;
|
|
const UDP_CHUNK_SIZE: usize = 1024;
|
|
const UDP_CONTENT_SIZE: usize = 8 * UDP_CHUNK_SIZE;
|
|
const MANGLED_UDP_HEADER_LENGTH: usize = 4 + 2 * (16 + 2);
|
|
const EXPECTED_MANGLED_UDP_LENGTH: usize =
|
|
UDP_CONTENT_SIZE + (UDP_CONTENT_SIZE / UDP_CHUNK_SIZE) * MANGLED_UDP_HEADER_LENGTH;
|
|
|
|
macro_rules! tcp_download_tests {
|
|
($($name:ident: $make_tunnel_fn:expr,)*) => {
|
|
$(
|
|
#[tokio::test]
|
|
async fn $name() {
|
|
common::set_up_logger();
|
|
let endpoint_address = common::make_endpoint_address();
|
|
|
|
let client_task = async {
|
|
let server_address = run_tcp_server(true);
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
let (conn_driver, io) = $make_tunnel_fn(endpoint_address, server_address.to_string()).await;
|
|
|
|
let exchange = async {
|
|
let mut io = io.await;
|
|
let mut total = 0;
|
|
let mut buf = [0; 64 * 1024];
|
|
while total < TCP_CONTENT_SIZE {
|
|
match io.read(&mut buf).await.unwrap() {
|
|
0 => break,
|
|
n => total += n,
|
|
}
|
|
}
|
|
assert_eq!(total, TCP_CONTENT_SIZE);
|
|
};
|
|
|
|
futures::pin_mut!(exchange);
|
|
match future::select(conn_driver, exchange).await {
|
|
future::Either::Left((r, exchange)) => {
|
|
info!("HTTP connection closed with result: {:?}", r);
|
|
exchange.await
|
|
}
|
|
future::Either::Right(_) => (),
|
|
}
|
|
};
|
|
|
|
tokio::select! {
|
|
_ = common::run_endpoint(&endpoint_address) => unreachable!(),
|
|
_ = client_task => (),
|
|
_ = tokio::time::sleep(Duration::from_secs(10)) => panic!("Timed out"),
|
|
}
|
|
}
|
|
)*
|
|
}
|
|
}
|
|
|
|
macro_rules! tcp_upload_tests {
|
|
($($name:ident: $make_tunnel_fn:expr,)*) => {
|
|
$(
|
|
#[tokio::test]
|
|
async fn $name() {
|
|
common::set_up_logger();
|
|
let endpoint_address = common::make_endpoint_address();
|
|
|
|
let client_task = async {
|
|
let server_address = run_tcp_server(false);
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
let (conn_driver, io) = $make_tunnel_fn(endpoint_address, server_address.to_string()).await;
|
|
|
|
let exchange = async {
|
|
let mut io = io.await;
|
|
let mut content = common::make_stream_of_chunks(TCP_CONTENT_SIZE, None);
|
|
while let Some(chunk) = content.next().await {
|
|
io.write_all(chunk).await.unwrap();
|
|
}
|
|
io.flush().await.unwrap();
|
|
|
|
let mut ack = [0; 1];
|
|
assert_eq!(io.read(&mut ack).await.unwrap(), 1);
|
|
};
|
|
|
|
futures::pin_mut!(exchange);
|
|
match future::select(conn_driver, exchange).await {
|
|
future::Either::Left((r, exchange)) => {
|
|
info!("HTTP connection closed with result: {:?}", r);
|
|
exchange.await
|
|
}
|
|
future::Either::Right(_) => (),
|
|
}
|
|
};
|
|
|
|
tokio::select! {
|
|
_ = common::run_endpoint(&endpoint_address) => unreachable!(),
|
|
_ = client_task => (),
|
|
_ = tokio::time::sleep(Duration::from_secs(10)) => panic!("Timed out"),
|
|
}
|
|
}
|
|
)*
|
|
}
|
|
}
|
|
|
|
tcp_download_tests! {
|
|
h1_tcp_download: make_h1_tunnel,
|
|
h2_tcp_download: make_h2_tunnel,
|
|
}
|
|
|
|
tcp_upload_tests! {
|
|
h1_tcp_upload: make_h1_tunnel,
|
|
h2_tcp_upload: make_h2_tunnel,
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn h2_udp_download() {
|
|
common::set_up_logger();
|
|
let endpoint_address = common::make_endpoint_address();
|
|
|
|
let client_task = async {
|
|
let server_address = run_udp_server(true);
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
let (conn_driver, io) = make_h2_tunnel(endpoint_address, "_udp2".to_string()).await;
|
|
|
|
let exchange = async {
|
|
let mut io = io.await;
|
|
let hole_puncher = encode_udp_chunk(&server_address, &[1]);
|
|
io.write_all(&hole_puncher).await.unwrap();
|
|
|
|
let mut total = 0;
|
|
let mut buf = [0; 64 * 1024];
|
|
while total < EXPECTED_MANGLED_UDP_LENGTH {
|
|
match io.read(&mut buf).await.unwrap() {
|
|
0 => break,
|
|
n => total += n,
|
|
}
|
|
}
|
|
assert_eq!(total, EXPECTED_MANGLED_UDP_LENGTH);
|
|
};
|
|
|
|
futures::pin_mut!(exchange);
|
|
match future::select(conn_driver, exchange).await {
|
|
future::Either::Left((r, exchange)) => {
|
|
info!("HTTP connection closed with result: {:?}", r);
|
|
exchange.await
|
|
}
|
|
future::Either::Right(_) => (),
|
|
}
|
|
};
|
|
|
|
tokio::select! {
|
|
_ = common::run_endpoint(&endpoint_address) => unreachable!(),
|
|
_ = client_task => (),
|
|
_ = tokio::time::sleep(Duration::from_secs(10)) => panic!("Timed out"),
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn h2_udp_upload() {
|
|
common::set_up_logger();
|
|
let endpoint_address = common::make_endpoint_address();
|
|
|
|
let client_task = async {
|
|
let server_address = run_udp_server(false);
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
let (conn_driver, io) = make_h2_tunnel(endpoint_address, "_udp2".to_string()).await;
|
|
|
|
let exchange = async {
|
|
let mut io = io.await;
|
|
|
|
let mut content = common::make_stream_of_chunks(UDP_CONTENT_SIZE, Some(UDP_CHUNK_SIZE))
|
|
.map(|x| encode_udp_chunk(&server_address, x));
|
|
while let Some(chunk) = content.next().await {
|
|
io.write_all(&chunk).await.unwrap();
|
|
}
|
|
|
|
let mut ack = [0; UDP_CHUNK_SIZE];
|
|
assert_eq!(
|
|
io.read(&mut ack).await.unwrap(),
|
|
MANGLED_UDP_HEADER_LENGTH + 1
|
|
);
|
|
};
|
|
|
|
futures::pin_mut!(exchange);
|
|
match future::select(conn_driver, exchange).await {
|
|
future::Either::Left((r, exchange)) => {
|
|
info!("HTTP connection closed with result: {:?}", r);
|
|
exchange.await
|
|
}
|
|
future::Either::Right(_) => (),
|
|
}
|
|
};
|
|
|
|
tokio::select! {
|
|
_ = common::run_endpoint(&endpoint_address) => unreachable!(),
|
|
_ = client_task => (),
|
|
_ = tokio::time::sleep(Duration::from_secs(10)) => panic!("Timed out"),
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn h3_tcp_download() {
|
|
common::set_up_logger();
|
|
let endpoint_address = common::make_endpoint_address();
|
|
|
|
let client_task = async {
|
|
let server_address = run_tcp_server(true);
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
let mut conn =
|
|
common::Http3Session::connect(&endpoint_address, common::MAIN_DOMAIN_NAME, None).await;
|
|
|
|
let (response, _) = conn
|
|
.exchange(
|
|
Request::connect(server_address.to_string())
|
|
.body(hyper::Body::empty())
|
|
.unwrap(),
|
|
)
|
|
.await;
|
|
assert_eq!(response.status, http::StatusCode::OK);
|
|
|
|
let mut total = 0;
|
|
let mut buf = [0; 64 * 1024];
|
|
while total < TCP_CONTENT_SIZE {
|
|
match conn.recv(&mut buf).await {
|
|
0 => break,
|
|
n => total += n,
|
|
}
|
|
}
|
|
assert_eq!(total, TCP_CONTENT_SIZE);
|
|
};
|
|
|
|
tokio::select! {
|
|
_ = common::run_endpoint(&endpoint_address) => unreachable!(),
|
|
_ = client_task => (),
|
|
_ = tokio::time::sleep(Duration::from_secs(10)) => panic!("Timed out"),
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn h3_tcp_upload() {
|
|
common::set_up_logger();
|
|
let endpoint_address = common::make_endpoint_address();
|
|
|
|
let client_task = async {
|
|
let server_address = run_tcp_server(false);
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
let mut conn =
|
|
common::Http3Session::connect(&endpoint_address, common::MAIN_DOMAIN_NAME, None).await;
|
|
|
|
let (response, _) = conn
|
|
.exchange(
|
|
Request::connect(server_address.to_string())
|
|
.body(hyper::Body::empty())
|
|
.unwrap(),
|
|
)
|
|
.await;
|
|
assert_eq!(response.status, http::StatusCode::OK);
|
|
|
|
conn.send(common::make_stream_of_chunks(TCP_CONTENT_SIZE, None))
|
|
.await;
|
|
let mut ack = [0; 1];
|
|
assert_eq!(conn.recv(&mut ack).await, 1);
|
|
};
|
|
|
|
tokio::select! {
|
|
_ = common::run_endpoint(&endpoint_address) => unreachable!(),
|
|
_ = client_task => (),
|
|
_ = tokio::time::sleep(Duration::from_secs(10)) => panic!("Timed out"),
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn h3_udp_download() {
|
|
common::set_up_logger();
|
|
let endpoint_address = common::make_endpoint_address();
|
|
|
|
let client_task = async {
|
|
let server_address = run_udp_server(true);
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
let mut conn =
|
|
common::Http3Session::connect(&endpoint_address, common::MAIN_DOMAIN_NAME, None).await;
|
|
|
|
let (response, _) = conn
|
|
.exchange(
|
|
Request::connect("_udp2")
|
|
.body(hyper::Body::empty())
|
|
.unwrap(),
|
|
)
|
|
.await;
|
|
assert_eq!(response.status, http::StatusCode::OK);
|
|
|
|
let hole_puncher = encode_udp_chunk(&server_address, &[1]);
|
|
conn.send(futures::stream::iter(std::iter::once(hole_puncher)))
|
|
.await;
|
|
|
|
let mut total = 0;
|
|
let mut buf = [0; 64 * 1024];
|
|
while total < EXPECTED_MANGLED_UDP_LENGTH {
|
|
match conn.recv(&mut buf).await {
|
|
0 => break,
|
|
n => total += n,
|
|
}
|
|
}
|
|
assert_eq!(total, EXPECTED_MANGLED_UDP_LENGTH);
|
|
};
|
|
|
|
tokio::select! {
|
|
_ = common::run_endpoint(&endpoint_address) => unreachable!(),
|
|
_ = client_task => (),
|
|
_ = tokio::time::sleep(Duration::from_secs(10)) => panic!("Timed out"),
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn h3_udp_upload() {
|
|
common::set_up_logger();
|
|
let endpoint_address = common::make_endpoint_address();
|
|
|
|
let client_task = async {
|
|
let server_address = run_udp_server(false);
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
let mut conn =
|
|
common::Http3Session::connect(&endpoint_address, common::MAIN_DOMAIN_NAME, None).await;
|
|
|
|
let (response, _) = conn
|
|
.exchange(
|
|
Request::connect("_udp2")
|
|
.body(hyper::Body::empty())
|
|
.unwrap(),
|
|
)
|
|
.await;
|
|
assert_eq!(response.status, http::StatusCode::OK);
|
|
|
|
conn.send(
|
|
common::make_stream_of_chunks(UDP_CONTENT_SIZE, Some(UDP_CHUNK_SIZE))
|
|
.map(|x| encode_udp_chunk(&server_address, x)),
|
|
)
|
|
.await;
|
|
|
|
let mut ack = [0; UDP_CHUNK_SIZE];
|
|
assert_eq!(conn.recv(&mut ack).await, MANGLED_UDP_HEADER_LENGTH + 1);
|
|
};
|
|
|
|
tokio::select! {
|
|
_ = common::run_endpoint(&endpoint_address) => unreachable!(),
|
|
_ = client_task => (),
|
|
_ = tokio::time::sleep(Duration::from_secs(10)) => panic!("Timed out"),
|
|
}
|
|
}
|
|
|
|
async fn make_h1_tunnel(
|
|
endpoint_address: SocketAddr,
|
|
server_address: String,
|
|
) -> (
|
|
Pin<Box<dyn Future<Output = ()>>>,
|
|
Pin<Box<dyn Future<Output = impl AsyncRead + AsyncWrite + Unpin + Send>>>,
|
|
) {
|
|
let stream =
|
|
common::establish_tls_connection(common::MAIN_DOMAIN_NAME, &endpoint_address, None).await;
|
|
|
|
let (mut request, conn) = hyper::client::conn::Builder::new()
|
|
.handshake(stream)
|
|
.await
|
|
.unwrap();
|
|
|
|
let conn_driver = async move { conn.await.unwrap() }.boxed();
|
|
|
|
let exchange = async move {
|
|
let rr = Request::builder()
|
|
.version(http::Version::HTTP_11)
|
|
.method(http::Method::CONNECT)
|
|
.uri(server_address)
|
|
.body(hyper::Body::empty())
|
|
.unwrap();
|
|
let response = request.send_request(rr).await.unwrap();
|
|
info!("CONNECT response: {:?}", response);
|
|
assert_eq!(response.status(), http::StatusCode::OK);
|
|
|
|
hyper::upgrade::on(response).await.unwrap()
|
|
}
|
|
.boxed();
|
|
|
|
(conn_driver, exchange)
|
|
}
|
|
|
|
async fn make_h2_tunnel(
|
|
endpoint_address: SocketAddr,
|
|
server_address: String,
|
|
) -> (
|
|
Pin<Box<dyn Future<Output = ()>>>,
|
|
Pin<Box<dyn Future<Output = impl AsyncRead + AsyncWrite + Unpin + Send>>>,
|
|
) {
|
|
let stream = common::establish_tls_connection(
|
|
common::MAIN_DOMAIN_NAME,
|
|
&endpoint_address,
|
|
Some(net_utils::HTTP2_ALPN.as_bytes()),
|
|
)
|
|
.await;
|
|
|
|
let (mut request, conn) = hyper::client::conn::Builder::new()
|
|
.http2_only(true)
|
|
.handshake(stream)
|
|
.await
|
|
.unwrap();
|
|
|
|
let conn_driver = async move { conn.await.unwrap() }.boxed();
|
|
|
|
let exchange = async move {
|
|
let rr = Request::builder()
|
|
.version(http::Version::HTTP_2)
|
|
.method(http::Method::CONNECT)
|
|
.uri(server_address)
|
|
.body(hyper::Body::empty())
|
|
.unwrap();
|
|
let response = request.send_request(rr).await.unwrap();
|
|
info!("CONNECT response: {:?}", response);
|
|
assert_eq!(response.status(), http::StatusCode::OK);
|
|
|
|
hyper::upgrade::on(response).await.unwrap()
|
|
}
|
|
.boxed();
|
|
|
|
(conn_driver, exchange)
|
|
}
|
|
|
|
fn run_tcp_server(is_download: bool) -> SocketAddr {
|
|
let server = std::net::TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
|
|
let _ = server.set_nonblocking(true);
|
|
let server_addr = server.local_addr().unwrap();
|
|
|
|
thread::spawn(move || {
|
|
let rt = tokio::runtime::Builder::new_current_thread()
|
|
.enable_all()
|
|
.build()
|
|
.unwrap();
|
|
rt.block_on(async move {
|
|
let server = TcpListener::from_std(server).unwrap();
|
|
let (mut socket, peer) = server.accept().await.unwrap();
|
|
info!("New connection from {}", peer);
|
|
|
|
if is_download {
|
|
let mut content = common::make_stream_of_chunks(TCP_CONTENT_SIZE, None);
|
|
while let Some(chunk) = content.next().await {
|
|
socket.write_all(chunk).await.unwrap();
|
|
}
|
|
} else {
|
|
let mut total = 0;
|
|
let mut buf = [0; 64 * 1024];
|
|
while total < TCP_CONTENT_SIZE {
|
|
match socket.read(&mut buf).await.unwrap() {
|
|
0 => break,
|
|
n => total += n,
|
|
}
|
|
}
|
|
|
|
assert_eq!(total, TCP_CONTENT_SIZE);
|
|
let ack = 1_u8;
|
|
socket.write_all(&[ack]).await.unwrap();
|
|
}
|
|
|
|
socket.flush().await.unwrap();
|
|
});
|
|
});
|
|
|
|
server_addr
|
|
}
|
|
|
|
fn run_udp_server(is_download: bool) -> SocketAddr {
|
|
let server = std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
|
|
let _ = server.set_nonblocking(true);
|
|
let server_addr = server.local_addr().unwrap();
|
|
|
|
thread::spawn(move || {
|
|
let rt = tokio::runtime::Builder::new_current_thread()
|
|
.enable_all()
|
|
.build()
|
|
.unwrap();
|
|
rt.block_on(async move {
|
|
let server = UdpSocket::from_std(server).unwrap();
|
|
if is_download {
|
|
let mut buf = [0; UDP_CHUNK_SIZE];
|
|
let (n, peer) = server.recv_from(&mut buf).await.unwrap();
|
|
assert_eq!(n, 1);
|
|
|
|
let mut content =
|
|
common::make_stream_of_chunks(UDP_CONTENT_SIZE, Some(UDP_CHUNK_SIZE));
|
|
while let Some(chunk) = content.next().await {
|
|
server.send_to(chunk, peer).await.unwrap();
|
|
}
|
|
} else {
|
|
let mut peer = None;
|
|
let mut total = 0;
|
|
let mut buf = [0; UDP_CHUNK_SIZE];
|
|
while total < UDP_CONTENT_SIZE {
|
|
let (n, p) = server.recv_from(&mut buf).await.unwrap();
|
|
assert_eq!(*peer.get_or_insert(p), p);
|
|
total += n;
|
|
}
|
|
|
|
assert_eq!(total, UDP_CONTENT_SIZE);
|
|
let ack = 1_u8;
|
|
server.send_to(&[ack], peer.unwrap()).await.unwrap();
|
|
}
|
|
});
|
|
});
|
|
|
|
server_addr
|
|
}
|
|
|
|
fn encode_udp_chunk(destination: &SocketAddr, payload: &[u8]) -> Vec<u8> {
|
|
const APP_NAME: &str = "test";
|
|
const SOURCE_IP: Ipv4Addr = Ipv4Addr::LOCALHOST;
|
|
const SOURCE_PORT: u16 = 1234;
|
|
|
|
let mut buffer = vec![];
|
|
buffer.put_u32((2 * (16 + 2) + 1 + APP_NAME.len() + payload.len()) as u32);
|
|
buffer.put_slice(&[0; 12]);
|
|
buffer.put_slice(&SOURCE_IP.octets());
|
|
buffer.put_u16(SOURCE_PORT);
|
|
buffer.put_slice(&[0; 12]);
|
|
buffer.put_slice(&match destination.ip() {
|
|
IpAddr::V4(ip) => ip.octets(),
|
|
_ => unreachable!(),
|
|
});
|
|
buffer.put_u16(destination.port());
|
|
buffer.put_u8(APP_NAME.len() as u8);
|
|
buffer.put_slice(APP_NAME.as_bytes());
|
|
buffer.put_slice(payload);
|
|
|
|
buffer
|
|
}
|