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_v2;
14use std::num::NonZeroUsize;
15
16mod parse;
17
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
19#[repr(u8)]
20enum Protocol {
21 Unspec = 0x00,
22 TcpV4 = 0x11,
23 UdpV4 = 0x12,
24 TcpV6 = 0x21,
25 UdpV6 = 0x22,
26 }
29
30#[derive(Debug, PartialEq, Eq, Clone, Copy)]
31#[repr(u8)]
32enum Command {
33 Local = 0x00,
34 Proxy = 0x01,
35}
36
37#[derive(Debug, PartialEq, Eq, Clone)]
38enum Address {
39 None,
40 V4 {
41 src: std::net::SocketAddrV4,
42 dst: std::net::SocketAddrV4,
43 },
44 V6 {
45 src: std::net::SocketAddrV6,
46 dst: std::net::SocketAddrV6,
47 },
48 }
53
54#[derive(Debug, Clone)]
55pub struct ProxyHdrV2 {
56 command: Command,
57 protocol: Protocol,
58 address: Address,
61}
62
63#[derive(Debug, Clone)]
64pub enum RemoteAddress {
65 Local,
66 Invalid,
67 TcpV4 {
68 src: std::net::SocketAddrV4,
69 dst: std::net::SocketAddrV4,
70 },
71 UdpV4 {
72 src: std::net::SocketAddrV4,
73 dst: std::net::SocketAddrV4,
74 },
75 TcpV6 {
76 src: std::net::SocketAddrV6,
77 dst: std::net::SocketAddrV6,
78 },
79 UdpV6 {
80 src: std::net::SocketAddrV6,
81 dst: std::net::SocketAddrV6,
82 },
83}
84
85#[derive(Debug)]
86pub enum Error {
87 Incomplete { need: NonZeroUsize },
88 Invalid,
89 UnableToComplete,
90}
91
92impl ProxyHdrV2 {
93 pub fn parse(input_data: &[u8]) -> Result<(usize, Self), Error> {
94 match parse_proxy_hdr_v2(input_data) {
95 Ok((remainder, hdr)) => {
96 let took = input_data.len() - remainder.len();
97 Ok((took, hdr))
98 }
99 Err(nom::Err::Incomplete(nom::Needed::Size(need))) => Err(Error::Incomplete { need }),
100 Err(nom::Err::Incomplete(nom::Needed::Unknown)) => Err(Error::UnableToComplete),
101
102 Err(nom::Err::Error(err)) => {
103 tracing::error!(?err);
104 Err(Error::Invalid)
105 }
106 Err(nom::Err::Failure(err)) => {
107 tracing::error!(?err);
108 Err(Error::Invalid)
109 }
110 }
111 }
112
113 pub fn to_remote_addr(self) -> RemoteAddress {
114 match (self.command, self.protocol, self.address) {
115 (Command::Local, _, _) => RemoteAddress::Local,
116 (Command::Proxy, Protocol::TcpV4, Address::V4 { src, dst }) => {
117 RemoteAddress::TcpV4 { src, dst }
118 }
119 (Command::Proxy, Protocol::UdpV4, Address::V4 { src, dst }) => {
120 RemoteAddress::UdpV4 { src, dst }
121 }
122 (Command::Proxy, Protocol::TcpV6, Address::V6 { src, dst }) => {
123 RemoteAddress::TcpV6 { src, dst }
124 }
125 (Command::Proxy, Protocol::UdpV6, Address::V6 { src, dst }) => {
126 RemoteAddress::UdpV6 { src, dst }
127 }
128 _ => RemoteAddress::Invalid,
129 }
130 }
131}
132
133#[cfg(feature = "tokio")]
134#[derive(Debug)]
135pub enum AsyncReadError {
136 Io(std::io::Error),
137 Invalid,
138 UnableToComplete,
139 RequestTooLarge,
140 InconsistentRead,
141}
142
143#[cfg(feature = "tokio")]
144impl ProxyHdrV2 {
145 pub async fn parse_from_read<S>(mut stream: S) -> Result<(S, ProxyHdrV2), AsyncReadError>
146 where
147 S: tokio::io::AsyncReadExt + std::marker::Unpin,
148 {
149 use tracing::{debug, error};
150
151 const HDR_SIZE_LIMIT: usize = 512;
152
153 let mut buf = vec![0; 16];
154
155 let mut took = stream
159 .read_exact(&mut buf)
160 .await
161 .map_err(AsyncReadError::Io)?;
162
163 match ProxyHdrV2::parse(&buf) {
164 Ok((_, hdr)) => return Ok((stream, hdr)),
166 Err(Error::Incomplete { need }) => {
168 let resize_to = buf.len() + usize::from(need);
169 if resize_to > HDR_SIZE_LIMIT {
172 error!(
173 "proxy header request was larger than {} bytes, refusing to proceed.",
174 HDR_SIZE_LIMIT
175 );
176 return Err(AsyncReadError::RequestTooLarge);
177 }
178 buf.resize(resize_to, 0);
179 }
180 Err(Error::Invalid) => {
181 debug!(proxy_binary_dump = %hex::encode(&buf));
182 error!("proxy header was invalid");
183 return Err(AsyncReadError::Invalid);
184 }
185 Err(Error::UnableToComplete) => {
186 debug!(proxy_binary_dump = %hex::encode(&buf));
187 error!("proxy header was incomplete");
188 return Err(AsyncReadError::UnableToComplete);
189 }
190 };
191
192 took += stream
194 .read_exact(&mut buf[16..])
195 .await
196 .map_err(AsyncReadError::Io)?;
197
198 match ProxyHdrV2::parse(&buf) {
199 Ok((hdr_took, _)) if hdr_took != took => {
200 error!("proxy header read an inconsistent amount from stream.");
202 return Err(AsyncReadError::InconsistentRead);
203 }
204 Ok((_, hdr)) =>
205 {
207 Ok((stream, hdr))
208 }
209 Err(Error::Incomplete { need: _ }) => {
210 error!("proxy header could not be read to the end.");
211 return Err(AsyncReadError::UnableToComplete);
212 }
213 Err(Error::Invalid) => {
214 debug!(proxy_binary_dump = %hex::encode(&buf));
215 error!("proxy header was invalid");
216 return Err(AsyncReadError::Invalid);
217 }
218 Err(Error::UnableToComplete) => {
219 debug!(proxy_binary_dump = %hex::encode(&buf));
220 error!("proxy header was incomplete");
221 return Err(AsyncReadError::UnableToComplete);
222 }
223 }
224 }
225}