diff --git a/Cargo.toml b/Cargo.toml index 85250ca0..5af087dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ camellia-cfb = ["openssl"] single-threaded = [] trust-dns = ["trust-dns-resolver"] openssl-vendored = ["openssl/vendored"] +local-dns-relay = [] [dependencies] log = "0.4" @@ -80,6 +81,7 @@ byte_string = "1.0" libsodium-sys = { version = "0.2", optional = true } miscreant = { version = "0.5", optional = true } trust-dns-resolver = { version = "0.19", features = ["dns-over-rustls", "dns-over-https-rustls"], optional = true } +trust-dns-proto = "0.19" hkdf = "0.8" hmac = "0.7" sha-1 = "0.8" @@ -108,7 +110,6 @@ lazy_static = "1.4" winapi = { version = "0.3", features = ["mswsock", "winsock2"] } [target.'cfg(target_os = "android")'.dependencies] -trust-dns-proto = "0.19" # Just for the ioctl call macro [target.'cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies] diff --git a/src/acl/mod.rs b/src/acl/mod.rs index 34a4539f..b692e8f7 100644 --- a/src/acl/mod.rs +++ b/src/acl/mod.rs @@ -264,7 +264,6 @@ impl AccessControl { /// Check if domain name is in proxy_list. /// If so, it should be resolved from remote (for Android's DNS relay) - #[cfg(target_os = "android")] pub async fn check_qname_in_proxy_list(&self, addr: &Address) -> bool { // Addresses in proxy_list will be proxied if self.white_list.check_address_matched(addr) { @@ -278,11 +277,6 @@ impl AccessControl { /// /// FIXME: This function may perform a DNS resolution pub async fn check_target_bypassed(&self, context: &Context, addr: &Address) -> bool { - // Always redirect TCP DNS query (for android) - if cfg!(target_os = "android") && addr.port() == 53 { - return false; - } - // Addresses in bypass_list will be bypassed if self.black_list.check_address_matched(addr) { return true; diff --git a/src/bin/local.rs b/src/bin/local.rs index 26fdea53..6951d804 100644 --- a/src/bin/local.rs +++ b/src/bin/local.rs @@ -166,7 +166,7 @@ fn main() { ); } - #[cfg(target_os = "android")] + #[cfg(feature = "local-dns-relay")] { app = app .arg( @@ -184,7 +184,7 @@ fn main() { .help("Specify the address of remote DNS server (only for Android)"), ) .arg( - Arg::with_name("DNS_RELAY_ADDR") + Arg::with_name("DNS_LOCAL_ADDR") .long("dns-relay") .takes_value(true) .default_value("127.0.0.1:5450") @@ -252,7 +252,7 @@ fn main() { } } - #[cfg(target_os = "android")] + #[cfg(feature = "local-dns-relay")] { use std::net::SocketAddr; @@ -268,9 +268,9 @@ fn main() { config.remote_dns_addr = Some(addr); } - if let Some(dns_relay_addr) = matches.value_of("DNS_RELAY_ADDR") { - let addr = dns_relay_addr.parse::().expect("dns relay address"); - config.dns_relay_addr = Some(addr); + if let Some(dns_relay_addr) = matches.value_of("DNS_LOCAL_ADDR") { + let addr = dns_relay_addr.parse::().expect("dns relay address"); + config.dns_local_addr = Some(addr); } } @@ -284,7 +284,7 @@ fn main() { .parse::() .expect("`local-addr` should be \"IP:Port\" or \"Domain:Port\""); - config.local = Some(local_addr); + config.local_addr = Some(local_addr); } if matches.is_present("UDP_ONLY") { @@ -330,7 +330,7 @@ fn main() { // DONE READING options - if config.local.is_none() { + if config.local_addr.is_none() { eprintln!( "missing `local_address`, consider specifying it by --local-addr command line option, \ or \"local_address\" and \"local_port\" in configuration file" diff --git a/src/bin/manager.rs b/src/bin/manager.rs index 3b205043..9bc29fa1 100644 --- a/src/bin/manager.rs +++ b/src/bin/manager.rs @@ -139,7 +139,7 @@ fn main() { Err(..) => ServerAddr::from((bind_addr, 0)), }; - config.local = Some(bind_addr); + config.local_addr = Some(bind_addr); } if matches.is_present("UDP_ONLY") { @@ -159,7 +159,7 @@ fn main() { } if let Some(m) = matches.value_of("MANAGER_ADDRESS") { - config.manager_address = Some( + config.manager_addr = Some( m.parse::() .expect("\"IP:Port\", \"Domain:Port\" or \"/path/to/unix.sock\" for `manager_address`"), ); @@ -180,7 +180,7 @@ fn main() { // DONE reading options - if config.manager_address.is_none() { + if config.manager_addr.is_none() { eprintln!( "missing `manager_address`, consider specifying it by --manager-address command line option, \ or \"manager_address\" and \"manager_port\" keys in configuration file" diff --git a/src/bin/redir.rs b/src/bin/redir.rs index 95d13f39..5623736f 100644 --- a/src/bin/redir.rs +++ b/src/bin/redir.rs @@ -221,7 +221,7 @@ fn main() { .parse::() .expect("`local-addr` should be \"IP:Port\" or \"Domain:Port\""); - config.local = Some(local_addr); + config.local_addr = Some(local_addr); } if let Some(url) = matches.value_of("FORWARD_ADDR") { @@ -281,7 +281,7 @@ fn main() { // DONE READING options - if config.local.is_none() { + if config.local_addr.is_none() { eprintln!( "missing `local_address`, consider specifying it by --local-addr command line option, \ or \"local_address\" and \"local_port\" in configuration file" diff --git a/src/bin/server.rs b/src/bin/server.rs index 93802000..0ab0de25 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -190,7 +190,7 @@ fn main() { Err(..) => ServerAddr::from((bind_addr, 0)), }; - config.local = Some(bind_addr); + config.local_addr = Some(bind_addr); } if matches.is_present("UDP_ONLY") { @@ -222,7 +222,7 @@ fn main() { }; if let Some(m) = matches.value_of("MANAGER_ADDRESS") { - config.manager_address = Some( + config.manager_addr = Some( m.parse::() .expect("\"IP:Port\", \"Domain:Port\" or \"/path/to/unix.sock\" for `manager_address`"), ); diff --git a/src/bin/tunnel.rs b/src/bin/tunnel.rs index 4dfa371f..a0dcf8b0 100644 --- a/src/bin/tunnel.rs +++ b/src/bin/tunnel.rs @@ -196,7 +196,7 @@ fn main() { .parse::() .expect("`local-addr` should be \"IP:Port\" or \"Domain:Port\""); - config.local = Some(local_addr); + config.local_addr = Some(local_addr); }; if let Some(url) = matches.value_of("FORWARD_ADDR") { @@ -243,7 +243,7 @@ fn main() { // DONE READING options - if config.local.is_none() { + if config.local_addr.is_none() { eprintln!( "missing `local_address`, consider specifying it by --local-addr command line option, \ or \"local_address\" and \"local_port\" in configuration file" diff --git a/src/config.rs b/src/config.rs index d778fe2d..f03e75bd 100644 --- a/src/config.rs +++ b/src/config.rs @@ -599,6 +599,11 @@ pub enum ConfigType { /// Requires `local` configuration RedirLocal, + /// Config for dns relay local + /// + /// Requires `local` configuration + DnsLocal, + /// Config for server Server, @@ -610,7 +615,11 @@ impl ConfigType { /// Check if it is local server type pub fn is_local(self) -> bool { match self { - ConfigType::Socks5Local | ConfigType::HttpLocal | ConfigType::TunnelLocal | ConfigType::RedirLocal => true, + ConfigType::Socks5Local + | ConfigType::HttpLocal + | ConfigType::TunnelLocal + | ConfigType::RedirLocal + | ConfigType::DnsLocal => true, ConfigType::Server | ConfigType::Manager => false, } } @@ -618,7 +627,11 @@ impl ConfigType { /// Check if it is remote server type pub fn is_server(self) -> bool { match self { - ConfigType::Socks5Local | ConfigType::HttpLocal | ConfigType::TunnelLocal | ConfigType::RedirLocal => false, + ConfigType::Socks5Local + | ConfigType::HttpLocal + | ConfigType::TunnelLocal + | ConfigType::RedirLocal + | ConfigType::DnsLocal => false, ConfigType::Manager => false, ConfigType::Server => true, } @@ -864,7 +877,7 @@ pub struct Config { /// Remote ShadowSocks server configurations pub server: Vec, /// Local server's bind address, or ShadowSocks server's outbound address - pub local: Option, + pub local_addr: Option, /// Destination address for tunnel pub forward: Option
, /// DNS configuration, uses system-wide DNS configuration by default @@ -882,7 +895,7 @@ pub struct Config { /// Set `TCP_NODELAY` socket option pub no_delay: bool, /// Address of `ss-manager`. Send servers' statistic data to the manager server - pub manager_address: Option, + pub manager_addr: Option, /// Manager's default method pub manager_method: Option, /// Config is for Client or Server @@ -906,17 +919,20 @@ pub struct Config { pub protect_path: Option, /// Path for local DNS resolver, only for Android pub local_dns_path: Option, - /// Interanl DNS's bind address - #[cfg(target_os = "android")] - pub dns_relay_addr: Option, + /// Internal DNS's bind address + #[cfg(feature = "local-dns-relay")] + pub dns_local_addr: Option, /// Local DNS's address - #[cfg(target_os = "android")] + /// + /// Sending DNS query directly to this address pub local_dns_addr: Option, /// Remote DNS's address - #[cfg(target_os = "android")] + /// + /// Sending DNS query through proxy to this address pub remote_dns_addr: Option
, /// Uses IPv6 addresses first - /// This would affect DNS resolution logic + /// + /// Set to `true` if you want to query IPv6 addresses before IPv4 pub ipv6_first: bool, } @@ -984,12 +1000,12 @@ impl Config { pub fn new(config_type: ConfigType) -> Config { Config { server: Vec::new(), - local: None, + local_addr: None, forward: None, dns: None, mode: Mode::TcpOnly, no_delay: false, - manager_address: None, + manager_addr: None, manager_method: None, config_type, udp_timeout: None, @@ -1001,11 +1017,9 @@ impl Config { stat_path: None, protect_path: None, local_dns_path: None, - #[cfg(target_os = "android")] - dns_relay_addr: None, - #[cfg(target_os = "android")] + #[cfg(feature = "local-dns-relay")] + dns_local_addr: None, local_dns_addr: None, - #[cfg(target_os = "android")] remote_dns_addr: None, ipv6_first: false, } @@ -1041,7 +1055,7 @@ impl Config { } }; - nconfig.local = Some(local); + nconfig.local_addr = Some(local); } // Standard config @@ -1151,7 +1165,7 @@ impl Config { } }; - nconfig.manager_address = Some(manager); + nconfig.manager_addr = Some(manager); } // DNS @@ -1258,7 +1272,7 @@ impl Config { /// Check if all required fields are already set pub fn check_integrity(&self) -> Result<(), Error> { if self.config_type.is_local() { - if self.local.is_some() { + if self.local_addr.is_some() { return Ok(()); } @@ -1284,13 +1298,13 @@ impl Config { } if self.config_type.is_manager() { - if self.manager_address.is_some() { + if self.manager_addr.is_some() { return Ok(()); } let err = Error::new( ErrorKind::MissingField, - "missing `manager_address` and `manager_port` in configuration", + "missing `manager_addr` and `manager_port` in configuration", None, ); return Err(err); @@ -1306,7 +1320,7 @@ impl fmt::Display for Config { let mut jconf = SSConfig::default(); - if let Some(ref client) = self.local { + if let Some(ref client) = self.local_addr { match *client { ServerAddr::SocketAddr(ref sa) => { jconf.local_address = Some(sa.ip().to_string()); @@ -1366,7 +1380,7 @@ impl fmt::Display for Config { } } - if let Some(ref ma) = self.manager_address { + if let Some(ref ma) = self.manager_addr { jconf.manager_address = Some(match *ma { ManagerAddr::SocketAddr(ref saddr) => saddr.ip().to_string(), ManagerAddr::DomainName(ref dname, ..) => dname.clone(), diff --git a/src/context.rs b/src/context.rs index a017f763..d760c7a3 100644 --- a/src/context.rs +++ b/src/context.rs @@ -9,13 +9,12 @@ use std::{ }, }; -#[cfg(target_os = "android")] +#[cfg(feature = "local-dns-relay")] use std::net::IpAddr; -#[cfg(target_os = "android")] -use lru_time_cache::LruCache; - use bloomfilter::Bloom; +#[cfg(feature = "local-dns-relay")] +use lru_time_cache::LruCache; use spin::Mutex; use tokio::runtime::Handle; #[cfg(feature = "trust-dns")] @@ -119,8 +118,9 @@ pub struct ServerState { dns_resolver: Option, } +#[cfg(feature = "trust-dns")] impl ServerState { - #[cfg(feature = "trust-dns")] + /// Create a global shared server state pub async fn new_shared(config: &Config, rt: Handle) -> SharedServerState { let state = ServerState { dns_resolver: match create_resolver(config.get_dns_config(), config.timeout, config.ipv6_first, rt).await { @@ -132,18 +132,20 @@ impl ServerState { Arc::new(state) } - #[cfg(not(feature = "trust-dns"))] - pub async fn new_shared(_config: &Config, _rt: Handle) -> SharedServerState { - Arc::new(ServerState {}) - } - /// Get the global shared resolver - #[cfg(feature = "trust-dns")] pub fn dns_resolver(&self) -> Option<&TokioAsyncResolver> { self.dns_resolver.as_ref() } } +#[cfg(not(feature = "trust-dns"))] +impl ServerState { + /// Create a global shared server state + pub async fn new_shared(_config: &Config, _rt: Handle) -> SharedServerState { + Arc::new(ServerState {}) + } +} + /// `ServerState` wrapped in `Arc` pub type SharedServerState = Arc; @@ -166,7 +168,7 @@ pub struct Context { local_flow_statistic: ServerFlowStatistic, // For DNS relay's ACL domain name reverse lookup - #[cfg(target_os = "android")] + #[cfg(feature = "local-dns-relay")] reverse_lookup_cache: Mutex>, } @@ -177,7 +179,7 @@ impl Context { /// Create a non-shared Context fn new(config: Config, server_state: SharedServerState) -> Context { let nonce_ppbloom = Mutex::new(PingPongBloom::new(config.config_type)); - #[cfg(target_os = "android")] + #[cfg(feature = "local-dns-relay")] let reverse_lookup_cache = Mutex::new(LruCache::::with_capacity(8192)); Context { @@ -186,7 +188,7 @@ impl Context { server_running: AtomicBool::new(true), nonce_ppbloom, local_flow_statistic: ServerFlowStatistic::new(), - #[cfg(target_os = "android")] + #[cfg(feature = "local-dns-relay")] reverse_lookup_cache, } } @@ -283,7 +285,7 @@ impl Context { } /// Add a record to the reverse lookup cache - #[cfg(target_os = "android")] + #[cfg(feature = "local-dns-relay")] pub fn add_to_reverse_lookup_cache(&self, addr: IpAddr, qname: String) { let mut reverse_lookup_cache = self.reverse_lookup_cache.lock(); reverse_lookup_cache.insert(addr, qname); @@ -291,7 +293,6 @@ impl Context { /// Check if domain name is in proxy_list. /// If so, it should be resolved from remote (for Android's DNS relay) - #[cfg(target_os = "android")] pub async fn check_qname_in_proxy_list(&self, qname: &Address) -> bool { match self.config.acl { // Proxy everything by default @@ -306,7 +307,7 @@ impl Context { // Proxy everything by default None => false, Some(ref a) => { - #[cfg(target_os = "android")] + #[cfg(feature = "local-dns-relay")] { if let Address::SocketAddress(ref saddr) = target { // do the reverse lookup in our local cache diff --git a/src/relay/dns.rs b/src/relay/dns.rs new file mode 100644 index 00000000..30c879e9 --- /dev/null +++ b/src/relay/dns.rs @@ -0,0 +1,76 @@ +//! DNS relay + +use std::io::{self, ErrorKind}; + +use futures::{future::select_all, FutureExt}; +use log::{debug, error, trace, warn}; +use tokio::runtime::Handle; + +use crate::{ + config::{Config, ConfigType}, + context::{Context, ServerState}, + plugin::{PluginMode, Plugins}, + relay::{dnsrelay::run as run_dns, utils::set_nofile}, +}; + +/// DNS relay server running under local environment. +pub async fn run(mut config: Config, rt: Handle) -> io::Result<()> { + trace!("initializing local server with {:?}", config); + + assert_eq!(config.config_type, ConfigType::DnsLocal); + + if let Some(nofile) = config.nofile { + debug!("setting RLIMIT_NOFILE to {}", nofile); + if let Err(err) = set_nofile(nofile) { + match err.kind() { + ErrorKind::PermissionDenied => { + warn!("insufficient permission to change RLIMIT_NOFILE, try to restart as root user"); + } + ErrorKind::InvalidInput => { + warn!("invalid `nofile` value {}, decrease it and try again", nofile); + } + _ => { + error!("failed to set RLIMIT_NOFILE with value {}, error: {}", nofile, err); + } + } + return Err(err); + } + } + + // Create a context containing a DNS resolver and server running state flag. + let state = ServerState::new_shared(&config, rt).await; + + let mut vf = Vec::new(); + + let context = if config.mode.enable_tcp() { + // Run TCP local server if + // + // 1. Enabled TCP relay + // 2. Not in tunnel mode. (Socks5 UDP relay requires TCP port enabled) + + if config.has_server_plugins() { + let plugins = Plugins::launch_plugins(&mut config, PluginMode::Client)?; + + // Wait until all plugins actually start + // Some plugins require quite a lot bootstrap time + Plugins::check_plugins_started(&config).await?; + + vf.push(plugins.into_future().boxed()); + } + + Context::new_shared(config, state) + } else { + Context::new_shared(config, state) + }; + + let server = run_dns(context.clone()); + vf.push(server.boxed()); + + let (res, ..) = select_all(vf.into_iter()).await; + error!("one of servers exited unexpectly, result: {:?}", res); + + // Tells all detached tasks to exit + context.set_server_stopped(); + + Err(io::Error::new(io::ErrorKind::Other, "server exited unexpectly")) +} diff --git a/src/relay/dnsrelay/mod.rs b/src/relay/dnsrelay/mod.rs index e9325c85..38b74c65 100644 --- a/src/relay/dnsrelay/mod.rs +++ b/src/relay/dnsrelay/mod.rs @@ -1,18 +1,18 @@ use std::{ io, net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}, - sync::Arc, time::Duration, }; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::UdpSocket, + sync::mpsc, }; use byteorder::{BigEndian, ByteOrder}; use futures::future; -use log::{debug, error, info, trace}; +use log::{debug, error, info}; use rand::Rng; use trust_dns_proto::{ @@ -21,9 +21,14 @@ use trust_dns_proto::{ }; use crate::{ - config::ConfigType, - context::{Context, SharedContext}, - relay::{socks5::Address, tcprelay::client::Socks5Client, utils::try_timeout}, + config::{ConfigType, ServerConfig}, + context::SharedContext, + relay::{ + loadbalancing::server::{PlainPingBalancer, ServerType}, + socks5::Address, + tcprelay::ProxyStream, + utils::try_timeout, + }, }; async fn udp_lookup(qname: &Name, qtype: RecordType, server: &SocketAddr) -> io::Result { @@ -50,8 +55,14 @@ async fn udp_lookup(qname: &Name, qtype: RecordType, server: &SocketAddr) -> io: Ok(Message::from_vec(&res_buffer)?) } -async fn socks5_lookup(qname: &Name, qtype: RecordType, socks5: &SocketAddr, ns: &Address) -> io::Result { - let mut stream = Socks5Client::connect(ns, &socks5).await?; +async fn proxy_lookup( + context: SharedContext, + svr_cfg: &ServerConfig, + ns: &Address, + qname: &Name, + qtype: RecordType, +) -> io::Result { + let mut stream = ProxyStream::connect_proxied(context, svr_cfg, ns).await?; let mut message = Message::new(); let mut query = Query::new(); @@ -83,14 +94,13 @@ async fn socks5_lookup(qname: &Name, qtype: RecordType, socks5: &SocketAddr, ns: } async fn acl_lookup( - context: &Context, + context: SharedContext, + svr_cfg: &ServerConfig, local: &SocketAddr, - socks5: &SocketAddr, + remote: &Address, qname: &Name, qtype: RecordType, ) -> io::Result { - let remote = context.config().remote_dns_addr.as_ref().expect("remote dns addr"); - // Start querying name servers debug!( "attempting lookup of {:?} {} with ns {} and {:?}", @@ -101,7 +111,7 @@ async fn acl_lookup( let local_response_fut = try_timeout(udp_lookup(qname, qtype, local), timeout); let timeout = Some(Duration::new(3, 0)); - let remote_response_fut = try_timeout(socks5_lookup(qname, qtype, socks5, remote), timeout); + let remote_response_fut = try_timeout(proxy_lookup(context.clone(), svr_cfg, remote, qname, qtype), timeout); let (local_response, remote_response) = future::join(local_response_fut, remote_response_fut).await; let local_response = local_response.unwrap_or_else(|_| Message::new()); @@ -152,27 +162,46 @@ async fn acl_lookup( } /// Start a DNS relay local server -pub async fn run(shared_context: SharedContext) -> io::Result<()> { - // Local must be socks5 protocol! - assert_eq!(shared_context.config().config_type, ConfigType::Socks5Local); +pub async fn run(context: SharedContext) -> io::Result<()> { + let local_addr = match context.config().config_type { + ConfigType::DnsLocal => { + // Standalone server + context.config().local_addr.as_ref().expect("local config") + } + #[cfg(feature = "local-dns-relay")] + c if c.is_local() => { + // Integrated mode + context.config().dns_local_addr.as_ref().expect("dns relay addr") + } + _ => { + panic!("ConfigType must be DnsLocal"); + } + }; - let local_addr = shared_context.config().local_dns_addr.expect("local dns addr"); - trace!("local DNS server: {}", local_addr); + let bind_addr = local_addr.bind_addr(&*context).await?; - let socks5_config = shared_context.config().local.as_ref().expect("socks5 bind addr"); - let socks5_addr = socks5_config.bind_addr(&shared_context).await?; - trace!("socks5 server: {}", socks5_addr); + let socket = UdpSocket::bind(&bind_addr).await?; - let listen_addr = shared_context.config().dns_relay_addr.expect("dns relay"); + let actual_local_addr = socket.local_addr()?; + info!("shadowsocks DNS relay listening on {}", actual_local_addr); - let mut socket = UdpSocket::bind(listen_addr).await?; + let (mut rx, mut tx) = socket.split(); + let (qtx, mut qrx) = mpsc::channel::<(SocketAddr, Vec)>(1024); - let actual_listen_addr = socket.local_addr()?; - info!("shadowsocks DNS relay listening on {}", actual_listen_addr); + tokio::spawn(async move { + while let Some((src, pkt)) = qrx.recv().await { + if let Err(err) = tx.send_to(&pkt, &src).await { + error!("failed to send packet {} bytes to {}, error: {}", pkt.len(), src, err); + } + } + }); + + // FIXME: We use TCP to send remote queries by default, which should be configuable. + let balancer = PlainPingBalancer::new(context.clone(), ServerType::Tcp).await; loop { let mut req_buffer: [u8; 512] = [0; 512]; - let (_, src) = match socket.recv_from(&mut req_buffer).await { + let (_, src) = match rx.recv_from(&mut req_buffer).await { Ok(x) => x, Err(e) => { error!("DNS relay read from UDP socket error: {}", e); @@ -188,9 +217,11 @@ pub async fn run(shared_context: SharedContext) -> io::Result<()> { } }; - debug!("received query: {:?}", request); + debug!("received src: {}, query: {:?}", src, request); - let context = Arc::clone(&shared_context); + let context = context.clone(); + let mut qtx = qtx.clone(); + let server = balancer.pick_server(); tokio::spawn(async move { let mut message = Message::new(); @@ -202,18 +233,20 @@ pub async fn run(shared_context: SharedContext) -> io::Result<()> { if request.queries().is_empty() { message.set_response_code(ResponseCode::FormErr); } else { + let config = context.config(); + let local_addr = config.local_dns_addr.as_ref().expect("local query DNS address"); + let remote_addr = config.remote_dns_addr.as_ref().expect("remote query DNS address"); + let question = &request.queries()[0]; - let r = acl_lookup( - &context, - &local_addr, - &socks5_addr, - question.name(), - question.query_type(), - ) - .await; + let qname = question.name(); + let qtype = question.query_type(); + let svr_cfg = server.server_config(); + + let r = acl_lookup(context.clone(), svr_cfg, &local_addr, &remote_addr, qname, qtype).await; if let Ok(result) = r { + #[cfg(feature = "local-dns-relay")] for rec in result.answers() { debug!("dns answer: {:?}", rec); @@ -225,7 +258,7 @@ pub async fn run(shared_context: SharedContext) -> io::Result<()> { RData::A(ref ip) => context.add_to_reverse_lookup_cache(IpAddr::from(*ip), name), RData::AAAA(ref ip) => context.add_to_reverse_lookup_cache(IpAddr::from(*ip), name), _ => (), - }; + } } message = result; @@ -235,15 +268,16 @@ pub async fn run(shared_context: SharedContext) -> io::Result<()> { } } - debug!("dns final response: {:?}", message); + debug!("DNS src: {}, final response: {:?}", src, message); - let res_buffer = message.to_vec().expect("parse message"); - let mut socket = UdpSocket::bind(("0.0.0.0", 0)).await.expect("bind socket"); - - match socket.send_to(&res_buffer, src).await { - Ok(_) => {} - Err(e) => { - error!("failed to send DNS response, error: {:?}", e); + match message.to_vec() { + Err(err) => { + error!("failed to serialize message, error: {}", err); + } + Ok(res_buffer) => { + if let Err(..) = qtx.send((src, res_buffer)).await { + error!("DNS send back queue is closed unexpectly"); + } } } }); diff --git a/src/relay/local.rs b/src/relay/local.rs index a77da7de..3c8e5be9 100644 --- a/src/relay/local.rs +++ b/src/relay/local.rs @@ -13,9 +13,6 @@ use crate::{ relay::{tcprelay::local::run as run_tcp, udprelay::local::run as run_udp, utils::set_nofile}, }; -#[cfg(target_os = "android")] -use crate::relay::dnsrelay::run as run_dns_relay; - /// Relay server running under local environment. pub async fn run(mut config: Config, rt: Handle) -> io::Result<()> { trace!("initializing local server with {:?}", config); @@ -99,11 +96,15 @@ pub async fn run(mut config: Config, rt: Handle) -> io::Result<()> { vf.push(udp_fut.boxed()); } - #[cfg(target_os = "android")] + #[cfg(feature = "local-dns-relay")] { - // For Android's local resolver - let dns_relay = run_dns_relay(context.clone()); - vf.push(dns_relay.boxed()); + if context.config().dns_local_addr.is_some() { + use crate::relay::dnsrelay::run as run_dns; + + // DNS relay local server + let dns_relay = run_dns(context.clone()); + vf.push(dns_relay.boxed()); + } } if cfg!(target_os = "android") && context.config().stat_path.is_some() { diff --git a/src/relay/manager.rs b/src/relay/manager.rs index 5ec4e8d8..c5a392ed 100644 --- a/src/relay/manager.rs +++ b/src/relay/manager.rs @@ -418,7 +418,7 @@ impl ManagerService { let mut config = Config::new(ConfigType::Server); config.server.push(svr_cfg); - config.local = self.context.config().local.clone(); + config.local_addr = self.context.config().local_addr.clone(); if let Some(mode) = p.mode { config.mode = match mode.parse::() { @@ -558,10 +558,10 @@ pub async fn run(config: Config, rt: Handle) -> io::Result<()> { let state = ServerState::new_shared(&config, rt.clone()).await; let context = Context::new_shared(config, state.clone()); - let bind_addr = match context.config().manager_address { + let bind_addr = match context.config().manager_addr { Some(ref a) => a, None => { - let err = Error::new(ErrorKind::Other, "missing `manager_address` in configuration"); + let err = Error::new(ErrorKind::Other, "missing `manager_addr` in configuration"); return Err(err); } }; @@ -574,7 +574,7 @@ pub async fn run(config: Config, rt: Handle) -> io::Result<()> { if !config.server.is_empty() { for svr_cfg in &config.server { let mut clean_config = Config::new(ConfigType::Server); - clean_config.local = config.local.clone(); + clean_config.local_addr = config.local_addr.clone(); clean_config.mode = config.mode; clean_config.no_delay = config.no_delay; clean_config.udp_timeout = config.udp_timeout; diff --git a/src/relay/mod.rs b/src/relay/mod.rs index c22824f1..fd9af63b 100644 --- a/src/relay/mod.rs +++ b/src/relay/mod.rs @@ -1,7 +1,7 @@ //! Relay server in local and server side implementations. +pub mod dns; pub(crate) mod dns_resolver; -#[cfg(target_os = "android")] pub mod dnsrelay; pub(crate) mod flow; pub(crate) mod loadbalancing; diff --git a/src/relay/server.rs b/src/relay/server.rs index dafc8419..c177078f 100644 --- a/src/relay/server.rs +++ b/src/relay/server.rs @@ -93,7 +93,7 @@ pub(crate) async fn run_with( // If specified manager-address, reports transmission statistic to it // // Dont do that if server is created by manager - if context.config().manager_address.is_some() { + if context.config().manager_addr.is_some() { let report_fut = manager_report_task(context.clone(), flow_stat); vf.push(report_fut.boxed()); } @@ -108,7 +108,7 @@ pub(crate) async fn run_with( } async fn manager_report_task(context: SharedContext, flow_stat: SharedMultiServerFlowStatistic) -> io::Result<()> { - let manager_addr = context.config().manager_address.as_ref().unwrap(); + let manager_addr = context.config().manager_addr.as_ref().unwrap(); let mut socket = ManagerDatagram::bind_for(manager_addr).await?; while context.server_running() { diff --git a/src/relay/tcprelay/http_local.rs b/src/relay/tcprelay/http_local.rs index 091620cc..89612bdd 100644 --- a/src/relay/tcprelay/http_local.rs +++ b/src/relay/tcprelay/http_local.rs @@ -638,7 +638,7 @@ impl ServerData for ServerScore { /// Starts a TCP local server with HTTP proxy protocol pub async fn run(context: SharedContext) -> io::Result<()> { - let local_addr = context.config().local.as_ref().expect("local config"); + let local_addr = context.config().local_addr.as_ref().expect("local config"); let bind_addr = local_addr.bind_addr(&*context).await?; let bypass_client = Client::builder().build::<_, Body>(DirectConnector::new(context.clone())); diff --git a/src/relay/tcprelay/local.rs b/src/relay/tcprelay/local.rs index b4af48b1..f989c270 100644 --- a/src/relay/tcprelay/local.rs +++ b/src/relay/tcprelay/local.rs @@ -13,6 +13,7 @@ pub async fn run(context: SharedContext) -> io::Result<()> { ConfigType::Socks5Local => socks5_local::run(context).await, ConfigType::HttpLocal => http_local::run(context).await, ConfigType::RedirLocal => redir_local::run(context).await, + ConfigType::DnsLocal => unreachable!(), ConfigType::Server => unreachable!(), ConfigType::Manager => unreachable!(), } diff --git a/src/relay/tcprelay/proxy_stream.rs b/src/relay/tcprelay/proxy_stream.rs index ee8d1ba5..2f6a69c0 100644 --- a/src/relay/tcprelay/proxy_stream.rs +++ b/src/relay/tcprelay/proxy_stream.rs @@ -279,9 +279,11 @@ async fn connect_proxy_server(context: &Context, svr_cfg: &ServerConfig) -> io:: let svr_addr = match context.config().config_type { ConfigType::Server => svr_cfg.addr(), - ConfigType::Socks5Local | ConfigType::TunnelLocal | ConfigType::HttpLocal | ConfigType::RedirLocal => { - svr_cfg.external_addr() - } + ConfigType::Socks5Local + | ConfigType::TunnelLocal + | ConfigType::HttpLocal + | ConfigType::RedirLocal + | ConfigType::DnsLocal => svr_cfg.external_addr(), ConfigType::Manager => unreachable!("ConfigType::Manager shouldn't need to connect to proxy server"), }; diff --git a/src/relay/tcprelay/redir_local.rs b/src/relay/tcprelay/redir_local.rs index d673e73e..d1a0bb0e 100644 --- a/src/relay/tcprelay/redir_local.rs +++ b/src/relay/tcprelay/redir_local.rs @@ -88,7 +88,7 @@ async fn handle_redir_client(server: &SharedPlainServerStatistic, s: TcpStream, } pub async fn run(context: SharedContext) -> io::Result<()> { - let local_addr = context.config().local.as_ref().expect("local config"); + let local_addr = context.config().local_addr.as_ref().expect("local config"); let bind_addr = local_addr.bind_addr(&*context).await?; let redir_ty = context.config().tcp_redir; diff --git a/src/relay/tcprelay/server.rs b/src/relay/tcprelay/server.rs index 2096f0b2..f22b45d9 100644 --- a/src/relay/tcprelay/server.rs +++ b/src/relay/tcprelay/server.rs @@ -70,7 +70,7 @@ async fn handle_client( return Ok(()); } - let bind_addr = match context.config().local { + let bind_addr = match context.config().local_addr { None => None, Some(ref addr) => { let ba = addr.bind_addr(&*context).await?; diff --git a/src/relay/tcprelay/socks5_local.rs b/src/relay/tcprelay/socks5_local.rs index f9df6173..139db040 100644 --- a/src/relay/tcprelay/socks5_local.rs +++ b/src/relay/tcprelay/socks5_local.rs @@ -218,7 +218,7 @@ async fn handle_socks5_client( /// Starts a TCP local server with Socks5 proxy protocol pub async fn run(context: SharedContext) -> io::Result<()> { - let local_addr = context.config().local.as_ref().expect("local config"); + let local_addr = context.config().local_addr.as_ref().expect("local config"); let bind_addr = local_addr.bind_addr(&*context).await?; let mut listener = TcpListener::bind(&bind_addr) diff --git a/src/relay/tcprelay/tunnel_local.rs b/src/relay/tcprelay/tunnel_local.rs index 05974dd8..fd356ac4 100644 --- a/src/relay/tcprelay/tunnel_local.rs +++ b/src/relay/tcprelay/tunnel_local.rs @@ -94,7 +94,7 @@ pub async fn run(context: SharedContext) -> io::Result<()> { "TCP relay must be enabled for TUNNEL" ); - let local_addr = context.config().local.as_ref().expect("local config"); + let local_addr = context.config().local_addr.as_ref().expect("local config"); let bind_addr = local_addr.bind_addr(&*context).await?; let mut listener = TcpListener::bind(&bind_addr) diff --git a/src/relay/udprelay/local.rs b/src/relay/udprelay/local.rs index 104f75a2..5112d3d5 100644 --- a/src/relay/udprelay/local.rs +++ b/src/relay/udprelay/local.rs @@ -12,6 +12,7 @@ pub async fn run(context: SharedContext) -> io::Result<()> { ConfigType::Socks5Local => socks5_local::run(context).await, ConfigType::RedirLocal => redir_local::run(context).await, ConfigType::HttpLocal => unreachable!(), + ConfigType::DnsLocal => unreachable!(), ConfigType::Server => unreachable!(), ConfigType::Manager => unreachable!(), } diff --git a/src/relay/udprelay/redir_local.rs b/src/relay/udprelay/redir_local.rs index fa3b1d43..42ed5aa8 100644 --- a/src/relay/udprelay/redir_local.rs +++ b/src/relay/udprelay/redir_local.rs @@ -74,7 +74,7 @@ impl ProxySend for ProxyHandler { /// Starts a UDP local server pub async fn run(context: SharedContext) -> io::Result<()> { - let local_addr = context.config().local.as_ref().expect("missing local config"); + let local_addr = context.config().local_addr.as_ref().expect("missing local config"); let bind_addr = local_addr.bind_addr(&*context).await?; let ty = context.config().udp_redir; diff --git a/src/relay/udprelay/server.rs b/src/relay/udprelay/server.rs index d0942a37..4fab482c 100644 --- a/src/relay/udprelay/server.rs +++ b/src/relay/udprelay/server.rs @@ -57,7 +57,7 @@ impl UdpAssociation { mut response_tx: mpsc::Sender<(SocketAddr, BytesMut)>, ) -> io::Result { // Create a socket for receiving packets - let local_addr = match context.config().local { + let local_addr = match context.config().local_addr { None => { // Let system allocate an address for us SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0) diff --git a/src/relay/udprelay/socks5_local.rs b/src/relay/udprelay/socks5_local.rs index abe34f5d..40ce242e 100644 --- a/src/relay/udprelay/socks5_local.rs +++ b/src/relay/udprelay/socks5_local.rs @@ -83,7 +83,7 @@ fn assemble_packet(addr: Address, pkt: &[u8]) -> Bytes { /// Starts a UDP local server pub async fn run(context: SharedContext) -> io::Result<()> { - let local_addr = context.config().local.as_ref().expect("local config"); + let local_addr = context.config().local_addr.as_ref().expect("local config"); let bind_addr = local_addr.bind_addr(&*context).await?; let l = create_udp_socket(&bind_addr).await?; diff --git a/src/relay/udprelay/tunnel_local.rs b/src/relay/udprelay/tunnel_local.rs index 7af6f896..fd40a652 100644 --- a/src/relay/udprelay/tunnel_local.rs +++ b/src/relay/udprelay/tunnel_local.rs @@ -43,7 +43,7 @@ impl ProxySend for ProxyHandler { /// Starts a UDP local server pub async fn run(context: SharedContext) -> io::Result<()> { - let local_addr = context.config().local.as_ref().expect("local config"); + let local_addr = context.config().local_addr.as_ref().expect("local config"); let bind_addr = local_addr.bind_addr(&*context).await?; let l = create_udp_socket(&bind_addr).await?; diff --git a/tests/socks5.rs b/tests/socks5.rs index 2ef67cee..7b4a749c 100644 --- a/tests/socks5.rs +++ b/tests/socks5.rs @@ -39,7 +39,7 @@ impl Socks5TestServer { }, cli_config: { let mut cfg = Config::new(ConfigType::Socks5Local); - cfg.local = Some(ServerAddr::from(local_addr)); + cfg.local_addr = Some(ServerAddr::from(local_addr)); cfg.server = vec![ServerConfig::basic(svr_addr, pwd.to_owned(), method)]; cfg.mode = if enable_udp { Mode::TcpAndUdp } else { Mode::TcpOnly }; cfg diff --git a/tests/udp.rs b/tests/udp.rs index 65a50f86..061c31bc 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -46,7 +46,7 @@ fn get_svr_config() -> Config { fn get_cli_config() -> Config { let mut cfg = Config::new(ConfigType::Socks5Local); - cfg.local = Some(LOCAL_ADDR.parse().unwrap()); + cfg.local_addr = Some(LOCAL_ADDR.parse().unwrap()); cfg.server = vec![ServerConfig::basic( SERVER_ADDR.parse().unwrap(), PASSWORD.to_owned(),