1use std::net::SocketAddr;
8
9use tokio::{
10 io::copy_bidirectional,
11 net::{TcpListener, TcpStream},
12};
13
14#[derive(Debug)]
16pub struct FixedUpstreamTcpRelay {
17 listener: TcpListener,
18 upstream_addr: SocketAddr,
19}
20
21impl FixedUpstreamTcpRelay {
22 pub async fn bind(listen_addr: SocketAddr, upstream_addr: SocketAddr) -> std::io::Result<Self> {
24 Ok(Self {
25 listener: TcpListener::bind(listen_addr).await?,
26 upstream_addr,
27 })
28 }
29
30 pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
32 self.listener.local_addr()
33 }
34
35 #[must_use]
37 pub fn upstream_addr(&self) -> SocketAddr {
38 self.upstream_addr
39 }
40
41 pub async fn run(self) -> std::io::Result<()> {
43 let listen_addr = self.listener.local_addr()?;
44 eprintln!(
45 "fixed-upstream TCP relay listening on {listen_addr}, upstream {}",
46 self.upstream_addr
47 );
48
49 loop {
50 let (downstream, _) = self.listener.accept().await?;
51 let upstream_addr = self.upstream_addr;
52 tokio::spawn(async move {
53 if let Err(error) = relay_connection(downstream, upstream_addr).await {
54 eprintln!("TCP relay connection failed: {error}");
55 }
56 });
57 }
58 }
59}
60
61async fn relay_connection(
62 mut downstream: TcpStream,
63 upstream_addr: SocketAddr,
64) -> std::io::Result<()> {
65 let mut upstream = TcpStream::connect(upstream_addr).await?;
66 copy_bidirectional(&mut downstream, &mut upstream).await?;
67 Ok(())
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use tokio::io::{AsyncReadExt, AsyncWriteExt};
74
75 #[tokio::test]
76 async fn relays_bytes_only_to_the_explicit_loopback_upstream() {
77 let upstream_listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
78 let upstream_addr = upstream_listener.local_addr().unwrap();
79 let relay = FixedUpstreamTcpRelay::bind("127.0.0.1:0".parse().unwrap(), upstream_addr)
80 .await
81 .unwrap();
82 assert_eq!(relay.upstream_addr(), upstream_addr);
83 let relay_addr = relay.local_addr().unwrap();
84 let relay_task = tokio::spawn(relay.run());
85
86 let upstream_task = tokio::spawn(async move {
87 let (mut connection, _) = upstream_listener.accept().await.unwrap();
88 let mut request = [0_u8; 4];
89 connection.read_exact(&mut request).await.unwrap();
90 assert_eq!(&request, b"ping");
91 connection.write_all(b"pong").await.unwrap();
92 connection.shutdown().await.unwrap();
93 });
94
95 let mut client = TcpStream::connect(relay_addr).await.unwrap();
96 client.write_all(b"ping").await.unwrap();
97 let mut response = [0_u8; 4];
98 client.read_exact(&mut response).await.unwrap();
99 assert_eq!(&response, b"pong");
100
101 upstream_task.await.unwrap();
102 relay_task.abort();
103 }
104}