1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! Mixed HTTP+SOCKS5 server handler.
//!
//! This module provides a server handler that auto-detects whether the client
//! is speaking HTTP or SOCKS5 based on the first byte of the connection:
//! - 0x05 = SOCKS5 (RFC 1928 specifies version byte first)
//! - Anything else = HTTP
//!
//! This is similar to mihomo's mixed-port feature.
use std::net::IpAddr;
use std::sync::Arc;
use async_trait::async_trait;
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
use crate::async_stream::AsyncStream;
use crate::client_proxy_selector::ClientProxySelector;
use crate::http_handler::setup_http_server_stream_inner;
use crate::resolver::Resolver;
use crate::socks_handler::{VER_SOCKS5, setup_socks_server_stream_inner};
use crate::stream_reader::StreamReader;
use crate::tcp::tcp_handler::{TcpServerHandler, TcpServerSetupResult};
/// Mixed HTTP+SOCKS5 server handler.
///
/// Auto-detects the protocol from the first byte and delegates to the
/// appropriate handler implementation.
#[derive(Debug)]
pub struct MixedTcpServerHandler {
/// Authentication for both HTTP and SOCKS5
auth_info: Option<(String, String)>,
/// Pre-computed HTTP auth token (base64 encoded)
http_auth_token: Option<String>,
/// Enable UDP functionality for SOCKS5 (UDP ASSOCIATE and UDP-over-TCP)
udp_enabled: bool,
/// IP address to bind UDP sockets on (same as TCP server)
bind_ip: IpAddr,
/// Proxy selector for outbound connections
proxy_selector: Arc<ClientProxySelector>,
/// DNS resolver
resolver: Arc<dyn Resolver>,
}
impl MixedTcpServerHandler {
/// Create a new mixed HTTP+SOCKS5 server handler.
///
/// # Arguments
/// * `auth_info` - Optional username/password for authentication (used for both HTTP and SOCKS5)
/// * `udp_enabled` - Enable UDP functionality for SOCKS5 (UDP ASSOCIATE and UDP-over-TCP)
/// * `bind_ip` - IP address to bind UDP sockets on (should match TCP server)
/// * `proxy_selector` - Proxy selector for outbound connections
/// * `resolver` - DNS resolver
pub fn new(
auth_info: Option<(String, String)>,
udp_enabled: bool,
bind_ip: IpAddr,
proxy_selector: Arc<ClientProxySelector>,
resolver: Arc<dyn Resolver>,
) -> Self {
let http_auth_token = auth_info
.as_ref()
.map(|(username, password)| BASE64.encode(format!("{username}:{password}")));
Self {
auth_info,
http_auth_token,
udp_enabled,
bind_ip,
proxy_selector,
resolver,
}
}
}
#[async_trait]
impl TcpServerHandler for MixedTcpServerHandler {
async fn setup_server_stream(
&self,
mut server_stream: Box<dyn AsyncStream>,
) -> std::io::Result<TcpServerSetupResult> {
let mut stream_reader = StreamReader::new_with_buffer_size(400);
// Peek at first byte to detect protocol
let first_byte = stream_reader.peek_u8(&mut server_stream).await?;
if first_byte == VER_SOCKS5 {
// SOCKS5 protocol
log::debug!("Mixed handler: detected SOCKS5 protocol");
let udp_bind_ip = if self.udp_enabled {
Some(self.bind_ip)
} else {
None
};
setup_socks_server_stream_inner(
self.auth_info.as_ref(),
udp_bind_ip,
&self.proxy_selector,
&self.resolver,
server_stream,
stream_reader,
)
.await
} else {
// HTTP protocol
log::debug!(
"Mixed handler: detected HTTP protocol (first byte: 0x{:02x})",
first_byte
);
setup_http_server_stream_inner(
self.http_auth_token.as_deref(),
server_stream,
stream_reader,
self.proxy_selector.clone(),
)
.await
}
}
}