1use std::collections::HashMap;
2use std::fmt;
3use std::net::IpAddr;
4use std::pin::Pin;
5use std::sync::atomic::{AtomicU64, Ordering};
6use std::sync::{Arc, Mutex, MutexGuard};
7use std::task::{Context, Poll};
8use std::time::{Duration, Instant};
9
10static AUTH_CACHE_EPOCH: std::sync::LazyLock<Instant> = std::sync::LazyLock::new(Instant::now);
11
12use eggress_core::BoxStream;
13use eggress_core::{ClientIdentity, ProtocolId, TargetAddr, TargetHost};
14use tokio::io::{
15 AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt,
16};
17use zeroize::Zeroize;
18
19use crate::auth::parse_basic_auth;
20
21pub struct AuthReuseCache {
28 timeout: Duration,
29 entries: Mutex<HashMap<IpAddr, AuthReuseEntry>>,
30 max_entries: usize,
31 last_sweep_nanos: AtomicU64,
35}
36
37struct AuthReuseEntry {
38 identity: ClientIdentity,
39 last_authenticated: Instant,
40}
41
42impl AuthReuseCache {
43 pub const DEFAULT_MAX_ENTRIES: usize = 4096;
44
45 const SWEEP_INTERVAL: Duration = Duration::from_secs(30);
49
50 pub fn new(timeout: Duration) -> Self {
51 Self {
52 timeout,
53 entries: Mutex::new(HashMap::new()),
54 max_entries: Self::DEFAULT_MAX_ENTRIES,
55 last_sweep_nanos: AtomicU64::new(0),
56 }
57 }
58
59 fn lock_entries(&self) -> MutexGuard<'_, HashMap<IpAddr, AuthReuseEntry>> {
60 self.entries.lock().unwrap_or_else(|error| {
61 tracing::warn!("auth reuse cache was poisoned; clearing it: {error}");
62 let mut entries = error.into_inner();
63 entries.clear();
64 self.entries.clear_poison();
65 entries
66 })
67 }
68
69 pub fn lookup(&self, peer_ip: IpAddr) -> Option<ClientIdentity> {
70 let mut entries = self.lock_entries();
71 let entry = entries.get(&peer_ip)?;
72 if Instant::now().duration_since(entry.last_authenticated) > self.timeout {
73 entries.remove(&peer_ip);
74 return None;
75 }
76 Some(entry.identity.clone())
77 }
78
79 pub fn record(&self, peer_ip: IpAddr, identity: ClientIdentity) {
80 let mut entries = self.lock_entries();
81 let now = Instant::now();
82 if entries.len() >= self.max_entries {
89 let now_nanos = u64::try_from(
90 now.checked_duration_since(*AUTH_CACHE_EPOCH)
91 .unwrap_or(Duration::ZERO)
92 .as_nanos(),
93 )
94 .unwrap_or(u64::MAX);
95 let last_sweep = self.last_sweep_nanos.load(Ordering::Acquire);
96 let interval_nanos = u64::try_from(Self::SWEEP_INTERVAL.as_nanos()).unwrap_or(u64::MAX);
97 if now_nanos.saturating_sub(last_sweep) >= interval_nanos {
98 self.last_sweep_nanos.store(now_nanos, Ordering::Release);
99 entries.retain(|_, entry| {
100 now.duration_since(entry.last_authenticated) <= self.timeout
101 });
102 }
103 }
104 if entries.len() >= self.max_entries && !entries.contains_key(&peer_ip) {
105 if let Some(oldest) = entries
106 .iter()
107 .min_by_key(|(_, entry)| entry.last_authenticated)
108 .map(|(ip, _)| *ip)
109 {
110 entries.remove(&oldest);
111 }
112 }
113 entries.insert(
114 peer_ip,
115 AuthReuseEntry {
116 identity,
117 last_authenticated: now,
118 },
119 );
120 }
121
122 pub fn len(&self) -> usize {
123 self.lock_entries().len()
124 }
125
126 pub fn is_empty(&self) -> bool {
127 self.len() == 0
128 }
129}
130
131#[derive(Clone)]
132pub enum InboundAuthentication {
133 None,
134 UsernamePassword {
135 username: String,
136 password: String,
137 },
138 UsernamePasswordWithReuse {
139 username: String,
140 password: String,
141 reuse: Arc<AuthReuseCache>,
142 },
143}
144
145impl Drop for InboundAuthentication {
146 fn drop(&mut self) {
147 match self {
148 Self::None => {}
149 Self::UsernamePassword { password, .. }
150 | Self::UsernamePasswordWithReuse { password, .. } => password.zeroize(),
151 }
152 }
153}
154
155impl fmt::Debug for InboundAuthentication {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 match self {
158 InboundAuthentication::None => write!(f, "InboundAuthentication::None"),
159 InboundAuthentication::UsernamePassword { .. } => {
160 write!(f, "InboundAuthentication::UsernamePassword {{ .. }}")
161 }
162 InboundAuthentication::UsernamePasswordWithReuse { .. } => write!(
163 f,
164 "InboundAuthentication::UsernamePasswordWithReuse {{ .. }}"
165 ),
166 }
167 }
168}
169
170impl fmt::Display for InboundAuthentication {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 match self {
173 InboundAuthentication::None => write!(f, "none"),
174 InboundAuthentication::UsernamePassword { .. } => write!(f, "username/password"),
175 InboundAuthentication::UsernamePasswordWithReuse { .. } => {
176 write!(f, "username/password with IP reuse")
177 }
178 }
179 }
180}
181
182pub(crate) fn auth_credentials(
183 auth: &InboundAuthentication,
184) -> Option<(&str, &str, Option<&AuthReuseCache>)> {
185 match auth {
186 InboundAuthentication::None => None,
187 InboundAuthentication::UsernamePassword { username, password } => {
188 Some((username, password, None))
189 }
190 InboundAuthentication::UsernamePasswordWithReuse {
191 username,
192 password,
193 reuse,
194 } => Some((username, password, Some(reuse))),
195 }
196}
197
198pub(crate) fn cached_identity(
199 auth: &InboundAuthentication,
200 peer_ip: Option<IpAddr>,
201) -> Option<ClientIdentity> {
202 let (_, _, reuse) = auth_credentials(auth)?;
203 peer_ip.and_then(|ip| reuse.and_then(|cache| cache.lookup(ip)))
204}
205
206pub(crate) fn record_authenticated(
207 auth: &InboundAuthentication,
208 peer_ip: Option<IpAddr>,
209 identity: &ClientIdentity,
210) {
211 let Some((_, _, Some(cache))) = auth_credentials(auth) else {
212 return;
213 };
214 if let Some(ip) = peer_ip {
215 cache.record(ip, identity.clone());
216 }
217}
218
219#[derive(Debug, thiserror::Error)]
221pub enum AcceptError {
222 #[error("protocol error")]
223 Protocol(#[source] Box<dyn std::error::Error + Send + Sync>),
224 #[error("authentication failed")]
225 AuthenticationFailed,
226}
227
228impl From<Box<dyn std::error::Error + Send + Sync>> for AcceptError {
229 fn from(e: Box<dyn std::error::Error + Send + Sync>) -> Self {
230 AcceptError::Protocol(e)
231 }
232}
233
234pub enum AcceptedSession {
236 Tunnel(PendingTunnel),
237 HttpForward(PendingHttpForward),
238 UdpAssociate(PendingUdpAssociate),
239 Echo(BoxStream),
240}
241
242pub struct PendingTunnel {
245 pub target: TargetAddr,
246 pub client: BoxStream,
247 pub protocol: TunnelProtocol,
248 pub reply_context: ReplyContext,
249 pub identity: ClientIdentity,
250}
251
252pub struct PendingHttpForward {
254 pub target: TargetAddr,
255 pub client: BoxStream,
256 pub request: eggress_protocol_http::forward::ForwardRequest,
257 pub identity: ClientIdentity,
258}
259
260pub struct PendingUdpAssociate {
262 pub client: BoxStream,
263 pub protocol: TunnelProtocol,
264 pub identity: ClientIdentity,
265 pub client_hint: Option<TargetAddr>,
266}
267
268#[derive(Debug, Clone, Copy, PartialEq, Eq)]
270pub enum TunnelProtocol {
271 HttpConnect,
272 Http2,
273 Http3,
274 WebSocket,
275 Socks4,
276 Socks5,
277 Shadowsocks,
278 ShadowsocksR,
279 Trojan,
280 Raw,
281}
282
283pub enum ReplyContext {
285 Http,
286 Http2,
287 Http3,
288 WebSocket,
289 Socks4,
290 Socks5,
291 Shadowsocks,
292 Trojan,
293 Raw,
294}
295
296#[derive(Clone)]
298pub struct InboundShadowsocksConfig {
299 pub method: String,
300 pub password: String,
301 #[cfg(feature = "pproxy-legacy")]
302 pub auth_prefix: Option<Vec<u8>>,
303 #[cfg(feature = "pproxy-legacy")]
304 pub plugins: Vec<String>,
305}
306
307impl Drop for InboundShadowsocksConfig {
308 fn drop(&mut self) {
309 self.password.zeroize();
310 }
311}
312
313impl fmt::Debug for InboundShadowsocksConfig {
314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315 let mut ds = f.debug_struct("InboundShadowsocksConfig");
316 ds.field("method", &self.method);
317 ds.field("password", &"***");
318 #[cfg(feature = "pproxy-legacy")]
319 {
320 ds.field("auth_prefix", &self.auth_prefix);
321 ds.field("plugins", &self.plugins);
322 }
323 ds.finish()
324 }
325}
326
327#[derive(Clone)]
329pub struct InboundTrojanConfig {
330 pub password: String,
331 pub fallback: Option<String>,
335}
336
337impl Drop for InboundTrojanConfig {
338 fn drop(&mut self) {
339 self.password.zeroize();
340 }
341}
342
343impl fmt::Debug for InboundTrojanConfig {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 f.debug_struct("InboundTrojanConfig")
346 .field("password", &"***")
347 .field("fallback", &self.fallback)
348 .finish()
349 }
350}
351
352struct PrefixedStream {
354 prefix: std::io::Cursor<Vec<u8>>,
355 inner: BoxStream,
356}
357
358impl PrefixedStream {
359 fn new(prefix: Vec<u8>, inner: BoxStream) -> Self {
360 Self {
361 prefix: std::io::Cursor::new(prefix),
362 inner,
363 }
364 }
365}
366
367impl AsyncRead for PrefixedStream {
368 fn poll_read(
369 mut self: Pin<&mut Self>,
370 cx: &mut Context<'_>,
371 buf: &mut tokio::io::ReadBuf<'_>,
372 ) -> Poll<std::io::Result<()>> {
373 let pos = self.prefix.position() as usize;
374 let len = self.prefix.get_ref().len();
375 if pos < len {
376 let remaining = &self.prefix.get_ref()[pos..];
377 let to_copy = remaining.len().min(buf.remaining());
378 buf.put_slice(&remaining[..to_copy]);
379 self.prefix.set_position((pos + to_copy) as u64);
380 return Poll::Ready(Ok(()));
381 }
382 Pin::new(&mut self.inner).poll_read(cx, buf)
383 }
384}
385
386impl AsyncWrite for PrefixedStream {
387 fn poll_write(
388 mut self: Pin<&mut Self>,
389 cx: &mut Context<'_>,
390 buf: &[u8],
391 ) -> Poll<std::io::Result<usize>> {
392 Pin::new(&mut self.inner).poll_write(cx, buf)
393 }
394
395 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
396 Pin::new(&mut self.inner).poll_flush(cx)
397 }
398
399 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
400 Pin::new(&mut self.inner).poll_shutdown(cx)
401 }
402}
403
404pub async fn accept(
408 client: BoxStream,
409 protocols: &[ProtocolId],
410 auth: &InboundAuthentication,
411 shadowsocks_config: Option<&InboundShadowsocksConfig>,
412 #[cfg(feature = "extended")] shadowsocks_metrics: Option<
413 &std::sync::Arc<eggress_protocol_shadowsocks::ShadowsocksMetrics>,
414 >,
415 #[cfg(not(feature = "extended"))] shadowsocks_metrics: Option<&()>,
416 trojan_config: Option<&InboundTrojanConfig>,
417) -> Result<AcceptedSession, AcceptError> {
418 #[cfg(not(feature = "extended"))]
419 let _ = (shadowsocks_config, shadowsocks_metrics, trojan_config);
420 accept_with_fixed_target(
421 client,
422 protocols,
423 auth,
424 shadowsocks_config,
425 shadowsocks_metrics,
426 trojan_config,
427 None,
428 )
429 .await
430}
431
432pub async fn accept_with_fixed_target(
433 client: BoxStream,
434 protocols: &[ProtocolId],
435 auth: &InboundAuthentication,
436 shadowsocks_config: Option<&InboundShadowsocksConfig>,
437 #[cfg(feature = "extended")] shadowsocks_metrics: Option<
438 &std::sync::Arc<eggress_protocol_shadowsocks::ShadowsocksMetrics>,
439 >,
440 #[cfg(not(feature = "extended"))] shadowsocks_metrics: Option<&()>,
441 trojan_config: Option<&InboundTrojanConfig>,
442 fixed_target: Option<&TargetAddr>,
443) -> Result<AcceptedSession, AcceptError> {
444 accept_with_fixed_target_for_peer(
445 client,
446 protocols,
447 auth,
448 shadowsocks_config,
449 shadowsocks_metrics,
450 trojan_config,
451 fixed_target,
452 None,
453 )
454 .await
455}
456
457#[allow(clippy::too_many_arguments)]
458pub async fn accept_with_fixed_target_for_peer(
459 client: BoxStream,
460 protocols: &[ProtocolId],
461 auth: &InboundAuthentication,
462 shadowsocks_config: Option<&InboundShadowsocksConfig>,
463 #[cfg(feature = "extended")] shadowsocks_metrics: Option<
464 &std::sync::Arc<eggress_protocol_shadowsocks::ShadowsocksMetrics>,
465 >,
466 #[cfg(not(feature = "extended"))] shadowsocks_metrics: Option<&()>,
467 trojan_config: Option<&InboundTrojanConfig>,
468 fixed_target: Option<&TargetAddr>,
469 peer_ip: Option<IpAddr>,
470) -> Result<AcceptedSession, AcceptError> {
471 #[cfg(not(feature = "extended"))]
472 let _ = (shadowsocks_config, shadowsocks_metrics, trojan_config);
473 #[cfg(feature = "extended")]
474 #[inline]
475 fn shadows_metrics(
476 m: Option<&std::sync::Arc<eggress_protocol_shadowsocks::ShadowsocksMetrics>>,
477 ) -> Option<std::sync::Arc<eggress_protocol_shadowsocks::ShadowsocksMetrics>> {
478 m.cloned()
479 }
480 let mut stream = client;
481 if protocols.len() == 1 && protocols.contains(&ProtocolId::Echo) {
482 return Ok(AcceptedSession::Echo(stream));
483 }
484 if protocols.len() == 1 && protocols.contains(&ProtocolId::Raw) {
485 let target = fixed_target
486 .cloned()
487 .ok_or_else(|| AcceptError::Protocol("raw listener requires fixed_target".into()))?;
488 return Ok(AcceptedSession::Tunnel(PendingTunnel {
489 target,
490 client: stream,
491 protocol: TunnelProtocol::Raw,
492 reply_context: ReplyContext::Raw,
493 identity: ClientIdentity::Anonymous,
494 }));
495 }
496 let mut first_byte = [0u8; 1];
497 stream
498 .read_exact(&mut first_byte)
499 .await
500 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
501
502 let has_socks5 = protocols.contains(&ProtocolId::Socks5);
503 let has_socks4 = protocols.contains(&ProtocolId::Socks4);
504 let has_http = protocols.contains(&ProtocolId::Http);
505
506 if first_byte[0] == 0x05 && has_socks5 {
508 tracing::trace!(
509 "detected protocol: socks5 (first_byte={:#04x})",
510 first_byte[0]
511 );
512 let stream: BoxStream = Box::new(PrefixedStream::new(first_byte.to_vec(), stream));
513 return accept_socks5(stream, auth, peer_ip).await;
514 }
515
516 if first_byte[0] == 0x04 && has_socks4 {
518 tracing::trace!(
519 "detected protocol: socks4 (first_byte={:#04x})",
520 first_byte[0]
521 );
522 let stream: BoxStream = Box::new(PrefixedStream::new(first_byte.to_vec(), stream));
523 return accept_socks4(stream, auth, peer_ip).await;
524 }
525
526 if has_http {
528 let mut prefix = vec![first_byte[0]];
530 let mut buf = [0u8; 32];
531 let n = stream
532 .read(&mut buf)
533 .await
534 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
535 prefix.extend_from_slice(&buf[..n]);
536
537 match detect_http_method(&prefix) {
538 DetectResult::Match => {
539 tracing::trace!(
540 "detected protocol: http (prefix={:?})",
541 &prefix[..prefix.len().min(16)]
542 );
543 let stream: BoxStream = Box::new(PrefixedStream::new(prefix, stream));
544 return accept_http(stream, auth, peer_ip).await;
545 }
546 DetectResult::NeedMore => {
547 let mut more = [0u8; 32];
549 let n = stream
550 .read(&mut more)
551 .await
552 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
553 prefix.extend_from_slice(&more[..n]);
554 match detect_http_method(&prefix) {
555 DetectResult::Match => {
556 tracing::trace!(
557 "detected protocol: http (prefix={:?})",
558 &prefix[..prefix.len().min(16)]
559 );
560 let stream: BoxStream = Box::new(PrefixedStream::new(prefix, stream));
561 return accept_http(stream, auth, peer_ip).await;
562 }
563 DetectResult::NoMatch => {
564 return Err(AcceptError::Protocol(
565 "no matching protocol for listener".into(),
566 ));
567 }
568 DetectResult::NeedMore => {
569 return Err(AcceptError::Protocol(
570 "no matching protocol for listener".into(),
571 ));
572 }
573 }
574 }
575 DetectResult::NoMatch => {
576 return Err(AcceptError::Protocol(
577 "no matching protocol for listener".into(),
578 ));
579 }
580 }
581 }
582
583 #[cfg(feature = "extended")]
585 if protocols.len() == 1 && protocols.contains(&ProtocolId::Shadowsocks) {
586 if let Some(ss_config) = shadowsocks_config {
587 let stream: BoxStream = Box::new(PrefixedStream::new(first_byte.to_vec(), stream));
588 match eggress_protocol_shadowsocks::CipherMethod::parse_method(&ss_config.method) {
589 Ok(method) => {
590 let (ss_stream, target_addr) =
591 eggress_protocol_shadowsocks::tcp::shadowsocks_accept(
592 stream,
593 &ss_config.password,
594 method,
595 shadows_metrics(shadowsocks_metrics),
596 )
597 .await
598 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
599
600 return Ok(AcceptedSession::Tunnel(PendingTunnel {
601 target: target_addr,
602 client: ss_stream,
603 protocol: TunnelProtocol::Shadowsocks,
604 reply_context: ReplyContext::Shadowsocks,
605 identity: ClientIdentity::Anonymous,
606 }));
607 }
608 Err(modern_error) => {
609 #[cfg(feature = "legacy-crypto")]
610 if let Ok(legacy_method) =
611 eggress_protocol_shadowsocks::legacy::LegacyMethod::parse(&ss_config.method)
612 {
613 let (ss_stream, target_addr) =
614 eggress_protocol_shadowsocks::legacy::legacy_accept(
615 stream,
616 legacy_method,
617 ss_config.password.as_bytes(),
618 )
619 .await
620 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
621
622 return Ok(AcceptedSession::Tunnel(PendingTunnel {
623 target: target_addr,
624 client: ss_stream,
625 protocol: TunnelProtocol::Shadowsocks,
626 reply_context: ReplyContext::Shadowsocks,
627 identity: ClientIdentity::Anonymous,
628 }));
629 }
630 if let Some(m) = shadowsocks_metrics {
631 m.record_tcp_unsupported_method_reject();
632 }
633 return Err(AcceptError::Protocol(Box::new(modern_error)));
634 }
635 }
636 }
637 return Err(AcceptError::Protocol(
638 "shadowsocks listener requires shadowsocks config".into(),
639 ));
640 }
641
642 #[cfg(feature = "pproxy-legacy")]
643 if protocols.len() == 1 && protocols.contains(&ProtocolId::ShadowsocksR) {
644 let ssr_config = shadowsocks_config
645 .filter(|config| config.method == "ssr")
646 .ok_or_else(|| AcceptError::Protocol("SSR listener requires SSR config".into()))?;
647 let plugins =
648 eggress_protocol_shadowsocks::compat::plugin::parse_plugins(&ssr_config.plugins)
649 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
650 let stream: BoxStream = Box::new(PrefixedStream::new(first_byte.to_vec(), stream));
651 let (ss_stream, target_addr) = eggress_protocol_shadowsocks::compat::ssr::ssr_accept(
652 stream,
653 &eggress_protocol_shadowsocks::compat::ssr::SsrConfig {
654 auth_prefix: ssr_config.auth_prefix.clone(),
655 plugins,
656 },
657 )
658 .await
659 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
660 return Ok(AcceptedSession::Tunnel(PendingTunnel {
661 target: target_addr,
662 client: ss_stream,
663 protocol: TunnelProtocol::ShadowsocksR,
664 reply_context: ReplyContext::Shadowsocks,
665 identity: ClientIdentity::Anonymous,
666 }));
667 }
668 #[cfg(not(feature = "pproxy-legacy"))]
669 if protocols.len() == 1 && protocols.contains(&ProtocolId::ShadowsocksR) {
670 return Err(AcceptError::Protocol(
671 "SSR compatibility support is not included in this build".into(),
672 ));
673 }
674 #[cfg(not(feature = "extended"))]
675 if protocols.len() == 1 && protocols.contains(&ProtocolId::Shadowsocks) {
676 return Err(AcceptError::Protocol(
677 "shadowsocks support not included in this build".into(),
678 ));
679 }
680
681 #[cfg(feature = "extended")]
683 if protocols.len() == 1 && protocols.contains(&ProtocolId::Trojan) {
684 if let Some(trojan_cfg) = trojan_config {
685 use tokio::io::AsyncReadExt;
686
687 let mut hash_prefix = [0u8; 56];
691 hash_prefix[0] = first_byte[0];
695 stream
696 .read_exact(&mut hash_prefix[1..])
697 .await
698 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
699
700 let password_matches =
701 eggress_protocol_trojan::trojan_check_password(&hash_prefix, &trojan_cfg.password);
702
703 if password_matches {
704 let prefixed = PrefixedStream::new(hash_prefix.to_vec(), stream);
706 let boxed: BoxStream = Box::new(prefixed);
707 let (trojan_stream, result) =
708 eggress_protocol_trojan::trojan_accept(boxed, &trojan_cfg.password)
709 .await
710 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
711
712 return Ok(AcceptedSession::Tunnel(PendingTunnel {
713 target: result.target,
714 client: trojan_stream,
715 protocol: TunnelProtocol::Trojan,
716 reply_context: ReplyContext::Trojan,
717 identity: ClientIdentity::Anonymous,
718 }));
719 }
720
721 if let Some(ref fallback_target) = trojan_cfg.fallback {
723 let mut delimiter = [0u8; 2];
728 stream
729 .read_exact(&mut delimiter)
730 .await
731 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
732 if delimiter != *b"\r\n" {
733 tracing::warn!(?delimiter, "trojan fallback delimiter was not CRLF");
734 }
735 let target: TargetAddr = fallback_target.parse().map_err(|e: String| {
736 AcceptError::Protocol(format!("invalid trojan fallback address: {e}").into())
737 })?;
738 tracing::debug!("trojan auth failed, falling back to {}", fallback_target);
739 return Ok(AcceptedSession::Tunnel(PendingTunnel {
740 target,
741 client: stream,
742 protocol: TunnelProtocol::Trojan,
743 reply_context: ReplyContext::Trojan,
744 identity: ClientIdentity::Anonymous,
745 }));
746 }
747
748 return Err(AcceptError::AuthenticationFailed);
749 }
750 return Err(AcceptError::Protocol(
751 "trojan listener requires trojan config".into(),
752 ));
753 }
754 #[cfg(not(feature = "extended"))]
755 if protocols.len() == 1 && protocols.contains(&ProtocolId::Trojan) {
756 return Err(AcceptError::Protocol(
757 "trojan support not included in this build".into(),
758 ));
759 }
760
761 Err(AcceptError::Protocol(
762 "no matching protocol for listener".into(),
763 ))
764}
765
766enum DetectResult {
767 Match,
768 NeedMore,
769 NoMatch,
770}
771
772fn detect_http_method(prefix: &[u8]) -> DetectResult {
773 if let Some(space_pos) = prefix.iter().position(|&b| b == b' ') {
775 let method_token = &prefix[..space_pos];
776 if method_token.is_empty() || method_token.len() > 16 {
777 return DetectResult::NoMatch;
778 }
779 let is_valid_method = method_token
782 .iter()
783 .all(|&b| b.is_ascii_uppercase() || b == b'-' || b.is_ascii_lowercase());
784 if is_valid_method {
785 DetectResult::Match
786 } else {
787 DetectResult::NoMatch
788 }
789 } else {
790 if prefix.len() > 16 {
792 return DetectResult::NoMatch;
793 }
794 let is_valid_prefix = prefix
796 .iter()
797 .all(|&b| b.is_ascii_uppercase() || b == b'-' || b.is_ascii_lowercase());
798 if is_valid_prefix {
799 DetectResult::NeedMore
800 } else {
801 DetectResult::NoMatch
802 }
803 }
804}
805
806async fn accept_socks5(
807 stream: BoxStream,
808 auth: &InboundAuthentication,
809 peer_ip: Option<IpAddr>,
810) -> Result<AcceptedSession, AcceptError> {
811 use eggress_protocol_socks::socks5::server::{
812 read_auth_request, read_method_negotiation, read_socks5_request, send_auth_response,
813 send_connect_reply, Socks5Command, CMD_BIND, REP_COMMAND_NOT_SUPPORTED,
814 };
815
816 let (mut reader, mut writer) = tokio::io::split(stream);
817 let methods = read_method_negotiation(&mut reader)
818 .await
819 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
820
821 const AUTH_NONE: u8 = 0x00;
823 const AUTH_USERNAME_PASSWORD: u8 = 0x02;
824 const AUTH_NO_ACCEPTABLE: u8 = 0xFF;
825
826 let cached = cached_identity(auth, peer_ip);
827 let selected_method = match (auth_credentials(auth), cached.is_some()) {
828 (None, _) | (Some(_), true) if methods.contains(&AUTH_NONE) => AUTH_NONE,
829 (Some(_), _) if methods.contains(&AUTH_USERNAME_PASSWORD) => AUTH_USERNAME_PASSWORD,
830 (None, _) => AUTH_NO_ACCEPTABLE,
831 (Some(_), _) => AUTH_NO_ACCEPTABLE,
832 };
833
834 use tokio::io::AsyncWriteExt;
836 writer
837 .write_all(&[0x05, selected_method])
838 .await
839 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
840 writer
841 .flush()
842 .await
843 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
844
845 if selected_method == AUTH_NO_ACCEPTABLE {
846 return Err(AcceptError::Protocol(Box::new(
847 eggress_protocol_socks::error::Socks5Error::MethodNegotiationFailed,
848 )));
849 }
850
851 let mut identity = cached.unwrap_or(ClientIdentity::Anonymous);
853 if selected_method == AUTH_USERNAME_PASSWORD {
854 let (username, password, _) = match auth_credentials(auth) {
855 Some(creds) => creds,
856 None => return Err(AcceptError::AuthenticationFailed),
857 };
858 match read_auth_request(&mut reader, username, password).await {
859 Ok(client_username) => {
860 identity = ClientIdentity::Username(client_username);
861 record_authenticated(auth, peer_ip, &identity);
862 send_auth_response(&mut writer, true)
863 .await
864 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
865 }
866 Err(_) => {
867 let _ = send_auth_response(&mut writer, false).await;
868 return Err(AcceptError::AuthenticationFailed);
869 }
870 }
871 }
872
873 let (command, socks_addr) = read_socks5_request(&mut reader)
874 .await
875 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
876
877 match command {
878 Socks5Command::Connect => {
879 let target = socks_addr_to_target(&socks_addr);
880 let stream: BoxStream = Box::new(tokio::io::join(reader, writer));
881
882 Ok(AcceptedSession::Tunnel(PendingTunnel {
883 target,
884 client: stream,
885 protocol: TunnelProtocol::Socks5,
886 reply_context: ReplyContext::Socks5,
887 identity,
888 }))
889 }
890 Socks5Command::UdpAssociate => {
891 let client_hint = Some(socks_addr_to_target(&socks_addr));
892 let stream: BoxStream = Box::new(tokio::io::join(reader, writer));
893
894 Ok(AcceptedSession::UdpAssociate(PendingUdpAssociate {
895 client: stream,
896 protocol: TunnelProtocol::Socks5,
897 identity,
898 client_hint,
899 }))
900 }
901 Socks5Command::Bind => {
902 let _ = send_connect_reply(&mut writer, REP_COMMAND_NOT_SUPPORTED, &socks_addr).await;
903 Err(AcceptError::Protocol(Box::new(
904 eggress_protocol_socks::error::Socks5Error::UnsupportedCommand(CMD_BIND),
905 )))
906 }
907 }
908}
909
910async fn accept_socks4(
911 stream: BoxStream,
912 auth: &InboundAuthentication,
913 peer_ip: Option<IpAddr>,
914) -> Result<AcceptedSession, AcceptError> {
915 use eggress_protocol_socks::socks4::server::read_socks4_request;
916
917 let (mut reader, writer) = tokio::io::split(stream);
918 let request = read_socks4_request(&mut reader)
919 .await
920 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
921 let target = if let Some(ref domain) = request.domain {
922 TargetAddr {
923 host: TargetHost::Domain(domain.clone()),
924 port: request.port,
925 }
926 } else {
927 TargetAddr {
928 host: TargetHost::Ip(request.addr.ip()),
929 port: request.addr.port(),
930 }
931 };
932 let cached = cached_identity(auth, peer_ip);
933 if cached.is_none() {
934 if let Some((username, _, _)) = auth_credentials(auth) {
935 use subtle::ConstantTimeEq;
936 let user_ok: bool = request.user_id.as_bytes().ct_eq(username.as_bytes()).into();
937 if !user_ok {
938 return Err(AcceptError::AuthenticationFailed);
939 }
940 }
941 }
942 let identity = cached.unwrap_or({
943 if request.user_id.is_empty() {
944 ClientIdentity::Anonymous
945 } else {
946 ClientIdentity::Opaque(request.user_id)
947 }
948 });
949 if matches!(
950 identity,
951 ClientIdentity::Opaque(_) | ClientIdentity::Username(_)
952 ) {
953 record_authenticated(auth, peer_ip, &identity);
954 }
955 let stream: BoxStream = Box::new(tokio::io::join(reader, writer));
956
957 Ok(AcceptedSession::Tunnel(PendingTunnel {
958 target,
959 client: stream,
960 protocol: TunnelProtocol::Socks4,
961 reply_context: ReplyContext::Socks4,
962 identity,
963 }))
964}
965
966async fn accept_http(
967 stream: BoxStream,
968 auth: &InboundAuthentication,
969 peer_ip: Option<IpAddr>,
970) -> Result<AcceptedSession, AcceptError> {
971 let mut stream = tokio::io::BufReader::new(stream);
974 let head_buf = read_http_head(&mut stream).await?;
975
976 let method = {
977 let request_line = String::from_utf8_lossy(&head_buf);
978 request_line
979 .split_whitespace()
980 .next()
981 .unwrap_or("")
982 .to_ascii_lowercase()
983 };
984
985 if method == "connect" {
986 let request = parse_connect_request(&head_buf, &mut stream, auth, peer_ip).await?;
987 Ok(AcceptedSession::Tunnel(PendingTunnel {
988 target: request.target,
989 client: Box::new(stream),
990 protocol: TunnelProtocol::HttpConnect,
991 reply_context: ReplyContext::Http,
992 identity: request.identity,
993 }))
994 } else {
995 let head_str = String::from_utf8_lossy(&head_buf);
997 let cached = cached_identity(auth, peer_ip);
998 let proxy_auth = if cached.is_some() {
999 None
1000 } else if let Some((username, password, _)) = auth_credentials(auth) {
1001 let mut found_auth = None;
1002 for line in head_str.split("\r\n") {
1003 if let Some((name, value)) = parse_header_line_str(line) {
1004 if name.eq_ignore_ascii_case("Proxy-Authorization") {
1005 found_auth = parse_basic_auth(&value);
1006 break;
1007 }
1008 }
1009 }
1010 match found_auth {
1011 Some((user, pass)) => {
1012 use subtle::ConstantTimeEq;
1013 let user_ok: bool = user.as_bytes().ct_eq(username.as_bytes()).into();
1014 let pass_ok: bool = pass.as_bytes().ct_eq(password.as_bytes()).into();
1015 if !user_ok || !pass_ok {
1016 let _ = write_proxy_auth_required(&mut stream).await;
1017 return Err(AcceptError::AuthenticationFailed);
1018 }
1019 Some((user, pass))
1020 }
1021 None => {
1022 let _ = write_proxy_auth_required(&mut stream).await;
1023 return Err(AcceptError::AuthenticationFailed);
1024 }
1025 }
1026 } else {
1027 None
1028 };
1029 let identity = cached.unwrap_or_else(|| match &proxy_auth {
1030 Some((user, _)) => ClientIdentity::Username(user.clone()),
1031 None => ClientIdentity::Anonymous,
1032 });
1033 if matches!(identity, ClientIdentity::Username(_)) {
1034 record_authenticated(auth, peer_ip, &identity);
1035 }
1036 let _ = proxy_auth; let stream: BoxStream = Box::new(PrefixedStream::new(head_buf, Box::new(stream)));
1040
1041 let (request, client_stream) = eggress_protocol_http::forward_request(stream)
1042 .await
1043 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
1044
1045 let target = request.target.clone();
1046 Ok(AcceptedSession::HttpForward(PendingHttpForward {
1047 target,
1048 client: client_stream,
1049 request,
1050 identity,
1051 }))
1052 }
1053}
1054
1055struct ConnectRequest {
1056 target: TargetAddr,
1057 identity: ClientIdentity,
1058}
1059
1060async fn parse_connect_request<W: AsyncWrite + Unpin>(
1061 head_buf: &[u8],
1062 stream: &mut W,
1063 auth: &InboundAuthentication,
1064 peer_ip: Option<IpAddr>,
1065) -> Result<ConnectRequest, AcceptError> {
1066 let head_str = String::from_utf8_lossy(head_buf);
1067 let mut lines = head_str.split("\r\n");
1068
1069 let request_line = lines.next().ok_or_else(|| {
1070 AcceptError::Protocol(
1071 eggress_protocol_http::HttpError::MalformedRequest("empty request".into()).into(),
1072 )
1073 })?;
1074
1075 let parts: Vec<&str> = request_line.split_whitespace().collect();
1076 if parts.len() != 3 {
1077 return Err(AcceptError::Protocol(
1078 eggress_protocol_http::HttpError::MalformedRequest(format!(
1079 "expected 3 parts in request line, got {}",
1080 parts.len()
1081 ))
1082 .into(),
1083 ));
1084 }
1085
1086 let authority = parts[1];
1087 let target = parse_authority(authority)?;
1088
1089 let mut proxy_auth = None;
1091 let mut parsed_username: Option<String> = None;
1092 for line in lines {
1093 if line.is_empty() {
1094 break;
1095 }
1096 if let Some((name, value)) = parse_header_line_str(line) {
1097 if name.eq_ignore_ascii_case("Proxy-Authorization") {
1098 proxy_auth = parse_basic_auth(&value);
1099 if let Some((user, _)) = &proxy_auth {
1100 parsed_username = Some(user.clone());
1101 }
1102 }
1103 }
1104 }
1105
1106 let cached = cached_identity(auth, peer_ip);
1110 if cached.is_none() {
1111 if let Some((username, password, _)) = auth_credentials(auth) {
1112 match proxy_auth {
1113 Some((user, pass)) => {
1114 use subtle::ConstantTimeEq;
1115 let user_ok: bool = user.as_bytes().ct_eq(username.as_bytes()).into();
1116 let pass_ok: bool = pass.as_bytes().ct_eq(password.as_bytes()).into();
1117 if !user_ok || !pass_ok {
1118 let _ = write_proxy_auth_required(stream).await;
1119 return Err(AcceptError::AuthenticationFailed);
1120 }
1121 }
1122 None => {
1123 let _ = write_proxy_auth_required(stream).await;
1124 return Err(AcceptError::AuthenticationFailed);
1125 }
1126 }
1127 }
1128 }
1129
1130 let identity = cached.unwrap_or(match parsed_username {
1131 Some(user) => ClientIdentity::Username(user),
1132 None => ClientIdentity::Anonymous,
1133 });
1134 if matches!(identity, ClientIdentity::Username(_)) {
1135 record_authenticated(auth, peer_ip, &identity);
1136 }
1137
1138 Ok(ConnectRequest { target, identity })
1139}
1140
1141async fn read_http_head<R: AsyncBufRead + Unpin>(reader: &mut R) -> Result<Vec<u8>, AcceptError> {
1142 let mut head_buf = Vec::with_capacity(1024);
1143 let mut line = Vec::with_capacity(256);
1144 let mut header_count = 0;
1145
1146 loop {
1147 if head_buf.len() >= MAX_HEAD_SIZE {
1148 return Err(AcceptError::Protocol(
1149 eggress_protocol_http::HttpError::HeaderTooLarge.into(),
1150 ));
1151 }
1152
1153 line.clear();
1154 let remaining = MAX_HEAD_SIZE - head_buf.len();
1155 let n = reader
1156 .take((remaining + 1) as u64)
1157 .read_until(b'\n', &mut line)
1158 .await
1159 .map_err(|e| AcceptError::Protocol(Box::new(e)))?;
1160 if n == 0 {
1161 return Err(AcceptError::Protocol(
1162 eggress_protocol_http::HttpError::MalformedRequest(
1163 "unexpected EOF reading request".into(),
1164 )
1165 .into(),
1166 ));
1167 }
1168
1169 if head_buf.len() + line.len() > MAX_HEAD_SIZE {
1170 return Err(AcceptError::Protocol(
1171 eggress_protocol_http::HttpError::HeaderTooLarge.into(),
1172 ));
1173 }
1174 head_buf.extend_from_slice(&line);
1175
1176 if line.ends_with(b"\r\n") {
1177 header_count += 1;
1178 if header_count > MAX_HEADER_LINES {
1179 return Err(AcceptError::Protocol(
1180 eggress_protocol_http::HttpError::TooManyHeaders.into(),
1181 ));
1182 }
1183 }
1184 if head_buf.ends_with(b"\r\n\r\n") {
1185 return Ok(head_buf);
1186 }
1187 }
1188}
1189
1190fn parse_authority(
1191 authority: &str,
1192) -> Result<TargetAddr, Box<dyn std::error::Error + Send + Sync>> {
1193 if authority.starts_with('[') {
1194 let bracket_end = authority.find(']').ok_or_else(|| {
1195 eggress_protocol_http::HttpError::TargetParseError(
1196 "unclosed bracket in IPv6 address".into(),
1197 )
1198 })?;
1199
1200 let ip_str = &authority[1..bracket_end];
1201 let ip: std::net::IpAddr = ip_str.parse().map_err(|e| {
1202 eggress_protocol_http::HttpError::TargetParseError(format!("invalid IPv6 address: {e}"))
1203 })?;
1204
1205 let port_str = authority.get(bracket_end + 2..).ok_or_else(|| {
1206 eggress_protocol_http::HttpError::TargetParseError("missing port".into())
1207 })?;
1208
1209 if authority
1210 .as_bytes()
1211 .get(bracket_end + 1)
1212 .is_none_or(|&b| b != b':')
1213 {
1214 return Err(eggress_protocol_http::HttpError::TargetParseError(
1215 "expected ':' between IPv6 address and port".into(),
1216 )
1217 .into());
1218 }
1219
1220 let port: u16 = port_str.parse().map_err(|e| {
1221 eggress_protocol_http::HttpError::TargetParseError(format!("invalid port: {e}"))
1222 })?;
1223
1224 return Ok(TargetAddr {
1225 host: TargetHost::Ip(ip),
1226 port,
1227 });
1228 }
1229
1230 let colon_pos = authority.rfind(':').ok_or_else(|| {
1231 eggress_protocol_http::HttpError::TargetParseError("missing port in authority".into())
1232 })?;
1233
1234 let host_str = &authority[..colon_pos];
1235 let port_str = &authority[colon_pos + 1..];
1236
1237 let port: u16 = port_str.parse().map_err(|e| {
1238 eggress_protocol_http::HttpError::TargetParseError(format!("invalid port: {e}"))
1239 })?;
1240
1241 if let Ok(ip) = host_str.parse::<std::net::IpAddr>() {
1242 return Ok(TargetAddr {
1243 host: TargetHost::Ip(ip),
1244 port,
1245 });
1246 }
1247
1248 if host_str.is_empty() {
1249 return Err(eggress_protocol_http::HttpError::TargetParseError("empty host".into()).into());
1250 }
1251
1252 Ok(TargetAddr {
1253 host: TargetHost::Domain(host_str.to_string()),
1254 port,
1255 })
1256}
1257
1258fn socks_addr_to_target(addr: &eggress_protocol_socks::socks5::server::SocksAddr) -> TargetAddr {
1259 use eggress_protocol_socks::socks5::server::SocksAddr;
1260 match addr {
1261 SocksAddr::IPv4(octets, port) => TargetAddr {
1262 host: TargetHost::Ip(std::net::IpAddr::V4((*octets).into())),
1263 port: *port,
1264 },
1265 SocksAddr::IPv6(octets, port) => TargetAddr {
1266 host: TargetHost::Ip(std::net::IpAddr::V6((*octets).into())),
1267 port: *port,
1268 },
1269 SocksAddr::Domain(domain, port) => TargetAddr {
1270 host: TargetHost::Domain(domain.clone()),
1271 port: *port,
1272 },
1273 }
1274}
1275
1276const MAX_HEAD_SIZE: usize = 32 * 1024;
1278
1279const MAX_HEADER_LINES: usize = 128;
1281
1282fn parse_header_line_str(line: &str) -> Option<(String, String)> {
1284 let colon_pos = line.find(':')?;
1285 let name = line[..colon_pos].trim().to_string();
1286 let value = line[colon_pos + 1..].trim().to_string();
1287 Some((name, value))
1288}
1289
1290async fn write_proxy_auth_required<W: AsyncWrite + Unpin>(
1293 stream: &mut W,
1294) -> Result<(), std::io::Error> {
1295 let response = b"HTTP/1.1 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic realm=\"eggress\"\r\nContent-Length: 0\r\nConnection: close\r\n\r\n";
1296 stream.write_all(response).await?;
1297 stream.flush().await?;
1298 Ok(())
1299}
1300
1301#[cfg(test)]
1302mod tests {
1303 use super::*;
1304 use tokio::io::{AsyncReadExt, AsyncWriteExt};
1305
1306 #[test]
1307 fn poisoned_auth_cache_is_cleared_before_reuse() {
1308 let cache = Arc::new(AuthReuseCache::new(Duration::from_secs(60)));
1309 let poisoned = Arc::clone(&cache);
1310 let result = std::thread::spawn(move || {
1311 let _guard = poisoned.entries.lock().unwrap();
1312 panic!("poison auth cache mutex");
1313 })
1314 .join();
1315 assert!(result.is_err());
1316
1317 cache.record(
1318 "192.0.2.1".parse().unwrap(),
1319 ClientIdentity::Username("user".to_string()),
1320 );
1321 assert_eq!(cache.len(), 1);
1322 }
1323
1324 #[tokio::test]
1325 async fn test_accept_socks5() {
1326 let all_protocols: Vec<ProtocolId> =
1327 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1328 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1329 let addr = listener.local_addr().unwrap();
1330
1331 let server_jh = tokio::spawn(async move {
1332 let (stream, _) = listener.accept().await.unwrap();
1333 let boxed: BoxStream = Box::new(stream);
1334 let session = accept(
1335 boxed,
1336 &all_protocols,
1337 &InboundAuthentication::None,
1338 None,
1339 None,
1340 None,
1341 )
1342 .await
1343 .unwrap();
1344 match session {
1345 AcceptedSession::Tunnel(pending) => {
1346 assert_eq!(pending.protocol, TunnelProtocol::Socks5);
1347 assert_eq!(pending.target.port, 443);
1348 }
1349 _ => panic!("expected tunnel"),
1350 }
1351 });
1352
1353 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1354 stream.write_all(&[0x05, 0x01, 0x00]).await.unwrap();
1355 let mut response = [0u8; 2];
1356 stream.read_exact(&mut response).await.unwrap();
1357 assert_eq!(response, [0x05, 0x00]);
1358
1359 stream
1360 .write_all(&[0x05, 0x01, 0x00, 0x01, 10, 0, 0, 1])
1361 .await
1362 .unwrap();
1363 stream.write_all(&443u16.to_be_bytes()).await.unwrap();
1364
1365 server_jh.await.unwrap();
1366 }
1367
1368 #[tokio::test]
1369 async fn test_accept_socks4() {
1370 let all_protocols: Vec<ProtocolId> =
1371 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1372 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1373 let addr = listener.local_addr().unwrap();
1374
1375 let server_jh = tokio::spawn(async move {
1376 let (stream, _) = listener.accept().await.unwrap();
1377 let boxed: BoxStream = Box::new(stream);
1378 let session = accept(
1379 boxed,
1380 &all_protocols,
1381 &InboundAuthentication::None,
1382 None,
1383 None,
1384 None,
1385 )
1386 .await
1387 .unwrap();
1388 match session {
1389 AcceptedSession::Tunnel(pending) => {
1390 assert_eq!(pending.protocol, TunnelProtocol::Socks4);
1391 assert_eq!(pending.target.port, 80);
1392 }
1393 _ => panic!("expected tunnel"),
1394 }
1395 });
1396
1397 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1398 stream
1399 .write_all(&[0x04, 0x01, 0x00, 0x50, 10, 0, 0, 1])
1400 .await
1401 .unwrap();
1402 stream.write_all(&[0x00]).await.unwrap();
1403
1404 server_jh.await.unwrap();
1405 }
1406
1407 #[tokio::test]
1408 async fn test_accept_http_connect() {
1409 let all_protocols: Vec<ProtocolId> =
1410 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1411 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1412 let addr = listener.local_addr().unwrap();
1413
1414 let server_jh = tokio::spawn(async move {
1415 let (stream, _) = listener.accept().await.unwrap();
1416 let boxed: BoxStream = Box::new(stream);
1417 let session = accept(
1418 boxed,
1419 &all_protocols,
1420 &InboundAuthentication::None,
1421 None,
1422 None,
1423 None,
1424 )
1425 .await
1426 .unwrap();
1427 match session {
1428 AcceptedSession::Tunnel(pending) => {
1429 assert_eq!(pending.protocol, TunnelProtocol::HttpConnect);
1430 assert_eq!(pending.target.port, 443);
1431 }
1432 _ => panic!("expected tunnel"),
1433 }
1434 });
1435
1436 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1437 stream
1438 .write_all(b"CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n")
1439 .await
1440 .unwrap();
1441
1442 server_jh.await.unwrap();
1443 }
1444
1445 #[tokio::test]
1446 async fn test_accept_http_forward() {
1447 let all_protocols: Vec<ProtocolId> =
1448 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1449 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1450 let addr = listener.local_addr().unwrap();
1451
1452 let server_jh = tokio::spawn(async move {
1453 let (stream, _) = listener.accept().await.unwrap();
1454 let boxed: BoxStream = Box::new(stream);
1455 let session = accept(
1456 boxed,
1457 &all_protocols,
1458 &InboundAuthentication::None,
1459 None,
1460 None,
1461 None,
1462 )
1463 .await
1464 .unwrap();
1465 match session {
1466 AcceptedSession::HttpForward(pending) => {
1467 assert_eq!(pending.target.port, 80);
1468 assert_eq!(pending.request.method, "GET");
1469 }
1470 _ => panic!("expected http forward"),
1471 }
1472 });
1473
1474 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1475 stream
1476 .write_all(b"GET http://example.com/index.html HTTP/1.1\r\nHost: example.com\r\n\r\n")
1477 .await
1478 .unwrap();
1479
1480 server_jh.await.unwrap();
1481 }
1482
1483 #[tokio::test]
1484 async fn test_http_on_http_only_listener() {
1485 let protocols: Vec<ProtocolId> = vec![ProtocolId::Http];
1486 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1487 let addr = listener.local_addr().unwrap();
1488
1489 let server_jh = tokio::spawn(async move {
1490 let (stream, _) = listener.accept().await.unwrap();
1491 let boxed: BoxStream = Box::new(stream);
1492 let session = accept(
1493 boxed,
1494 &protocols,
1495 &InboundAuthentication::None,
1496 None,
1497 None,
1498 None,
1499 )
1500 .await
1501 .unwrap();
1502 match session {
1503 AcceptedSession::Tunnel(pending) => {
1504 assert_eq!(pending.protocol, TunnelProtocol::HttpConnect);
1505 }
1506 _ => panic!("expected tunnel"),
1507 }
1508 });
1509
1510 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1511 stream
1512 .write_all(b"CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n")
1513 .await
1514 .unwrap();
1515
1516 server_jh.await.unwrap();
1517 }
1518
1519 #[tokio::test]
1520 async fn test_socks5_on_http_only_listener_rejected() {
1521 let protocols: Vec<ProtocolId> = vec![ProtocolId::Http];
1522 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1523 let addr = listener.local_addr().unwrap();
1524
1525 let server_jh = tokio::spawn(async move {
1526 let (stream, _) = listener.accept().await.unwrap();
1527 let boxed: BoxStream = Box::new(stream);
1528 let result = accept(
1529 boxed,
1530 &protocols,
1531 &InboundAuthentication::None,
1532 None,
1533 None,
1534 None,
1535 )
1536 .await;
1537 assert!(result.is_err());
1538 });
1539
1540 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1541 stream.write_all(&[0x05, 0x01, 0x00]).await.unwrap();
1542
1543 server_jh.await.unwrap();
1544 }
1545
1546 #[tokio::test]
1547 async fn test_http_on_socks5_only_listener_rejected() {
1548 let protocols: Vec<ProtocolId> = vec![ProtocolId::Socks5];
1549 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1550 let addr = listener.local_addr().unwrap();
1551
1552 let server_jh = tokio::spawn(async move {
1553 let (stream, _) = listener.accept().await.unwrap();
1554 let boxed: BoxStream = Box::new(stream);
1555 let result = accept(
1556 boxed,
1557 &protocols,
1558 &InboundAuthentication::None,
1559 None,
1560 None,
1561 None,
1562 )
1563 .await;
1564 assert!(result.is_err());
1565 });
1566
1567 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1568 stream
1569 .write_all(b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
1570 .await
1571 .unwrap();
1572
1573 server_jh.await.unwrap();
1574 }
1575
1576 #[tokio::test]
1577 async fn test_socks5_on_mixed_listener_accepted() {
1578 let protocols: Vec<ProtocolId> = vec![ProtocolId::Http, ProtocolId::Socks5];
1579 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1580 let addr = listener.local_addr().unwrap();
1581
1582 let server_jh = tokio::spawn(async move {
1583 let (stream, _) = listener.accept().await.unwrap();
1584 let boxed: BoxStream = Box::new(stream);
1585 let session = accept(
1586 boxed,
1587 &protocols,
1588 &InboundAuthentication::None,
1589 None,
1590 None,
1591 None,
1592 )
1593 .await
1594 .unwrap();
1595 match session {
1596 AcceptedSession::Tunnel(pending) => {
1597 assert_eq!(pending.protocol, TunnelProtocol::Socks5);
1598 assert_eq!(pending.target.port, 443);
1599 }
1600 _ => panic!("expected tunnel"),
1601 }
1602 });
1603
1604 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1605 stream.write_all(&[0x05, 0x01, 0x00]).await.unwrap();
1606 let mut response = [0u8; 2];
1607 stream.read_exact(&mut response).await.unwrap();
1608 assert_eq!(response, [0x05, 0x00]);
1609
1610 stream
1611 .write_all(&[0x05, 0x01, 0x00, 0x01, 10, 0, 0, 1])
1612 .await
1613 .unwrap();
1614 stream.write_all(&443u16.to_be_bytes()).await.unwrap();
1615
1616 server_jh.await.unwrap();
1617 }
1618
1619 #[tokio::test]
1620 async fn test_http_on_mixed_listener_accepted() {
1621 let protocols: Vec<ProtocolId> = vec![ProtocolId::Http, ProtocolId::Socks5];
1622 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1623 let addr = listener.local_addr().unwrap();
1624
1625 let server_jh = tokio::spawn(async move {
1626 let (stream, _) = listener.accept().await.unwrap();
1627 let boxed: BoxStream = Box::new(stream);
1628 let session = accept(
1629 boxed,
1630 &protocols,
1631 &InboundAuthentication::None,
1632 None,
1633 None,
1634 None,
1635 )
1636 .await
1637 .unwrap();
1638 match session {
1639 AcceptedSession::Tunnel(pending) => {
1640 assert_eq!(pending.protocol, TunnelProtocol::HttpConnect);
1641 }
1642 _ => panic!("expected tunnel"),
1643 }
1644 });
1645
1646 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1647 stream
1648 .write_all(b"CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n")
1649 .await
1650 .unwrap();
1651
1652 server_jh.await.unwrap();
1653 }
1654
1655 #[cfg(feature = "extended")]
1656 #[tokio::test]
1657 async fn trojan_fallback_drops_rejected_handshake_bytes() {
1658 let mut input = vec![b'0'; 56];
1659 input.extend_from_slice(b"\r\npayload");
1660 let session = accept(
1661 Box::new(std::io::Cursor::new(input)),
1662 &[ProtocolId::Trojan],
1663 &InboundAuthentication::None,
1664 None,
1665 None,
1666 Some(&InboundTrojanConfig {
1667 password: "expected".to_string(),
1668 fallback: Some("example.com:80".to_string()),
1669 }),
1670 )
1671 .await
1672 .unwrap();
1673
1674 let AcceptedSession::Tunnel(mut pending) = session else {
1675 panic!("expected fallback tunnel");
1676 };
1677 let mut payload = Vec::new();
1678 pending.client.read_to_end(&mut payload).await.unwrap();
1679 assert_eq!(payload, b"payload");
1680 }
1681
1682 #[tokio::test]
1683 async fn test_random_binary_prefix_rejected() {
1684 let all_protocols: Vec<ProtocolId> =
1685 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1686 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1687 let addr = listener.local_addr().unwrap();
1688
1689 let server_jh = tokio::spawn(async move {
1690 let (stream, _) = listener.accept().await.unwrap();
1691 let boxed: BoxStream = Box::new(stream);
1692 let result = accept(
1693 boxed,
1694 &all_protocols,
1695 &InboundAuthentication::None,
1696 None,
1697 None,
1698 None,
1699 )
1700 .await;
1701 assert!(result.is_err());
1702 });
1703
1704 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1705 stream.write_all(&[0x00, 0x01, 0x02, 0x03]).await.unwrap();
1707
1708 server_jh.await.unwrap();
1709 }
1710
1711 #[tokio::test]
1712 async fn test_tls_client_hello_not_interpreted_as_http() {
1713 let protocols: Vec<ProtocolId> = vec![ProtocolId::Http];
1714 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1715 let addr = listener.local_addr().unwrap();
1716
1717 let server_jh = tokio::spawn(async move {
1718 let (stream, _) = listener.accept().await.unwrap();
1719 let boxed: BoxStream = Box::new(stream);
1720 let result = accept(
1721 boxed,
1722 &protocols,
1723 &InboundAuthentication::None,
1724 None,
1725 None,
1726 None,
1727 )
1728 .await;
1729 assert!(result.is_err());
1730 });
1731
1732 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1733 stream
1735 .write_all(&[0x16, 0x03, 0x01, 0x00, 0x05])
1736 .await
1737 .unwrap();
1738
1739 server_jh.await.unwrap();
1740 }
1741
1742 #[tokio::test]
1745 async fn test_socks5_auth_correct_credentials() {
1746 let all_protocols: Vec<ProtocolId> =
1747 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1748 let auth = InboundAuthentication::UsernamePassword {
1749 username: "user".to_string(),
1750 password: "secret".to_string(),
1751 };
1752 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1753 let addr = listener.local_addr().unwrap();
1754
1755 let server_jh = tokio::spawn(async move {
1756 let (stream, _) = listener.accept().await.unwrap();
1757 let boxed: BoxStream = Box::new(stream);
1758 let session = accept(boxed, &all_protocols, &auth, None, None, None)
1759 .await
1760 .unwrap();
1761 match session {
1762 AcceptedSession::Tunnel(pending) => {
1763 assert_eq!(pending.protocol, TunnelProtocol::Socks5);
1764 assert_eq!(pending.target.port, 443);
1765 }
1766 _ => panic!("expected tunnel"),
1767 }
1768 });
1769
1770 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1771 stream.write_all(&[0x05, 0x02, 0x00, 0x02]).await.unwrap();
1773 let mut response = [0u8; 2];
1775 stream.read_exact(&mut response).await.unwrap();
1776 assert_eq!(response, [0x05, 0x02]);
1777
1778 stream
1780 .write_all(&[0x01, 0x04, b'u', b's', b'e', b'r', 0x06])
1781 .await
1782 .unwrap();
1783 stream.write_all(b"secret").await.unwrap();
1784 let mut auth_resp = [0u8; 2];
1786 stream.read_exact(&mut auth_resp).await.unwrap();
1787 assert_eq!(auth_resp, [0x01, 0x00]);
1788
1789 stream
1791 .write_all(&[0x05, 0x01, 0x00, 0x01, 10, 0, 0, 1])
1792 .await
1793 .unwrap();
1794 stream.write_all(&443u16.to_be_bytes()).await.unwrap();
1795
1796 server_jh.await.unwrap();
1797 }
1798
1799 #[tokio::test]
1800 async fn test_socks5_auth_wrong_password() {
1801 let all_protocols: Vec<ProtocolId> =
1802 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1803 let auth = InboundAuthentication::UsernamePassword {
1804 username: "user".to_string(),
1805 password: "secret".to_string(),
1806 };
1807 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1808 let addr = listener.local_addr().unwrap();
1809
1810 let server_jh = tokio::spawn(async move {
1811 let (stream, _) = listener.accept().await.unwrap();
1812 let boxed: BoxStream = Box::new(stream);
1813 let result = accept(boxed, &all_protocols, &auth, None, None, None).await;
1814 assert!(matches!(result, Err(AcceptError::AuthenticationFailed)));
1815 });
1816
1817 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1818 stream.write_all(&[0x05, 0x02, 0x00, 0x02]).await.unwrap();
1819 let mut response = [0u8; 2];
1820 stream.read_exact(&mut response).await.unwrap();
1821 assert_eq!(response, [0x05, 0x02]);
1822
1823 stream
1825 .write_all(&[0x01, 0x04, b'u', b's', b'e', b'r', 0x05])
1826 .await
1827 .unwrap();
1828 stream.write_all(b"wrong").await.unwrap();
1829 let mut auth_resp = [0u8; 2];
1831 stream.read_exact(&mut auth_resp).await.unwrap();
1832 assert_eq!(auth_resp, [0x01, 0x01]);
1833
1834 server_jh.await.unwrap();
1835 }
1836
1837 #[tokio::test]
1838 async fn test_socks5_auth_no_auth_client_rejected() {
1839 let all_protocols: Vec<ProtocolId> =
1840 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1841 let auth = InboundAuthentication::UsernamePassword {
1842 username: "user".to_string(),
1843 password: "secret".to_string(),
1844 };
1845 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1846 let addr = listener.local_addr().unwrap();
1847
1848 let server_jh = tokio::spawn(async move {
1849 let (stream, _) = listener.accept().await.unwrap();
1850 let boxed: BoxStream = Box::new(stream);
1851 let result = accept(boxed, &all_protocols, &auth, None, None, None).await;
1852 assert!(result.is_err());
1853 });
1854
1855 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1856 stream.write_all(&[0x05, 0x01, 0x00]).await.unwrap();
1858 let mut response = [0u8; 2];
1860 stream.read_exact(&mut response).await.unwrap();
1861 assert_eq!(response, [0x05, 0xFF]);
1862
1863 server_jh.await.unwrap();
1864 }
1865
1866 #[tokio::test]
1867 async fn test_http_connect_auth_correct_credentials() {
1868 let all_protocols: Vec<ProtocolId> =
1869 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1870 let auth = InboundAuthentication::UsernamePassword {
1871 username: "user".to_string(),
1872 password: "pass".to_string(),
1873 };
1874 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1875 let addr = listener.local_addr().unwrap();
1876
1877 let server_jh = tokio::spawn(async move {
1878 let (stream, _) = listener.accept().await.unwrap();
1879 let boxed: BoxStream = Box::new(stream);
1880 let session = accept(boxed, &all_protocols, &auth, None, None, None)
1881 .await
1882 .unwrap();
1883 match session {
1884 AcceptedSession::Tunnel(pending) => {
1885 assert_eq!(pending.protocol, TunnelProtocol::HttpConnect);
1886 assert_eq!(pending.target.port, 443);
1887 }
1888 _ => panic!("expected tunnel"),
1889 }
1890 });
1891
1892 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1893 stream
1895 .write_all(
1896 b"CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\nProxy-Authorization: Basic dXNlcjpwYXNz\r\n\r\n",
1897 )
1898 .await
1899 .unwrap();
1900
1901 server_jh.await.unwrap();
1902 }
1903
1904 #[tokio::test]
1905 async fn test_http_connect_auth_missing_credentials() {
1906 let all_protocols: Vec<ProtocolId> =
1907 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1908 let auth = InboundAuthentication::UsernamePassword {
1909 username: "user".to_string(),
1910 password: "pass".to_string(),
1911 };
1912 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1913 let addr = listener.local_addr().unwrap();
1914
1915 let server_jh = tokio::spawn(async move {
1916 let (stream, _) = listener.accept().await.unwrap();
1917 let boxed: BoxStream = Box::new(stream);
1918 let result = accept(boxed, &all_protocols, &auth, None, None, None).await;
1919 assert!(matches!(result, Err(AcceptError::AuthenticationFailed)));
1920 });
1921
1922 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1923 stream
1924 .write_all(b"CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n")
1925 .await
1926 .unwrap();
1927
1928 let mut response = vec![0u8; 512];
1930 let n = stream.read(&mut response).await.unwrap();
1931 let response_str = String::from_utf8_lossy(&response[..n]);
1932 assert!(
1933 response_str.contains("407"),
1934 "expected 407, got: {response_str}"
1935 );
1936 assert!(
1937 response_str.contains("Proxy-Authenticate"),
1938 "expected Proxy-Authenticate header"
1939 );
1940
1941 server_jh.await.unwrap();
1942 }
1943
1944 #[tokio::test]
1945 async fn test_http_connect_auth_wrong_credentials() {
1946 let all_protocols: Vec<ProtocolId> =
1947 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1948 let auth = InboundAuthentication::UsernamePassword {
1949 username: "user".to_string(),
1950 password: "pass".to_string(),
1951 };
1952 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1953 let addr = listener.local_addr().unwrap();
1954
1955 let server_jh = tokio::spawn(async move {
1956 let (stream, _) = listener.accept().await.unwrap();
1957 let boxed: BoxStream = Box::new(stream);
1958 let result = accept(boxed, &all_protocols, &auth, None, None, None).await;
1959 assert!(matches!(result, Err(AcceptError::AuthenticationFailed)));
1960 });
1961
1962 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
1963 stream
1965 .write_all(
1966 b"CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\nProxy-Authorization: Basic dXNlcjp3cm9uZw==\r\n\r\n",
1967 )
1968 .await
1969 .unwrap();
1970
1971 let mut response = vec![0u8; 512];
1972 let n = stream.read(&mut response).await.unwrap();
1973 let response_str = String::from_utf8_lossy(&response[..n]);
1974 assert!(
1975 response_str.contains("407"),
1976 "expected 407, got: {response_str}"
1977 );
1978
1979 server_jh.await.unwrap();
1980 }
1981
1982 #[tokio::test]
1983 async fn test_http_connect_auth_malformed_base64() {
1984 let all_protocols: Vec<ProtocolId> =
1985 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
1986 let auth = InboundAuthentication::UsernamePassword {
1987 username: "user".to_string(),
1988 password: "pass".to_string(),
1989 };
1990 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
1991 let addr = listener.local_addr().unwrap();
1992
1993 let server_jh = tokio::spawn(async move {
1994 let (stream, _) = listener.accept().await.unwrap();
1995 let boxed: BoxStream = Box::new(stream);
1996 let result = accept(boxed, &all_protocols, &auth, None, None, None).await;
1997 assert!(matches!(result, Err(AcceptError::AuthenticationFailed)));
1998 });
1999
2000 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2001 stream
2002 .write_all(
2003 b"CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\nProxy-Authorization: Basic !!!invalid!!!\r\n\r\n",
2004 )
2005 .await
2006 .unwrap();
2007
2008 let mut response = vec![0u8; 512];
2009 let n = stream.read(&mut response).await.unwrap();
2010 let response_str = String::from_utf8_lossy(&response[..n]);
2011 assert!(
2012 response_str.contains("407"),
2013 "expected 407, got: {response_str}"
2014 );
2015
2016 server_jh.await.unwrap();
2017 }
2018
2019 #[tokio::test]
2020 async fn test_http_head_reader_preserves_prefetched_body() {
2021 let (mut client, server) = tokio::io::duplex(1024);
2022 client
2023 .write_all(b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\nbody")
2024 .await
2025 .unwrap();
2026 client.shutdown().await.unwrap();
2027
2028 let mut reader = tokio::io::BufReader::new(server);
2029 let head = read_http_head(&mut reader).await.unwrap();
2030 assert!(head.ends_with(b"\r\n\r\n"));
2031 let mut body = Vec::new();
2032 reader.read_to_end(&mut body).await.unwrap();
2033 assert_eq!(body, b"body");
2034 }
2035
2036 #[tokio::test]
2037 async fn test_http_forward_auth_correct_credentials() {
2038 let all_protocols: Vec<ProtocolId> =
2039 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2040 let auth = InboundAuthentication::UsernamePassword {
2041 username: "user".to_string(),
2042 password: "pass".to_string(),
2043 };
2044 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2045 let addr = listener.local_addr().unwrap();
2046
2047 let server_jh = tokio::spawn(async move {
2048 let (stream, _) = listener.accept().await.unwrap();
2049 let boxed: BoxStream = Box::new(stream);
2050 let session = accept(boxed, &all_protocols, &auth, None, None, None)
2051 .await
2052 .unwrap();
2053 match session {
2054 AcceptedSession::HttpForward(pending) => {
2055 assert_eq!(pending.target.port, 80);
2056 assert_eq!(pending.request.method, "GET");
2057 assert!(!pending
2059 .request
2060 .headers
2061 .iter()
2062 .any(|(name, _)| name.eq_ignore_ascii_case("Proxy-Authorization")));
2063 }
2064 _ => panic!("expected http forward"),
2065 }
2066 });
2067
2068 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2069 stream
2070 .write_all(
2071 b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\nProxy-Authorization: Basic dXNlcjpwYXNz\r\n\r\n",
2072 )
2073 .await
2074 .unwrap();
2075
2076 server_jh.await.unwrap();
2077 }
2078
2079 #[tokio::test]
2080 async fn test_http_forward_auth_missing_credentials() {
2081 let all_protocols: Vec<ProtocolId> =
2082 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2083 let auth = InboundAuthentication::UsernamePassword {
2084 username: "user".to_string(),
2085 password: "pass".to_string(),
2086 };
2087 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2088 let addr = listener.local_addr().unwrap();
2089
2090 let server_jh = tokio::spawn(async move {
2091 let (stream, _) = listener.accept().await.unwrap();
2092 let boxed: BoxStream = Box::new(stream);
2093 let result = accept(boxed, &all_protocols, &auth, None, None, None).await;
2094 assert!(matches!(result, Err(AcceptError::AuthenticationFailed)));
2095 });
2096
2097 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2098 stream
2099 .write_all(b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
2100 .await
2101 .unwrap();
2102
2103 let mut response = vec![0u8; 512];
2104 let n = stream.read(&mut response).await.unwrap();
2105 let response_str = String::from_utf8_lossy(&response[..n]);
2106 assert!(
2107 response_str.contains("407"),
2108 "expected 407, got: {response_str}"
2109 );
2110
2111 server_jh.await.unwrap();
2112 }
2113
2114 #[tokio::test]
2115 async fn test_http_forward_auth_wrong_credentials() {
2116 let all_protocols: Vec<ProtocolId> =
2117 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2118 let auth = InboundAuthentication::UsernamePassword {
2119 username: "user".to_string(),
2120 password: "pass".to_string(),
2121 };
2122 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2123 let addr = listener.local_addr().unwrap();
2124
2125 let server_jh = tokio::spawn(async move {
2126 let (stream, _) = listener.accept().await.unwrap();
2127 let boxed: BoxStream = Box::new(stream);
2128 let result = accept(boxed, &all_protocols, &auth, None, None, None).await;
2129 assert!(matches!(result, Err(AcceptError::AuthenticationFailed)));
2130 });
2131
2132 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2133 stream
2135 .write_all(
2136 b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\nProxy-Authorization: Basic dXNlcjp3cm9uZw==\r\n\r\n",
2137 )
2138 .await
2139 .unwrap();
2140
2141 let mut response = vec![0u8; 512];
2142 let n = stream.read(&mut response).await.unwrap();
2143 let response_str = String::from_utf8_lossy(&response[..n]);
2144 assert!(
2145 response_str.contains("407"),
2146 "expected 407, got: {response_str}"
2147 );
2148
2149 server_jh.await.unwrap();
2150 }
2151
2152 #[tokio::test]
2153 async fn test_socks5_udp_associate_returns_pending() {
2154 let all_protocols: Vec<ProtocolId> =
2155 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2156 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2157 let addr = listener.local_addr().unwrap();
2158
2159 let server_jh = tokio::spawn(async move {
2160 let (stream, _) = listener.accept().await.unwrap();
2161 let boxed: BoxStream = Box::new(stream);
2162 let session = accept(
2163 boxed,
2164 &all_protocols,
2165 &InboundAuthentication::None,
2166 None,
2167 None,
2168 None,
2169 )
2170 .await
2171 .unwrap();
2172 match session {
2173 AcceptedSession::UdpAssociate(pending) => {
2174 assert_eq!(pending.protocol, TunnelProtocol::Socks5);
2175 assert_eq!(
2176 pending.client_hint,
2177 Some(TargetAddr {
2178 host: TargetHost::Ip(std::net::IpAddr::V4(std::net::Ipv4Addr::new(
2179 0, 0, 0, 0
2180 ))),
2181 port: 0,
2182 })
2183 );
2184 }
2185 _ => panic!("expected UdpAssociate"),
2186 }
2187 });
2188
2189 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2190 stream.write_all(&[0x05, 0x01, 0x00]).await.unwrap();
2191 let mut response = [0u8; 2];
2192 stream.read_exact(&mut response).await.unwrap();
2193 assert_eq!(response, [0x05, 0x00]);
2194
2195 stream
2197 .write_all(&[0x05, 0x03, 0x00, 0x01, 0, 0, 0, 0])
2198 .await
2199 .unwrap();
2200 stream.write_all(&0u16.to_be_bytes()).await.unwrap();
2201
2202 server_jh.await.unwrap();
2203 }
2204
2205 #[tokio::test]
2206 async fn test_socks5_bind_rejected() {
2207 let all_protocols: Vec<ProtocolId> =
2208 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2209 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2210 let addr = listener.local_addr().unwrap();
2211
2212 let server_jh = tokio::spawn(async move {
2213 let (stream, _) = listener.accept().await.unwrap();
2214 let boxed: BoxStream = Box::new(stream);
2215 let result = accept(
2216 boxed,
2217 &all_protocols,
2218 &InboundAuthentication::None,
2219 None,
2220 None,
2221 None,
2222 )
2223 .await;
2224 assert!(result.is_err());
2225 });
2226
2227 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2228 stream.write_all(&[0x05, 0x01, 0x00]).await.unwrap();
2229 let mut response = [0u8; 2];
2230 stream.read_exact(&mut response).await.unwrap();
2231 assert_eq!(response, [0x05, 0x00]);
2232
2233 stream
2235 .write_all(&[0x05, 0x02, 0x00, 0x01, 10, 0, 0, 1])
2236 .await
2237 .unwrap();
2238 stream.write_all(&80u16.to_be_bytes()).await.unwrap();
2239
2240 let mut reply = [0u8; 10];
2242 stream.read_exact(&mut reply).await.unwrap();
2243 assert_eq!(reply[0], 0x05);
2244 assert_eq!(reply[1], 0x07); server_jh.await.unwrap();
2247 }
2248
2249 #[tokio::test]
2250 async fn test_socks5_connect_still_works() {
2251 let all_protocols: Vec<ProtocolId> =
2252 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2253 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2254 let addr = listener.local_addr().unwrap();
2255
2256 let server_jh = tokio::spawn(async move {
2257 let (stream, _) = listener.accept().await.unwrap();
2258 let boxed: BoxStream = Box::new(stream);
2259 let session = accept(
2260 boxed,
2261 &all_protocols,
2262 &InboundAuthentication::None,
2263 None,
2264 None,
2265 None,
2266 )
2267 .await
2268 .unwrap();
2269 match session {
2270 AcceptedSession::Tunnel(pending) => {
2271 assert_eq!(pending.protocol, TunnelProtocol::Socks5);
2272 assert_eq!(pending.target.port, 443);
2273 }
2274 _ => panic!("expected tunnel"),
2275 }
2276 });
2277
2278 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2279 stream.write_all(&[0x05, 0x01, 0x00]).await.unwrap();
2280 let mut response = [0u8; 2];
2281 stream.read_exact(&mut response).await.unwrap();
2282 assert_eq!(response, [0x05, 0x00]);
2283
2284 stream
2286 .write_all(&[0x05, 0x01, 0x00, 0x01, 10, 0, 0, 1])
2287 .await
2288 .unwrap();
2289 stream.write_all(&443u16.to_be_bytes()).await.unwrap();
2290
2291 server_jh.await.unwrap();
2292 }
2293
2294 #[tokio::test]
2295 async fn test_socks5_udp_associate_with_auth() {
2296 let all_protocols: Vec<ProtocolId> =
2297 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2298 let auth = InboundAuthentication::UsernamePassword {
2299 username: "user".to_string(),
2300 password: "secret".to_string(),
2301 };
2302 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2303 let addr = listener.local_addr().unwrap();
2304
2305 let server_jh = tokio::spawn(async move {
2306 let (stream, _) = listener.accept().await.unwrap();
2307 let boxed: BoxStream = Box::new(stream);
2308 let session = accept(boxed, &all_protocols, &auth, None, None, None)
2309 .await
2310 .unwrap();
2311 match session {
2312 AcceptedSession::UdpAssociate(pending) => {
2313 assert_eq!(pending.protocol, TunnelProtocol::Socks5);
2314 assert_eq!(
2315 pending.identity,
2316 ClientIdentity::Username("user".to_string())
2317 );
2318 }
2319 _ => panic!("expected UdpAssociate"),
2320 }
2321 });
2322
2323 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2324 stream.write_all(&[0x05, 0x02, 0x00, 0x02]).await.unwrap();
2325 let mut response = [0u8; 2];
2326 stream.read_exact(&mut response).await.unwrap();
2327 assert_eq!(response, [0x05, 0x02]);
2328
2329 stream
2331 .write_all(&[0x01, 0x04, b'u', b's', b'e', b'r', 0x06])
2332 .await
2333 .unwrap();
2334 stream.write_all(b"secret").await.unwrap();
2335 let mut auth_resp = [0u8; 2];
2336 stream.read_exact(&mut auth_resp).await.unwrap();
2337 assert_eq!(auth_resp, [0x01, 0x00]);
2338
2339 stream
2341 .write_all(&[0x05, 0x03, 0x00, 0x01, 0, 0, 0, 0])
2342 .await
2343 .unwrap();
2344 stream.write_all(&0u16.to_be_bytes()).await.unwrap();
2345
2346 server_jh.await.unwrap();
2347 }
2348
2349 #[tokio::test]
2352 async fn test_fragmented_first_byte_http() {
2353 let all_protocols: Vec<ProtocolId> =
2354 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2355 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2356 let addr = listener.local_addr().unwrap();
2357
2358 let server_jh = tokio::spawn(async move {
2359 let (stream, _) = listener.accept().await.unwrap();
2360 let boxed: BoxStream = Box::new(stream);
2361 let session = accept(
2362 boxed,
2363 &all_protocols,
2364 &InboundAuthentication::None,
2365 None,
2366 None,
2367 None,
2368 )
2369 .await
2370 .unwrap();
2371 match session {
2372 AcceptedSession::HttpForward(pending) => {
2373 assert_eq!(pending.request.method, "GET");
2374 }
2375 _ => panic!("expected http forward"),
2376 }
2377 });
2378
2379 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2380 stream.write_all(b"G").await.unwrap();
2382 stream.write_all(b"E").await.unwrap();
2383 stream.write_all(b"T").await.unwrap();
2384 stream
2385 .write_all(b" http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
2386 .await
2387 .unwrap();
2388
2389 server_jh.await.unwrap();
2390 }
2391
2392 #[tokio::test]
2393 async fn test_garbage_bytes_rejected() {
2394 let all_protocols: Vec<ProtocolId> =
2395 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2396 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2397 let addr = listener.local_addr().unwrap();
2398
2399 let server_jh = tokio::spawn(async move {
2400 let (stream, _) = listener.accept().await.unwrap();
2401 let boxed: BoxStream = Box::new(stream);
2402 let result = accept(
2403 boxed,
2404 &all_protocols,
2405 &InboundAuthentication::None,
2406 None,
2407 None,
2408 None,
2409 )
2410 .await;
2411 assert!(result.is_err());
2412 });
2413
2414 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2415 stream.write_all(&[0xAA, 0xBB, 0xCC, 0xDD]).await.unwrap();
2416
2417 server_jh.await.unwrap();
2418 }
2419
2420 #[tokio::test]
2421 async fn test_slow_socks5_detection() {
2422 let all_protocols: Vec<ProtocolId> =
2423 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2424 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2425 let addr = listener.local_addr().unwrap();
2426
2427 let server_jh = tokio::spawn(async move {
2428 let (stream, _) = listener.accept().await.unwrap();
2429 let boxed: BoxStream = Box::new(stream);
2430 let session = accept(
2431 boxed,
2432 &all_protocols,
2433 &InboundAuthentication::None,
2434 None,
2435 None,
2436 None,
2437 )
2438 .await
2439 .unwrap();
2440 match session {
2441 AcceptedSession::Tunnel(pending) => {
2442 assert_eq!(pending.protocol, TunnelProtocol::Socks5);
2443 assert_eq!(pending.target.port, 443);
2444 }
2445 _ => panic!("expected tunnel"),
2446 }
2447 });
2448
2449 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2450 stream.write_all(&[0x05]).await.unwrap();
2452 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
2453 stream.write_all(&[0x01, 0x00]).await.unwrap();
2455 let mut response = [0u8; 2];
2456 stream.read_exact(&mut response).await.unwrap();
2457 assert_eq!(response, [0x05, 0x00]);
2458
2459 stream
2461 .write_all(&[0x05, 0x01, 0x00, 0x01, 10, 0, 0, 1])
2462 .await
2463 .unwrap();
2464 stream.write_all(&443u16.to_be_bytes()).await.unwrap();
2465
2466 server_jh.await.unwrap();
2467 }
2468
2469 #[tokio::test]
2470 async fn test_http_connect_and_socks5_same_listener() {
2471 let protocols: Vec<ProtocolId> = vec![ProtocolId::Http, ProtocolId::Socks5];
2472 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2473 let addr = listener.local_addr().unwrap();
2474
2475 let client_jh1 = tokio::spawn(async move {
2477 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2478 stream
2479 .write_all(b"CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n")
2480 .await
2481 .unwrap();
2482 });
2483
2484 let (stream1, _) = listener.accept().await.unwrap();
2485 let p = protocols.clone();
2486 let server_jh1 = tokio::spawn(async move {
2487 let boxed: BoxStream = Box::new(stream1);
2488 let session = accept(boxed, &p, &InboundAuthentication::None, None, None, None)
2489 .await
2490 .unwrap();
2491 match session {
2492 AcceptedSession::Tunnel(pending) => {
2493 assert_eq!(pending.protocol, TunnelProtocol::HttpConnect);
2494 }
2495 _ => panic!("expected tunnel"),
2496 }
2497 });
2498
2499 client_jh1.await.unwrap();
2500 server_jh1.await.unwrap();
2501
2502 let client_jh2 = tokio::spawn(async move {
2504 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2505 stream.write_all(&[0x05, 0x01, 0x00]).await.unwrap();
2506 let mut response = [0u8; 2];
2507 stream.read_exact(&mut response).await.unwrap();
2508 assert_eq!(response, [0x05, 0x00]);
2509
2510 stream
2511 .write_all(&[0x05, 0x01, 0x00, 0x01, 10, 0, 0, 1])
2512 .await
2513 .unwrap();
2514 stream.write_all(&443u16.to_be_bytes()).await.unwrap();
2515 });
2516
2517 let (stream2, _) = listener.accept().await.unwrap();
2518 let server_jh2 = tokio::spawn(async move {
2519 let boxed: BoxStream = Box::new(stream2);
2520 let session = accept(
2521 boxed,
2522 &protocols,
2523 &InboundAuthentication::None,
2524 None,
2525 None,
2526 None,
2527 )
2528 .await
2529 .unwrap();
2530 match session {
2531 AcceptedSession::Tunnel(pending) => {
2532 assert_eq!(pending.protocol, TunnelProtocol::Socks5);
2533 assert_eq!(pending.target.port, 443);
2534 }
2535 _ => panic!("expected tunnel"),
2536 }
2537 });
2538
2539 client_jh2.await.unwrap();
2540 server_jh2.await.unwrap();
2541 }
2542
2543 #[tokio::test]
2544 async fn test_http_forward_and_socks4_same_listener() {
2545 let protocols: Vec<ProtocolId> = vec![ProtocolId::Http, ProtocolId::Socks4];
2546 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2547 let addr = listener.local_addr().unwrap();
2548
2549 let client_jh1 = tokio::spawn(async move {
2551 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2552 stream
2553 .write_all(b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
2554 .await
2555 .unwrap();
2556 });
2557
2558 let (stream1, _) = listener.accept().await.unwrap();
2559 let p = protocols.clone();
2560 let server_jh1 = tokio::spawn(async move {
2561 let boxed: BoxStream = Box::new(stream1);
2562 let session = accept(boxed, &p, &InboundAuthentication::None, None, None, None)
2563 .await
2564 .unwrap();
2565 match session {
2566 AcceptedSession::HttpForward(pending) => {
2567 assert_eq!(pending.request.method, "GET");
2568 assert_eq!(pending.target.port, 80);
2569 }
2570 _ => panic!("expected http forward"),
2571 }
2572 });
2573
2574 client_jh1.await.unwrap();
2575 server_jh1.await.unwrap();
2576
2577 let client_jh2 = tokio::spawn(async move {
2579 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2580 stream.write_all(&[0x04, 0x01]).await.unwrap();
2582 stream.write_all(&443u16.to_be_bytes()).await.unwrap();
2583 stream.write_all(&[10, 0, 0, 1]).await.unwrap();
2584 stream.write_all(&[0x00]).await.unwrap();
2585 });
2586
2587 let (stream2, _) = listener.accept().await.unwrap();
2588 let server_jh2 = tokio::spawn(async move {
2589 let boxed: BoxStream = Box::new(stream2);
2590 let session = accept(
2591 boxed,
2592 &protocols,
2593 &InboundAuthentication::None,
2594 None,
2595 None,
2596 None,
2597 )
2598 .await
2599 .unwrap();
2600 match session {
2601 AcceptedSession::Tunnel(pending) => {
2602 assert_eq!(pending.protocol, TunnelProtocol::Socks4);
2603 assert_eq!(pending.target.port, 443);
2604 }
2605 _ => panic!("expected tunnel"),
2606 }
2607 });
2608
2609 client_jh2.await.unwrap();
2610 server_jh2.await.unwrap();
2611 }
2612
2613 #[tokio::test]
2614 async fn test_fragmented_socks5_handshake() {
2615 let all_protocols: Vec<ProtocolId> =
2616 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2617 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2618 let addr = listener.local_addr().unwrap();
2619
2620 let server_jh = tokio::spawn(async move {
2621 let (stream, _) = listener.accept().await.unwrap();
2622 let boxed: BoxStream = Box::new(stream);
2623 let session = accept(
2624 boxed,
2625 &all_protocols,
2626 &InboundAuthentication::None,
2627 None,
2628 None,
2629 None,
2630 )
2631 .await
2632 .unwrap();
2633 match session {
2634 AcceptedSession::Tunnel(pending) => {
2635 assert_eq!(pending.protocol, TunnelProtocol::Socks5);
2636 assert_eq!(pending.target.port, 443);
2637 }
2638 _ => panic!("expected tunnel"),
2639 }
2640 });
2641
2642 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2643 stream.write_all(&[0x05]).await.unwrap();
2645 tokio::time::sleep(std::time::Duration::from_millis(50)).await;
2646 stream.write_all(&[0x01, 0x00]).await.unwrap();
2647
2648 let mut response = [0u8; 2];
2649 stream.read_exact(&mut response).await.unwrap();
2650 assert_eq!(response, [0x05, 0x00]);
2651
2652 stream
2654 .write_all(&[0x05, 0x01, 0x00, 0x01, 10, 0, 0, 1])
2655 .await
2656 .unwrap();
2657 stream.write_all(&443u16.to_be_bytes()).await.unwrap();
2658
2659 server_jh.await.unwrap();
2660 }
2661
2662 #[tokio::test]
2663 async fn test_malformed_http_request_rejected() {
2664 let all_protocols: Vec<ProtocolId> =
2665 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2666 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2667 let addr = listener.local_addr().unwrap();
2668
2669 let server_jh = tokio::spawn(async move {
2670 let (stream, _) = listener.accept().await.unwrap();
2671 let boxed: BoxStream = Box::new(stream);
2672 let result = accept(
2673 boxed,
2674 &all_protocols,
2675 &InboundAuthentication::None,
2676 None,
2677 None,
2678 None,
2679 )
2680 .await;
2681 assert!(result.is_err());
2682 });
2683
2684 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2685 stream
2687 .write_all(b"GET http://example.com HTTP/1.1\r\n")
2688 .await
2689 .unwrap();
2690 stream.shutdown().await.unwrap();
2692
2693 server_jh.await.unwrap();
2694 }
2695
2696 #[tokio::test]
2697 async fn test_empty_connection_closed() {
2698 let all_protocols: Vec<ProtocolId> =
2699 vec![ProtocolId::Http, ProtocolId::Socks4, ProtocolId::Socks5];
2700 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2701 let addr = listener.local_addr().unwrap();
2702
2703 let server_jh = tokio::spawn(async move {
2704 let (stream, _) = listener.accept().await.unwrap();
2705 let boxed: BoxStream = Box::new(stream);
2706 let result = accept(
2707 boxed,
2708 &all_protocols,
2709 &InboundAuthentication::None,
2710 None,
2711 None,
2712 None,
2713 )
2714 .await;
2715 assert!(result.is_err());
2716 });
2717
2718 let stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2719 drop(stream);
2721
2722 server_jh.await.unwrap();
2723 }
2724
2725 #[tokio::test]
2729 async fn test_mixed_protocols_with_auth_detection() {
2730 let protocols: Vec<ProtocolId> = vec![ProtocolId::Http, ProtocolId::Socks5];
2731 let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
2732 let addr = listener.local_addr().unwrap();
2733
2734 let client_jh1 = tokio::spawn(async move {
2737 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2738 stream
2739 .write_all(b"GET http://example.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n")
2740 .await
2741 .unwrap();
2742 });
2743
2744 let (stream1, _) = listener.accept().await.unwrap();
2745 let p = protocols.clone();
2746 let server_jh1 = tokio::spawn(async move {
2747 let boxed: BoxStream = Box::new(stream1);
2748 let session = accept(boxed, &p, &InboundAuthentication::None, None, None, None)
2749 .await
2750 .unwrap();
2751 match session {
2752 AcceptedSession::HttpForward(pending) => {
2753 assert_eq!(pending.request.method, "GET");
2754 }
2755 _ => panic!("expected http forward"),
2756 }
2757 });
2758
2759 client_jh1.await.unwrap();
2760 server_jh1.await.unwrap();
2761
2762 let client_jh2 = tokio::spawn(async move {
2764 let mut stream = tokio::net::TcpStream::connect(addr).await.unwrap();
2765 stream.write_all(&[0x05, 0x01, 0x00]).await.unwrap();
2766 let mut response = [0u8; 2];
2767 stream.read_exact(&mut response).await.unwrap();
2768 assert_eq!(response, [0x05, 0x00]);
2769 stream
2770 .write_all(&[0x05, 0x01, 0x00, 0x01, 10, 0, 0, 1])
2771 .await
2772 .unwrap();
2773 stream.write_all(&443u16.to_be_bytes()).await.unwrap();
2774 });
2775
2776 let (stream2, _) = listener.accept().await.unwrap();
2777 let server_jh2 = tokio::spawn(async move {
2778 let boxed: BoxStream = Box::new(stream2);
2779 let session = accept(
2780 boxed,
2781 &protocols,
2782 &InboundAuthentication::None,
2783 None,
2784 None,
2785 None,
2786 )
2787 .await
2788 .unwrap();
2789 match session {
2790 AcceptedSession::Tunnel(pending) => {
2791 assert_eq!(pending.protocol, TunnelProtocol::Socks5);
2792 assert_eq!(pending.target.port, 443);
2793 }
2794 _ => panic!("expected tunnel"),
2795 }
2796 });
2797
2798 client_jh2.await.unwrap();
2799 server_jh2.await.unwrap();
2800 }
2801
2802 #[test]
2803 fn auth_reuse_is_ip_scoped_and_bounded() {
2804 let cache = AuthReuseCache::new(Duration::from_secs(60));
2805 let first: IpAddr = "127.0.0.1".parse().unwrap();
2806 let second: IpAddr = "127.0.0.2".parse().unwrap();
2807 cache.record(first, ClientIdentity::Username("alice".to_string()));
2808 assert_eq!(
2809 cache.lookup(first),
2810 Some(ClientIdentity::Username("alice".to_string()))
2811 );
2812 assert_eq!(cache.lookup(second), None);
2813 assert_eq!(cache.len(), 1);
2814 }
2815
2816 #[test]
2817 fn zero_timeout_expires_after_authentication() {
2818 let cache = AuthReuseCache::new(Duration::ZERO);
2819 let peer: IpAddr = "127.0.0.1".parse().unwrap();
2820 cache.record(peer, ClientIdentity::Username("alice".to_string()));
2821 while cache.lookup(peer).is_some() {
2822 std::hint::spin_loop();
2823 }
2824 assert_eq!(cache.len(), 0);
2825 }
2826}