rws_gateway_api/gateway_api/
l4.rs1use std::collections::HashMap;
8use std::net::SocketAddr;
9use std::sync::atomic::{AtomicUsize, Ordering};
10use std::sync::{Arc, Mutex};
11use std::time::{Duration, Instant};
12
13use tokio::io::AsyncWriteExt;
14use tokio::net::{TcpStream, UdpSocket};
15
16use super::router::{host_score, pick_listener, PortListeners};
17use super::routes::CompiledHttpRoute;
18use super::translate::ListenerConfig;
19
20const MAX_CLIENT_HELLO: usize = 16 * 1024;
22pub const UDP_IDLE: Duration = Duration::from_secs(60);
24const CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
26
27pub fn pick_route<'a>(routes: &'a [CompiledHttpRoute], sni: Option<&str>) -> Option<&'a CompiledHttpRoute> {
31 let sni = sni.unwrap_or("").to_ascii_lowercase();
32 routes
33 .iter()
34 .filter_map(|route| {
35 let score = if route.hostnames.is_empty() { Some((0, 0)) } else { route.hostnames.iter().filter_map(|h| host_score(h, &sni)).max() };
36 score.map(|s| (s, route))
37 })
38 .max_by(|(a, ra), (b, rb)| a.cmp(b).then_with(|| (rb.created.as_str(), &rb.key).cmp(&(ra.created.as_str(), &ra.key))))
39 .map(|(_, route)| route)
40}
41
42pub fn pick_endpoint(route: &CompiledHttpRoute, counter: &AtomicUsize) -> Option<String> {
45 let rule = route.rules.iter().find(|r| !r.backends.is_empty())?;
46 let total: u32 = rule.backends.iter().map(|b| b.weight).sum();
47 if total == 0 {
48 return None;
49 }
50 let n = counter.fetch_add(1, Ordering::Relaxed);
51 let mut pick = ((n as u64).wrapping_mul(0x9E37_79B9_7F4A_7C15) >> 32) as u32 % total;
52 let backend = rule.backends.iter().find(|b| {
53 if pick < b.weight {
54 true
55 } else {
56 pick -= b.weight;
57 false
58 }
59 })?;
60 let endpoints = backend.endpoints.as_ref().ok().filter(|e| !e.is_empty())?;
61 Some(endpoints[n % endpoints.len()].clone())
62}
63
64fn target(listeners: &PortListeners, sni: Option<&str>, counter: &AtomicUsize) -> Option<String> {
66 let listeners: Arc<Vec<ListenerConfig>> = listeners.read().unwrap_or_else(|e| e.into_inner()).clone();
67 let listener = pick_listener(&listeners, &sni.unwrap_or("").to_ascii_lowercase())?;
68 pick_endpoint(pick_route(&listener.http_routes, sni)?, counter)
69}
70
71async fn connect(endpoint: &str) -> Option<TcpStream> {
72 let address: SocketAddr = endpoint.parse().ok()?;
73 tokio::time::timeout(CONNECT_TIMEOUT, TcpStream::connect(address)).await.ok()?.ok()
74}
75
76pub async fn serve_tcp(mut client: TcpStream, listeners: PortListeners, counter: Arc<AtomicUsize>) {
78 let Some(endpoint) = target(&listeners, None, &counter) else { return };
79 let Some(mut backend) = connect(&endpoint).await else { return };
80 let _ = tokio::io::copy_bidirectional(&mut client, &mut backend).await;
81}
82
83#[derive(Debug, PartialEq)]
85pub enum Hello {
86 NeedMore,
88 Sni(Option<String>),
90 NotTls,
92}
93
94pub fn client_hello(bytes: &[u8]) -> Hello {
96 let mut acceptor = rustls::server::Acceptor::default();
97 let mut input = bytes;
98 while !input.is_empty() {
99 if acceptor.read_tls(&mut input).is_err() {
100 return Hello::NotTls;
101 }
102 }
103 match acceptor.accept() {
104 Ok(Some(accepted)) => Hello::Sni(accepted.client_hello().server_name().map(str::to_string)),
105 Ok(None) => Hello::NeedMore,
106 Err(_) => Hello::NotTls,
107 }
108}
109
110pub async fn serve_tls_passthrough(mut client: TcpStream, listeners: PortListeners, counter: Arc<AtomicUsize>) {
113 use tokio::io::AsyncReadExt;
114 let mut hello = Vec::new();
115 let sni = loop {
116 let mut buffer = [0u8; 4096];
117 let n = match tokio::time::timeout(Duration::from_secs(10), client.read(&mut buffer)).await {
118 Ok(Ok(n)) if n > 0 => n,
119 _ => return,
120 };
121 hello.extend_from_slice(&buffer[..n]);
122 match client_hello(&hello) {
123 Hello::Sni(sni) => break sni,
124 Hello::NeedMore if hello.len() < MAX_CLIENT_HELLO => continue,
125 _ => return,
126 }
127 };
128 let Some(endpoint) = target(&listeners, sni.as_deref(), &counter) else { return };
129 let Some(mut backend) = connect(&endpoint).await else { return };
130 if backend.write_all(&hello).await.is_err() {
131 return;
132 }
133 let _ = tokio::io::copy_bidirectional(&mut client, &mut backend).await;
134}
135
136pub async fn serve_tls_terminate(client: TcpStream, acceptor: tokio_rustls::TlsAcceptor, listeners: PortListeners, counter: Arc<AtomicUsize>) {
139 let Ok(Ok(mut tls)) = tokio::time::timeout(Duration::from_secs(10), acceptor.accept(client)).await else { return };
140 let sni = tls.get_ref().1.server_name().map(str::to_string);
141 let Some(endpoint) = target(&listeners, sni.as_deref(), &counter) else { return };
142 let Some(mut backend) = connect(&endpoint).await else { return };
143 let _ = tokio::io::copy_bidirectional(&mut tls, &mut backend).await;
144}
145
146struct Session {
148 backend: Arc<UdpSocket>,
149 seen: Arc<Mutex<Instant>>,
150}
151
152pub async fn serve_udp(socket: Arc<UdpSocket>, listeners: PortListeners) {
158 let counter = Arc::new(AtomicUsize::new(0));
159 let mut session_tasks = tokio::task::JoinSet::new();
160 let sessions: Arc<Mutex<HashMap<SocketAddr, Session>>> = Arc::default();
161 let mut buffer = vec![0u8; 65_535];
162 loop {
163 while session_tasks.try_join_next().is_some() {}
165 let Ok((n, client)) = socket.recv_from(&mut buffer).await else {
166 tokio::time::sleep(Duration::from_millis(50)).await;
167 continue;
168 };
169 let existing = sessions.lock().unwrap_or_else(|e| e.into_inner()).get(&client).map(|s| (Arc::clone(&s.backend), Arc::clone(&s.seen)));
170 let (backend, seen) = match existing {
171 Some(session) => session,
172 None => {
173 let Some(endpoint) = target(&listeners, None, &counter) else { continue };
174 let Some(backend) = udp_connect(&endpoint).await else { continue };
175 let backend = Arc::new(backend);
176 let seen = Arc::new(Mutex::new(Instant::now()));
177 sessions.lock().unwrap_or_else(|e| e.into_inner()).insert(client, Session { backend: Arc::clone(&backend), seen: Arc::clone(&seen) });
178 session_tasks.spawn(udp_replies(Arc::clone(&socket), Arc::clone(&backend), client, Arc::clone(&seen), Arc::clone(&sessions)));
179 (backend, seen)
180 }
181 };
182 *seen.lock().unwrap_or_else(|e| e.into_inner()) = Instant::now();
183 let _ = backend.send(&buffer[..n]).await;
184 }
185}
186
187async fn udp_connect(endpoint: &str) -> Option<UdpSocket> {
188 let address: SocketAddr = endpoint.parse().ok()?;
189 let local = if address.is_ipv6() { "[::]:0" } else { "0.0.0.0:0" };
190 let socket = UdpSocket::bind(local).await.ok()?;
191 socket.connect(address).await.ok()?;
192 Some(socket)
193}
194
195async fn udp_replies(listener: Arc<UdpSocket>, backend: Arc<UdpSocket>, client: SocketAddr, seen: Arc<Mutex<Instant>>, sessions: Arc<Mutex<HashMap<SocketAddr, Session>>>) {
197 let mut buffer = vec![0u8; 65_535];
198 loop {
199 let idle = seen.lock().unwrap_or_else(|e| e.into_inner()).elapsed();
200 if idle >= UDP_IDLE {
201 break;
202 }
203 match tokio::time::timeout(UDP_IDLE - idle, backend.recv(&mut buffer)).await {
204 Ok(Ok(n)) => {
205 *seen.lock().unwrap_or_else(|e| e.into_inner()) = Instant::now();
206 let _ = listener.send_to(&buffer[..n], client).await;
207 }
208 Ok(Err(_)) => break,
209 Err(_) => continue, }
211 }
212 sessions.lock().unwrap_or_else(|e| e.into_inner()).remove(&client);
213}
214
215#[cfg(test)]
216mod tests;