From 89aeffe19529577c7aa8221209e1105412eb5144 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Mon, 21 Aug 2023 19:58:13 +0800 Subject: [PATCH 01/11] dns over tcp --- src/lib.rs | 8 ++++++++ src/main.rs | 8 ++++++++ src/tun2proxy.rs | 24 ++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index bdbf7a0..d21d99b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,6 +98,7 @@ impl std::fmt::Display for ProxyType { pub struct Options { virtual_dns: Option, mtu: Option, + dns_over_tcp: bool, } impl Options { @@ -107,6 +108,13 @@ impl Options { pub fn with_virtual_dns(mut self) -> Self { self.virtual_dns = Some(virtdns::VirtualDns::new()); + self.dns_over_tcp = false; + self + } + + pub fn with_dns_over_tcp(mut self) -> Self { + self.dns_over_tcp = true; + self.virtual_dns = None; self } diff --git a/src/main.rs b/src/main.rs index 1502599..6047dd9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,6 +40,10 @@ struct Args { /// Verbosity level #[arg(short, long, value_name = "level", value_enum, default_value = "info")] verbosity: ArgVerbosity, + + /// DNS over TCP + #[arg(long)] + dns_over_tcp: bool, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] @@ -79,6 +83,10 @@ fn main() -> ExitCode { options = options.with_virtual_dns(); } + if args.dns_over_tcp { + options = options.with_dns_over_tcp(); + } + let interface = match args.tun_fd { None => NetworkInterface::Named(args.tun.clone()), Some(fd) => { diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs index c405486..67574c7 100644 --- a/src/tun2proxy.rs +++ b/src/tun2proxy.rs @@ -527,6 +527,30 @@ impl<'a> TunToProxy<'a> { self.send_udp_packet_to_client(origin_dst, connection_info.src, response.as_slice())?; } else { // Another UDP packet + if self.options.dns_over_tcp && port == DNS_PORT { + if !self.connection_map.contains_key(&connection_info) { + log::info!("DNS over TCP {} ({})", connection_info, origin_dst); + let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, false)?; + #[rustfmt::skip] + let state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, false)?; + self.connection_map.insert(connection_info.clone(), state); + } else { + log::trace!("Subsequent dns over tcp packet {} ({})", connection_info, origin_dst); + } + + let len = payload.len() as u16; + let mut buf = Vec::with_capacity(2 + len as usize); + buf.extend_from_slice(&len.to_be_bytes()); + buf.extend_from_slice(payload); + + // TODO: Build an IP packet and inject it into the device. + self.device.inject_packet(&buf); + + self.expect_smoltcp_send()?; + self.tunsocket_read_and_forward(&connection_info)?; + self.write_to_server(&connection_info)?; + return Ok(()); + } if !self.connection_map.contains_key(&connection_info) { log::info!("UDP associate session {} ({})", connection_info, origin_dst); let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, true)?; From df7ecfd6a92fe12359ddef7a6bca1b0704ec9e0d Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Tue, 22 Aug 2023 17:57:59 +0800 Subject: [PATCH 02/11] minor changes --- src/tun2proxy.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs index 511ca42..e9776c8 100644 --- a/src/tun2proxy.rs +++ b/src/tun2proxy.rs @@ -496,6 +496,10 @@ impl<'a> TunToProxy<'a> { let server_addr = manager.get_server_addr(); let state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, false)?; self.connection_map.insert(info.clone(), state); + + self.expect_smoltcp_send()?; + self.tunsocket_read_and_forward(info)?; + self.write_to_server(info)?; } else { log::trace!("DNS over TCP subsequent packet {} ({})", info, origin_dst); } @@ -506,12 +510,16 @@ impl<'a> TunToProxy<'a> { buf.extend_from_slice(&len.to_be_bytes()); buf.extend_from_slice(payload); - // FIXME: Build an IP packet with TCP and inject it into the device. - self.device.inject_packet(&buf); + let err = "udp over tcp state not find"; + let state = self.connection_map.get_mut(info).ok_or(err)?; + if state.tcp_proxy_handler.connection_established() { + _ = state.mio_stream.write(&buf)?; + } else { + // FIXME: Build an IP packet with TCP and inject it into the device, + // or cache them and send them when the connection is established? + self.device.inject_packet(&buf); + } - self.expect_smoltcp_send()?; - self.tunsocket_read_and_forward(info)?; - self.write_to_server(info)?; return Ok(()); } From 1f5586b880b36ca003ee80cd3d47eb570b185901 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Tue, 22 Aug 2023 18:21:38 +0800 Subject: [PATCH 03/11] udp_over_tcp_data_cache --- src/tun2proxy.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs index e9776c8..de2167d 100644 --- a/src/tun2proxy.rs +++ b/src/tun2proxy.rs @@ -183,6 +183,8 @@ struct TcpConnectState { udp_token: Option, udp_origin_dst: Option, udp_data_cache: LinkedList>, + udp_over_tcp_expiry: Option<::std::time::Instant>, + udp_over_tcp_data_cache: LinkedList>, } pub(crate) trait TcpProxy { @@ -488,7 +490,7 @@ impl<'a> TunToProxy<'a> { payload: &[u8], ) -> Result<()> { if self.options.dns_over_tcp && origin_dst.port() == DNS_PORT { - dns::parse_data_to_dns_message(payload, false)?; + _ = dns::parse_data_to_dns_message(payload, false)?; if !self.connection_map.contains_key(info) { log::info!("DNS over TCP {} ({})", info, origin_dst); @@ -512,12 +514,14 @@ impl<'a> TunToProxy<'a> { let err = "udp over tcp state not find"; let state = self.connection_map.get_mut(info).ok_or(err)?; + state.udp_over_tcp_expiry = Some(Self::common_udp_life_timeout()); if state.tcp_proxy_handler.connection_established() { _ = state.mio_stream.write(&buf)?; } else { // FIXME: Build an IP packet with TCP and inject it into the device, // or cache them and send them when the connection is established? - self.device.inject_packet(&buf); + // self.device.inject_packet(&buf); + state.udp_over_tcp_data_cache.push_back(buf); } return Ok(()); @@ -541,7 +545,7 @@ impl<'a> TunToProxy<'a> { let err = "udp associate state not find"; let state = self.connection_map.get_mut(info).ok_or(err)?; assert!(state.udp_acco_expiry.is_some()); - state.udp_acco_expiry = Some(Self::udp_associate_timeout()); + state.udp_acco_expiry = Some(Self::common_udp_life_timeout()); // Add SOCKS5 UDP header to the incoming data let mut s5_udp_data = Vec::::new(); @@ -647,7 +651,7 @@ impl<'a> TunToProxy<'a> { self.poll.registry().register(&mut client, token, i)?; let expiry = if udp_associate { - Some(Self::udp_associate_timeout()) + Some(Self::common_udp_life_timeout()) } else { None }; @@ -674,11 +678,13 @@ impl<'a> TunToProxy<'a> { udp_token, udp_origin_dst: None, udp_data_cache: LinkedList::new(), + udp_over_tcp_expiry: None, + udp_over_tcp_data_cache: LinkedList::new(), }; Ok(state) } - fn udp_associate_timeout() -> ::std::time::Instant { + fn common_udp_life_timeout() -> ::std::time::Instant { ::std::time::Instant::now() + ::std::time::Duration::from_secs(UDP_ASSO_TIMEOUT) } @@ -813,7 +819,7 @@ impl<'a> TunToProxy<'a> { let err = "udp connection state not found"; let state = self.connection_map.get_mut(info).ok_or(err)?; assert!(state.udp_acco_expiry.is_some()); - state.udp_acco_expiry = Some(Self::udp_associate_timeout()); + state.udp_acco_expiry = Some(Self::common_udp_life_timeout()); let mut to_send: LinkedList> = LinkedList::new(); if let Some(udp_socket) = state.udp_socket.as_ref() { let mut buf = [0; 1 << 16]; From 49dca1b535cbfa616709ac2d467389835b88b4de Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Tue, 22 Aug 2023 18:32:10 +0800 Subject: [PATCH 04/11] process_incoming_udp_packets_dns_over_tcp --- src/tun2proxy.rs | 89 +++++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs index de2167d..f305323 100644 --- a/src/tun2proxy.rs +++ b/src/tun2proxy.rs @@ -482,6 +482,50 @@ impl<'a> TunToProxy<'a> { Ok(connection_info) } + fn process_incoming_udp_packets_dns_over_tcp( + &mut self, + manager: &Rc, + info: &ConnectionInfo, + origin_dst: SocketAddr, + payload: &[u8], + ) -> Result<()> { + _ = dns::parse_data_to_dns_message(payload, false)?; + + if !self.connection_map.contains_key(info) { + log::info!("DNS over TCP {} ({})", info, origin_dst); + let tcp_proxy_handler = manager.new_tcp_proxy(info, false)?; + let server_addr = manager.get_server_addr(); + let state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, false)?; + self.connection_map.insert(info.clone(), state); + + self.expect_smoltcp_send()?; + self.tunsocket_read_and_forward(info)?; + self.write_to_server(info)?; + } else { + log::trace!("DNS over TCP subsequent packet {} ({})", info, origin_dst); + } + + // Insert the DNS message length in front of the payload + let len = u16::try_from(payload.len())?; + let mut buf = Vec::with_capacity(2 + usize::from(len)); + buf.extend_from_slice(&len.to_be_bytes()); + buf.extend_from_slice(payload); + + let err = "udp over tcp state not find"; + let state = self.connection_map.get_mut(info).ok_or(err)?; + state.udp_over_tcp_expiry = Some(Self::common_udp_life_timeout()); + if state.tcp_proxy_handler.connection_established() { + _ = state.mio_stream.write(&buf)?; + } else { + // FIXME: Build an IP packet with TCP and inject it into the device, + // or cache them and send them when the connection is established? + // self.device.inject_packet(&buf); + state.udp_over_tcp_data_cache.push_back(buf); + } + + Ok(()) + } + fn process_incoming_udp_packets( &mut self, manager: &Rc, @@ -489,44 +533,6 @@ impl<'a> TunToProxy<'a> { origin_dst: SocketAddr, payload: &[u8], ) -> Result<()> { - if self.options.dns_over_tcp && origin_dst.port() == DNS_PORT { - _ = dns::parse_data_to_dns_message(payload, false)?; - - if !self.connection_map.contains_key(info) { - log::info!("DNS over TCP {} ({})", info, origin_dst); - let tcp_proxy_handler = manager.new_tcp_proxy(info, false)?; - let server_addr = manager.get_server_addr(); - let state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, false)?; - self.connection_map.insert(info.clone(), state); - - self.expect_smoltcp_send()?; - self.tunsocket_read_and_forward(info)?; - self.write_to_server(info)?; - } else { - log::trace!("DNS over TCP subsequent packet {} ({})", info, origin_dst); - } - - // Insert the DNS message length in front of the payload - let len = u16::try_from(payload.len())?; - let mut buf = Vec::with_capacity(2 + usize::from(len)); - buf.extend_from_slice(&len.to_be_bytes()); - buf.extend_from_slice(payload); - - let err = "udp over tcp state not find"; - let state = self.connection_map.get_mut(info).ok_or(err)?; - state.udp_over_tcp_expiry = Some(Self::common_udp_life_timeout()); - if state.tcp_proxy_handler.connection_established() { - _ = state.mio_stream.write(&buf)?; - } else { - // FIXME: Build an IP packet with TCP and inject it into the device, - // or cache them and send them when the connection is established? - // self.device.inject_packet(&buf); - state.udp_over_tcp_data_cache.push_back(buf); - } - - return Ok(()); - } - if !self.connection_map.contains_key(info) { log::info!("UDP associate session {} ({})", info, origin_dst); let tcp_proxy_handler = manager.new_tcp_proxy(info, true)?; @@ -617,7 +623,12 @@ impl<'a> TunToProxy<'a> { self.send_udp_packet_to_client(origin_dst, connection_info.src, response.as_slice())?; } else { // Another UDP packet - self.process_incoming_udp_packets(&manager, &connection_info, origin_dst, payload)?; + if self.options.dns_over_tcp && origin_dst.port() == DNS_PORT { + let info = &connection_info; + self.process_incoming_udp_packets_dns_over_tcp(&manager, info, origin_dst, payload)?; + } else { + self.process_incoming_udp_packets(&manager, &connection_info, origin_dst, payload)?; + } } } else { log::warn!("Unsupported protocol: {} ({})", connection_info, origin_dst); From 2211ec6d7a3585846e8b17155917aace2da63c3b Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Tue, 22 Aug 2023 18:43:45 +0800 Subject: [PATCH 05/11] renaming --- src/tun2proxy.rs | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs index f305323..60fac3f 100644 --- a/src/tun2proxy.rs +++ b/src/tun2proxy.rs @@ -581,23 +581,23 @@ impl<'a> TunToProxy<'a> { } let (info, _first_packet, payload_offset, payload_size) = result?; let origin_dst = SocketAddr::try_from(&info.dst)?; - let connection_info = self.preprocess_origin_connection_info(info)?; + let info = self.preprocess_origin_connection_info(info)?; let manager = self.get_connection_manager().ok_or("get connection manager")?; - if connection_info.protocol == IpProtocol::Tcp { + if info.protocol == IpProtocol::Tcp { if _first_packet { - let tcp_proxy_handler = manager.new_tcp_proxy(&connection_info, false)?; + let tcp_proxy_handler = manager.new_tcp_proxy(&info, false)?; let server = manager.get_server_addr(); let state = self.create_new_tcp_connection_state(server, origin_dst, tcp_proxy_handler, false)?; - self.connection_map.insert(connection_info.clone(), state); + self.connection_map.insert(info.clone(), state); - log::info!("Connect done {} ({})", connection_info, origin_dst); - } else if !self.connection_map.contains_key(&connection_info) { - // log::debug!("Drop middle session {} ({})", connection_info, origin_dst); + log::info!("Connect done {} ({})", info, origin_dst); + } else if !self.connection_map.contains_key(&info) { + // log::debug!("Drop middle session {} ({})", info, origin_dst); return Ok(()); } else { - // log::trace!("Subsequent packet {} ({})", connection_info, origin_dst); + // log::trace!("Subsequent packet {} ({})", info, origin_dst); } // Inject the packet to advance the remote proxy server smoltcp socket state @@ -609,29 +609,28 @@ impl<'a> TunToProxy<'a> { self.expect_smoltcp_send()?; // Read from the smoltcp socket and push the data to the connection handler. - self.tunsocket_read_and_forward(&connection_info)?; + self.tunsocket_read_and_forward(&info)?; // The connection handler builds up the connection or encapsulates the data. // Therefore, we now expect it to write data to the server. - self.write_to_server(&connection_info)?; - } else if connection_info.protocol == IpProtocol::Udp { - let port = connection_info.dst.port(); + self.write_to_server(&info)?; + } else if info.protocol == IpProtocol::Udp { + let port = info.dst.port(); let payload = &frame[payload_offset..payload_offset + payload_size]; if let (Some(virtual_dns), true) = (&mut self.options.virtual_dns, port == DNS_PORT) { - log::info!("DNS query via virtual DNS {} ({})", connection_info, origin_dst); + log::info!("DNS query via virtual DNS {} ({})", info, origin_dst); let response = virtual_dns.receive_query(payload)?; - self.send_udp_packet_to_client(origin_dst, connection_info.src, response.as_slice())?; + self.send_udp_packet_to_client(origin_dst, info.src, response.as_slice())?; } else { // Another UDP packet if self.options.dns_over_tcp && origin_dst.port() == DNS_PORT { - let info = &connection_info; - self.process_incoming_udp_packets_dns_over_tcp(&manager, info, origin_dst, payload)?; + self.process_incoming_udp_packets_dns_over_tcp(&manager, &info, origin_dst, payload)?; } else { - self.process_incoming_udp_packets(&manager, &connection_info, origin_dst, payload)?; + self.process_incoming_udp_packets(&manager, &info, origin_dst, payload)?; } } } else { - log::warn!("Unsupported protocol: {} ({})", connection_info, origin_dst); + log::warn!("Unsupported protocol: {} ({})", info, origin_dst); } Ok::<(), Error>(()) }; From 9880741dc1168eaa6ad1f79f7e9e1528ae36e6b7 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Tue, 22 Aug 2023 18:55:44 +0800 Subject: [PATCH 06/11] consume_cached_dns_over_tcp_packets --- src/tun2proxy.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs index 60fac3f..2a4aa47 100644 --- a/src/tun2proxy.rs +++ b/src/tun2proxy.rs @@ -526,6 +526,15 @@ impl<'a> TunToProxy<'a> { Ok(()) } + fn consume_cached_dns_over_tcp_packets(&mut self, info: &ConnectionInfo) -> Result<()> { + if let Some(state) = self.connection_map.get_mut(info) { + while let Some(buf) = state.udp_over_tcp_data_cache.pop_front() { + _ = state.mio_stream.write(&buf)?; + } + } + Ok(()) + } + fn process_incoming_udp_packets( &mut self, manager: &Rc, @@ -859,7 +868,7 @@ impl<'a> TunToProxy<'a> { Ok(()) } - fn comsume_cached_udp_packets(&mut self, info: &ConnectionInfo) -> Result<()> { + fn consume_cached_udp_packets(&mut self, info: &ConnectionInfo) -> Result<()> { // Try to send the first UDP packets to remote SOCKS5 server for UDP associate session if let Some(state) = self.connection_map.get_mut(info) { if let Some(udp_socket) = state.udp_socket.as_ref() { @@ -958,7 +967,8 @@ impl<'a> TunToProxy<'a> { // server. self.write_to_server(&conn_info)?; - self.comsume_cached_udp_packets(&conn_info)?; + self.consume_cached_dns_over_tcp_packets(&conn_info)?; + self.consume_cached_udp_packets(&conn_info)?; } if event.is_writable() { From d7d69ce927f59cdd13f9379fed6a8ab85e1ae625 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Tue, 22 Aug 2023 23:43:27 +0800 Subject: [PATCH 07/11] receive_dns_over_tcp_packet_and_write_to_client --- src/main.rs | 2 +- src/tun2proxy.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6047dd9..dc749c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,7 +41,7 @@ struct Args { #[arg(short, long, value_name = "level", value_enum, default_value = "info")] verbosity: ArgVerbosity, - /// DNS over TCP + /// Enable DNS over TCP #[arg(long)] dns_over_tcp: bool, } diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs index 2a4aa47..d33cb8c 100644 --- a/src/tun2proxy.rs +++ b/src/tun2proxy.rs @@ -8,7 +8,7 @@ use smoltcp::{ wire::{IpCidr, IpProtocol, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket, UDP_HEADER_LEN}, }; use socks5_impl::protocol::{Address, StreamOperation, UdpHeader, UserKey}; -use std::collections::LinkedList; +use std::{collections::LinkedList, convert::TryInto}; use std::{ collections::{HashMap, HashSet}, convert::{From, TryFrom}, @@ -535,6 +535,77 @@ impl<'a> TunToProxy<'a> { Ok(()) } + fn receive_dns_over_tcp_packet_and_write_to_client(&mut self, info: &ConnectionInfo) -> Result<()> { + let err = "udp connection state not found"; + let state = self.connection_map.get_mut(info).ok_or(err)?; + assert!(state.udp_over_tcp_expiry.is_some()); + state.udp_over_tcp_expiry = Some(Self::common_udp_life_timeout()); + + let mut buf = Vec::::new(); + let read = match state.mio_stream.read_to_end(&mut buf) { + Ok(read_result) => read_result, + Err(error) => { + if error.kind() != std::io::ErrorKind::WouldBlock { + log::error!("{} Read from proxy: {}", info.dst, error); + } + buf.len() + } + }; + if read == 0 { + return Ok(()); + } + let mut buf = buf[..read].to_vec(); + let mut to_send: LinkedList> = LinkedList::new(); + loop { + if buf.len() < 2 { + break; + } + let len = u16::from_be_bytes([buf[0], buf[1]]) as usize; + if buf.len() < len + 2 { + break; + } + let data = buf[2..len + 2].to_vec(); + + let message = dns::parse_data_to_dns_message(&data, false)?; + let name = dns::extract_domain_from_dns_message(&message)?; + let ip = dns::extract_ipaddr_from_dns_message(&message)?; + log::trace!("DNS over TCP ======== {} -> {}", name, ip); + + to_send.push_back(data); + if len + 2 == buf.len() { + break; + } + buf = buf[len + 2..].to_vec(); + } + + // Write to client + let src = info.dst.clone().try_into()?; + while let Some(packet) = to_send.pop_front() { + self.send_udp_packet_to_client(src, info.src, &packet)?; + } + Ok(()) + } + + fn udp_over_tcp_timeout_expired(&self, info: &ConnectionInfo) -> bool { + if let Some(state) = self.connection_map.get(info) { + if let Some(expiry) = state.udp_over_tcp_expiry { + return expiry < ::std::time::Instant::now(); + } + } + false + } + + fn clearup_expired_udp_over_tcp(&mut self) -> Result<()> { + let keys = self.connection_map.keys().cloned().collect::>(); + for key in keys { + if self.udp_over_tcp_timeout_expired(&key) { + log::trace!("UDP over TCP timeout: {}", key); + self.remove_connection(&key)?; + } + } + Ok(()) + } + fn process_incoming_udp_packets( &mut self, manager: &Rc, @@ -904,7 +975,15 @@ impl<'a> TunToProxy<'a> { let mut block = || -> Result<(), Error> { if event.is_readable() || event.is_read_closed() { - { + let established = self + .connection_map + .get(&conn_info) + .ok_or("")? + .tcp_proxy_handler + .connection_established(); + if self.options.dns_over_tcp && conn_info.dst.port() == DNS_PORT && established { + self.receive_dns_over_tcp_packet_and_write_to_client(&conn_info)?; + } else { let e = "connection state not found"; let state = self.connection_map.get_mut(&conn_info).ok_or(e)?; @@ -1005,6 +1084,7 @@ impl<'a> TunToProxy<'a> { } self.send_to_smoltcp()?; self.clearup_expired_udp_associate()?; + self.clearup_expired_udp_over_tcp()?; } } From 3b5f803da89c30bc6f7e6204092c7cfafdaf5f35 Mon Sep 17 00:00:00 2001 From: "B. Blechschmidt" Date: Tue, 22 Aug 2023 18:36:51 +0200 Subject: [PATCH 08/11] Get first version of DNS over TCP to work --- src/tun2proxy.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs index d33cb8c..bb6c532 100644 --- a/src/tun2proxy.rs +++ b/src/tun2proxy.rs @@ -514,15 +514,12 @@ impl<'a> TunToProxy<'a> { let err = "udp over tcp state not find"; let state = self.connection_map.get_mut(info).ok_or(err)?; state.udp_over_tcp_expiry = Some(Self::common_udp_life_timeout()); - if state.tcp_proxy_handler.connection_established() { - _ = state.mio_stream.write(&buf)?; - } else { - // FIXME: Build an IP packet with TCP and inject it into the device, - // or cache them and send them when the connection is established? - // self.device.inject_packet(&buf); - state.udp_over_tcp_data_cache.push_back(buf); - } + let data_event = IncomingDataEvent { + direction: IncomingDirection::FromClient, + buffer: &buf, + }; + state.tcp_proxy_handler.push_data(data_event)?; Ok(()) } From edb775941e66fbd010fc3511c42d93ac90707b62 Mon Sep 17 00:00:00 2001 From: "B. Blechschmidt" Date: Tue, 22 Aug 2023 22:18:15 +0200 Subject: [PATCH 09/11] Support multiple DNS queries with DNS over TCP --- src/tun2proxy.rs | 58 +++++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs index bb6c532..bb5592e 100644 --- a/src/tun2proxy.rs +++ b/src/tun2proxy.rs @@ -8,7 +8,7 @@ use smoltcp::{ wire::{IpCidr, IpProtocol, Ipv4Packet, Ipv6Packet, TcpPacket, UdpPacket, UDP_HEADER_LEN}, }; use socks5_impl::protocol::{Address, StreamOperation, UdpHeader, UserKey}; -use std::{collections::LinkedList, convert::TryInto}; +use std::collections::LinkedList; use std::{ collections::{HashMap, HashSet}, convert::{From, TryFrom}, @@ -185,6 +185,7 @@ struct TcpConnectState { udp_data_cache: LinkedList>, udp_over_tcp_expiry: Option<::std::time::Instant>, udp_over_tcp_data_cache: LinkedList>, + is_tcp_dns: bool, } pub(crate) trait TcpProxy { @@ -461,15 +462,7 @@ impl<'a> TunToProxy<'a> { fn preprocess_origin_connection_info(&mut self, info: ConnectionInfo) -> Result { let origin_dst = SocketAddr::try_from(&info.dst)?; let connection_info = match &mut self.options.virtual_dns { - None => { - let mut info = info; - let port = origin_dst.port(); - if port == DNS_PORT && info.protocol == IpProtocol::Udp && dns::addr_is_private(&origin_dst) { - let dns_addr: SocketAddr = "8.8.8.8:53".parse()?; // TODO: Configurable - info.dst = Address::from(dns_addr); - } - info - } + None => info, Some(virtual_dns) => { let dst_ip = origin_dst.ip(); virtual_dns.touch_ip(&dst_ip); @@ -485,17 +478,25 @@ impl<'a> TunToProxy<'a> { fn process_incoming_udp_packets_dns_over_tcp( &mut self, manager: &Rc, - info: &ConnectionInfo, + original_info: &ConnectionInfo, origin_dst: SocketAddr, payload: &[u8], ) -> Result<()> { _ = dns::parse_data_to_dns_message(payload, false)?; + let mut new_info = original_info.clone(); + let dns_addr: SocketAddr = "8.8.8.8:53".parse()?; + new_info.dst = Address::from(dns_addr); + + let info = &new_info; if !self.connection_map.contains_key(info) { log::info!("DNS over TCP {} ({})", info, origin_dst); + let tcp_proxy_handler = manager.new_tcp_proxy(info, false)?; let server_addr = manager.get_server_addr(); - let state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, false)?; + let mut state = self.create_new_tcp_connection_state(server_addr, origin_dst, tcp_proxy_handler, false)?; + state.is_tcp_dns = true; + state.udp_origin_dst = Some(SocketAddr::try_from(original_info.dst.clone())?); self.connection_map.insert(info.clone(), state); self.expect_smoltcp_send()?; @@ -538,20 +539,33 @@ impl<'a> TunToProxy<'a> { assert!(state.udp_over_tcp_expiry.is_some()); state.udp_over_tcp_expiry = Some(Self::common_udp_life_timeout()); - let mut buf = Vec::::new(); - let read = match state.mio_stream.read_to_end(&mut buf) { + // Code similar to the code in parent function. TODO: Cleanup. + let mut vecbuf = Vec::::new(); + let read_result = state.mio_stream.read_to_end(&mut vecbuf); + let read = match read_result { Ok(read_result) => read_result, Err(error) => { if error.kind() != std::io::ErrorKind::WouldBlock { log::error!("{} Read from proxy: {}", info.dst, error); } - buf.len() + vecbuf.len() } }; - if read == 0 { + + let data = vecbuf.as_slice(); + let data_event = IncomingDataEvent { + direction: IncomingDirection::FromServer, + buffer: &data[0..read], + }; + if let Err(error) = state.tcp_proxy_handler.push_data(data_event) { + log::error!("{}", error); + self.remove_connection(&info.clone())?; return Ok(()); } - let mut buf = buf[..read].to_vec(); + + let dns_event = state.tcp_proxy_handler.peek_data(OutgoingDirection::ToClient); + + let mut buf = dns_event.buffer.to_vec(); let mut to_send: LinkedList> = LinkedList::new(); loop { if buf.len() < 2 { @@ -566,8 +580,10 @@ impl<'a> TunToProxy<'a> { let message = dns::parse_data_to_dns_message(&data, false)?; let name = dns::extract_domain_from_dns_message(&message)?; let ip = dns::extract_ipaddr_from_dns_message(&message)?; - log::trace!("DNS over TCP ======== {} -> {}", name, ip); - + log::info!("DNS over TCP ======== {} -> {}", name, ip); + state + .tcp_proxy_handler + .consume_data(OutgoingDirection::ToClient, len + 2); to_send.push_back(data); if len + 2 == buf.len() { break; @@ -576,7 +592,7 @@ impl<'a> TunToProxy<'a> { } // Write to client - let src = info.dst.clone().try_into()?; + let src = state.udp_origin_dst.ok_or("Expected UDP addr")?; while let Some(packet) = to_send.pop_front() { self.send_udp_packet_to_client(src, info.src, &packet)?; } @@ -767,6 +783,7 @@ impl<'a> TunToProxy<'a> { udp_data_cache: LinkedList::new(), udp_over_tcp_expiry: None, udp_over_tcp_data_cache: LinkedList::new(), + is_tcp_dns: false, }; Ok(state) } @@ -980,6 +997,7 @@ impl<'a> TunToProxy<'a> { .connection_established(); if self.options.dns_over_tcp && conn_info.dst.port() == DNS_PORT && established { self.receive_dns_over_tcp_packet_and_write_to_client(&conn_info)?; + return Ok(()); } else { let e = "connection state not found"; let state = self.connection_map.get_mut(&conn_info).ok_or(e)?; From cdbed3ed9ba382838b0f4f3794a4013c92f9bcb9 Mon Sep 17 00:00:00 2001 From: "B. Blechschmidt" Date: Tue, 22 Aug 2023 22:39:00 +0200 Subject: [PATCH 10/11] Do not allow errors in printing function to screw up DNS lookups --- src/dns.rs | 1 + src/tun2proxy.rs | 39 +++++++++++++++++++++++---------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/dns.rs b/src/dns.rs index 33be70c..dbc7bfd 100644 --- a/src/dns.rs +++ b/src/dns.rs @@ -78,6 +78,7 @@ pub fn extract_ipaddr_from_dns_message(message: &Message) -> Result Result<(ConnectionInfo, bool, usize, usize) const SERVER_WRITE_CLOSED: u8 = 1; const CLIENT_WRITE_CLOSED: u8 = 2; -const UDP_ASSO_TIMEOUT: u64 = 10; // seconds +const UDP_ASSO_TIMEOUT: u64 = 10; +// seconds const DNS_PORT: u16 = 53; struct TcpConnectState { @@ -239,8 +242,8 @@ impl<'a> TunToProxy<'a> { .register(&mut exit_receiver, EXIT_TOKEN, Interest::READABLE)?; #[rustfmt::skip] - let config = match tun.capabilities().medium { - Medium::Ethernet => Config::new(smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()), + let config = match tun.capabilities().medium { + Medium::Ethernet => Config::new(smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()), Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), Medium::Ieee802154 => todo!(), }; @@ -578,9 +581,13 @@ impl<'a> TunToProxy<'a> { let data = buf[2..len + 2].to_vec(); let message = dns::parse_data_to_dns_message(&data, false)?; - let name = dns::extract_domain_from_dns_message(&message)?; - let ip = dns::extract_ipaddr_from_dns_message(&message)?; - log::info!("DNS over TCP ======== {} -> {}", name, ip); + + if let (Ok(name), Ok(ip)) = ( + dns::extract_domain_from_dns_message(&message), + dns::extract_ipaddr_from_dns_message(&message), + ) { + log::info!("DNS over TCP ======== {} -> {}", name, ip); + } state .tcp_proxy_handler .consume_data(OutgoingDirection::ToClient, len + 2); From 72a00af0ed350c267241d4d20aa5e7d5b1c7bd0f Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Wed, 23 Aug 2023 09:28:11 +0800 Subject: [PATCH 11/11] re-format code --- src/dns.rs | 1 - src/tun2proxy.rs | 75 ++++++++++++++++++++---------------------------- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/dns.rs b/src/dns.rs index dbc7bfd..33be70c 100644 --- a/src/dns.rs +++ b/src/dns.rs @@ -78,7 +78,6 @@ pub fn extract_ipaddr_from_dns_message(message: &Message) -> Result Result<(ConnectionInfo, bool, usize, usize) const SERVER_WRITE_CLOSED: u8 = 1; const CLIENT_WRITE_CLOSED: u8 = 2; -const UDP_ASSO_TIMEOUT: u64 = 10; -// seconds +const UDP_ASSO_TIMEOUT: u64 = 10; // seconds const DNS_PORT: u16 = 53; -struct TcpConnectState { +struct ConnectionState { smoltcp_handle: Option, mio_stream: TcpStream, token: Token, @@ -187,7 +184,6 @@ struct TcpConnectState { udp_origin_dst: Option, udp_data_cache: LinkedList>, udp_over_tcp_expiry: Option<::std::time::Instant>, - udp_over_tcp_data_cache: LinkedList>, is_tcp_dns: bool, } @@ -216,7 +212,7 @@ pub struct TunToProxy<'a> { tun: TunTapInterface, poll: Poll, iface: Interface, - connection_map: HashMap, + connection_map: HashMap, connection_manager: Option>, next_token: usize, sockets: SocketSet<'a>, @@ -242,7 +238,7 @@ impl<'a> TunToProxy<'a> { .register(&mut exit_receiver, EXIT_TOKEN, Interest::READABLE)?; #[rustfmt::skip] - let config = match tun.capabilities().medium { + let config = match tun.capabilities().medium { Medium::Ethernet => Config::new(smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into()), Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip), Medium::Ieee802154 => todo!(), @@ -440,7 +436,7 @@ impl<'a> TunToProxy<'a> { Ok(()) } - fn update_mio_socket_interest(poll: &mut Poll, state: &mut TcpConnectState) -> Result<()> { + fn update_mio_socket_interest(poll: &mut Poll, state: &mut ConnectionState) -> Result<()> { // Maybe we did not listen for any events before. Therefore, just swallow the error. if let Err(err) = poll.registry().deregister(&mut state.mio_stream) { log::trace!("{}", err); @@ -478,7 +474,7 @@ impl<'a> TunToProxy<'a> { Ok(connection_info) } - fn process_incoming_udp_packets_dns_over_tcp( + fn process_incoming_dns_over_tcp_packets( &mut self, manager: &Rc, original_info: &ConnectionInfo, @@ -502,6 +498,7 @@ impl<'a> TunToProxy<'a> { state.udp_origin_dst = Some(SocketAddr::try_from(original_info.dst.clone())?); self.connection_map.insert(info.clone(), state); + // TODO: Move this 3 lines to the function end? self.expect_smoltcp_send()?; self.tunsocket_read_and_forward(info)?; self.write_to_server(info)?; @@ -527,15 +524,6 @@ impl<'a> TunToProxy<'a> { Ok(()) } - fn consume_cached_dns_over_tcp_packets(&mut self, info: &ConnectionInfo) -> Result<()> { - if let Some(state) = self.connection_map.get_mut(info) { - while let Some(buf) = state.udp_over_tcp_data_cache.pop_front() { - _ = state.mio_stream.write(&buf)?; - } - } - Ok(()) - } - fn receive_dns_over_tcp_packet_and_write_to_client(&mut self, info: &ConnectionInfo) -> Result<()> { let err = "udp connection state not found"; let state = self.connection_map.get_mut(info).ok_or(err)?; @@ -580,18 +568,19 @@ impl<'a> TunToProxy<'a> { } let data = buf[2..len + 2].to_vec(); - let message = dns::parse_data_to_dns_message(&data, false)?; + let mut message = dns::parse_data_to_dns_message(&data, false)?; + + let name = dns::extract_domain_from_dns_message(&message)?; + let ip = dns::extract_ipaddr_from_dns_message(&message); + log::info!("DNS over TCP query result: {} -> {:?}", name, ip); - if let (Ok(name), Ok(ip)) = ( - dns::extract_domain_from_dns_message(&message), - dns::extract_ipaddr_from_dns_message(&message), - ) { - log::info!("DNS over TCP ======== {} -> {}", name, ip); - } state .tcp_proxy_handler .consume_data(OutgoingDirection::ToClient, len + 2); - to_send.push_back(data); + + dns::remove_ipv6_entries(&mut message); // TODO: Configurable + + to_send.push_back(message.to_vec()?); if len + 2 == buf.len() { break; } @@ -615,7 +604,7 @@ impl<'a> TunToProxy<'a> { false } - fn clearup_expired_udp_over_tcp(&mut self) -> Result<()> { + fn clearup_expired_dns_over_tcp(&mut self) -> Result<()> { let keys = self.connection_map.keys().cloned().collect::>(); for key in keys { if self.udp_over_tcp_timeout_expired(&key) { @@ -724,7 +713,7 @@ impl<'a> TunToProxy<'a> { } else { // Another UDP packet if self.options.dns_over_tcp && origin_dst.port() == DNS_PORT { - self.process_incoming_udp_packets_dns_over_tcp(&manager, &info, origin_dst, payload)?; + self.process_incoming_dns_over_tcp_packets(&manager, &info, origin_dst, payload)?; } else { self.process_incoming_udp_packets(&manager, &info, origin_dst, payload)?; } @@ -746,7 +735,7 @@ impl<'a> TunToProxy<'a> { dst: SocketAddr, tcp_proxy_handler: Box, udp_associate: bool, - ) -> Result { + ) -> Result { let mut socket = tcp::Socket::new( tcp::SocketBuffer::new(vec![0; 1024 * 128]), tcp::SocketBuffer::new(vec![0; 1024 * 128]), @@ -775,7 +764,7 @@ impl<'a> TunToProxy<'a> { } else { (None, None) }; - let state = TcpConnectState { + let state = ConnectionState { smoltcp_handle: Some(handle), mio_stream: client, token, @@ -789,7 +778,6 @@ impl<'a> TunToProxy<'a> { udp_origin_dst: None, udp_data_cache: LinkedList::new(), udp_over_tcp_expiry: None, - udp_over_tcp_data_cache: LinkedList::new(), is_tcp_dns: false, }; Ok(state) @@ -1068,7 +1056,6 @@ impl<'a> TunToProxy<'a> { // server. self.write_to_server(&conn_info)?; - self.consume_cached_dns_over_tcp_packets(&conn_info)?; self.consume_cached_udp_packets(&conn_info)?; } @@ -1106,7 +1093,7 @@ impl<'a> TunToProxy<'a> { } self.send_to_smoltcp()?; self.clearup_expired_udp_associate()?; - self.clearup_expired_udp_over_tcp()?; + self.clearup_expired_dns_over_tcp()?; } }