1#![deny(warnings)]
2#![warn(unused_extern_crates)]
3#![deny(clippy::todo)]
4#![deny(clippy::unimplemented)]
5#![deny(clippy::unwrap_used)]
6#![deny(clippy::expect_used)]
7#![deny(clippy::panic)]
8#![deny(clippy::unreachable)]
9#![deny(clippy::await_holding_lock)]
10#![deny(clippy::needless_pass_by_value)]
11#![deny(clippy::trivially_copy_pass_by_ref)]
12
13use crate::parse::{parse_proxy_hdr_v1, parse_proxy_hdr_v2};
14use std::num::NonZeroUsize;
15
16const NZ_ONE: NonZeroUsize = NonZeroUsize::new(1).expect("Invalid compile time constant");
17
18mod parse;
19
20#[derive(Debug, PartialEq, Eq, Clone, Copy)]
21#[repr(u8)]
22enum Protocol {
23 Unspec = 0x00,
24 TcpV4 = 0x11,
25 UdpV4 = 0x12,
26 TcpV6 = 0x21,
27 UdpV6 = 0x22,
28 }
31
32#[derive(Debug, PartialEq, Eq, Clone, Copy)]
33#[repr(u8)]
34enum Command {
35 Local = 0x00,
36 Proxy = 0x01,
37}
38
39#[derive(Debug, PartialEq, Eq, Clone)]
40enum Address {
41 None,
42 V4 {
43 src: std::net::SocketAddrV4,
44 dst: std::net::SocketAddrV4,
45 },
46 V6 {
47 src: std::net::SocketAddrV6,
48 dst: std::net::SocketAddrV6,
49 },
50 }
55
56#[derive(Debug, Clone)]
57pub enum RemoteAddress {
58 Local,
59 Invalid,
60 TcpV4 {
61 src: std::net::SocketAddrV4,
62 dst: std::net::SocketAddrV4,
63 },
64 UdpV4 {
65 src: std::net::SocketAddrV4,
66 dst: std::net::SocketAddrV4,
67 },
68 TcpV6 {
69 src: std::net::SocketAddrV6,
70 dst: std::net::SocketAddrV6,
71 },
72 UdpV6 {
73 src: std::net::SocketAddrV6,
74 dst: std::net::SocketAddrV6,
75 },
76}
77
78#[derive(Debug)]
79pub enum Error {
80 Incomplete { need: NonZeroUsize },
81 Invalid,
82 UnableToComplete,
83}
84
85#[derive(Debug, Clone)]
86pub struct ProxyHdrV2 {
87 command: Command,
88 protocol: Protocol,
89 address: Address,
92}
93
94impl ProxyHdrV2 {
95 pub fn parse(input_data: &[u8]) -> Result<(usize, Self), Error> {
96 match parse_proxy_hdr_v2(input_data) {
97 Ok((remainder, hdr)) => {
98 let took = input_data.len() - remainder.len();
99 Ok((took, hdr))
100 }
101 Err(nom::Err::Incomplete(nom::Needed::Size(need))) => Err(Error::Incomplete { need }),
102 Err(nom::Err::Incomplete(nom::Needed::Unknown)) => Err(Error::UnableToComplete),
104
105 Err(nom::Err::Error(err)) => {
106 tracing::error!(?err);
107 Err(Error::Invalid)
108 }
109 Err(nom::Err::Failure(err)) => {
110 tracing::error!(?err);
111 Err(Error::Invalid)
112 }
113 }
114 }
115
116 pub fn to_remote_addr(self) -> RemoteAddress {
117 match (self.command, self.protocol, self.address) {
118 (Command::Local, _, _) => RemoteAddress::Local,
119 (Command::Proxy, Protocol::TcpV4, Address::V4 { src, dst }) => {
120 RemoteAddress::TcpV4 { src, dst }
121 }
122 (Command::Proxy, Protocol::UdpV4, Address::V4 { src, dst }) => {
123 RemoteAddress::UdpV4 { src, dst }
124 }
125 (Command::Proxy, Protocol::TcpV6, Address::V6 { src, dst }) => {
126 RemoteAddress::TcpV6 { src, dst }
127 }
128 (Command::Proxy, Protocol::UdpV6, Address::V6 { src, dst }) => {
129 RemoteAddress::UdpV6 { src, dst }
130 }
131 _ => RemoteAddress::Invalid,
132 }
133 }
134}
135
136#[derive(Debug, Clone)]
137pub struct ProxyHdrV1 {
138 protocol: Protocol,
139 address: Address,
140}
141
142impl ProxyHdrV1 {
143 pub fn parse(input_data: &[u8]) -> Result<(usize, Self), Error> {
144 match parse_proxy_hdr_v1(input_data) {
145 Ok((remainder, hdr)) => {
146 let took = input_data.len() - remainder.len();
147 Ok((took, hdr))
148 }
149 Err(nom::Err::Incomplete(nom::Needed::Size(need))) => Err(Error::Incomplete { need }),
150 Err(nom::Err::Incomplete(nom::Needed::Unknown)) => {
152 Err(Error::Incomplete { need: NZ_ONE })
153 }
154
155 Err(nom::Err::Error(err)) => {
156 tracing::error!(?err);
157 Err(Error::Invalid)
158 }
159 Err(nom::Err::Failure(err)) => {
160 tracing::error!(?err);
161 Err(Error::Invalid)
162 }
163 }
164 }
165
166 pub fn to_remote_addr(self) -> RemoteAddress {
167 match (self.protocol, self.address) {
168 (Protocol::TcpV4, Address::V4 { src, dst }) => RemoteAddress::TcpV4 { src, dst },
169 (Protocol::UdpV4, Address::V4 { src, dst }) => RemoteAddress::UdpV4 { src, dst },
170 (Protocol::TcpV6, Address::V6 { src, dst }) => RemoteAddress::TcpV6 { src, dst },
171 (Protocol::UdpV6, Address::V6 { src, dst }) => RemoteAddress::UdpV6 { src, dst },
172 _ => RemoteAddress::Invalid,
173 }
174 }
175}
176
177#[cfg(feature = "tokio")]
178#[derive(Debug)]
179pub enum AsyncReadError {
180 Io(std::io::Error),
181 Invalid,
182 UnableToComplete,
183 RequestTooLarge,
184 InconsistentRead,
185}
186
187#[cfg(feature = "tokio")]
188impl ProxyHdrV2 {
189 pub async fn parse_from_read<S>(mut stream: S) -> Result<(S, ProxyHdrV2), AsyncReadError>
190 where
191 S: tokio::io::AsyncReadExt + std::marker::Unpin,
192 {
193 use tracing::{debug, error};
194
195 const HDR_SIZE_LIMIT: usize = 512;
196
197 let mut buf = vec![0; 16];
198
199 let mut took = stream
203 .read_exact(&mut buf)
204 .await
205 .map_err(AsyncReadError::Io)?;
206
207 match ProxyHdrV2::parse(&buf) {
208 Ok((_, hdr)) => return Ok((stream, hdr)),
210 Err(Error::Incomplete { need }) => {
212 let resize_to = buf.len() + usize::from(need);
213 if resize_to > HDR_SIZE_LIMIT {
216 error!(
217 "proxy header request was larger than {} bytes, refusing to proceed.",
218 HDR_SIZE_LIMIT
219 );
220 return Err(AsyncReadError::RequestTooLarge);
221 }
222 buf.resize(resize_to, 0);
223 }
224 Err(Error::Invalid) => {
225 debug!(proxy_binary_dump = %hex::encode(&buf));
226 error!("proxy header was invalid");
227 return Err(AsyncReadError::Invalid);
228 }
229 Err(Error::UnableToComplete) => {
230 debug!(proxy_binary_dump = %hex::encode(&buf));
231 error!("proxy header was incomplete");
232 return Err(AsyncReadError::UnableToComplete);
233 }
234 };
235
236 took += stream
238 .read_exact(&mut buf[16..])
239 .await
240 .map_err(AsyncReadError::Io)?;
241
242 match ProxyHdrV2::parse(&buf) {
243 Ok((hdr_took, _)) if hdr_took != took => {
244 error!("proxy header read an inconsistent amount from stream.");
246 Err(AsyncReadError::InconsistentRead)
247 }
248 Ok((_, hdr)) =>
249 {
251 Ok((stream, hdr))
252 }
253 Err(Error::Incomplete { need: _ }) => {
254 error!("proxy header could not be read to the end.");
255 Err(AsyncReadError::UnableToComplete)
256 }
257 Err(Error::Invalid) => {
258 debug!(proxy_binary_dump = %hex::encode(&buf));
259 error!("proxy header was invalid");
260 Err(AsyncReadError::Invalid)
261 }
262 Err(Error::UnableToComplete) => {
263 debug!(proxy_binary_dump = %hex::encode(&buf));
264 error!("proxy header was incomplete");
265 Err(AsyncReadError::UnableToComplete)
266 }
267 }
268 }
269}