use std::net::{IpAddr, SocketAddr};
const PROXY_V2_SIGNATURE: [u8; 12] = [
0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A,
];
const PROXY_V2_VERSION: u8 = 0x20; const PROXY_CMD_PROXY: u8 = 0x01;
const AF_UNSPEC: u8 = 0x00;
const AF_INET: u8 = 0x10; const AF_INET6: u8 = 0x20;
const STREAM: u8 = 0x01;
pub fn build_proxy_v2_header(client_addr: &SocketAddr, server_addr: &SocketAddr) -> Vec<u8> {
let mut header = Vec::with_capacity(52);
header.extend_from_slice(&PROXY_V2_SIGNATURE);
let (af_proto, addr_len, addr_data) = match (client_addr.ip(), server_addr.ip()) {
(IpAddr::V4(src_ip), IpAddr::V4(dst_ip)) => {
let af_proto = AF_INET | STREAM;
let mut data = Vec::with_capacity(12);
data.extend_from_slice(&src_ip.octets()); data.extend_from_slice(&dst_ip.octets()); data.extend_from_slice(&client_addr.port().to_be_bytes()); data.extend_from_slice(&server_addr.port().to_be_bytes()); (af_proto, 12u16, data)
}
(IpAddr::V6(src_ip), IpAddr::V6(dst_ip)) => {
let af_proto = AF_INET6 | STREAM;
let mut data = Vec::with_capacity(36);
data.extend_from_slice(&src_ip.octets()); data.extend_from_slice(&dst_ip.octets()); data.extend_from_slice(&client_addr.port().to_be_bytes()); data.extend_from_slice(&server_addr.port().to_be_bytes()); (af_proto, 36u16, data)
}
(IpAddr::V4(src_ip), IpAddr::V6(_)) => {
let src_v6 = src_ip.to_ipv6_mapped();
let dst_v6 = match server_addr.ip() {
IpAddr::V6(ip) => ip,
_ => unreachable!(),
};
let af_proto = AF_INET6 | STREAM;
let mut data = Vec::with_capacity(36);
data.extend_from_slice(&src_v6.octets());
data.extend_from_slice(&dst_v6.octets());
data.extend_from_slice(&client_addr.port().to_be_bytes());
data.extend_from_slice(&server_addr.port().to_be_bytes());
(af_proto, 36u16, data)
}
(IpAddr::V6(_), IpAddr::V4(dst_ip)) => {
let src_v6 = match client_addr.ip() {
IpAddr::V6(ip) => ip,
_ => unreachable!(),
};
let dst_v6 = dst_ip.to_ipv6_mapped();
let af_proto = AF_INET6 | STREAM;
let mut data = Vec::with_capacity(36);
data.extend_from_slice(&src_v6.octets());
data.extend_from_slice(&dst_v6.octets());
data.extend_from_slice(&client_addr.port().to_be_bytes());
data.extend_from_slice(&server_addr.port().to_be_bytes());
(af_proto, 36u16, data)
}
};
header.push(PROXY_V2_VERSION | PROXY_CMD_PROXY);
header.push(af_proto);
header.extend_from_slice(&addr_len.to_be_bytes());
header.extend_from_slice(&addr_data);
header
}
pub fn build_proxy_v2_local_header() -> Vec<u8> {
let mut header = Vec::with_capacity(16);
header.extend_from_slice(&PROXY_V2_SIGNATURE);
header.push(PROXY_V2_VERSION);
header.push(AF_UNSPEC);
header.extend_from_slice(&0u16.to_be_bytes());
header
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
#[test]
fn test_proxy_v2_header_ipv4() {
let client = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 1, 100), 54321));
let server = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(10, 0, 0, 1), 25565));
let header = build_proxy_v2_header(&client, &server);
assert_eq!(&header[0..12], &PROXY_V2_SIGNATURE);
assert_eq!(header[12], 0x21);
assert_eq!(header[13], 0x11);
assert_eq!(&header[14..16], &12u16.to_be_bytes());
assert_eq!(header.len(), 28);
assert_eq!(&header[16..20], &[192, 168, 1, 100]);
assert_eq!(&header[20..24], &[10, 0, 0, 1]);
assert_eq!(&header[24..26], &54321u16.to_be_bytes());
assert_eq!(&header[26..28], &25565u16.to_be_bytes());
}
#[test]
fn test_proxy_v2_header_ipv6() {
let client = SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1),
54321,
0,
0,
));
let server = SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 2),
25565,
0,
0,
));
let header = build_proxy_v2_header(&client, &server);
assert_eq!(&header[0..12], &PROXY_V2_SIGNATURE);
assert_eq!(header[12], 0x21);
assert_eq!(header[13], 0x21);
assert_eq!(&header[14..16], &36u16.to_be_bytes());
assert_eq!(header.len(), 52);
}
#[test]
fn test_proxy_v2_local_header() {
let header = build_proxy_v2_local_header();
assert_eq!(&header[0..12], &PROXY_V2_SIGNATURE);
assert_eq!(header[12], 0x20);
assert_eq!(header[13], 0x00);
assert_eq!(&header[14..16], &0u16.to_be_bytes());
assert_eq!(header.len(), 16);
}
}