use std::sync::Arc;
#[cfg(feature = "local-dns")]
use std::{net::IpAddr, time::Duration};
#[cfg(feature = "local-dns")]
use lru_time_cache::LruCache;
use shadowsocks::{
config::ServerType,
context::{Context, SharedContext},
dns_resolver::DnsResolver,
net::{AcceptOpts, ConnectOpts},
relay::Address,
};
#[cfg(feature = "local-dns")]
use tokio::sync::Mutex;
#[cfg(feature = "local-fake-dns")]
use tokio::sync::RwLock;
use crate::{acl::AccessControl, config::SecurityConfig, net::FlowStat};
#[cfg(feature = "local-fake-dns")]
use super::fake_dns::manager::FakeDnsManager;
#[derive(Clone)]
pub struct ServiceContext {
context: SharedContext,
connect_opts: ConnectOpts,
accept_opts: AcceptOpts,
acl: Option<Arc<AccessControl>>,
flow_stat: Arc<FlowStat>,
#[cfg(feature = "local-dns")]
reverse_lookup_cache: Arc<Mutex<LruCache<IpAddr, bool>>>,
#[cfg(feature = "local-fake-dns")]
fake_dns_manager: Arc<RwLock<Vec<Arc<FakeDnsManager>>>>,
}
impl Default for ServiceContext {
fn default() -> Self {
Self::new()
}
}
impl ServiceContext {
pub fn new() -> Self {
Self {
context: Context::new_shared(ServerType::Local),
connect_opts: ConnectOpts::default(),
accept_opts: AcceptOpts::default(),
acl: None,
flow_stat: Arc::new(FlowStat::new()),
#[cfg(feature = "local-dns")]
reverse_lookup_cache: Arc::new(Mutex::new(LruCache::with_expiry_duration_and_capacity(
Duration::from_secs(3 * 24 * 60 * 60),
10240, ))),
#[cfg(feature = "local-fake-dns")]
fake_dns_manager: Arc::new(RwLock::new(Vec::new())),
}
}
pub fn context(&self) -> SharedContext {
self.context.clone()
}
pub fn context_ref(&self) -> &Context {
self.context.as_ref()
}
pub fn set_connect_opts(&mut self, connect_opts: ConnectOpts) {
self.connect_opts = connect_opts;
}
pub fn connect_opts_ref(&self) -> &ConnectOpts {
&self.connect_opts
}
pub fn set_accept_opts(&mut self, accept_opts: AcceptOpts) {
self.accept_opts = accept_opts;
}
pub fn accept_opts(&self) -> AcceptOpts {
self.accept_opts.clone()
}
pub fn set_acl(&mut self, acl: Arc<AccessControl>) {
self.acl = Some(acl);
}
pub fn acl(&self) -> Option<&AccessControl> {
self.acl.as_deref()
}
pub fn flow_stat(&self) -> Arc<FlowStat> {
self.flow_stat.clone()
}
pub fn flow_stat_ref(&self) -> &FlowStat {
self.flow_stat.as_ref()
}
pub fn set_dns_resolver(&mut self, resolver: Arc<DnsResolver>) {
let context = Arc::get_mut(&mut self.context).expect("cannot set DNS resolver on a shared context");
context.set_dns_resolver(resolver)
}
pub fn dns_resolver(&self) -> &DnsResolver {
self.context.dns_resolver()
}
pub async fn check_target_bypassed(&self, addr: &Address) -> bool {
match self.acl {
None => false,
Some(ref acl) => {
#[cfg(feature = "local-dns")]
{
if let Address::SocketAddress(saddr) = addr {
let mut reverse_lookup_cache = self.reverse_lookup_cache.lock().await;
if let Some(forward) = reverse_lookup_cache.get(&saddr.ip()) {
return !*forward;
}
}
}
acl.check_target_bypassed(&self.context, addr).await
}
}
}
#[cfg(feature = "local-dns")]
pub async fn add_to_reverse_lookup_cache(&self, addr: IpAddr, forward: bool) {
let is_exception = forward
!= match self.acl {
None => true,
Some(ref a) => a.check_ip_in_proxy_list(&addr),
};
let mut reverse_lookup_cache = self.reverse_lookup_cache.lock().await;
match reverse_lookup_cache.get_mut(&addr) {
Some(value) => {
if is_exception {
*value = forward;
} else {
reverse_lookup_cache.remove(&addr);
}
}
None => {
if is_exception {
reverse_lookup_cache.insert(addr, forward);
}
}
}
}
pub fn set_ipv6_first(&mut self, ipv6_first: bool) {
let context = Arc::get_mut(&mut self.context).expect("cannot set ipv6_first on a shared context");
context.set_ipv6_first(ipv6_first);
}
pub fn set_security_config(&mut self, security: &SecurityConfig) {
let context = Arc::get_mut(&mut self.context).expect("cannot set security on a shared context");
context.set_replay_attack_policy(security.replay_attack.policy);
}
#[cfg(feature = "local-fake-dns")]
pub async fn add_fake_dns_manager(&self, manager: Arc<FakeDnsManager>) {
let mut managers = self.fake_dns_manager.write().await;
managers.push(manager);
}
#[cfg(feature = "local-fake-dns")]
pub async fn try_map_fake_address(&self, addr: &Address) -> Option<Address> {
let socket_addr = match addr {
Address::DomainNameAddress(..) => return None,
Address::SocketAddress(socket_addr) => socket_addr,
};
let ip_addr = socket_addr.ip();
for mgr in self.fake_dns_manager.read().await.iter() {
if let Ok(Some(name)) = mgr.map_ip_domain(ip_addr).await {
let new_addr = Address::DomainNameAddress(name.to_string(), socket_addr.port());
log::trace!("fakedns mapped {} -> {}", addr, new_addr);
return Some(new_addr);
}
}
None
}
}