diff --git a/.vscode/settings.json b/.vscode/settings.json index 9eb94d8..fb0827b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { - "rust-analyzer.cargo.target": "x86_64-pc-windows-gnu", + // "rust-analyzer.cargo.target": "x86_64-pc-windows-gnu", // "rust-analyzer.cargo.features": ["tun"] } \ No newline at end of file diff --git a/src/bin/punchnet/local_udp_info.rs b/src/bin/punchnet/local_udp_info.rs index 298c6a0..3913894 100644 --- a/src/bin/punchnet/local_udp_info.rs +++ b/src/bin/punchnet/local_udp_info.rs @@ -2,17 +2,30 @@ use std::{net::SocketAddr, sync::atomic::Ordering, time::Duration}; use num_enum::TryFromPrimitive; use punchnet::get_edge; -use sdlan_sn_rs::utils::Mac; +use sdlan_sn_rs::utils::{Mac, Result, SDLanError}; use serde::{Deserialize, Serialize}; use tokio::{net::UdpSocket, time::sleep}; +/// 这里是punchnet list等命令与punchnet主进程通信的消息结构 +/// data-type: u8 +/// data-len: u16 +/// data: json #[derive(TryFromPrimitive)] #[repr(u8)] pub enum InfoFuncCode { + // query for info Info = 0x00, + // query for exit node list, + ExitNodeList = 0x01, InfoFeedback = 0x80, + ExitNodeListFeedback = 0x81, +} + +#[derive(Serialize, Deserialize)] +pub struct ExitNodeListFeedback { + pub nodes: Vec, } #[derive(Serialize, Deserialize)] @@ -26,79 +39,99 @@ pub struct InfoFeedback { pub rx_sup: u64, } -pub async fn query_for_info() { +pub async fn do_query(data_type: u8, data: &[u8], timeout: Duration) -> Result{ let Ok(udp) = UdpSocket::bind("127.0.0.1:0").await else { eprintln!("failed to create"); - return; + return Err(SDLanError::IOError("failed to create".to_owned())); }; let remote = format!("127.0.0.1:{}", LOCAL_INFO_UDP_PORT).parse::().unwrap(); - let buf = vec![InfoFuncCode::Info as u8, 0, 0]; + let mut buf = Vec::with_capacity(1+2 + data.len()); + buf.push(data_type); + buf.extend_from_slice(&(data.len() as u16).to_be_bytes()); + buf.extend_from_slice(data); if let Err(e) = udp.send_to(buf.as_slice(), remote).await { eprintln!("failed to send query info"); - return; + return Err(SDLanError::IOError("failed to send query".to_owned())); } let mut buf = vec![0;1024]; tokio::select! { data = udp.recv_from(&mut buf) => { - if let Ok((size, from)) = data { + if let Ok((size, _from)) = data { if size < 3 { eprintln!("no byte received"); - return; + return Err(SDLanError::IOError("no bytes received".to_owned())); } buf.truncate(size); - let Ok(typecode) = InfoFuncCode::try_from_primitive(buf[0]) else { + let Ok(_typecode) = InfoFuncCode::try_from_primitive(buf[0]) else { eprintln!("invalid type: {}", buf[0]); - return; + return Err(SDLanError::IOError("invalid byte".to_owned())); }; let size = u16::from_be_bytes([buf[1], buf[2]]); if buf.len() as u16 != size + 3 { eprintln!("info length error: buf.len={}, size={}", buf.len(), size); - return; - } - - match typecode { - InfoFuncCode::InfoFeedback => { - let Ok(data) = serde_json::from_slice::(&buf[3..]) else { - eprintln!("failed to marshal to json"); - return; - }; - println!("punchnet info:"); - let ip = data.ip; - println!(" ip: {}.{}.{}.{}", - ((ip>>24) & 0xff) as u8, - ((ip>>16) & 0xff) as u8, - ((ip>>8) & 0xff) as u8, - ((ip) & 0xff) as u8, - ); - let mac = data.mac; - println!(" mac: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] - ); - - println!(" p2p tx: {} bytes", data.tx_p2p); - println!(" p2p rx: {} bytes", data.rx_p2p); - - println!(" super tx: {} bytes", data.tx_sup); - println!(" super rx: {} bytes", data.rx_sup); - } - _other => { - } + return Err(SDLanError::IOError("info length error".to_owned())); } + let result = serde_json::from_slice::(&buf[3..])?; + return Ok(result); } else { eprintln!("failed to recv from punchnet"); } - return; + return Err(SDLanError::IOError("failed to recv from punchnet".to_owned())); } - _ = sleep(Duration::from_secs(3)) => { + _ = sleep(timeout) => { eprintln!("query timed out, is punchnet running?"); - return; + return Err(SDLanError::IOError("timed out".to_owned())); + } + } +} + +pub async fn query_for_exit_node_list() { + let result = do_query::(InfoFuncCode::ExitNodeList as u8, &[], Duration::from_secs(3)).await; + match result { + Ok(data) => { + println!("exit nodes:"); + for node in &data.nodes { + println!(" {}", node); + } + } + Err(e) => { + eprintln!("failed to query for exit node list: {}", e.as_str()); + } + } +} + +pub async fn query_for_info() { + let result = do_query::(InfoFuncCode::Info as u8, &[], Duration::from_secs(3)).await; + match result { + Ok(data) => { + println!("punchnet info:"); + let ip = data.ip; + println!(" ip: {}.{}.{}.{}", + ((ip>>24) & 0xff) as u8, + ((ip>>16) & 0xff) as u8, + ((ip>>8) & 0xff) as u8, + ((ip) & 0xff) as u8, + ); + let mac = data.mac; + println!(" mac: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] + ); + + println!(" p2p tx: {} bytes", data.tx_p2p); + println!(" p2p rx: {} bytes", data.rx_p2p); + + println!(" super tx: {} bytes", data.tx_sup); + println!(" super rx: {} bytes", data.rx_sup); + } + Err(e) => { + eprintln!("failed to query for info: {}", e.as_str()); } } } diff --git a/src/bin/punchnet/main.rs b/src/bin/punchnet/main.rs index 85274d2..ddebb95 100755 --- a/src/bin/punchnet/main.rs +++ b/src/bin/punchnet/main.rs @@ -18,6 +18,7 @@ use libc::{SIGTERM, kill}; use punchnet::CachedLoginInfo; use punchnet::CommandLineInput2; use punchnet::Commands; +use punchnet::ExitNodeCmd; use punchnet::get_access_token; use punchnet::get_base_dir; use punchnet::get_edge; @@ -47,6 +48,7 @@ use crate::api::connect; use crate::api::login_with_token; use crate::api::login_with_user_pass; use crate::local_udp_info::handle_query_for_info_info; +use crate::local_udp_info::query_for_exit_node_list; use crate::local_udp_info::query_for_info; @@ -324,6 +326,25 @@ fn main() { process::exit(0); } + Commands::ExitNode(cmd) => { + let rt = Runtime::new().unwrap(); + rt.block_on(async move { + match cmd { + ExitNodeCmd::List => { + query_for_exit_node_list().await; + } + ExitNodeCmd::Start(info) => { + + } + ExitNodeCmd::Stop => { + + } + } + + }); + process::exit(0); + } + #[cfg(not(target_os = "windows"))] Commands::Stop => { match fs::read_to_string("/tmp/punchnet.pid") { @@ -352,7 +373,6 @@ fn main() { process::exit(0); } Commands::Info => { - let rt = Runtime::new().unwrap(); rt.block_on(async move { query_for_info().await; diff --git a/src/network/packet.rs b/src/network/packet.rs index 7331dfa..d434cff 100755 --- a/src/network/packet.rs +++ b/src/network/packet.rs @@ -77,12 +77,14 @@ pub async fn read_and_parse_packet( */ buf.truncate(size); + println!("handle udp packet"); match handle_packet(eee, from, &buf).await { Ok(_) => {} Err(e) => { error!("failed to handle_packet: {:?}", e); } } + println!("handle udp packet ok"); } } @@ -888,7 +890,17 @@ async fn handle_tun_packet( let protocol = ipv4.protocol; match protocol { ip_number::TCP => { - let tcp_header = headers.transport.unwrap().tcp().unwrap(); + + let Some(transport) = headers.transport else { + error!("failed to get transport header"); + return; + }; + let Some(tcp_header) = transport.tcp() else { + error!("failed to get udp header"); + return; + }; + + // let tcp_header = headers.transport.unwrap().tcp().unwrap(); let five_tuple = FiveTuple { src_ip: ipv4.destination.into(), dst_ip: ipv4.source.into(), @@ -905,7 +917,16 @@ async fn handle_tun_packet( } } ip_number::UDP => { - let udp_header = headers.transport.unwrap().udp().unwrap(); + let Some(transport) = headers.transport else { + error!("failed to get transport header"); + return; + }; + let Some(udp_header) = transport.udp() else { + error!("failed to get udp header"); + return; + }; + + // let udp_header = headers.transport.unwrap().udp().unwrap(); let five_tuple = FiveTuple { src_ip: ipv4.destination.into(), dst_ip: ipv4.source.into(), diff --git a/src/network/tun_linux.rs b/src/network/tun_linux.rs index 383af46..d0470ca 100755 --- a/src/network/tun_linux.rs +++ b/src/network/tun_linux.rs @@ -250,7 +250,7 @@ impl TunTapPacketHandler for Iface { use bytes::Bytes; if hdr.ether_type == EtherType::ARP { - use crate::network::{ARP_REPLY, ARP_REQUEST, ArpHdr}; + use crate::network::{ARP_REQUEST, ArpHdr}; let arp = ArpHdr::from_slice(&data); match arp.opcode { @@ -287,11 +287,14 @@ impl TunTapPacketHandler for Iface { if let Some(ip) = headers.net { match ip { etherparse::NetHeaders::Ipv4(ipv4, _) => { - use etherparse::ip_number::{self, ICMP}; + if u32::from_be_bytes(ipv4.destination) == u32::from_be_bytes([10, 10, 2, 6]) { + let raw_data = &data[14..]; + println!("got packet to 10.10.2.6(size={}): {:?}", raw_data.len(), raw_data); + } use crate::FiveTuple; use etherparse::IpNumber; - + if let Some(transport) = headers.transport { match ipv4.protocol { IpNumber::TCP => { diff --git a/src/utils/acl_session.rs b/src/utils/acl_session.rs index 1d37082..3f97a93 100644 --- a/src/utils/acl_session.rs +++ b/src/utils/acl_session.rs @@ -143,6 +143,7 @@ impl RuleCache { if allow_routing { return (true, false); } + return (true, false); error!("is identity ok? {:?}", info); if self.session_table.process_packet(&info) { diff --git a/src/utils/command.rs b/src/utils/command.rs index fa6237a..3e96349 100755 --- a/src/utils/command.rs +++ b/src/utils/command.rs @@ -26,6 +26,9 @@ pub enum Commands { Info, + #[command(subcommand)] + ExitNode(ExitNodeCmd), + RouteAdd(RouteCmdInfo), RouteDel(RouteCmdInfo), RouteList, @@ -34,6 +37,22 @@ pub enum Commands { Stop, } +#[derive(Subcommand, Debug)] +pub enum ExitNodeCmd { + // list current exit node, and the now used exit node, + List, + + /// just exit the node, but keep the process running + Start(ExitNodeInfo), + Stop, +} + +#[derive(Args, Debug)] +pub struct ExitNodeInfo { + #[arg(short, long, default_value="")] + pub name: String, +} + #[derive(Args, Debug)] pub struct StartArguments { #[arg(short, long, default_value="")]