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 pick_backend(route, counter).map(|(endpoint, _)| endpoint)
46}
47
48type BackendTlsOf = Option<Result<super::backend_tls::BackendTls, String>>;
51
52fn pick_backend(route: &CompiledHttpRoute, counter: &AtomicUsize) -> Option<(String, BackendTlsOf)> {
54 let rule = route.rules.iter().find(|r| !r.backends.is_empty())?;
55 let total: u32 = rule.backends.iter().map(|b| b.weight).sum();
56 if total == 0 {
57 return None;
58 }
59 let n = counter.fetch_add(1, Ordering::Relaxed);
60 let mut pick = ((n as u64).wrapping_mul(0x9E37_79B9_7F4A_7C15) >> 32) as u32 % total;
61 let backend = rule.backends.iter().find(|b| {
62 if pick < b.weight {
63 true
64 } else {
65 pick -= b.weight;
66 false
67 }
68 })?;
69 let endpoints = backend.endpoints.as_ref().ok().filter(|e| !e.is_empty())?;
70 Some((endpoints[n % endpoints.len()].clone(), backend.tls.clone()))
71}
72
73fn target(listeners: &PortListeners, sni: Option<&str>, counter: &AtomicUsize) -> Option<String> {
75 let listeners: Arc<Vec<ListenerConfig>> = listeners.read().unwrap_or_else(|e| e.into_inner()).clone();
76 let listener = pick_listener(&listeners, &sni.unwrap_or("").to_ascii_lowercase())?;
77 pick_endpoint(pick_route(&listener.http_routes, sni)?, counter)
78}
79
80async fn connect(endpoint: &str) -> Option<TcpStream> {
81 let address: SocketAddr = endpoint.parse().ok()?;
82 tokio::time::timeout(CONNECT_TIMEOUT, TcpStream::connect(address)).await.ok()?.ok()
83}
84
85pub async fn serve_tcp(mut client: TcpStream, listeners: PortListeners, counter: Arc<AtomicUsize>) {
87 let Some(endpoint) = target(&listeners, None, &counter) else { return };
88 let Some(mut backend) = connect(&endpoint).await else { return };
89 let _ = tokio::io::copy_bidirectional(&mut client, &mut backend).await;
90}
91
92#[derive(Debug, PartialEq)]
94pub enum Hello {
95 NeedMore,
97 Sni(Option<String>),
99 NotTls,
101}
102
103pub fn client_hello(bytes: &[u8]) -> Hello {
105 let mut acceptor = rustls::server::Acceptor::default();
106 let mut input = bytes;
107 while !input.is_empty() {
108 if acceptor.read_tls(&mut input).is_err() {
109 return Hello::NotTls;
110 }
111 }
112 match acceptor.accept() {
113 Ok(Some(accepted)) => Hello::Sni(accepted.client_hello().server_name().map(str::to_string)),
114 Ok(None) => Hello::NeedMore,
115 Err(_) => Hello::NotTls,
116 }
117}
118
119pub async fn serve_tls_passthrough(mut client: TcpStream, listeners: PortListeners, counter: Arc<AtomicUsize>) {
122 use tokio::io::AsyncReadExt;
123 let mut hello = Vec::new();
124 let sni = loop {
125 let mut buffer = [0u8; 4096];
126 let n = match tokio::time::timeout(Duration::from_secs(10), client.read(&mut buffer)).await {
127 Ok(Ok(n)) if n > 0 => n,
128 _ => return,
129 };
130 hello.extend_from_slice(&buffer[..n]);
131 match client_hello(&hello) {
132 Hello::Sni(sni) => break sni,
133 Hello::NeedMore if hello.len() < MAX_CLIENT_HELLO => continue,
134 _ => return,
135 }
136 };
137 let Some(endpoint) = target(&listeners, sni.as_deref(), &counter) else { return };
138 let Some(mut backend) = connect(&endpoint).await else { return };
139 if backend.write_all(&hello).await.is_err() {
140 return;
141 }
142 let _ = tokio::io::copy_bidirectional(&mut client, &mut backend).await;
143}
144
145pub async fn serve_tls_terminate(client: TcpStream, acceptor: tokio_rustls::TlsAcceptor, listeners: PortListeners, counter: Arc<AtomicUsize>) {
148 let Ok(Ok(mut tls)) = tokio::time::timeout(Duration::from_secs(10), acceptor.accept(client)).await else { return };
149 let sni = tls.get_ref().1.server_name().map(str::to_string);
150 let current: Arc<Vec<ListenerConfig>> = listeners.read().unwrap_or_else(|e| e.into_inner()).clone();
151 let Some(listener) = pick_listener(¤t, &sni.as_deref().unwrap_or("").to_ascii_lowercase()) else { return };
152 let Some((endpoint, backend_tls)) = pick_route(&listener.http_routes, sni.as_deref()).and_then(|route| pick_backend(route, &counter)) else { return };
153 let Some(backend) = connect(&endpoint).await else { return };
154 match backend_tls {
155 None => {
156 let mut backend = backend;
157 let _ = tokio::io::copy_bidirectional(&mut tls, &mut backend).await;
158 }
159 Some(Ok(policy)) => {
161 let Ok(name) = rustls::pki_types::ServerName::try_from(policy.server_name.clone()) else { return };
162 let connector = tokio_rustls::TlsConnector::from(Arc::clone(&policy.config));
163 let Ok(Ok(mut backend)) = tokio::time::timeout(CONNECT_TIMEOUT, connector.connect(name, backend)).await else { return };
164 let _ = tokio::io::copy_bidirectional(&mut tls, &mut backend).await;
165 }
166 Some(Err(_)) => {}
167 }
168}
169
170async fn peek_sni(client: &TcpStream) -> Result<Option<String>, ()> {
174 let deadline = Instant::now() + Duration::from_secs(10);
175 let mut buffer = vec![0u8; MAX_CLIENT_HELLO];
176 let mut last = 0;
177 loop {
178 let left = deadline.saturating_duration_since(Instant::now());
179 let n = match tokio::time::timeout(left, client.peek(&mut buffer)).await {
180 Ok(Ok(n)) if n > 0 => n,
181 _ => return Err(()),
182 };
183 match client_hello(&buffer[..n]) {
184 Hello::Sni(sni) => return Ok(sni),
185 Hello::NeedMore if n < MAX_CLIENT_HELLO => {
186 if n == last {
189 tokio::time::sleep(Duration::from_millis(5)).await;
190 }
191 last = n;
192 }
193 _ => return Err(()),
194 }
195 }
196}
197
198pub async fn serve_tls_mixed(client: TcpStream, acceptor: Option<tokio_rustls::TlsAcceptor>, listeners: PortListeners, counter: Arc<AtomicUsize>) {
202 let current: Arc<Vec<ListenerConfig>> = listeners.read().unwrap_or_else(|e| e.into_inner()).clone();
203 let all_terminate = current.iter().all(|l| l.tls.is_some());
204 if all_terminate {
205 if let Some(acceptor) = acceptor {
206 serve_tls_terminate(client, acceptor, listeners, counter).await;
207 }
208 return;
209 }
210 let Ok(sni) = peek_sni(&client).await else { return };
211 let terminate = pick_listener(¤t, &sni.as_deref().unwrap_or("").to_ascii_lowercase()).is_some_and(|l| l.tls.is_some());
212 match acceptor {
213 Some(acceptor) if terminate => serve_tls_terminate(client, acceptor, listeners, counter).await,
214 _ if terminate => {}
215 _ => {
216 let mut client = client;
217 let Some(endpoint) = target(&listeners, sni.as_deref(), &counter) else { return };
218 let Some(mut backend) = connect(&endpoint).await else { return };
219 let _ = tokio::io::copy_bidirectional(&mut client, &mut backend).await;
220 }
221 }
222}
223
224struct Session {
226 backend: Arc<UdpSocket>,
227 seen: Arc<Mutex<Instant>>,
228}
229
230pub async fn serve_udp(socket: Arc<UdpSocket>, listeners: PortListeners) {
236 let counter = Arc::new(AtomicUsize::new(0));
237 let mut session_tasks = tokio::task::JoinSet::new();
238 let sessions: Arc<Mutex<HashMap<SocketAddr, Session>>> = Arc::default();
239 let mut buffer = vec![0u8; 65_535];
240 loop {
241 while session_tasks.try_join_next().is_some() {}
243 let Ok((n, client)) = socket.recv_from(&mut buffer).await else {
244 tokio::time::sleep(Duration::from_millis(50)).await;
245 continue;
246 };
247 let existing = sessions.lock().unwrap_or_else(|e| e.into_inner()).get(&client).map(|s| (Arc::clone(&s.backend), Arc::clone(&s.seen)));
248 let (backend, seen) = match existing {
249 Some(session) => session,
250 None => {
251 let Some(endpoint) = target(&listeners, None, &counter) else { continue };
252 let Some(backend) = udp_connect(&endpoint).await else { continue };
253 let backend = Arc::new(backend);
254 let seen = Arc::new(Mutex::new(Instant::now()));
255 sessions.lock().unwrap_or_else(|e| e.into_inner()).insert(client, Session { backend: Arc::clone(&backend), seen: Arc::clone(&seen) });
256 session_tasks.spawn(udp_replies(Arc::clone(&socket), Arc::clone(&backend), client, Arc::clone(&seen), Arc::clone(&sessions)));
257 (backend, seen)
258 }
259 };
260 *seen.lock().unwrap_or_else(|e| e.into_inner()) = Instant::now();
261 let _ = backend.send(&buffer[..n]).await;
262 }
263}
264
265async fn udp_connect(endpoint: &str) -> Option<UdpSocket> {
266 let address: SocketAddr = endpoint.parse().ok()?;
267 let local = if address.is_ipv6() { "[::]:0" } else { "0.0.0.0:0" };
268 let socket = UdpSocket::bind(local).await.ok()?;
269 socket.connect(address).await.ok()?;
270 Some(socket)
271}
272
273async fn udp_replies(listener: Arc<UdpSocket>, backend: Arc<UdpSocket>, client: SocketAddr, seen: Arc<Mutex<Instant>>, sessions: Arc<Mutex<HashMap<SocketAddr, Session>>>) {
275 let mut buffer = vec![0u8; 65_535];
276 loop {
277 let idle = seen.lock().unwrap_or_else(|e| e.into_inner()).elapsed();
278 if idle >= UDP_IDLE {
279 break;
280 }
281 match tokio::time::timeout(UDP_IDLE - idle, backend.recv(&mut buffer)).await {
282 Ok(Ok(n)) => {
283 *seen.lock().unwrap_or_else(|e| e.into_inner()) = Instant::now();
284 let _ = listener.send_to(&buffer[..n], client).await;
285 }
286 Ok(Err(_)) => break,
287 Err(_) => continue, }
289 }
290 sessions.lock().unwrap_or_else(|e| e.into_inner()).remove(&client);
291}
292
293#[cfg(test)]
294mod tests;