1use std::net::SocketAddr;
24
25const PP2_SIGNATURE: [u8; 12] = [
27 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A,
28];
29
30const PP2_VER_CMD_PROXY: u8 = 0x21;
32
33const PP2_FAM_INET_DGRAM: u8 = 0x12;
35
36const PP2_FAM_INET6_DGRAM: u8 = 0x22;
38
39pub fn dgram_header(client: SocketAddr, backend: SocketAddr) -> Vec<u8> {
48 match (client, backend) {
49 (SocketAddr::V4(src), SocketAddr::V4(dst)) => {
50 let mut h = Vec::with_capacity(16 + 12);
51 h.extend_from_slice(&PP2_SIGNATURE);
52 h.push(PP2_VER_CMD_PROXY);
53 h.push(PP2_FAM_INET_DGRAM);
54 h.extend_from_slice(&12u16.to_be_bytes());
55 h.extend_from_slice(&src.ip().octets());
56 h.extend_from_slice(&dst.ip().octets());
57 h.extend_from_slice(&src.port().to_be_bytes());
58 h.extend_from_slice(&dst.port().to_be_bytes());
59 h
60 }
61 (SocketAddr::V6(src), SocketAddr::V6(dst)) => {
62 let mut h = Vec::with_capacity(16 + 36);
63 h.extend_from_slice(&PP2_SIGNATURE);
64 h.push(PP2_VER_CMD_PROXY);
65 h.push(PP2_FAM_INET6_DGRAM);
66 h.extend_from_slice(&36u16.to_be_bytes());
67 h.extend_from_slice(&src.ip().octets());
68 h.extend_from_slice(&dst.ip().octets());
69 h.extend_from_slice(&src.port().to_be_bytes());
70 h.extend_from_slice(&dst.port().to_be_bytes());
71 h
72 }
73 _ => {
74 let mut h = Vec::with_capacity(16);
76 h.extend_from_slice(&PP2_SIGNATURE);
77 h.push(PP2_VER_CMD_PROXY);
78 h.push(0x00); h.extend_from_slice(&0u16.to_be_bytes());
80 h
81 }
82 }
83}
84
85pub fn prepend_dgram_header(
88 payload: &mut Vec<u8>,
89 client: SocketAddr,
90 backend: SocketAddr,
91) -> usize {
92 let header = dgram_header(client, backend);
93 let header_len = header.len();
94 let mut prefixed = Vec::with_capacity(header_len + payload.len());
96 prefixed.extend_from_slice(&header);
97 prefixed.append(payload);
98 *payload = prefixed;
99 header_len
100}
101
102#[cfg(test)]
103mod tests {
104 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
105
106 use super::*;
107
108 #[test]
109 fn dgram_header_ipv4_exact_bytes() {
110 let client = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(125, 25, 10, 1)), 8080);
112 let backend = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 4, 5, 8)), 4200);
113 let header = dgram_header(client, backend);
114 let expected: [u8; 28] = [
115 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54,
116 0x0A, 0x21, 0x12, 0x00, 0x0C, 0x7D, 0x19, 0x0A, 0x01, 0x0A, 0x04, 0x05, 0x08, 0x1F, 0x90, 0x10, 0x68, ];
125 assert_eq!(&expected[..], &header[..]);
126 assert_eq!(header.len(), 28);
127 }
128
129 #[test]
130 fn dgram_header_ipv6_exact_bytes() {
131 let client = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080);
133 let backend = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 4200);
134 let header = dgram_header(client, backend);
135 let expected: [u8; 52] = [
136 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54,
137 0x0A, 0x21, 0x22, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144 0x00, 0x01, 0x1F, 0x90, 0x10, 0x68, ];
148 assert_eq!(&expected[..], &header[..]);
149 assert_eq!(header.len(), 52);
150 }
151
152 #[test]
153 fn prepend_shifts_payload() {
154 let client = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 1234);
155 let backend = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(5, 6, 7, 8)), 5678);
156 let mut payload = b"DNSQUERY".to_vec();
157 let n = prepend_dgram_header(&mut payload, client, backend);
158 assert_eq!(n, 28);
159 assert_eq!(&payload[..12], &PP2_SIGNATURE);
160 assert_eq!(payload[12], PP2_VER_CMD_PROXY);
161 assert_eq!(payload[13], PP2_FAM_INET_DGRAM);
162 assert_eq!(&payload[28..], b"DNSQUERY");
163 }
164
165 #[test]
166 fn mixed_family_emits_unspec() {
167 let client = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 1);
168 let backend = SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 2);
169 let header = dgram_header(client, backend);
170 assert_eq!(header.len(), 16);
171 assert_eq!(header[12], PP2_VER_CMD_PROXY);
172 assert_eq!(header[13], 0x00); assert_eq!(&header[14..16], &[0x00, 0x00]); }
175}