feat(local): basic implementation of fake-dns

This commit is contained in:
zonyitoo
2024-03-31 03:15:05 +08:00
parent be77407bbd
commit 355bd8a5c3
18 changed files with 1511 additions and 216 deletions
Generated
+455 -203
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -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"]
+7
View File
@@ -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"
+11
View File
@@ -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(())
}
+90 -8
View File
@@ -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) {
+55
View File
@@ -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;
}