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