1#![allow(missing_docs)]
2
3mod auth;
4mod startup;
5
6use bytes::Bytes;
7use std::collections::HashMap;
8use std::future::Future;
9use std::sync::Arc;
10
11use futures_util::{try_join, SinkExt};
12use tokio::io::{copy_bidirectional, AsyncRead, AsyncWrite, AsyncWriteExt};
13use tokio::net::TcpStream;
14use tokio_util::codec::{Framed, FramedParts};
15
16use postgres_protocol::message::startup::{CancelData, StartupData, StartupResponse};
17
18use crate::config::SslNegotiation;
19use crate::connect_proxy::connect_proxy;
20use crate::proxy::startup::{read_frontend_startup, StartupCodec, StartupInfo};
21use crate::tls::{MakeTlsConnect, TlsConnect};
22use crate::{CancelToken, Config, Error, Socket};
23
24pub trait ClientBouncer: Clone + Sync + Send + 'static {
26 type Tls: MakeTlsConnect<Socket> + Send + Clone + 'static;
27 type Future: Future<Output = Result<AcceptConn<Self::Tls>, RejectConn>> + Send + 'static;
28
29 fn handle_startup(&self, info: &StartupData) -> Self::Future;
33}
34
35pub enum RejectConn {
37 UnknownDatabase,
38 UnknownUser,
39 InternalError,
40}
41
42impl RejectConn {
43 pub fn sqlstate(&self) -> &'static str {
45 match self {
46 RejectConn::UnknownDatabase => "3D000", RejectConn::UnknownUser => "28000", RejectConn::InternalError => "XX000", }
50 }
51}
52
53impl std::fmt::Display for RejectConn {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 RejectConn::UnknownDatabase => write!(f, "unknown database"),
57 RejectConn::UnknownUser => write!(f, "unknown user"),
58 RejectConn::InternalError => write!(f, "internal error"),
59 }
60 }
61}
62
63pub struct AcceptConn<T> {
65 pub auth_method: AuthMethod,
67
68 pub tls: T,
70
71 pub backend_config: Arc<Config>,
73}
74
75pub enum AuthMethod {
77 Trust,
79
80 Password(String),
82}
83
84#[derive(Clone)]
85pub struct ProxyManager<B>
86where
87 B: ClientBouncer,
88{
89 bouncer: B,
90
91 cancel_handles: Arc<tokio::sync::RwLock<HashMap<CancelKey, CancelHandle<B::Tls>>>>,
93}
94
95impl<B> ProxyManager<B>
97where
98 B: ClientBouncer,
99 <B::Tls as MakeTlsConnect<Socket>>::TlsConnect: Send,
100 <B::Tls as MakeTlsConnect<Socket>>::Stream: Send,
101 <<B::Tls as MakeTlsConnect<Socket>>::TlsConnect as TlsConnect<Socket>>::Future: Send,
102{
103 pub fn new(bouncer: B) -> Self {
104 Self {
105 bouncer,
106 cancel_handles: Arc::default(),
107 }
108 }
109
110 pub async fn handle_conn(self, client_stream: TcpStream) {
112 let mut startup_stream = Framed::new(client_stream, StartupCodec::new());
113
114 let Some((mut accept, startup_data)) = self.client_startup(&mut startup_stream).await
116 else {
117 return;
118 };
119
120 let client_params = client_params(&startup_data, &accept.backend_config);
122 let backend_info =
123 match connect_proxy(&mut accept.tls, &accept.backend_config, &client_params).await {
124 Ok(backend_info) => backend_info,
125 Err(err) => {
126 _ = startup_stream
127 .send(StartupResponse::ErrorResponse {
128 code: "08006", message: format!("backend connection failed: {:?}", err),
130 })
131 .await;
132 return;
133 }
134 };
135
136 let cancel_registration = match backend_info.backend_key {
139 Some((process_id, secret_key)) => {
140 let reg = CancelHandleRegistration {
141 key: CancelKey {
142 process_id,
143 secret_key,
144 },
145 lock: self.cancel_handles.clone(),
146 };
147 reg.register(CancelHandle {
148 token: CancelToken {
149 socket_config: Some(backend_info.socket_config),
150 ssl_mode: accept.backend_config.ssl_mode,
151 process_id,
152 secret_key,
153 ssl_negotiation: SslNegotiation::Postgres,
154 },
155 tls: accept.tls,
156 })
157 .await;
158 Some(reg)
159 }
160 None => None,
161 };
162
163 if self
165 .complete_client_init(
166 &mut startup_stream,
167 &backend_info.parameters,
168 backend_info.backend_key,
169 )
170 .await
171 .is_err()
172 {
173 if let Some(reg) = cancel_registration {
175 reg.deregister().await;
176 }
177 return;
178 }
179
180 let proxy_result = {
182 let mut backend_parts = backend_info.backend.into_parts();
183 let mut client_parts = startup_stream.into_parts();
184 proxy_data(&mut client_parts, &mut backend_parts).await
185 };
186
187 if let Some(reg) = cancel_registration {
189 reg.deregister().await;
190 }
191
192 match proxy_result {
193 Ok(()) => log::debug!("proxy connection closed"),
194 Err(err) => log::error!("proxy connection error: {}", err),
195 }
196 }
197
198 async fn client_startup<S>(
203 &self,
204 startup_stream: &mut Framed<S, StartupCodec>,
205 ) -> Option<(AcceptConn<B::Tls>, StartupData)>
206 where
207 S: AsyncRead + AsyncWrite + Unpin,
208 {
209 match read_frontend_startup(startup_stream).await.ok()? {
211 StartupInfo::Cancel(cancel) => {
212 self.handle_cancel(cancel).await;
213 None
216 }
217
218 StartupInfo::Startup(raw) => {
219 let startup_data = StartupData::parse(raw).ok()?;
221 match self.bouncer.handle_startup(&startup_data).await {
222 Ok(accept) => {
223 match accept
225 .auth_method
226 .authenticate(startup_stream, &startup_data)
227 .await
228 {
229 Ok(()) => {
230 Some((accept, startup_data))
232 }
233 Err(err) => {
234 log::error!("authentication failed: {}", err);
236
237 _ = startup_stream
239 .send(StartupResponse::ErrorResponse {
240 code: "28P01", message: "authentication failed".to_string(),
242 })
243 .await;
244 None
245 }
246 }
247 }
248 Err(reject) => {
249 log::error!("connection rejected: {}", reject);
250 _ = startup_stream
252 .send(StartupResponse::ErrorResponse {
253 code: reject.sqlstate(),
254 message: format!("connection rejected: {}", reject),
255 })
256 .await;
257 None
258 }
259 }
260 }
261 }
262 }
263
264 async fn complete_client_init<S>(
265 &self,
266 startup_stream: &mut Framed<S, StartupCodec>,
267 parameters: &HashMap<String, String>,
268 backend_key: Option<(i32, i32)>,
269 ) -> Result<(), Error>
270 where
271 S: AsyncRead + AsyncWrite + Unpin,
272 {
273 startup_stream
275 .feed(StartupResponse::AuthenticationOk)
276 .await
277 .map_err(Error::io)?;
278
279 let mut parameters = parameters
281 .iter()
282 .map(|(k, v)| (k.clone(), v.clone()))
283 .collect::<Vec<_>>();
284
285 parameters.sort_by(|a, b| a.0.cmp(&b.0));
286 for (key, value) in parameters {
287 let msg = StartupResponse::ParameterStatus {
288 key: key.clone(),
289 value: Bytes::from(value),
290 };
291 startup_stream.feed(msg).await.map_err(Error::io)?;
292 }
293
294 if let Some((process_id, secret_key)) = backend_key {
297 startup_stream
298 .feed(StartupResponse::BackendKeyData {
299 process_id,
300 secret_key,
301 })
302 .await
303 .map_err(Error::io)?;
304 }
305
306 startup_stream
308 .feed(StartupResponse::ReadyForQuery)
309 .await
310 .map_err(Error::io)?;
311
312 startup_stream.flush().await.map_err(Error::io)?;
314
315 Ok(())
316 }
317
318 async fn handle_cancel(&self, cancel: CancelData) {
320 let key = CancelKey {
321 process_id: cancel.process_id,
322 secret_key: cancel.secret_key,
323 };
324
325 let handle = self
328 .cancel_handles
329 .read()
330 .await
331 .get(&key)
332 .map(|handle| (handle.token.clone(), handle.tls.clone()));
333 if let Some((token, tls)) = handle {
334 _ = token.cancel_query(tls).await;
335 }
336 }
337}
338
339fn client_params(startup: &StartupData, config: &Config) -> Vec<(String, String)> {
345 let mut params = Vec::new();
346 for (key, value) in &startup.parameters {
347 if matches!(key.as_str(), "user" | "database") || key.starts_with("_pq_.") {
348 continue;
349 }
350 let Ok(value) = std::str::from_utf8(value) else {
351 log::warn!("dropping startup parameter {key}: its value is not UTF-8");
352 continue;
353 };
354 let value = match (key.as_str(), config.get_options()) {
355 ("options", Some(options)) => format!("{options} {value}"),
356 _ => value.to_string(),
357 };
358 params.push((key.clone(), value));
359 }
360 params.sort();
361 params
362}
363
364async fn proxy_data<C, CC, S, SC>(
365 client: &mut FramedParts<C, CC>,
366 server: &mut FramedParts<S, SC>,
367) -> Result<(), Error>
368where
369 C: AsyncRead + AsyncWrite + Unpin,
370 S: AsyncRead + AsyncWrite + Unpin,
371{
372 write_pending(client, server).await?;
374
375 copy_bidirectional(&mut client.io, &mut server.io)
377 .await
378 .map_err(Error::io)?;
379
380 Ok(())
381}
382
383async fn write_pending<C, CC, S, SC>(
384 client: &mut FramedParts<C, CC>,
385 server: &mut FramedParts<S, SC>,
386) -> Result<(), Error>
387where
388 C: AsyncRead + AsyncWrite + Unpin,
389 S: AsyncRead + AsyncWrite + Unpin,
390{
391 let a = server.io.write_all(&server.write_buf);
393 let b = client.io.write_all(&client.write_buf);
394 try_join!(a, b).map_err(Error::io)?;
395
396 let c = client.io.write_all(&server.read_buf);
397 let d = server.io.write_all(&client.read_buf);
398 try_join!(c, d).map_err(Error::io)?;
399 Ok(())
400}
401
402struct CancelHandle<T> {
403 token: CancelToken,
404 tls: T,
405}
406
407#[derive(Clone, Hash, PartialEq, Eq)]
409struct CancelKey {
410 process_id: i32,
411 secret_key: i32,
412}
413
414struct CancelHandleRegistration<T> {
415 key: CancelKey,
416 lock: Arc<tokio::sync::RwLock<HashMap<CancelKey, CancelHandle<T>>>>,
417}
418
419impl<T> CancelHandleRegistration<T> {
420 pub async fn register(&self, handle: CancelHandle<T>) {
421 self.lock.write().await.insert(self.key.clone(), handle);
422 }
423
424 pub async fn deregister(&self) {
425 self.lock.write().await.remove(&self.key);
426 }
427}