mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-11-06 08:14:07 +00:00
feat(local): basic implementation of fake-dns
This commit is contained in:
Generated
+455
-203
File diff suppressed because it is too large
Load Diff
@@ -123,6 +123,8 @@ local-tunnel = ["local", "shadowsocks-service/local-tunnel"]
|
||||
local-socks4 = ["local", "shadowsocks-service/local-socks4"]
|
||||
# Enable Tun interface protocol for sslocal
|
||||
local-tun = ["local", "shadowsocks-service/local-tun", "ipnet"]
|
||||
# Enable Fake DNS for sslocal
|
||||
local-fake-dns = ["local", "shadowsocks-service/local-fake-dns"]
|
||||
|
||||
# ssurl support outline (ssconf) URL
|
||||
utility-url-outline = ["reqwest"]
|
||||
|
||||
@@ -94,6 +94,8 @@ local-tunnel = ["local"]
|
||||
local-socks4 = ["local"]
|
||||
# Enable Tun interface protocol for sslocal
|
||||
local-tun = ["local", "etherparse", "tun", "smoltcp"]
|
||||
# Enable Fake DNS
|
||||
local-fake-dns = ["local", "trust-dns", "sled", "prost", "prost-build"]
|
||||
|
||||
# Enable Stream Cipher Protocol
|
||||
# WARN: Stream Cipher Protocol is proved to be insecure
|
||||
@@ -128,6 +130,8 @@ bytes = "1.6"
|
||||
byte_string = "1.0"
|
||||
byteorder = "1.5"
|
||||
rand = { version = "0.8", features = ["small_rng"] }
|
||||
sled = { version = "0.34.7", features = ["compression"], optional = true }
|
||||
prost = { version = "0.11.9", optional = true }
|
||||
|
||||
futures = "0.3"
|
||||
tokio = { version = "1.5", features = [
|
||||
@@ -186,6 +190,9 @@ nix = { version = "0.28", features = ["ioctl"] }
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-sys = { version = "0.52", features = ["Win32_Networking_WinSock"] }
|
||||
|
||||
[build-dependencies]
|
||||
prost-build = { version = "0.11.9", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
byteorder = "1.5"
|
||||
env_logger = "0.11"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
use std::io;
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
prost_build::compile_protos(
|
||||
&["src/local/fake_dns/proto/fake_dns.proto"],
|
||||
&["src/local/fake_dns/proto/"],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -62,20 +62,15 @@ use cfg_if::cfg_if;
|
||||
use hickory_resolver::config::{NameServerConfig, Protocol, ResolverConfig};
|
||||
#[cfg(feature = "local-tun")]
|
||||
use ipnet::IpNet;
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
use ipnet::{Ipv4Net, Ipv6Net};
|
||||
use log::warn;
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(any(feature = "local-tunnel", feature = "local-dns"))]
|
||||
use shadowsocks::relay::socks5::Address;
|
||||
use shadowsocks::{
|
||||
config::{
|
||||
ManagerAddr,
|
||||
Mode,
|
||||
ReplayAttackPolicy,
|
||||
ServerAddr,
|
||||
ServerConfig,
|
||||
ServerUser,
|
||||
ServerUserManager,
|
||||
ServerWeight,
|
||||
ManagerAddr, Mode, ReplayAttackPolicy, ServerAddr, ServerConfig, ServerUser, ServerUserManager, ServerWeight,
|
||||
},
|
||||
crypto::CipherKind,
|
||||
plugin::PluginConfig,
|
||||
@@ -308,6 +303,20 @@ struct SSLocalExtConfig {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
socks5_auth_config_path: Option<String>,
|
||||
|
||||
/// Fake DNS
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub fake_dns_record_expire_duration: Option<u64>,
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub fake_dns_ipv4_network: Option<String>,
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub fake_dns_ipv6_network: Option<String>,
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub fake_dns_database_path: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
acl: Option<String>,
|
||||
}
|
||||
@@ -760,6 +769,8 @@ pub enum ProtocolType {
|
||||
Dns,
|
||||
#[cfg(feature = "local-tun")]
|
||||
Tun,
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
FakeDns,
|
||||
}
|
||||
|
||||
impl ProtocolType {
|
||||
@@ -777,6 +788,8 @@ impl ProtocolType {
|
||||
ProtocolType::Dns => "dns",
|
||||
#[cfg(feature = "local-tun")]
|
||||
ProtocolType::Tun => "tun",
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
ProtocolType::FakeDns => "fake-dns",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -794,6 +807,8 @@ impl ProtocolType {
|
||||
"dns",
|
||||
#[cfg(feature = "local-tun")]
|
||||
"tun",
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
"fake-dns",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -824,6 +839,8 @@ impl FromStr for ProtocolType {
|
||||
"dns" => Ok(ProtocolType::Dns),
|
||||
#[cfg(feature = "local-tun")]
|
||||
"tun" => Ok(ProtocolType::Tun),
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
"fake-dns" => Ok(ProtocolType::FakeDns),
|
||||
_ => Err(ProtocolTypeError),
|
||||
}
|
||||
}
|
||||
@@ -835,6 +852,7 @@ pub struct LocalConfig {
|
||||
/// Listen address for local servers
|
||||
pub addr: Option<ServerAddr>,
|
||||
|
||||
/// Local Protocol
|
||||
pub protocol: ProtocolType,
|
||||
|
||||
/// Mode
|
||||
@@ -935,6 +953,19 @@ pub struct LocalConfig {
|
||||
/// SOCKS5 Authentication configuration
|
||||
#[cfg(feature = "local")]
|
||||
pub socks5_auth: Socks5AuthConfig,
|
||||
|
||||
/// Fake DNS record expire seconds
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
pub fake_dns_record_expire_duration: Option<Duration>,
|
||||
/// Fake DNS IPv4 allocation space
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
pub fake_dns_ipv4_network: Option<Ipv4Net>,
|
||||
/// Fake DNS IPv6 allocation space
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
pub fake_dns_ipv6_network: Option<Ipv6Net>,
|
||||
/// Fake DNS storage database path
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
pub fake_dns_database_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl LocalConfig {
|
||||
@@ -991,6 +1022,15 @@ impl LocalConfig {
|
||||
|
||||
#[cfg(feature = "local")]
|
||||
socks5_auth: Socks5AuthConfig::default(),
|
||||
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_record_expire_duration: None,
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_ipv4_network: None,
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_ipv6_network: None,
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_database_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1664,6 +1704,36 @@ impl Config {
|
||||
local_config.socks5_auth = Socks5AuthConfig::load_from_file(&socks5_auth_config_path)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
{
|
||||
if let Some(d) = local.fake_dns_record_expire_duration {
|
||||
local_config.fake_dns_record_expire_duration = Some(Duration::from_secs(d));
|
||||
}
|
||||
if let Some(n) = local.fake_dns_ipv4_network {
|
||||
match n.parse::<Ipv4Net>() {
|
||||
Ok(n) => local_config.fake_dns_ipv4_network = Some(n),
|
||||
Err(..) => {
|
||||
let err =
|
||||
Error::new(ErrorKind::Malformed, "invalid `fake_dns_ipv4_network`", None);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(n) = local.fake_dns_ipv6_network {
|
||||
match n.parse::<Ipv6Net>() {
|
||||
Ok(n) => local_config.fake_dns_ipv6_network = Some(n),
|
||||
Err(..) => {
|
||||
let err =
|
||||
Error::new(ErrorKind::Malformed, "invalid `fake_dns_ipv6_network`", None);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(p) = local.fake_dns_database_path {
|
||||
local_config.fake_dns_database_path = Some(p.into());
|
||||
}
|
||||
}
|
||||
|
||||
let mut local_instance = LocalInstanceConfig {
|
||||
config: local_config,
|
||||
acl: None,
|
||||
@@ -2597,6 +2667,18 @@ impl fmt::Display for Config {
|
||||
#[cfg(feature = "local")]
|
||||
socks5_auth_config_path: None,
|
||||
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_record_expire_duration: local.fake_dns_record_expire_duration.map(|d| d.as_secs()),
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_ipv4_network: local.fake_dns_ipv4_network.map(|n| n.to_string()),
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_ipv6_network: local.fake_dns_ipv6_network.map(|n| n.to_string()),
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_database_path: local
|
||||
.fake_dns_database_path
|
||||
.as_ref()
|
||||
.and_then(|n| n.to_str().map(ToOwned::to_owned)),
|
||||
|
||||
acl: local_instance
|
||||
.acl
|
||||
.as_ref()
|
||||
|
||||
@@ -15,9 +15,14 @@ use shadowsocks::{
|
||||
};
|
||||
#[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;
|
||||
|
||||
/// Local Service Context
|
||||
#[derive(Clone)]
|
||||
pub struct ServiceContext {
|
||||
@@ -34,6 +39,9 @@ pub struct ServiceContext {
|
||||
// For DNS relay's ACL domain name reverse lookup -- whether the IP shall be forwarded
|
||||
#[cfg(feature = "local-dns")]
|
||||
reverse_lookup_cache: Arc<Mutex<LruCache<IpAddr, bool>>>,
|
||||
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_manager: Arc<RwLock<Option<Arc<FakeDnsManager>>>>,
|
||||
}
|
||||
|
||||
impl Default for ServiceContext {
|
||||
@@ -56,6 +64,8 @@ impl ServiceContext {
|
||||
Duration::from_secs(3 * 24 * 60 * 60),
|
||||
10240, // XXX: It should be enough for a normal user.
|
||||
))),
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_manager: Arc::new(RwLock::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,4 +190,35 @@ impl ServiceContext {
|
||||
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);
|
||||
}
|
||||
|
||||
/// Set Fake DNS manager
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
pub async fn set_fake_dns_manager(&self, manager: Arc<FakeDnsManager>) {
|
||||
let mut opt = self.fake_dns_manager.write().await;
|
||||
assert!(opt.is_none(), "one server instance can have only one fake-dns instance");
|
||||
*opt = Some(manager);
|
||||
}
|
||||
|
||||
/// Fake DNS maps IP to Domain
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
pub async fn try_map_fake_address(&self, addr: &Address) -> Option<Address> {
|
||||
match *(self.fake_dns_manager.read().await) {
|
||||
None => None,
|
||||
Some(ref mgr) => match addr {
|
||||
Address::DomainNameAddress(..) => None,
|
||||
Address::SocketAddress(socket_addr) => {
|
||||
let ip_addr = socket_addr.ip();
|
||||
match mgr.map_ip_domain(ip_addr).await {
|
||||
Ok(None) => None,
|
||||
Ok(Some(name)) => {
|
||||
let new_addr = Address::DomainNameAddress(name.to_string(), socket_addr.port());
|
||||
log::trace!("fakedns mapped {} -> {}", addr, new_addr);
|
||||
Some(new_addr)
|
||||
}
|
||||
Err(..) => None,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
//! Fake DNS manager
|
||||
|
||||
use std::{
|
||||
io::{self, Cursor},
|
||||
iter::Cycle,
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||
path::Path,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use hickory_resolver::proto::rr::Name;
|
||||
use ipnet::{Ipv4AddrRange, Ipv4Net, Ipv6AddrRange, Ipv6Net};
|
||||
use log::{trace, warn};
|
||||
use prost::Message;
|
||||
use sled::{Config as SledConfig, Db as SledDatabase};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use super::proto;
|
||||
|
||||
const FAKE_DNS_MANAGER_STORAGE_VERSION: u32 = 1;
|
||||
|
||||
/// Fake DNS manager
|
||||
pub struct FakeDnsManager {
|
||||
db: SledDatabase,
|
||||
ipv4_network: Mutex<Cycle<Ipv4AddrRange>>,
|
||||
ipv6_network: Mutex<Cycle<Ipv6AddrRange>>,
|
||||
expire_duration: Duration,
|
||||
}
|
||||
|
||||
macro_rules! map_domain_ip {
|
||||
($self:ident, $domain:ident, $addr_ty:ty, $addr_field:ident, $network_field:ident) => {{
|
||||
let name2ip_key = FakeDnsManager::get_name2ip_key($domain);
|
||||
|
||||
loop {
|
||||
let name2ip_value = $self.db.get(&name2ip_key)?;
|
||||
|
||||
let mut domain_name_mapping = proto::DomainNameMapping::default();
|
||||
|
||||
if let Some(ref v) = name2ip_value {
|
||||
domain_name_mapping = proto::DomainNameMapping::decode(&mut Cursor::new(v))?;
|
||||
|
||||
if !domain_name_mapping.$addr_field.is_empty() {
|
||||
match domain_name_mapping.$addr_field.parse::<$addr_ty>() {
|
||||
Ok(i) => {
|
||||
let now = FakeDnsManager::get_current_timestamp();
|
||||
let expire_secs =
|
||||
FakeDnsManager::get_current_timestamp() + $self.expire_duration.as_secs() as i64;
|
||||
|
||||
let mut reallocate = true;
|
||||
if domain_name_mapping.expire_time >= now {
|
||||
// Not expired yet.
|
||||
reallocate = false;
|
||||
} else {
|
||||
// Expired. Try to reuse.
|
||||
|
||||
let ip2name_key = FakeDnsManager::get_ip2name_key(i.into());
|
||||
let ip2name_value = $self.db.get(&ip2name_key)?;
|
||||
if let Some(ref v) = ip2name_value {
|
||||
let mut ip_mapping = proto::IpAddrMapping::decode(&mut Cursor::new(v))?;
|
||||
if ip_mapping.domain_name == $domain.to_string() {
|
||||
// Try to extend its expire time
|
||||
ip_mapping.expire_time = expire_secs;
|
||||
let nv = ip_mapping.encode_to_vec();
|
||||
if let Ok(..) =
|
||||
$self
|
||||
.db
|
||||
.compare_and_swap(&ip2name_key, ip2name_value.as_ref(), Some(nv))
|
||||
{
|
||||
reallocate = false;
|
||||
} else {
|
||||
// CAS Failed, retry
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !reallocate {
|
||||
domain_name_mapping.expire_time = expire_secs;
|
||||
let nv = domain_name_mapping.encode_to_vec();
|
||||
|
||||
// Ignore update error. It is ok if expire_time is updated by another thread.
|
||||
let _ = $self
|
||||
.db
|
||||
.compare_and_swap(&name2ip_key, name2ip_value.as_ref(), Some(nv));
|
||||
trace!(
|
||||
"fakedns mapping {} -> {}, expires {}",
|
||||
$domain,
|
||||
i,
|
||||
domain_name_mapping.expire_time
|
||||
);
|
||||
return Ok((i, $self.expire_duration));
|
||||
}
|
||||
}
|
||||
Err(..) => {
|
||||
warn!("failed to parse {}, going to replace", domain_name_mapping.$addr_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a new IPv4 address for this domain
|
||||
'alloc_network: while let Some(ip) = $self.$network_field.lock().await.next() {
|
||||
let ip2name_key = FakeDnsManager::get_ip2name_key(ip.into());
|
||||
|
||||
loop {
|
||||
let ip2name_value = $self.db.get(&ip2name_key)?;
|
||||
if let Some(ref v) = ip2name_value {
|
||||
let ip_mapping = proto::IpAddrMapping::decode(&mut Cursor::new(v))?;
|
||||
|
||||
let now = FakeDnsManager::get_current_timestamp();
|
||||
if ip_mapping.expire_time > now {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut ip_mapping = proto::IpAddrMapping::default();
|
||||
|
||||
let expire_secs = FakeDnsManager::get_current_timestamp() + $self.expire_duration.as_secs() as i64;
|
||||
ip_mapping.expire_time = expire_secs;
|
||||
ip_mapping.domain_name = $domain.to_string();
|
||||
|
||||
let nv = ip_mapping.encode_to_vec();
|
||||
|
||||
if let Ok(..) = $self.db.compare_and_swap(&ip2name_key, ip2name_value, Some(nv.clone())) {
|
||||
// Replace name2ip
|
||||
|
||||
domain_name_mapping.$addr_field = ip.to_string();
|
||||
domain_name_mapping.expire_time = ip_mapping.expire_time;
|
||||
let nv = domain_name_mapping.encode_to_vec();
|
||||
|
||||
if let Ok(..) = $self
|
||||
.db
|
||||
.compare_and_swap(&name2ip_key, name2ip_value.as_ref(), Some(nv))
|
||||
{
|
||||
trace!(
|
||||
"fakedns mapping {} -> {}, expires {} created",
|
||||
$domain,
|
||||
ip,
|
||||
domain_name_mapping.expire_time
|
||||
);
|
||||
|
||||
return Ok((ip, $self.expire_duration));
|
||||
} else {
|
||||
// name2ip CAS failed. Some other thread already allocated an address for this name.
|
||||
let _ = $self.db.remove(&ip2name_key);
|
||||
break 'alloc_network;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
impl FakeDnsManager {
|
||||
pub fn open<P: AsRef<Path>>(
|
||||
db_path: P,
|
||||
ipv4_network: Ipv4Net,
|
||||
ipv6_network: Ipv6Net,
|
||||
expire_duration: Duration,
|
||||
) -> io::Result<FakeDnsManager> {
|
||||
let db = SledConfig::new()
|
||||
.cache_capacity(10 * 1024 * 1024)
|
||||
.use_compression(true)
|
||||
.mode(sled::Mode::HighThroughput)
|
||||
.flush_every_ms(Some(1 * 1000))
|
||||
.path(db_path)
|
||||
.open()?;
|
||||
|
||||
let ipv4_network_str = ipv4_network.to_string();
|
||||
let ipv6_network_str = ipv6_network.to_string();
|
||||
|
||||
let mut recreate_database = true;
|
||||
|
||||
let key = "shadowsocks_fakedns_meta";
|
||||
if let Some(v) = db.get(key)? {
|
||||
let c = proto::StorageMeta::decode(&mut Cursor::new(&v))?;
|
||||
if c.version == FAKE_DNS_MANAGER_STORAGE_VERSION {
|
||||
if ipv4_network_str != c.ipv4_network || ipv6_network_str != c.ipv6_network {
|
||||
warn!(
|
||||
"IPv4 network {} (storage {}), IPv6 network {} (storage {}) not match",
|
||||
ipv4_network_str, c.ipv4_network, ipv6_network_str, c.ipv6_network
|
||||
);
|
||||
} else {
|
||||
recreate_database = false;
|
||||
}
|
||||
} else {
|
||||
warn!("storage version {} not match", c.version);
|
||||
}
|
||||
}
|
||||
|
||||
if recreate_database {
|
||||
let _ = db.clear();
|
||||
|
||||
let mut c = proto::StorageMeta::default();
|
||||
c.ipv4_network = ipv4_network_str;
|
||||
c.ipv6_network = ipv6_network_str;
|
||||
c.version = FAKE_DNS_MANAGER_STORAGE_VERSION;
|
||||
|
||||
let v = c.encode_to_vec();
|
||||
db.insert(key, v)?;
|
||||
|
||||
trace!("FakeDNS database created. {:?}", c);
|
||||
}
|
||||
|
||||
Ok(FakeDnsManager {
|
||||
db,
|
||||
ipv4_network: Mutex::new(ipv4_network.hosts().cycle()),
|
||||
ipv6_network: Mutex::new(ipv6_network.hosts().cycle()),
|
||||
expire_duration,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_current_timestamp() -> i64 {
|
||||
SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.expect("SystemTime")
|
||||
.as_secs() as i64
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_name2ip_key(domain: &Name) -> String {
|
||||
format!("shadowsocks_fakedns_name2ip_{domain}")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_ip2name_key(ip: IpAddr) -> String {
|
||||
format!("shadowsocks_fakedns_ip2name_{ip}")
|
||||
}
|
||||
|
||||
/// Get or create an IPv4 mapping for `domain`
|
||||
pub async fn map_domain_ipv4(&self, domain: &Name) -> io::Result<(Ipv4Addr, Duration)> {
|
||||
map_domain_ip!(self, domain, Ipv4Addr, ipv4_addr, ipv4_network)
|
||||
}
|
||||
|
||||
/// Get or create an IPv6 mapping for `domain`
|
||||
pub async fn map_domain_ipv6(&self, domain: &Name) -> io::Result<(Ipv6Addr, Duration)> {
|
||||
map_domain_ip!(self, domain, Ipv6Addr, ipv6_addr, ipv6_network)
|
||||
}
|
||||
|
||||
/// Get IP mapped domain name
|
||||
pub async fn map_ip_domain(&self, ip: IpAddr) -> io::Result<Option<Name>> {
|
||||
let ip2name_key = FakeDnsManager::get_ip2name_key(ip);
|
||||
|
||||
let ip2name_value = self.db.get(&ip2name_key)?;
|
||||
match ip2name_value {
|
||||
None => Ok(None),
|
||||
Some(ref v) => {
|
||||
let mut ip_mapping = proto::IpAddrMapping::decode(&mut Cursor::new(v))?;
|
||||
|
||||
let now = FakeDnsManager::get_current_timestamp();
|
||||
if ip_mapping.expire_time >= now {
|
||||
// Ok. It is not expired yet. Try to extend its expire time.
|
||||
ip_mapping.expire_time = now + self.expire_duration.as_secs() as i64;
|
||||
let nv = ip_mapping.encode_to_vec();
|
||||
let _ = self
|
||||
.db
|
||||
.compare_and_swap(&ip2name_key, ip2name_value.as_ref(), Some(nv))?;
|
||||
|
||||
// Update name2ip's expire time
|
||||
|
||||
let name = match ip_mapping.domain_name.parse::<Name>() {
|
||||
Ok(n) => n,
|
||||
Err(..) => return Ok(None),
|
||||
};
|
||||
|
||||
{
|
||||
let name2ip_key = FakeDnsManager::get_name2ip_key(&name);
|
||||
let name2ip_value = self.db.get(&name2ip_key)?;
|
||||
match name2ip_value {
|
||||
Some(ref v) => {
|
||||
let mut domain_name_mapping = proto::DomainNameMapping::decode(&mut Cursor::new(v))?;
|
||||
domain_name_mapping.expire_time = ip_mapping.expire_time;
|
||||
let nv = domain_name_mapping.encode_to_vec();
|
||||
let _ = self.db.compare_and_swap(&name2ip_key, name2ip_value.as_ref(), Some(nv));
|
||||
}
|
||||
None => {
|
||||
// Interesting. No name2ip.
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(name))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//! Fake DNS
|
||||
//!
|
||||
//! Start a DNS resolver server, mapping requested names to local private network addresses.
|
||||
//! When local server receives proxy request with those mapped addresses, it could translate
|
||||
//! it back to the original domain names.
|
||||
//!
|
||||
//! It normally cooperates with `local-redir` and `local-tun`.
|
||||
|
||||
pub use self::server::{FakeDns, FakeDnsBuilder};
|
||||
|
||||
pub mod manager;
|
||||
mod processor;
|
||||
mod proto;
|
||||
pub mod server;
|
||||
mod tcp_server;
|
||||
mod udp_server;
|
||||
@@ -0,0 +1,64 @@
|
||||
//! DNS request processor
|
||||
|
||||
use std::io;
|
||||
|
||||
use hickory_resolver::proto::{
|
||||
op::{header::MessageType, response_code::ResponseCode, Message, OpCode},
|
||||
rr::{
|
||||
rdata::{A, AAAA},
|
||||
DNSClass, RData, Record, RecordType,
|
||||
},
|
||||
};
|
||||
use log::{debug, trace};
|
||||
|
||||
use super::manager::FakeDnsManager;
|
||||
|
||||
pub async fn handle_dns_request(req_message: &Message, manager: &FakeDnsManager) -> io::Result<Message> {
|
||||
let mut rsp_message = Message::new();
|
||||
rsp_message.set_id(req_message.id());
|
||||
rsp_message.set_message_type(MessageType::Response);
|
||||
rsp_message.set_recursion_desired(false);
|
||||
rsp_message.set_recursion_available(false);
|
||||
|
||||
if req_message.op_code() != OpCode::Query || req_message.message_type() != MessageType::Query {
|
||||
rsp_message.set_response_code(ResponseCode::NotImp);
|
||||
} else {
|
||||
for query in req_message.queries() {
|
||||
if query.query_class() != DNSClass::IN {
|
||||
let record = Record::<RData>::with(query.name().clone(), query.query_type(), 0);
|
||||
rsp_message.add_answer(record);
|
||||
continue;
|
||||
}
|
||||
|
||||
match query.query_type() {
|
||||
RecordType::A => {
|
||||
let (ip_addr, expire_duration) = manager.map_domain_ipv4(query.name()).await?;
|
||||
let [a, b, c, d] = ip_addr.octets();
|
||||
|
||||
let mut record =
|
||||
Record::<RData>::with(query.name().clone(), RecordType::A, expire_duration.as_secs() as u32);
|
||||
record.set_dns_class(query.query_class());
|
||||
record.set_data(Some(RData::A(A::new(a, b, c, d))));
|
||||
rsp_message.add_answer(record);
|
||||
}
|
||||
RecordType::AAAA => {
|
||||
let (ip_addr, expire_duration) = manager.map_domain_ipv6(query.name()).await?;
|
||||
let [a, b, c, d, e, f, g, h] = ip_addr.segments();
|
||||
|
||||
let mut record =
|
||||
Record::<RData>::with(query.name().clone(), RecordType::AAAA, expire_duration.as_secs() as u32);
|
||||
record.set_dns_class(query.query_class());
|
||||
record.set_data(Some(RData::AAAA(AAAA::new(a, b, c, d, e, f, g, h))));
|
||||
rsp_message.add_answer(record);
|
||||
}
|
||||
_ => {
|
||||
debug!("fakedns {} not supported. {:?}", query.query_type(), query);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trace!("QUERY {:?} ANSWER {:?}", req_message, rsp_message);
|
||||
|
||||
Ok(rsp_message)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package shadowsocks_service.local.fake_dns.proto;
|
||||
|
||||
message StorageMeta {
|
||||
string ipv4_network = 1; // IPv4 allocation pool
|
||||
string ipv6_network = 2; // IPv6 allocation pool
|
||||
uint32 version = 3; // Storage Version
|
||||
}
|
||||
|
||||
// ip2domain_{IP_ADDR}
|
||||
message IpAddrMapping {
|
||||
string domain_name = 1; // Original Domain
|
||||
int64 expire_time = 2;
|
||||
}
|
||||
|
||||
// domain2ip_{DOMAIN_NAME}
|
||||
message DomainNameMapping {
|
||||
string ipv4_addr = 1; // Mapped IPv4 Address
|
||||
string ipv6_addr = 2; // Mapped IPv6 Address
|
||||
int64 expire_time = 3;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
//! FakeDNS protobuf
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/shadowsocks_service.local.fake_dns.proto.rs"));
|
||||
@@ -0,0 +1,138 @@
|
||||
//! Fake DNS server
|
||||
|
||||
use std::{
|
||||
io,
|
||||
net::{Ipv4Addr, Ipv6Addr},
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use futures::{future, FutureExt};
|
||||
use ipnet::{Ipv4Net, Ipv6Net};
|
||||
use shadowsocks::config::{Mode, ServerAddr};
|
||||
|
||||
use crate::local::context::ServiceContext;
|
||||
|
||||
use super::{manager::FakeDnsManager, tcp_server::FakeDnsTcpServer, udp_server::FakeDnsUdpServer};
|
||||
|
||||
/// Fake DNS builder
|
||||
pub struct FakeDnsBuilder {
|
||||
context: Arc<ServiceContext>,
|
||||
mode: Mode,
|
||||
client_addr: ServerAddr,
|
||||
database_path: PathBuf,
|
||||
ipv4_network: Ipv4Net,
|
||||
ipv6_network: Ipv6Net,
|
||||
expire_duration: Duration,
|
||||
}
|
||||
|
||||
impl FakeDnsBuilder {
|
||||
/// Create a new Fake DNS server
|
||||
pub fn new(client_addr: ServerAddr) -> FakeDnsBuilder {
|
||||
let context = ServiceContext::new();
|
||||
FakeDnsBuilder::with_context(Arc::new(context), client_addr)
|
||||
}
|
||||
|
||||
/// Create a new Fake DNS server with context
|
||||
pub fn with_context(context: Arc<ServiceContext>, client_addr: ServerAddr) -> FakeDnsBuilder {
|
||||
FakeDnsBuilder {
|
||||
context,
|
||||
mode: Mode::TcpAndUdp,
|
||||
client_addr,
|
||||
database_path: "shadowsocks-fakedns.sled".into(),
|
||||
ipv4_network: Ipv4Net::new(Ipv4Addr::new(172, 16, 0, 0), 12).unwrap(),
|
||||
ipv6_network: Ipv6Net::new(Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0), 7).unwrap(),
|
||||
expire_duration: Duration::from_secs(10),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set IPv4 network
|
||||
pub fn set_ipv4_network(&mut self, ipv4_network: Ipv4Net) {
|
||||
self.ipv4_network = ipv4_network;
|
||||
}
|
||||
|
||||
/// Set IPv6 network
|
||||
pub fn set_ipv6_network(&mut self, ipv6_network: Ipv6Net) {
|
||||
self.ipv6_network = ipv6_network;
|
||||
}
|
||||
|
||||
/// Set expire duration
|
||||
pub fn set_expire_duration(&mut self, expire: Duration) {
|
||||
self.expire_duration = expire;
|
||||
}
|
||||
|
||||
/// Set database path
|
||||
pub fn set_database_path<P: AsRef<Path>>(&mut self, database_path: P) {
|
||||
self.database_path = database_path.as_ref().to_path_buf();
|
||||
}
|
||||
|
||||
/// Build Fake DNS server
|
||||
pub async fn build(self) -> io::Result<FakeDns> {
|
||||
let manager = FakeDnsManager::open(
|
||||
&self.database_path,
|
||||
self.ipv4_network,
|
||||
self.ipv6_network,
|
||||
self.expire_duration,
|
||||
)?;
|
||||
let manager = Arc::new(manager);
|
||||
|
||||
let mut tcp_server = None;
|
||||
if self.mode.enable_tcp() {
|
||||
let server = FakeDnsTcpServer::new(self.context.clone(), &self.client_addr, manager.clone()).await?;
|
||||
tcp_server = Some(server);
|
||||
}
|
||||
|
||||
let mut udp_server = None;
|
||||
if self.mode.enable_udp() {
|
||||
let server = FakeDnsUdpServer::new(self.context.clone(), &self.client_addr, manager.clone()).await?;
|
||||
udp_server = Some(server);
|
||||
}
|
||||
|
||||
Ok(FakeDns {
|
||||
tcp_server,
|
||||
udp_server,
|
||||
manager,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Fake DNS server instance
|
||||
pub struct FakeDns {
|
||||
tcp_server: Option<FakeDnsTcpServer>,
|
||||
udp_server: Option<FakeDnsUdpServer>,
|
||||
manager: Arc<FakeDnsManager>,
|
||||
}
|
||||
|
||||
impl FakeDns {
|
||||
/// TCP Server instance
|
||||
pub fn tcp_server(&self) -> Option<&FakeDnsTcpServer> {
|
||||
self.tcp_server.as_ref()
|
||||
}
|
||||
|
||||
/// UDP Server instance
|
||||
pub fn udp_server(&self) -> Option<&FakeDnsUdpServer> {
|
||||
self.udp_server.as_ref()
|
||||
}
|
||||
|
||||
/// Get the manager
|
||||
pub fn clone_manager(&self) -> Arc<FakeDnsManager> {
|
||||
self.manager.clone()
|
||||
}
|
||||
|
||||
/// Run server
|
||||
pub async fn run(self) -> io::Result<()> {
|
||||
let mut vfut = Vec::new();
|
||||
|
||||
if let Some(tcp_server) = self.tcp_server {
|
||||
vfut.push(tcp_server.run().boxed());
|
||||
}
|
||||
|
||||
if let Some(udp_server) = self.udp_server {
|
||||
vfut.push(udp_server.run().boxed());
|
||||
}
|
||||
|
||||
let (res, ..) = future::select_all(vfut).await;
|
||||
res
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
//! Fake DNS TCP server
|
||||
|
||||
use std::{
|
||||
io::{self, ErrorKind},
|
||||
net::SocketAddr,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use hickory_resolver::proto::{
|
||||
op::{header::MessageType, response_code::ResponseCode, Message},
|
||||
serialize::binary::{BinEncodable, BinEncoder, EncodeMode},
|
||||
};
|
||||
use log::{error, trace};
|
||||
use shadowsocks::{lookup_then, net::TcpListener as ShadowTcpListener, ServerAddr};
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::TcpStream,
|
||||
time,
|
||||
};
|
||||
|
||||
use crate::local::context::ServiceContext;
|
||||
|
||||
use super::{manager::FakeDnsManager, processor::handle_dns_request};
|
||||
|
||||
/// Fake DNS TCP server
|
||||
pub struct FakeDnsTcpServer {
|
||||
context: Arc<ServiceContext>,
|
||||
listener: ShadowTcpListener,
|
||||
manager: Arc<FakeDnsManager>,
|
||||
}
|
||||
|
||||
impl FakeDnsTcpServer {
|
||||
pub(crate) async fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
client_config: &ServerAddr,
|
||||
manager: Arc<FakeDnsManager>,
|
||||
) -> io::Result<FakeDnsTcpServer> {
|
||||
let listener = match *client_config {
|
||||
ServerAddr::SocketAddr(ref saddr) => {
|
||||
ShadowTcpListener::bind_with_opts(saddr, context.accept_opts()).await?
|
||||
}
|
||||
ServerAddr::DomainName(ref dname, port) => {
|
||||
lookup_then!(context.context_ref(), dname, port, |addr| {
|
||||
ShadowTcpListener::bind_with_opts(&addr, context.accept_opts()).await
|
||||
})?
|
||||
.1
|
||||
}
|
||||
};
|
||||
|
||||
Ok(FakeDnsTcpServer {
|
||||
context,
|
||||
listener,
|
||||
manager,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get TCP local address
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.listener.local_addr()
|
||||
}
|
||||
|
||||
/// Run TCP server loop
|
||||
pub async fn run(self) -> io::Result<()> {
|
||||
loop {
|
||||
let (stream, peer_addr) = match self.listener.accept().await {
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
error!("failed to accept Fake DNS connection, err: {}", err);
|
||||
time::sleep(Duration::from_secs(1)).await;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
trace!("Fake DNS accepted TCP client {}", peer_addr);
|
||||
|
||||
let context = self.context.clone();
|
||||
let manager = self.manager.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = FakeDnsTcpServer::handle_client(context, peer_addr, stream, manager).await {
|
||||
error!(
|
||||
"failed to handle Fake DNS tcp client, peer: {}, err: {}",
|
||||
peer_addr, err
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_client(
|
||||
_context: Arc<ServiceContext>,
|
||||
peer_addr: SocketAddr,
|
||||
mut stream: TcpStream,
|
||||
manager: Arc<FakeDnsManager>,
|
||||
) -> io::Result<()> {
|
||||
let mut length_buf = [0u8; 2];
|
||||
let mut message_buf = BytesMut::new();
|
||||
|
||||
loop {
|
||||
match stream.read_exact(&mut length_buf).await {
|
||||
Ok(..) => {}
|
||||
Err(ref err) if err.kind() == ErrorKind::UnexpectedEof => {
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
error!("udp tcp {} read length failed, error: {}", peer_addr, err);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
let length = BigEndian::read_u16(&length_buf) as usize;
|
||||
|
||||
message_buf.clear();
|
||||
message_buf.reserve(length);
|
||||
unsafe {
|
||||
message_buf.advance_mut(length);
|
||||
}
|
||||
|
||||
match stream.read_exact(&mut message_buf).await {
|
||||
Ok(..) => {}
|
||||
Err(err) => {
|
||||
error!("dns tcp {} read message failed, error: {}", peer_addr, err);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
let req_message = match Message::from_vec(&message_buf) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
error!("dns tcp {} parse message failed, error: {}", peer_addr, err);
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
|
||||
let rsp_message = match handle_dns_request(&req_message, &manager).await {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
error!("failed to handle DNS request, error: {}", err);
|
||||
|
||||
let mut rsp_message = Message::new();
|
||||
rsp_message.set_id(req_message.id());
|
||||
rsp_message.set_message_type(MessageType::Response);
|
||||
rsp_message.set_response_code(ResponseCode::ServFail);
|
||||
|
||||
rsp_message
|
||||
}
|
||||
};
|
||||
|
||||
let mut rsp_buffer = Vec::with_capacity(2 + 512);
|
||||
rsp_buffer.resize(2, 0);
|
||||
let mut rsp_encoder = BinEncoder::with_offset(&mut rsp_buffer, 2, EncodeMode::Normal);
|
||||
rsp_message.emit(&mut rsp_encoder)?;
|
||||
|
||||
let rsp_length = (rsp_buffer.len() - 2) as u16;
|
||||
BigEndian::write_u16(&mut rsp_buffer[0..2], rsp_length);
|
||||
|
||||
stream.write_all(&rsp_buffer).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//! Fake DNS UDP server
|
||||
|
||||
use std::{io, net::SocketAddr, sync::Arc, time::Duration};
|
||||
|
||||
use hickory_resolver::proto::op::{header::MessageType, response_code::ResponseCode, Message};
|
||||
use log::error;
|
||||
use shadowsocks::{lookup_then, net::UdpSocket as ShadowUdpSocket, ServerAddr};
|
||||
use tokio::time;
|
||||
|
||||
use crate::local::context::ServiceContext;
|
||||
|
||||
use super::{manager::FakeDnsManager, processor::handle_dns_request};
|
||||
|
||||
/// Fake DNS UDP server instance
|
||||
pub struct FakeDnsUdpServer {
|
||||
listener: ShadowUdpSocket,
|
||||
manager: Arc<FakeDnsManager>,
|
||||
}
|
||||
|
||||
impl FakeDnsUdpServer {
|
||||
pub(crate) async fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
client_config: &ServerAddr,
|
||||
manager: Arc<FakeDnsManager>,
|
||||
) -> io::Result<FakeDnsUdpServer> {
|
||||
let listener = match *client_config {
|
||||
ServerAddr::SocketAddr(ref saddr) => {
|
||||
ShadowUdpSocket::listen_with_opts(saddr, context.accept_opts()).await?
|
||||
}
|
||||
ServerAddr::DomainName(ref dname, port) => {
|
||||
lookup_then!(context.context_ref(), dname, port, |addr| {
|
||||
ShadowUdpSocket::listen_with_opts(&addr, context.accept_opts()).await
|
||||
})?
|
||||
.1
|
||||
}
|
||||
};
|
||||
|
||||
Ok(FakeDnsUdpServer { listener, manager })
|
||||
}
|
||||
|
||||
/// Get UDP local address
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.listener.local_addr()
|
||||
}
|
||||
|
||||
/// Start server accept loop
|
||||
pub async fn run(self) -> io::Result<()> {
|
||||
let mut buffer = [0u8; 65535];
|
||||
loop {
|
||||
let (n, peer_addr) = match self.listener.recv_from(&mut buffer).await {
|
||||
Ok(n) => n,
|
||||
Err(err) => {
|
||||
error!("fakedns UDP recv_from failed, error: {}", err);
|
||||
time::sleep(Duration::from_secs(1)).await;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let req_message = match Message::from_vec(&buffer[..n]) {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
error!("failed to parse DNS request, error: {}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let rsp_message = match handle_dns_request(&req_message, &self.manager).await {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
error!("failed to handle DNS request, error: {}", err);
|
||||
|
||||
let mut rsp_message = Message::new();
|
||||
rsp_message.set_id(req_message.id());
|
||||
rsp_message.set_message_type(MessageType::Response);
|
||||
rsp_message.set_response_code(ResponseCode::ServFail);
|
||||
|
||||
rsp_message
|
||||
}
|
||||
};
|
||||
|
||||
let rsp_buffer = rsp_message.to_vec()?;
|
||||
let _ = self.listener.send_to(&rsp_buffer, peer_addr).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ use self::{
|
||||
|
||||
#[cfg(feature = "local-dns")]
|
||||
use self::dns::{Dns, DnsBuilder};
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
use self::fake_dns::{FakeDns, FakeDnsBuilder};
|
||||
#[cfg(feature = "local-http")]
|
||||
use self::http::{Http, HttpBuilder};
|
||||
#[cfg(feature = "local-redir")]
|
||||
@@ -44,6 +46,8 @@ use self::tunnel::{Tunnel, TunnelBuilder};
|
||||
pub mod context;
|
||||
#[cfg(feature = "local-dns")]
|
||||
pub mod dns;
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
pub mod fake_dns;
|
||||
#[cfg(feature = "local-http")]
|
||||
pub mod http;
|
||||
pub mod loadbalancing;
|
||||
@@ -97,6 +101,8 @@ pub struct Server {
|
||||
dns_servers: Vec<Dns>,
|
||||
#[cfg(feature = "local-redir")]
|
||||
redir_servers: Vec<Redir>,
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_servers: Vec<FakeDns>,
|
||||
#[cfg(feature = "local-flow-stat")]
|
||||
local_stat_addr: Option<LocalFlowStatAddress>,
|
||||
#[cfg(feature = "local-flow-stat")]
|
||||
@@ -239,6 +245,8 @@ impl Server {
|
||||
dns_servers: Vec::new(),
|
||||
#[cfg(feature = "local-redir")]
|
||||
redir_servers: Vec::new(),
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
fake_dns_servers: Vec::new(),
|
||||
#[cfg(feature = "local-flow-stat")]
|
||||
local_stat_addr: config.local_stat_addr,
|
||||
#[cfg(feature = "local-flow-stat")]
|
||||
@@ -481,6 +489,32 @@ impl Server {
|
||||
let server = builder.build().await?;
|
||||
local_server.tun_servers.push(server);
|
||||
}
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
ProtocolType::FakeDns => {
|
||||
let client_addr = match local_config.addr {
|
||||
Some(a) => a,
|
||||
None => return Err(io::Error::new(ErrorKind::Other, "dns requires local address")),
|
||||
};
|
||||
|
||||
let mut builder = FakeDnsBuilder::new(client_addr);
|
||||
if let Some(n) = local_config.fake_dns_ipv4_network {
|
||||
builder.set_ipv4_network(n);
|
||||
}
|
||||
if let Some(n) = local_config.fake_dns_ipv6_network {
|
||||
builder.set_ipv6_network(n);
|
||||
}
|
||||
if let Some(exp) = local_config.fake_dns_record_expire_duration {
|
||||
builder.set_expire_duration(exp);
|
||||
}
|
||||
if let Some(p) = local_config.fake_dns_database_path {
|
||||
builder.set_database_path(p);
|
||||
}
|
||||
let server = builder.build().await?;
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
context.set_fake_dns_manager(server.clone_manager()).await;
|
||||
|
||||
local_server.fake_dns_servers.push(server);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,6 +554,11 @@ impl Server {
|
||||
vfut.push(ServerHandle(tokio::spawn(svr.run())));
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
for svr in self.fake_dns_servers {
|
||||
vfut.push(ServerHandle(tokio::spawn(svr.run())));
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-flow-stat")]
|
||||
if let Some(stat_addr) = self.local_stat_addr {
|
||||
// For Android's flow statistic
|
||||
@@ -571,6 +610,12 @@ impl Server {
|
||||
pub fn redir_servers(&self) -> &[Redir] {
|
||||
&self.redir_servers
|
||||
}
|
||||
|
||||
/// Get Fake DNS instances
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
pub fn fake_dns_servers(&self) -> &[FakeDns] {
|
||||
&self.fake_dns_servers
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-flow-stat")]
|
||||
|
||||
@@ -54,7 +54,12 @@ impl AutoProxyClientStream {
|
||||
A: Into<Address>,
|
||||
{
|
||||
// Connect directly.
|
||||
let addr = addr.into();
|
||||
#[cfg_attr(not(feature = "local-fake-dns"), allow(unused_mut))]
|
||||
let mut addr = addr.into();
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
if let Some(mapped_addr) = context.try_map_fake_address(&addr).await {
|
||||
addr = mapped_addr;
|
||||
}
|
||||
let stream =
|
||||
TcpStream::connect_remote_with_opts(context.context_ref(), &addr, context.connect_opts_ref()).await?;
|
||||
Ok(AutoProxyClientStream::Bypassed(stream))
|
||||
@@ -69,6 +74,12 @@ impl AutoProxyClientStream {
|
||||
where
|
||||
A: Into<Address>,
|
||||
{
|
||||
#[cfg_attr(not(feature = "local-fake-dns"), allow(unused_mut))]
|
||||
let mut addr = addr.into();
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
if let Some(mapped_addr) = context.try_map_fake_address(&addr).await {
|
||||
addr = mapped_addr;
|
||||
}
|
||||
let flow_stat = context.flow_stat();
|
||||
let stream = match ProxyClientStream::connect_with_opts_map(
|
||||
context.context(),
|
||||
|
||||
@@ -29,9 +29,7 @@ use shadowsocks::{
|
||||
use crate::{
|
||||
local::{context::ServiceContext, loadbalancing::PingBalancer},
|
||||
net::{
|
||||
packet_window::PacketWindowFilter,
|
||||
MonProxySocket,
|
||||
UDP_ASSOCIATION_KEEP_ALIVE_CHANNEL_SIZE,
|
||||
packet_window::PacketWindowFilter, MonProxySocket, UDP_ASSOCIATION_KEEP_ALIVE_CHANNEL_SIZE,
|
||||
UDP_ASSOCIATION_SEND_CHANNEL_SIZE,
|
||||
},
|
||||
};
|
||||
@@ -98,7 +96,13 @@ where
|
||||
}
|
||||
|
||||
/// Sends `data` from `peer_addr` to `target_addr`
|
||||
pub async fn send_to(&mut self, peer_addr: SocketAddr, target_addr: Address, data: &[u8]) -> io::Result<()> {
|
||||
#[cfg_attr(not(feature = "local-fake-dns"), allow(unused_mut))]
|
||||
pub async fn send_to(&mut self, peer_addr: SocketAddr, mut target_addr: Address, data: &[u8]) -> io::Result<()> {
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
if let Some(mapped_addr) = self.context.try_map_fake_address(&target_addr).await {
|
||||
target_addr = mapped_addr;
|
||||
}
|
||||
|
||||
// Check or (re)create an association
|
||||
|
||||
if let Some(assoc) = self.assoc_map.get(&peer_addr) {
|
||||
|
||||
@@ -443,6 +443,43 @@ pub fn define_command_line_options(mut app: Command) -> Command {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
{
|
||||
app = app
|
||||
.arg(
|
||||
Arg::new("FAKE_DNS_RECORD_EXPIRE_DURATION")
|
||||
.long("fake-dns-record-expire-duration")
|
||||
.num_args(1)
|
||||
.action(ArgAction::Set)
|
||||
.value_parser(clap::value_parser!(u64))
|
||||
.help("Fake DNS record expire duration in seconds"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("FAKE_DNS_IPV4_NETWORK")
|
||||
.long("fake-dns-ipv4-network")
|
||||
.num_args(1)
|
||||
.action(ArgAction::Set)
|
||||
.value_parser(vparser::parse_ipnet)
|
||||
.help("Fake DNS IPv4 address network"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("FAKE_DNS_IPV6_NETWORK")
|
||||
.long("fake-dns-ipv6-network")
|
||||
.num_args(1)
|
||||
.action(ArgAction::Set)
|
||||
.value_parser(vparser::parse_ipnet)
|
||||
.help("Fake DNS IPv6 address network"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("FAKE_DNS_DATABASE_PATH")
|
||||
.long("fake-dns-database-path")
|
||||
.num_args(1)
|
||||
.action(ArgAction::Set)
|
||||
.value_hint(ValueHint::AnyPath)
|
||||
.help("Fake DNS database storage path"),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
app = app
|
||||
@@ -728,6 +765,24 @@ pub fn create(matches: &ArgMatches) -> Result<(Runtime, impl Future<Output = Exi
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-fake-dns")]
|
||||
{
|
||||
use ipnet::{Ipv4Net, Ipv6Net};
|
||||
|
||||
if let Some(d) = matches.get_one::<u64>("FAKE_DNS_RECORD_EXPIRE_DURATION") {
|
||||
local_config.fake_dns_record_expire_duration = Some(Duration::from_secs(*d));
|
||||
}
|
||||
if let Some(n) = matches.get_one::<Ipv4Net>("FAKE_DNS_IPV4_NETWORK") {
|
||||
local_config.fake_dns_ipv4_network = Some(*n);
|
||||
}
|
||||
if let Some(n) = matches.get_one::<Ipv6Net>("FAKE_DNS_IPV6_NETWORK") {
|
||||
local_config.fake_dns_ipv6_network = Some(*n);
|
||||
}
|
||||
if let Some(p) = matches.get_one::<PathBuf>("FAKE_DNS_DATABASE_PATH").cloned() {
|
||||
local_config.fake_dns_database_path = Some(p);
|
||||
}
|
||||
}
|
||||
|
||||
if matches.get_flag("UDP_ONLY") {
|
||||
local_config.mode = Mode::UdpOnly;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user