1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
15
16pub const V2_SIGNATURE: [u8; 12] = [0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A];
18
19pub const V1_MAX: usize = 107;
21
22pub const V2_MAX: usize = 16 + 1024;
24
25pub fn enabled() -> bool {
28 static ENABLED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
29 *ENABLED.get_or_init(|| std::env::var("RWS_CONFIG_PROXY_PROTOCOL").is_ok_and(|v| v.trim().eq_ignore_ascii_case("true")))
30}
31
32#[derive(Debug, PartialEq, Eq)]
34pub enum Parsed {
35 Need(usize),
37 Header { source: Option<SocketAddr>, len: usize },
40 Invalid(&'static str),
42}
43
44pub fn parse(buf: &[u8]) -> Parsed {
49 if buf.len() < 12 {
50 return Parsed::Need(12);
51 }
52 if buf[..12] == V2_SIGNATURE {
53 return parse_v2(buf);
54 }
55 if buf.starts_with(b"PROXY ") {
56 return parse_v1(buf);
57 }
58 Parsed::Invalid("no PROXY protocol header")
59}
60
61fn parse_v1(buf: &[u8]) -> Parsed {
62 let Some(end) = buf.windows(2).position(|w| w == b"\r\n") else {
63 return if buf.len() >= V1_MAX { Parsed::Invalid("PROXY v1 header too long") } else { Parsed::Need(buf.len() + 1) };
64 };
65 let len = end + 2;
66 let Ok(line) = std::str::from_utf8(&buf[..end]) else { return Parsed::Invalid("PROXY v1 header isn't text") };
67 let mut fields = line.split(' ');
68 fields.next(); match fields.next() {
70 Some("UNKNOWN") => Parsed::Header { source: None, len },
71 Some(family @ ("TCP4" | "TCP6")) => {
72 let (Some(src), Some(_dst), Some(sport), Some(_dport), None) =
73 (fields.next(), fields.next(), fields.next(), fields.next(), fields.next())
74 else {
75 return Parsed::Invalid("PROXY v1 header has the wrong number of fields");
76 };
77 let ip: Option<IpAddr> = match family {
78 "TCP4" => src.parse::<Ipv4Addr>().ok().map(IpAddr::V4),
79 _ => src.parse::<Ipv6Addr>().ok().map(IpAddr::V6),
80 };
81 match (ip, sport.parse::<u16>()) {
82 (Some(ip), Ok(port)) => Parsed::Header { source: Some(SocketAddr::new(ip, port)), len },
83 _ => Parsed::Invalid("PROXY v1 header has a bad address or port"),
84 }
85 }
86 _ => Parsed::Invalid("PROXY v1 header has an unknown protocol"),
87 }
88}
89
90fn parse_v2(buf: &[u8]) -> Parsed {
91 if buf.len() < 16 {
92 return Parsed::Need(16);
93 }
94 let version_command = buf[12];
95 if version_command >> 4 != 2 {
96 return Parsed::Invalid("PROXY v2 header has an unknown version");
97 }
98 let len = 16 + u16::from_be_bytes([buf[14], buf[15]]) as usize;
99 if len > V2_MAX {
100 return Parsed::Invalid("PROXY v2 header too long");
101 }
102 if buf.len() < len {
103 return Parsed::Need(len);
104 }
105 let body = &buf[16..len];
106 match version_command & 0x0F {
107 0x0 => return Parsed::Header { source: None, len }, 0x1 => {} _ => return Parsed::Invalid("PROXY v2 header has an unknown command"),
110 }
111 let source = match buf[13] {
113 0x11 if body.len() >= 12 => {
114 let ip = Ipv4Addr::new(body[0], body[1], body[2], body[3]);
115 Some(SocketAddr::new(IpAddr::V4(ip), u16::from_be_bytes([body[8], body[9]])))
116 }
117 0x21 if body.len() >= 36 => {
118 let mut octets = [0u8; 16];
119 octets.copy_from_slice(&body[..16]);
120 Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(octets)), u16::from_be_bytes([body[32], body[33]])))
121 }
122 0x11 | 0x21 => return Parsed::Invalid("PROXY v2 header is shorter than its addresses"),
123 _ => None, };
125 Parsed::Header { source, len }
126}
127
128#[cfg(feature = "http2")]
131pub async fn read_async<S: tokio::io::AsyncRead + Unpin>(stream: &mut S) -> std::io::Result<Option<SocketAddr>> {
132 use tokio::io::AsyncReadExt;
133 let mut buf = Vec::with_capacity(32);
134 loop {
135 match parse(&buf) {
136 Parsed::Need(n) => {
137 let start = buf.len();
138 buf.resize(n, 0);
139 stream.read_exact(&mut buf[start..]).await?;
140 }
141 Parsed::Header { source, .. } => return Ok(source),
142 Parsed::Invalid(why) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, why)),
143 }
144 }
145}
146
147pub fn read_blocking<S: std::io::Read>(stream: &mut S) -> std::io::Result<Option<SocketAddr>> {
149 let mut buf = Vec::with_capacity(32);
150 loop {
151 match parse(&buf) {
152 Parsed::Need(n) => {
153 let start = buf.len();
154 buf.resize(n, 0);
155 stream.read_exact(&mut buf[start..])?;
156 }
157 Parsed::Header { source, .. } => return Ok(source),
158 Parsed::Invalid(why) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, why)),
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 fn v2(command: u8, family: u8, addresses: &[u8]) -> Vec<u8> {
168 let mut header = V2_SIGNATURE.to_vec();
169 header.push(0x20 | command);
170 header.push(family);
171 header.extend_from_slice(&(addresses.len() as u16).to_be_bytes());
172 header.extend_from_slice(addresses);
173 header
174 }
175
176 #[test]
177 fn version_1_tcp4_tcp6_and_unknown() {
178 let header = b"PROXY TCP4 203.0.113.7 10.0.0.1 51234 443\r\n";
179 let line = [header.as_slice(), b"GET / HTTP/1.1\r\n"].concat();
180 assert_eq!(Parsed::Header { source: Some("203.0.113.7:51234".parse().unwrap()), len: header.len() }, parse(&line));
181 let line = b"PROXY TCP6 2001:db8::7 2001:db8::1 51234 443\r\n";
182 assert_eq!(Parsed::Header { source: Some("[2001:db8::7]:51234".parse().unwrap()), len: line.len() }, parse(line));
183 assert_eq!(Parsed::Header { source: None, len: 15 }, parse(b"PROXY UNKNOWN\r\n\x16\x03\x01"));
184 }
185
186 #[test]
187 fn version_1_is_read_a_byte_at_a_time_after_twelve() {
188 assert_eq!(Parsed::Need(12), parse(b"PROXY"));
189 assert_eq!(Parsed::Need(13), parse(b"PROXY TCP4 2"));
190 }
191
192 #[test]
193 fn version_1_rejects_broken_headers() {
194 for bad in [
195 &b"PROXY TCP4 203.0.113.7 10.0.0.1 51234\r\n"[..],
196 b"PROXY TCP4 2001:db8::1 10.0.0.1 1 2\r\n",
197 b"PROXY TCP4 203.0.113.7 10.0.0.1 99999 443\r\n",
198 b"PROXY UDP4 203.0.113.7 10.0.0.1 1 2\r\n",
199 b"GET / HTTP/1.1\r\nHost: x\r\n\r\n",
200 ] {
201 assert!(matches!(parse(bad), Parsed::Invalid(_)), "{:?}", String::from_utf8_lossy(bad));
202 }
203 let long = [b"PROXY ".as_slice(), &[b'1'; V1_MAX]].concat();
204 assert!(matches!(parse(&long), Parsed::Invalid(_)));
205 }
206
207 #[test]
208 fn version_2_inet_inet6_local_and_tlvs() {
209 let mut addresses = vec![203, 0, 113, 7, 10, 0, 0, 1];
210 addresses.extend_from_slice(&51234u16.to_be_bytes());
211 addresses.extend_from_slice(&443u16.to_be_bytes());
212 addresses.extend_from_slice(&[0x04, 0x00, 0x01, 0x00]); let header = v2(1, 0x11, &addresses);
214 assert_eq!(Parsed::Header { source: Some("203.0.113.7:51234".parse().unwrap()), len: header.len() }, parse(&header));
215
216 let mut addresses = "2001:db8::7".parse::<Ipv6Addr>().unwrap().octets().to_vec();
217 addresses.extend_from_slice(&"2001:db8::1".parse::<Ipv6Addr>().unwrap().octets());
218 addresses.extend_from_slice(&[0xC8, 0x22, 0x01, 0xBB]);
219 let header = v2(1, 0x21, &addresses);
220 assert_eq!(Parsed::Header { source: Some("[2001:db8::7]:51234".parse().unwrap()), len: 52 }, parse(&header));
221
222 assert_eq!(Parsed::Header { source: None, len: 16 }, parse(&v2(0, 0x00, &[])));
223 assert_eq!(Parsed::Need(16), parse(&V2_SIGNATURE));
224 assert_eq!(Parsed::Need(28), parse(&v2(1, 0x11, &[0; 12])[..20]));
225 }
226
227 #[test]
228 fn version_2_rejects_broken_headers() {
229 assert!(matches!(parse(&v2(1, 0x11, &[0; 4])), Parsed::Invalid(_)));
230 assert!(matches!(parse(&v2(7, 0x11, &[0; 12])), Parsed::Invalid(_)));
231 let mut wrong_version = v2(1, 0x11, &[0; 12]);
232 wrong_version[12] = 0x11;
233 assert!(matches!(parse(&wrong_version), Parsed::Invalid(_)));
234 let mut huge = V2_SIGNATURE.to_vec();
235 huge.extend_from_slice(&[0x21, 0x11, 0xFF, 0xFF]);
236 assert!(matches!(parse(&huge), Parsed::Invalid(_)));
237 }
238
239 #[test]
240 fn the_readers_stop_at_the_end_of_the_header() {
241 let mut input: &[u8] = b"PROXY TCP4 203.0.113.7 10.0.0.1 51234 443\r\nGET /";
242 assert_eq!(Some("203.0.113.7:51234".parse().unwrap()), read_blocking(&mut input).unwrap());
243 assert_eq!(b"GET /", input);
244 let mut input: &[u8] = b"GET / HTTP/1.1\r\n\r\n";
245 assert!(read_blocking(&mut input).is_err());
246 }
247
248 #[cfg(feature = "http2")]
249 #[tokio::test]
250 async fn the_async_reader_stops_at_the_end_of_the_header() {
251 let mut header = v2(1, 0x11, &[192, 0, 2, 1, 10, 0, 0, 1, 0x30, 0x39, 0x01, 0xBB]);
252 header.extend_from_slice(b"\x16\x03\x01");
253 let mut input: &[u8] = &header;
254 assert_eq!(Some("192.0.2.1:12345".parse().unwrap()), read_async(&mut input).await.unwrap());
255 assert_eq!(b"\x16\x03\x01", input);
256 }
257}