use std::net::SocketAddr;
use tako_rs_core::conn_info::ConnInfo;
mod header;
#[cfg(not(feature = "compio"))]
pub(crate) mod listener;
mod v1;
mod v2;
pub use header::ProxyHeader;
pub use header::ProxyTlsInfo;
pub use header::ProxyTlv;
pub use header::ProxyTransport;
pub use header::ProxyVersion;
const PROXY_V2_SIG: [u8; 12] = *b"\r\n\r\n\0\r\nQUIT\n";
enum Signature {
V1,
V2,
}
fn signature(sig: &[u8; 12]) -> std::io::Result<Signature> {
if *sig == PROXY_V2_SIG {
Ok(Signature::V2)
} else if sig.starts_with(b"PROXY ") {
Ok(Signature::V1)
} else {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"invalid PROXY protocol header: unrecognized signature",
))
}
}
#[cfg(not(feature = "compio"))]
pub async fn read_proxy_protocol<R: tokio::io::AsyncReadExt + Unpin>(
reader: &mut R,
) -> std::io::Result<ProxyHeader> {
let mut sig = [0u8; 12];
reader.read_exact(&mut sig).await?;
match signature(&sig)? {
Signature::V2 => {
let mut hdr = [0u8; 4];
reader.read_exact(&mut hdr).await?;
let mut addr_buf = vec![0u8; v2::address_len(&hdr)?];
if !addr_buf.is_empty() {
reader.read_exact(&mut addr_buf).await?;
}
Ok(v2::parse_v2(&sig, &hdr, &addr_buf))
}
Signature::V1 => {
let mut line = Vec::from(&sig[..]);
loop {
let mut byte = [0u8; 1];
reader.read_exact(&mut byte).await?;
line.push(byte[0]);
if v1::line_complete(&line)? {
break;
}
}
v1::parse_v1(&line)
}
}
}
#[cfg(feature = "compio")]
pub async fn read_proxy_protocol<R: compio::io::AsyncRead>(
reader: &mut R,
) -> std::io::Result<ProxyHeader> {
let sig: [u8; 12] = read_exact_compio(reader, vec![0u8; 12])
.await?
.try_into()
.expect("read_exact fills the 12-byte signature");
match signature(&sig)? {
Signature::V2 => {
let hdr: [u8; 4] = read_exact_compio(reader, vec![0u8; 4])
.await?
.try_into()
.expect("read_exact fills the 4-byte header");
let addr_len = v2::address_len(&hdr)?;
let addr_buf = if addr_len == 0 {
Vec::new()
} else {
read_exact_compio(reader, vec![0u8; addr_len]).await?
};
Ok(v2::parse_v2(&sig, &hdr, &addr_buf))
}
Signature::V1 => {
let mut line = Vec::from(&sig[..]);
let mut byte = vec![0u8; 1];
loop {
byte = read_exact_compio(reader, byte).await?;
line.push(byte[0]);
if v1::line_complete(&line)? {
break;
}
}
v1::parse_v1(&line)
}
}
}
#[cfg(feature = "compio")]
async fn read_exact_compio<R: compio::io::AsyncRead>(
reader: &mut R,
buf: Vec<u8>,
) -> std::io::Result<Vec<u8>> {
let compio::BufResult(result, buf) = compio::io::AsyncReadExt::read_exact(reader, buf).await;
result.map(|()| buf)
}
pub(crate) fn apply_to_request<B>(req: &mut http::Request<B>, header: &ProxyHeader) {
let headers = req.headers_mut();
headers.remove(http::header::FORWARDED);
headers.remove("x-forwarded-for");
headers.remove("x-forwarded-host");
headers.remove("x-forwarded-proto");
if let Some(addr) = header.source {
if let Ok(value) = http::HeaderValue::from_str(&forwarded_for(addr)) {
headers.insert(http::header::FORWARDED, value);
}
req.extensions_mut().insert(addr);
req.extensions_mut().insert(ConnInfo::tcp(addr));
}
req.extensions_mut().insert(header.clone());
}
fn forwarded_for(addr: SocketAddr) -> String {
match addr {
SocketAddr::V4(v4) => format!("for=\"{}:{}\"", v4.ip(), v4.port()),
SocketAddr::V6(v6) => format!("for=\"[{}]:{}\"", v6.ip(), v6.port()),
}
}