use async_trait::async_trait;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use tokio::io::AsyncWriteExt;
use crate::address::NetLocation;
use crate::anytls::anytls_padding::PaddingFactory;
use crate::anytls::anytls_server_session::AnyTlsSession;
use crate::async_stream::AsyncStream;
use crate::client_proxy_selector::ClientProxySelector;
use crate::copy_bidirectional::copy_bidirectional;
use crate::resolver::Resolver;
use crate::stream_reader::StreamReader;
use crate::tcp::tcp_handler::{TcpServerHandler, TcpServerSetupResult};
use crate::util::write_all;
use aws_lc_rs::digest::{SHA256, digest};
#[derive(Debug)]
pub struct AnyTlsServerHandler {
users: HashMap<[u8; 32], String>,
hash_prefixes: HashSet<[u8; 8]>,
padding: Arc<PaddingFactory>,
resolver: Arc<dyn Resolver>,
proxy_provider: Arc<ClientProxySelector>,
udp_enabled: bool,
fallback: Option<NetLocation>,
}
impl AnyTlsServerHandler {
pub fn new(
users: Vec<(String, String)>,
padding: Arc<PaddingFactory>,
resolver: Arc<dyn Resolver>,
proxy_provider: Arc<ClientProxySelector>,
udp_enabled: bool,
fallback: Option<NetLocation>,
) -> Self {
let mut user_map = HashMap::with_capacity(users.len());
let mut hash_prefixes = HashSet::with_capacity(users.len());
for (name, password) in users {
let hash_result = digest(&SHA256, password.as_bytes());
let mut password_hash = [0u8; 32];
password_hash.copy_from_slice(hash_result.as_ref());
let prefix: [u8; 8] = password_hash[..8].try_into().unwrap();
hash_prefixes.insert(prefix);
user_map.insert(password_hash, name);
}
Self {
users: user_map,
hash_prefixes,
padding,
resolver,
proxy_provider,
udp_enabled,
fallback,
}
}
}
#[async_trait]
impl TcpServerHandler for AnyTlsServerHandler {
async fn setup_server_stream(
&self,
mut server_stream: Box<dyn AsyncStream>,
) -> std::io::Result<TcpServerSetupResult> {
let mut reader = StreamReader::new();
let prefix_data = reader.peek_slice(&mut server_stream, 8).await?;
if !self.hash_prefixes.contains(prefix_data) {
log::debug!("AnyTLS quick fallback: 8-byte prefix doesn't match any user");
if let Some(ref fallback) = self.fallback {
return self.fallback_to_dest(server_stream, reader, fallback).await;
}
return Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"authentication failed (prefix mismatch)",
));
}
let auth_data = reader.peek_slice(&mut server_stream, 32).await?;
let user_name = match self.users.get(auth_data) {
Some(name) => {
log::debug!("AnyTLS user authenticated: {}", name);
reader.consume(32);
name.clone()
}
None => {
log::debug!("AnyTLS authentication failed: unknown password");
if let Some(ref fallback) = self.fallback {
return self.fallback_to_dest(server_stream, reader, fallback).await;
}
return Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"authentication failed",
));
}
};
let padding_len = reader.read_u16_be(&mut server_stream).await?;
if padding_len > 0 {
let _ = reader
.read_slice(&mut *server_stream, padding_len as usize)
.await?;
}
let initial_data = reader.unparsed_data_owned();
let session = AnyTlsSession::new_server_with_initial_data(
server_stream,
Arc::clone(&self.padding),
Arc::clone(&self.resolver),
Arc::clone(&self.proxy_provider),
self.udp_enabled,
user_name,
initial_data,
);
Ok(TcpServerSetupResult::Session(Box::pin(async move {
if let Err(e) = session.run().await {
log::debug!("AnyTLS session ended: {}", e);
}
})))
}
}
impl AnyTlsServerHandler {
async fn fallback_to_dest(
&self,
mut client_stream: Box<dyn AsyncStream>,
reader: StreamReader,
fallback: &NetLocation,
) -> std::io::Result<TcpServerSetupResult> {
log::debug!("AnyTLS FALLBACK: Connecting to fallback: {}", fallback);
let unconsumed_data = reader.unparsed_data();
let dest_addr = crate::resolver::resolve_single_address(&self.resolver, fallback).await?;
log::debug!("AnyTLS FALLBACK: Resolved {} to {}", fallback, dest_addr);
let socket = crate::socket_util::new_tcp_socket(None, dest_addr.is_ipv6())?;
let mut dest_stream: Box<dyn AsyncStream> = Box::new(socket.connect(dest_addr).await?);
log::debug!(
"AnyTLS FALLBACK: Connected to fallback, forwarding {} bytes",
unconsumed_data.len()
);
if !unconsumed_data.is_empty() {
write_all(&mut dest_stream, unconsumed_data).await?;
dest_stream.flush().await?;
}
log::debug!("AnyTLS FALLBACK: Spawning bidirectional copy");
Ok(TcpServerSetupResult::Session(Box::pin(async move {
let result = copy_bidirectional(
&mut *client_stream,
&mut *dest_stream,
false, false, )
.await;
futures::join!(
crate::util::shutdown_stream(&mut client_stream),
crate::util::shutdown_stream(&mut dest_stream),
);
if let Err(e) = result {
log::debug!("AnyTLS FALLBACK: Connection ended: {}", e);
} else {
log::debug!("AnyTLS FALLBACK: Connection completed");
}
})))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn compute_password_hash(password: &str) -> [u8; 32] {
let hash_result = digest(&SHA256, password.as_bytes());
let mut hash = [0u8; 32];
hash.copy_from_slice(hash_result.as_ref());
hash
}
#[test]
fn test_password_hashing() {
let hash = compute_password_hash("secret123");
let expected = digest(&SHA256, b"secret123");
let mut expected_bytes = [0u8; 32];
expected_bytes.copy_from_slice(expected.as_ref());
assert_eq!(hash, expected_bytes);
}
#[test]
fn test_different_passwords_different_hashes() {
let hash1 = compute_password_hash("pass1");
let hash2 = compute_password_hash("pass2");
assert_ne!(hash1, hash2);
}
#[test]
fn test_hash_map_and_prefix_construction() {
let users = vec![
("alice".to_string(), "password1".to_string()),
("bob".to_string(), "password2".to_string()),
];
let hash1 = compute_password_hash("password1");
let hash2 = compute_password_hash("password2");
let mut user_map = HashMap::with_capacity(users.len());
let mut hash_prefixes = HashSet::with_capacity(users.len());
for (name, password) in users {
let hash = compute_password_hash(&password);
let prefix: [u8; 8] = hash[..8].try_into().unwrap();
hash_prefixes.insert(prefix);
user_map.insert(hash, name);
}
assert_eq!(user_map.len(), 2);
assert_eq!(hash_prefixes.len(), 2);
let prefix1_slice: &[u8] = &hash1[..8];
let prefix2_slice: &[u8] = &hash2[..8];
assert!(hash_prefixes.contains(prefix1_slice));
assert!(hash_prefixes.contains(prefix2_slice));
let random_prefix: &[u8] = &[0x47, 0x45, 0x54, 0x20, 0x2f, 0x20, 0x48, 0x54]; assert!(!hash_prefixes.contains(random_prefix));
let hash1_slice: &[u8] = &hash1[..];
assert!(user_map.contains_key(hash1_slice));
assert_eq!(user_map.get(hash1_slice).unwrap(), "alice");
let hash2_slice: &[u8] = &hash2[..];
assert_eq!(user_map.get(hash2_slice).unwrap(), "bob");
}
}