diff options
| -rw-r--r-- | .woodpecker.yaml | 34 | ||||
| -rw-r--r-- | Cargo.lock | 4 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | build.rs | 6 | ||||
| -rw-r--r-- | src/config.rs | 16 | ||||
| -rw-r--r-- | src/geoparsers/geoip2.rs | 4 | ||||
| -rw-r--r-- | src/geoparsers/mod.rs | 2 | ||||
| -rw-r--r-- | src/geoparsers/v2ray/parsing.rs | 2 | ||||
| -rw-r--r-- | src/geoparsers/v2ray/types.rs | 12 | ||||
| -rw-r--r-- | src/lib.rs | 2 | ||||
| -rw-r--r-- | src/main.rs | 7 | ||||
| -rw-r--r-- | src/sniffing/headers.rs | 142 | ||||
| -rw-r--r-- | src/sniffing/metadata.rs | 1 | ||||
| -rw-r--r-- | src/startup.rs | 4 | ||||
| -rw-r--r-- | tests/headers.rs | 32 | ||||
| -rw-r--r-- | tests/v2ray_geosite.rs | 16 |
16 files changed, 181 insertions, 104 deletions
diff --git a/.woodpecker.yaml b/.woodpecker.yaml new file mode 100644 index 0000000..e22e3fa --- /dev/null +++ b/.woodpecker.yaml @@ -0,0 +1,34 @@ +steps: + build: + image: rust:1.94.0-bullseye + environment: + RUST_BACKTRACE: 1 + CARGO_TERM_COLOR: always + commands: + - rustup default stable + - cargo build --verbose --release --jobs 4 + when: + branch: main + event: [ push, pull_request ] + tests: + image: rust + environment: + RUST_BACKTRACE: 1 + CARGO_TERM_COLOR: always + commands: + - cargo test --verbose --jobs 4 -- --test-threads=4 + when: + event: [ pull_request, push ] + branch: main + clippy_and_fmt: + image: rust:1.94.0-bullseye + environment: + RUST_BACKTRACE: 1 + CARGO_TERM_COLOR: always + commands: + - rustup component add clippy rustfmt + - cargo fmt --all --check + - cargo clippy --jobs 4 -- -D clippy::all # -D warnings + when: + branch: main + event: [ pull_request ] @@ -449,9 +449,6 @@ name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -dependencies = [ - "serde", -] [[package]] name = "c2rust-bitfields" @@ -2349,7 +2346,6 @@ name = "nsc" version = "0.1.0" dependencies = [ "arti-client", - "bytes", "ipnet", "iptables", "maxminddb", @@ -11,7 +11,6 @@ prost-build = "0.14.3" [dependencies] arti-client = "0.40.0" -bytes = { version = "1.11.1", features = ["serde"] } ipnet = "2.12.0" iptables = "0.6.0" maxminddb = "0.27.3" @@ -2,13 +2,11 @@ use std::io::Result; fn main() -> Result<()> { let out_dir = std::path::PathBuf::from("src/geoparsers/v2ray/"); - + prost_build::Config::new() .out_dir(&out_dir) .compile_protos( - &[ - "src/geoparsers/v2ray/proto_src/geosite.proto", - ], + &["src/geoparsers/v2ray/proto_src/geosite.proto"], &["src/geoparsers/v2ray/proto_src/"], )?; diff --git a/src/config.rs b/src/config.rs index 846a1b5..a5739ec 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,14 +17,14 @@ pub struct Config { pub mode: RunTypes, } -// TODO: Think how to add other anonymisers -// Like VPN on localhost:10808 -// it can be like: -// ```toml +// TODO: Think how to add other anonymisers +// Like VPN on localhost:10808 +// it can be like: +// ```toml // [[proxy]] -// name = "VPN" -// addr = "127.0.0.1:10808" -// type = "SOCKS5" # ... +// name = "VPN" +// addr = "127.0.0.1:10808" +// type = "SOCKS5" # ... // ``` impl Default for Config { @@ -39,5 +39,3 @@ impl Default for Config { } } } - - diff --git a/src/geoparsers/geoip2.rs b/src/geoparsers/geoip2.rs index 9f2cc8b..1fd6ab1 100644 --- a/src/geoparsers/geoip2.rs +++ b/src/geoparsers/geoip2.rs @@ -3,8 +3,8 @@ use maxminddb::{Reader, geoip2}; use serde::Deserialize; use std::net::IpAddr; -// For now only MMDB because i cant found .proto schemes of -// V2Ray GeoSite.dat :(( +// For now only MMDB because i cant found .proto schemes of +// V2Ray GeoSite.dat :(( // TODO: V2Ray protobuf parsing && Test 4 ts /// Interface enum for `dst_addr` info diff --git a/src/geoparsers/mod.rs b/src/geoparsers/mod.rs index 7828b9b..c3cfece 100644 --- a/src/geoparsers/mod.rs +++ b/src/geoparsers/mod.rs @@ -1,3 +1,3 @@ pub mod geoip2; pub mod toml; -pub mod v2ray;
\ No newline at end of file +pub mod v2ray; diff --git a/src/geoparsers/v2ray/parsing.rs b/src/geoparsers/v2ray/parsing.rs index 0f897bf..4f0bbba 100644 --- a/src/geoparsers/v2ray/parsing.rs +++ b/src/geoparsers/v2ray/parsing.rs @@ -1,6 +1,6 @@ use crate::geoparsers::v2ray::types::{Domain, GeoSite, GeoSiteList}; -use prost::bytes::Buf; use prost::Message; +use prost::bytes::Buf; use std::fs; pub struct GeoSiteService { diff --git a/src/geoparsers/v2ray/types.rs b/src/geoparsers/v2ray/types.rs index d7c0436..b82fbbf 100644 --- a/src/geoparsers/v2ray/types.rs +++ b/src/geoparsers/v2ray/types.rs @@ -33,17 +33,7 @@ pub mod domain { } } /// Type of domain value. - #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - ::prost::Enumeration - )] + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum Type { /// The value is used as is. @@ -1,4 +1,4 @@ -pub mod sniffing; pub mod config; pub mod geoparsers; +pub mod sniffing; pub mod startup; diff --git a/src/main.rs b/src/main.rs index df1a31c..c2b6707 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,11 @@ //mod routing; -//mod config; -//mod geoparsers; -//pub mod sniffing; +mod config; +mod geoparsers; +pub mod sniffing; //mod startup; use nsc::startup::init; fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> { - init() } diff --git a/src/sniffing/headers.rs b/src/sniffing/headers.rs index 40a311c..1db0da6 100644 --- a/src/sniffing/headers.rs +++ b/src/sniffing/headers.rs @@ -1,5 +1,5 @@ -use tun::Error; use std::fmt; +use tun::Error; // Here we will recieve bytes and try to get their destanation & apply Rules for them. use crate::config::Config; @@ -8,14 +8,14 @@ use crate::config::Config; pub enum Protocol { TCP, UDP, - Unsupported(u8) + Unsupported(u8), } type SourceV4Ip = Ipv4; type SourceV6Ip = Ipv6; #[derive(PartialEq, Debug)] pub enum IpVersion { V4, - V6 + V6, } type Ipv4 = [u8; 4]; type Ipv6 = [u16; 8]; @@ -29,7 +29,7 @@ pub enum PacketInfo { dst_ip: Ipv4, dst_port: Port, protocol: Protocol, - dns: bool + dns: bool, }, // <https://www.geeksforgeeks.org/computer-networks/internet-protocol-version-6-ipv6-header/> V6 { @@ -38,21 +38,59 @@ pub enum PacketInfo { dst_ip: Ipv6, dst_port: Port, protocol: Protocol, - dns: bool - } + dns: bool, + }, } impl fmt::Display for PacketInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.version() == &IpVersion::V4 { - let src_ip = self.src_ipv4_ip().unwrap(); - let dst_ip = self.dst_ipv4_ip().unwrap(); - write!(f, "{}.{}.{}.{}:{} -> {}.{}.{}.{}:{} {:?} is dns? {:?}", src_ip[0], src_ip[1], src_ip[2], src_ip[3], self.src_port(), dst_ip[0], dst_ip[1], dst_ip[2], dst_ip[3], self.dst_port(), self.protocol(), self.dns()) + let src_ip = self.src_ipv4_ip().unwrap(); + let dst_ip = self.dst_ipv4_ip().unwrap(); + write!( + f, + "{}.{}.{}.{}:{} -> {}.{}.{}.{}:{} {:?} is dns? {:?}", + src_ip[0], + src_ip[1], + src_ip[2], + src_ip[3], + self.src_port(), + dst_ip[0], + dst_ip[1], + dst_ip[2], + dst_ip[3], + self.dst_port(), + self.protocol(), + self.dns() + ) } else { - let src_ip = self.src_ipv6_ip().unwrap(); - let dst_ip = self.dst_ipv6_ip().unwrap(); + let src_ip = self.src_ipv6_ip().unwrap(); + let dst_ip = self.dst_ipv6_ip().unwrap(); // y:y:y:y:y:y:y:y = 8 hexademical; y = segment, pair of 2 u8 big endian - write!(f, "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x} port:{} -> {:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x} port:{} {:?} is dns? {:?}", src_ip[0], src_ip[1], src_ip[2], src_ip[3], src_ip[4], src_ip[5], src_ip[6], src_ip[7], self.src_port(), dst_ip[0], dst_ip[1], dst_ip[2], dst_ip[3], dst_ip[4], dst_ip[5], dst_ip[6], dst_ip[7], self.dst_port(), self.protocol(), self.dns()) + write!( + f, + "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x} port:{} -> {:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x} port:{} {:?} is dns? {:?}", + src_ip[0], + src_ip[1], + src_ip[2], + src_ip[3], + src_ip[4], + src_ip[5], + src_ip[6], + src_ip[7], + self.src_port(), + dst_ip[0], + dst_ip[1], + dst_ip[2], + dst_ip[3], + dst_ip[4], + dst_ip[5], + dst_ip[6], + dst_ip[7], + self.dst_port(), + self.protocol(), + self.dns() + ) } } } @@ -60,20 +98,20 @@ impl fmt::Display for PacketInfo { impl PacketInfo { pub fn dns(&self) -> &bool { match self { - PacketInfo::V4 { dns, ..} => dns, - PacketInfo::V6 { dns, ..} => dns, + PacketInfo::V4 { dns, .. } => dns, + PacketInfo::V6 { dns, .. } => dns, } } pub fn src_ipv6_ip(&self) -> Option<&SourceV6Ip> { match self { PacketInfo::V6 { src_ip, .. } => Some(src_ip), - _ => None + _ => None, } } pub fn dst_ipv6_ip(&self) -> Option<&SourceV6Ip> { match self { PacketInfo::V6 { dst_ip, .. } => Some(dst_ip), - _ => None + _ => None, } } pub fn src_ipv4_ip(&self) -> Option<&SourceV4Ip> { @@ -85,25 +123,25 @@ impl PacketInfo { pub fn dst_ipv4_ip(&self) -> Option<&SourceV4Ip> { match self { PacketInfo::V4 { dst_ip, .. } => Some(dst_ip), - _ => None + _ => None, } } pub fn src_port(&self) -> &Port { match self { PacketInfo::V4 { src_port, .. } => src_port, - PacketInfo::V6 { src_port, .. } => src_port + PacketInfo::V6 { src_port, .. } => src_port, } } pub fn dst_port(&self) -> &Port { match self { PacketInfo::V4 { dst_port, .. } => dst_port, - PacketInfo::V6 { dst_port, .. } => dst_port + PacketInfo::V6 { dst_port, .. } => dst_port, } } pub fn version(&self) -> &IpVersion { match self { - PacketInfo::V4 { .. }=> &IpVersion::V4, - PacketInfo::V6 { .. }=> &IpVersion::V6 + PacketInfo::V4 { .. } => &IpVersion::V4, + PacketInfo::V6 { .. } => &IpVersion::V6, } } pub fn protocol(&self) -> &Protocol { @@ -125,39 +163,51 @@ pub fn sniff_raw_packets(packet: &Packet) -> SniffedPacket { 4 => { // Internet Header Length (IHL). let ihl = (packet[0] & 0x0F) as usize * 4; - let dst_port = Port::from_be_bytes([packet[ihl+2], packet[ihl+3]]); + let dst_port = Port::from_be_bytes([packet[ihl + 2], packet[ihl + 3]]); let dns; - if dst_port == 53 { dns = true; } else { dns = false; }; - let v4 = PacketInfo::V4{ + if dst_port == 53 { + dns = true; + } else { + dns = false; + }; + let v4 = PacketInfo::V4 { src_ip: <Ipv4>::try_from(&packet[12..16])?, - src_port: Port::from_be_bytes([packet[ihl], packet[ihl+1]]), + src_port: Port::from_be_bytes([packet[ihl], packet[ihl + 1]]), dst_ip: <Ipv4>::try_from(&packet[16..20])?, dst_port, protocol: match packet[9] { 6 => Protocol::TCP, 17 => Protocol::UDP, - p => Protocol::Unsupported(p) + p => Protocol::Unsupported(p), }, - dns + dns, }; - if !matches!(v4.protocol(), Protocol::Unsupported(_)) { - println!("{v4}"); - } else { - // TODO: make --debug option which will include this diagnostic, for general use this + if !matches!(v4.protocol(), Protocol::Unsupported(_)) { + println!("{v4}"); + } else { + // TODO: make --debug option which will include this diagnostic, for general use this // should be off - // println!("oppsie unsupported protocol: {:?}", v4.protocol()); + // println!("oppsie unsupported protocol: {:?}", v4.protocol()); } Ok(v4) - }, + } 6 => { // y:y:y:y:y:y:y:y hexademical; y = segment, pair of 2 u8 in big endian - let src_ip = std::array::from_fn(|i| u16::from_be_bytes([packet[8 + i*2], packet[8 + i*2 + 1]])); - let dst_ip = std::array::from_fn(|i| u16::from_be_bytes([packet[24 + i*2], packet[24 + i*2 + 1]])); + let src_ip = std::array::from_fn(|i| { + u16::from_be_bytes([packet[8 + i * 2], packet[8 + i * 2 + 1]]) + }); + let dst_ip = std::array::from_fn(|i| { + u16::from_be_bytes([packet[24 + i * 2], packet[24 + i * 2 + 1]]) + }); let dst_port = Port::from_be_bytes([packet[42], packet[43]]); let dns; - if dst_port == 53 { dns = true; } else { dns = false; }; - let v6 = PacketInfo::V6{ + if dst_port == 53 { + dns = true; + } else { + dns = false; + }; + let v6 = PacketInfo::V6 { src_ip, src_port: Port::from_be_bytes([packet[40], packet[41]]), dst_ip, @@ -165,22 +215,20 @@ pub fn sniff_raw_packets(packet: &Packet) -> SniffedPacket { protocol: match packet[6] { 6 => Protocol::TCP, 17 => Protocol::UDP, - p => Protocol::Unsupported(p) + p => Protocol::Unsupported(p), }, - dns + dns, }; - if !matches!(v6.protocol(), Protocol::Unsupported(_)) { - println!("{v6}"); - } else { - // TODO: make --debug option which will include this diagnostic, for general use this + if !matches!(v6.protocol(), Protocol::Unsupported(_)) { + println!("{v6}"); + } else { + // TODO: make --debug option which will include this diagnostic, for general use this // should be off - // println!("oppsie unsupported protocol: {:?}", v6.protocol()); + // println!("oppsie unsupported protocol: {:?}", v6.protocol()); } Ok(v6) - }, - ver => { - Err(format!("unsuppiorted ver: {ver}").into()) } + ver => Err(format!("unsuppiorted ver: {ver}").into()), } } diff --git a/src/sniffing/metadata.rs b/src/sniffing/metadata.rs index e69de29..8b13789 100644 --- a/src/sniffing/metadata.rs +++ b/src/sniffing/metadata.rs @@ -0,0 +1 @@ + diff --git a/src/startup.rs b/src/startup.rs index 10a8441..3a1a09a 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -1,7 +1,7 @@ // Here we iniitialize systems crucial for nsc -use std::io::Read; -use crate::sniffing::headers::sniff_raw_packets; use crate::sniffing::headers::Protocol; +use crate::sniffing::headers::sniff_raw_packets; +use std::io::Read; pub fn init() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> { let mut config = tun::Configuration::default(); config diff --git a/tests/headers.rs b/tests/headers.rs index 3c1b74a..ff51111 100644 --- a/tests/headers.rs +++ b/tests/headers.rs @@ -1,8 +1,8 @@ use nsc::sniffing::*; use crate::headers::Protocol; -use nsc::sniffing::headers::sniff_raw_packets; use nsc::sniffing::headers::PacketInfo; +use nsc::sniffing::headers::sniff_raw_packets; #[test] fn generic_typeck() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { @@ -115,7 +115,8 @@ fn generic_typeck() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { // [0] IPv4 TCP 192.168.1.100:4832 → 93.184.216.34:443 assert_eq!( sniff_raw_packets(test_suite[0])?, - PacketInfo::V6 { dns: false, + PacketInfo::V6 { + dns: false, src_ip: [192, 168, 1, 100], src_port: 4832, dst_ip: [93, 184, 216, 34], @@ -127,7 +128,8 @@ fn generic_typeck() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { // [1] IPv4 UDP 10.0.0.9:5353 → 224.0.0.251:5353 (mDNS) assert_eq!( sniff_raw_packets(test_suite[1])?, - PacketInfo::V6 { dns: false, + PacketInfo::V6 { + dns: false, src_ip: [10, 0, 0, 9], src_port: 5353, dst_ip: [224, 0, 0, 251], @@ -139,7 +141,8 @@ fn generic_typeck() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { // [2] IPv4 UDP 10.0.0.9:1024 → 8.8.8.8:53 (DNS) assert_eq!( sniff_raw_packets(test_suite[2])?, - PacketInfo::V6 { dns: false, + PacketInfo::V6 { + dns: false, src_ip: [10, 0, 0, 9], src_port: 1024, dst_ip: [8, 8, 8, 8], @@ -151,7 +154,8 @@ fn generic_typeck() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { // [3] IPv4 TCP 10.0.0.5:54321 → 10.0.0.1:80 (HTTP) assert_eq!( sniff_raw_packets(test_suite[3])?, - PacketInfo::V6 { dns: false, + PacketInfo::V6 { + dns: false, src_ip: [10, 0, 0, 5], src_port: 54321, dst_ip: [10, 0, 0, 1], @@ -163,7 +167,8 @@ fn generic_typeck() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { // [4] IPv4 TCP 172.16.0.1:65535 → 172.16.0.2:8080 assert_eq!( sniff_raw_packets(test_suite[4])?, - PacketInfo::V6 { dns: false, + PacketInfo::V6 { + dns: false, src_ip: [172, 16, 0, 1], src_port: 65535, dst_ip: [172, 16, 0, 2], @@ -175,7 +180,8 @@ fn generic_typeck() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { // [5] IPv4 TCP IHL=6 10.0.0.1:9090 → 10.0.0.2:22 (requires IHL-based offset) assert_eq!( sniff_raw_packets(test_suite[5])?, - PacketInfo::V6 { dns: false, + PacketInfo::V6 { + dns: false, src_ip: [10, 0, 0, 1], src_port: 9090, dst_ip: [10, 0, 0, 2], @@ -187,7 +193,8 @@ fn generic_typeck() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { // [6] IPv4 ICMP (unsupported, "ports" are just ICMP body bytes) assert_eq!( sniff_raw_packets(test_suite[6])?, - PacketInfo::V6 { dns: false, + PacketInfo::V6 { + dns: false, src_ip: [10, 0, 0, 9], src_port: 2048, dst_ip: [10, 0, 0, 1], @@ -199,7 +206,8 @@ fn generic_typeck() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { // [7] IPv6 TCP [::1]:4000 → [2606:4700::1]:443 assert_eq!( sniff_raw_packets(test_suite[7])?, - PacketInfo::V6 { dns: false, + PacketInfo::V6 { + dns: false, src_ip: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], src_port: 4000, dst_ip: [0x26, 0x06, 0x47, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], @@ -211,7 +219,8 @@ fn generic_typeck() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { // [8] IPv6 UDP [fd00::9]:1234 → [fd00::1]:53 (requires fixing 17 => UDP) assert_eq!( sniff_raw_packets(test_suite[8])?, - PacketInfo::V6 { dns: false, + PacketInfo::V6 { + dns: false, src_ip: [0xFD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9], src_port: 1234, dst_ip: [0xFD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], @@ -223,7 +232,8 @@ fn generic_typeck() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { // [9] IPv6 ICMPv6 (unsupported, "ports" are ICMPv6 body bytes) assert_eq!( sniff_raw_packets(test_suite[9])?, - PacketInfo::V6 { dns: false, + PacketInfo::V6 { + dns: false, src_ip: [0xFE, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], src_port: 32768, dst_ip: [0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], diff --git a/tests/v2ray_geosite.rs b/tests/v2ray_geosite.rs index f4ea664..d400839 100644 --- a/tests/v2ray_geosite.rs +++ b/tests/v2ray_geosite.rs @@ -30,7 +30,11 @@ fn get_geosite_service() -> Result<GeoSiteService, Box<dyn std::error::Error>> { #[test] fn geosite_service_creation() { let service = get_geosite_service(); - assert!(service.is_ok(), "Failed to create GeoSiteService: {:?}", service.err()); + assert!( + service.is_ok(), + "Failed to create GeoSiteService: {:?}", + service.err() + ); } #[test] @@ -51,7 +55,10 @@ fn lookup_nonexistent_domain() { }; let result = service.lookup(domain.value.as_str()); - assert!(result.is_none(), "Should return none for not existing domain"); + assert!( + result.is_none(), + "Should return none for not existing domain" + ); println!("{:?}", result); } @@ -59,10 +66,7 @@ fn lookup_nonexistent_domain() { fn geosite_list_not_empty() { let service = get_geosite_service().expect("Failed to create service"); - assert!( - !service.is_empty(), - "GeoSiteList should not be empty" - ); + assert!(!service.is_empty(), "GeoSiteList should not be empty"); println!("Loaded {} GeoSite entries", service.len()); } |
