1use tokio::io::AsyncWriteExt;
2
3use crate::accept::{PendingTunnel, ReplyContext, TunnelProtocol};
4use crate::error::SessionOpenError;
5use eggress_protocol_socks::socks5::server::SocksAddr;
6
7fn unspecified_ipv4() -> std::net::SocketAddr {
8 std::net::SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED), 0)
9}
10
11pub async fn send_tunnel_success(
13 pending: &mut PendingTunnel,
14 _bound_addr: Option<std::net::SocketAddr>,
15) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
16 match (&pending.protocol, &pending.reply_context) {
17 (TunnelProtocol::HttpConnect, ReplyContext::Http) => {
18 pending
19 .client
20 .write_all(b"HTTP/1.1 200 Connection Established\r\n\r\n")
21 .await?;
22 }
23 (TunnelProtocol::Http2, ReplyContext::Http2)
24 | (TunnelProtocol::Http3, ReplyContext::Http3)
25 | (TunnelProtocol::WebSocket, ReplyContext::WebSocket) => {}
26 (TunnelProtocol::Socks4, ReplyContext::Socks4) => {
27 eggress_protocol_socks::socks4::server::write_socks4_reply(
28 &mut pending.client,
29 eggress_protocol_socks::socks4::server::Socks4Status::Granted,
30 unspecified_ipv4(),
31 )
32 .await?;
33 }
34 (TunnelProtocol::Socks5, ReplyContext::Socks5) => {
35 let bind_addr = SocksAddr::IPv4([0, 0, 0, 0], 0);
36 eggress_protocol_socks::socks5::server::send_connect_reply(
37 &mut pending.client,
38 0x00,
39 &bind_addr,
40 )
41 .await?;
42 }
43 (TunnelProtocol::Shadowsocks, ReplyContext::Shadowsocks) => {
44 }
46 (TunnelProtocol::Trojan, ReplyContext::Trojan) => {
47 }
49 (TunnelProtocol::Raw, ReplyContext::Raw) => {}
50 _ => {
51 return Err("mismatched protocol and reply context".into());
52 }
53 }
54 Ok(())
55}
56
57pub async fn send_tunnel_failure(
59 pending: &mut PendingTunnel,
60 error: &SessionOpenError,
61) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
62 match (&pending.protocol, &pending.reply_context) {
63 (TunnelProtocol::HttpConnect, ReplyContext::Http) => {
64 let status = http_failure_status(error);
65 pending.client.write_all(status).await?;
66 }
67 (TunnelProtocol::Http2, ReplyContext::Http2)
68 | (TunnelProtocol::Http3, ReplyContext::Http3)
69 | (TunnelProtocol::WebSocket, ReplyContext::WebSocket) => {
70 pending.client.shutdown().await.ok();
71 }
72 (TunnelProtocol::Socks4, ReplyContext::Socks4) => {
73 eggress_protocol_socks::socks4::server::write_socks4_reply(
74 &mut pending.client,
75 eggress_protocol_socks::socks4::server::Socks4Status::Failed,
76 unspecified_ipv4(),
77 )
78 .await?;
79 }
80 (TunnelProtocol::Socks5, ReplyContext::Socks5) => {
81 let rep = socks5_failure_rep(error);
82 let bind_addr = SocksAddr::IPv4([0, 0, 0, 0], 0);
83 eggress_protocol_socks::socks5::server::send_connect_reply(
84 &mut pending.client,
85 rep,
86 &bind_addr,
87 )
88 .await?;
89 }
90 (TunnelProtocol::Shadowsocks, ReplyContext::Shadowsocks) => {
91 pending.client.shutdown().await.ok();
93 }
94 (TunnelProtocol::Trojan, ReplyContext::Trojan) => {
95 pending.client.shutdown().await.ok();
97 }
98 (TunnelProtocol::Raw, ReplyContext::Raw) => {
99 pending.client.shutdown().await.ok();
100 }
101 _ => {
102 return Err("mismatched protocol and reply context".into());
103 }
104 }
105 Ok(())
106}
107
108pub async fn send_http_forward_failure(
110 client: &mut eggress_core::BoxStream,
111 error: &SessionOpenError,
112) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
113 let status = http_failure_status(error);
114 client.write_all(status).await?;
115 Ok(())
116}
117
118pub async fn send_http_expectation_failed(
121 client: &mut eggress_core::BoxStream,
122) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
123 client
124 .write_all(b"HTTP/1.1 417 Expectation Failed\r\nConnection: close\r\n\r\n")
125 .await?;
126 client.shutdown().await?;
127 Ok(())
128}
129
130pub async fn send_http_upgrade_unsupported(
133 client: &mut eggress_core::BoxStream,
134) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
135 client
136 .write_all(b"HTTP/1.1 501 Not Implemented\r\nConnection: close\r\n\r\n")
137 .await?;
138 client.shutdown().await?;
139 Ok(())
140}
141
142fn http_failure_status(error: &SessionOpenError) -> &'static [u8] {
143 match error {
144 SessionOpenError::Timeout => b"HTTP/1.1 504 Gateway Timeout\r\nConnection: close\r\n\r\n",
145 SessionOpenError::PolicyDenied => b"HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n",
146 _ => b"HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\n\r\n",
147 }
148}
149
150fn socks5_failure_rep(error: &SessionOpenError) -> u8 {
151 match error {
152 SessionOpenError::Timeout => 0x06,
153 SessionOpenError::PolicyDenied => 0x02,
154 SessionOpenError::NetworkUnreachable => 0x03,
155 SessionOpenError::HostUnreachable | SessionOpenError::Dns => 0x04,
156 SessionOpenError::Refused => 0x05,
157 _ => 0x01,
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use crate::accept::{PendingTunnel, ReplyContext, TunnelProtocol};
165 use eggress_core::{TargetAddr, TargetHost};
166 use tokio::io::AsyncReadExt;
167
168 fn make_pending(
169 protocol: TunnelProtocol,
170 reply_context: ReplyContext,
171 ) -> (PendingTunnel, tokio::io::DuplexStream) {
172 let (client_stream, server_stream) = tokio::io::duplex(1024);
173 let pending = PendingTunnel {
174 target: TargetAddr {
175 host: TargetHost::Domain("example.com".into()),
176 port: 443,
177 },
178 client: Box::new(client_stream),
179 protocol,
180 reply_context,
181 identity: eggress_core::ClientIdentity::Anonymous,
182 };
183 (pending, server_stream)
184 }
185
186 #[tokio::test]
187 async fn test_send_tunnel_success_http() {
188 let (mut pending, mut server) =
189 make_pending(TunnelProtocol::HttpConnect, ReplyContext::Http);
190 send_tunnel_success(&mut pending, None).await.unwrap();
191
192 let mut response = vec![0u8; 1024];
193 let n = server.read(&mut response).await.unwrap();
194 let s = String::from_utf8_lossy(&response[..n]);
195 assert!(s.contains("200"));
196 }
197
198 #[tokio::test]
199 async fn test_send_tunnel_success_socks4() {
200 let (mut pending, mut server) = make_pending(TunnelProtocol::Socks4, ReplyContext::Socks4);
201 send_tunnel_success(&mut pending, None).await.unwrap();
202
203 let mut response = [0u8; 8];
204 server.read_exact(&mut response).await.unwrap();
205 assert_eq!(response[0], 0x00);
206 assert_eq!(response[1], 90); }
208
209 #[tokio::test]
210 async fn test_send_tunnel_success_socks5() {
211 let (mut pending, mut server) = make_pending(TunnelProtocol::Socks5, ReplyContext::Socks5);
212 send_tunnel_success(&mut pending, None).await.unwrap();
213
214 let mut response = [0u8; 10];
215 server.read_exact(&mut response).await.unwrap();
216 assert_eq!(response[0], 0x05);
217 assert_eq!(response[1], 0x00); }
219
220 #[tokio::test]
221 async fn test_send_tunnel_failure_http_timeout() {
222 let (mut pending, mut server) =
223 make_pending(TunnelProtocol::HttpConnect, ReplyContext::Http);
224 send_tunnel_failure(&mut pending, &SessionOpenError::Timeout)
225 .await
226 .unwrap();
227
228 let mut response = vec![0u8; 1024];
229 let n = server.read(&mut response).await.unwrap();
230 let s = String::from_utf8_lossy(&response[..n]);
231 assert!(s.contains("504"));
232 }
233
234 #[tokio::test]
235 async fn test_send_tunnel_failure_socks5_refused() {
236 let (mut pending, mut server) = make_pending(TunnelProtocol::Socks5, ReplyContext::Socks5);
237 send_tunnel_failure(&mut pending, &SessionOpenError::Refused)
238 .await
239 .unwrap();
240
241 let mut response = [0u8; 10];
242 server.read_exact(&mut response).await.unwrap();
243 assert_eq!(response[0], 0x05);
244 assert_eq!(response[1], 0x05); }
246
247 #[tokio::test]
248 async fn test_send_http_forward_failure() {
249 let (client_stream, mut server) = tokio::io::duplex(1024);
250 let mut client: eggress_core::BoxStream = Box::new(client_stream);
251 send_http_forward_failure(&mut client, &SessionOpenError::Refused)
252 .await
253 .unwrap();
254
255 let mut response = vec![0u8; 1024];
256 let n = server.read(&mut response).await.unwrap();
257 let s = String::from_utf8_lossy(&response[..n]);
258 assert!(s.contains("502"));
259 }
260}