Refactor: Fully migrated parser to full prost crate, auto-fmt; Fix: Remove no more needed tests

This commit is contained in:
Namilsk 2026-04-28 17:29:31 +03:00
parent c1bf670152
commit 0d81bae32a
8 changed files with 75 additions and 96 deletions

View file

@ -8,9 +8,10 @@ use std::net::IpAddr;
// TODO: V2Ray protobuf parsing && Test 4 ts // TODO: V2Ray protobuf parsing && Test 4 ts
/// Interface enum for `dst_addr` info /// Interface enum for `dst_addr` info
#[allow(dead_code)]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub enum RouteType { pub enum RouteType {
/// GeoSite MMDB type, like `category-ads-all` /// GeoSite type, like `category-ads-all`
GeoSite(String), GeoSite(String),
/// Result with GeoCode like "RU" /// Result with GeoCode like "RU"
GeoIp(String), GeoIp(String),
@ -18,6 +19,7 @@ pub enum RouteType {
} }
/// Routing actions /// Routing actions
#[allow(dead_code)]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub enum RouteAction { pub enum RouteAction {
#[serde(alias = "block")] #[serde(alias = "block")]

40
src/geoparsers/geosite.rs Normal file
View file

@ -0,0 +1,40 @@
use crate::geoparsers::v2ray::parsing::decode_geosite_stream;
use crate::geoparsers::v2ray::types::{GeoSite, GeoSiteList};
use std::fs;
use std::io::{BufReader, Read};
pub struct GeoSiteService {
index: GeoSiteList,
}
impl GeoSiteService {
// TODO: Make more smart memory mapping; geosite files can be > 70MB
pub fn new(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let file = fs::File::open(path)?;
let reader = BufReader::new(file);
let geosite_list = decode_geosite_stream(reader)?;
Ok(Self {
index: geosite_list,
})
}
// Idk but i think it can work
pub fn lookup(&self, value: &str) -> Option<&GeoSite> {
self.index
.entry
.iter()
.find(|site| site.domain.iter().any(|d| d.value == value))
}
/// Returns the number of GeoSite entries in the list
pub fn len(&self) -> usize {
self.index.entry.len()
}
/// Returns true if the GeoSite list is empty
pub fn is_empty(&self) -> bool {
self.index.entry.is_empty()
}
}

View file

@ -1,3 +1,4 @@
pub mod geoip2; pub mod geoip2;
pub mod geosite;
pub mod toml; pub mod toml;
pub mod v2ray; pub mod v2ray;

View file

@ -1,2 +1,2 @@
pub mod parsing; pub(crate) mod parsing;
pub mod types; pub mod types;

View file

@ -1,79 +1,15 @@
use crate::geoparsers::v2ray::types::{Domain, GeoSite, GeoSiteList}; use crate::geoparsers::v2ray::types::GeoSiteList;
use prost::Message; use prost::Message;
use prost::bytes::Buf; use std::io::Read;
use std::fs;
pub struct GeoSiteService { /// Decodes full geosite protobuf; TODO: MMAP
index: GeoSiteList, pub fn decode_geosite_stream<R: Read>(
} mut reader: R,
) -> Result<GeoSiteList, Box<dyn std::error::Error>> {
impl GeoSiteService { let mut buf = Vec::new();
// TODO: Make more smart memory mapping; geosite files can be > 70MB reader.read_to_end(&mut buf)?;
pub fn new(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let bytes = fs::read(path)?; let list = GeoSiteList::decode(&buf[..])?;
let geosite_list = decode_geosite_stream(&bytes)?;
Ok(list)
Ok(Self {
index: geosite_list,
})
}
// Idk but i think it can work
pub fn lookup(&self, value: &str) -> Option<&GeoSite> {
self.index
.entry
.iter()
.find(|site| site.domain.iter().any(|d| d.value == value))
}
/// Returns the number of GeoSite entries in the list
pub fn len(&self) -> usize {
self.index.entry.len()
}
/// Returns true if the GeoSite list is empty
pub fn is_empty(&self) -> bool {
self.index.entry.is_empty()
}
}
/// Decode a stream of length-delimited GeoSite messages
/// `geosite.dat` ts is not one protobuf-message, stream of length-delimited messages
/// so we need ts helper
fn decode_geosite_stream(bytes: &[u8]) -> Result<GeoSiteList, Box<dyn std::error::Error>> {
let mut buf = bytes;
let mut entries = Vec::new();
while buf.has_remaining() {
// Read tag (0x0a field 1, wire type 2)
let tag = buf.get_u8();
if tag != 0x0a {
return Err(format!("Unexpected tag: {:#04x}", tag).into());
}
// varint
let mut len = 0usize;
let mut shift = 0;
loop {
if !buf.has_remaining() {
return Err("Unexpected end of buffer while reading varint".into());
}
let b = buf.get_u8();
len |= ((b & 0x7f) as usize) << shift;
if b & 0x80 == 0 {
break;
}
shift += 7;
if shift >= 70 {
return Err("Varint too long".into());
}
}
let entry_bytes = &buf[..len];
let site = GeoSite::decode(entry_bytes)?;
entries.push(site);
buf.advance(len);
}
Ok(GeoSiteList { entry: entries })
} }

View file

@ -33,17 +33,7 @@ pub mod domain {
} }
} }
/// Type of domain value. /// Type of domain value.
#[derive( #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
Clone,
Copy,
Debug,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
::prost::Enumeration
)]
#[repr(i32)] #[repr(i32)]
pub enum Type { pub enum Type {
/// The value is used as is. /// The value is used as is.

View file

@ -69,9 +69,9 @@ impl fmt::Display for PacketInfo {
// y:y:y:y:y:y:y:y = 8 hexademical; y = segment, pair of 2 u8 big endian // y:y:y:y:y:y:y:y = 8 hexademical; y = segment, pair of 2 u8 big endian
write!( write!(
f, f,
// FIXME: fe80:0:0:0:93a:245e:daac:7a75 -> ff12:0:0:0:0:0:0:8384 // FIXME: fe80:0:0:0:93a:245e:daac:7a75 -> ff12:0:0:0:0:0:0:8384
// we should drop zeroes in display: fe80::93a:245e:daac:7a75 -> ff12::8384 // we should drop zeroes in display: fe80::93a:245e:daac:7a75 -> ff12::8384
// NOTE: fe80:0:0:93a:0:245e and fe80:0:93a:0:0:245e can NOT both be compressed to fe80::93a::245e by obvious reasons // NOTE: fe80:0:0:93a:0:245e and fe80:0:93a:0:0:245e can NOT both be compressed to fe80::93a::245e by obvious reasons
"{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x} port:{} -> {:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x} port:{} {:?} is dns? {:?}", "{: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[0],
src_ip[1], src_ip[1],

View file

@ -1,5 +1,5 @@
use nsc::geoparsers::v2ray::parsing::GeoSiteService; use nsc::geoparsers::geosite::GeoSiteService;
use nsc::geoparsers::v2ray::types::Domain; use nsc::geoparsers::v2ray::types::{Domain, domain};
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
@ -7,11 +7,11 @@ fn download_geosite() -> Result<PathBuf, Box<dyn std::error::Error>> {
let tmp_dir = std::env::temp_dir().join("seccontrol_test"); let tmp_dir = std::env::temp_dir().join("seccontrol_test");
fs::create_dir_all(&tmp_dir)?; fs::create_dir_all(&tmp_dir)?;
let geosite_path = tmp_dir.join("geosite.dat"); let geosite_path = tmp_dir.join("dlc.dat");
if !geosite_path.exists() { if !geosite_path.exists() {
// Use v2fly domain-list-community which has standard protobuf format // Use v2fly domain-list-community which has standard protobuf format
let url = "https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat"; let url = "https://github.com/v2fly/domain-list-community/releases/download/20260330021310/dlc.dat";
let response = ureq::get(url).call()?; let response = ureq::get(url).call()?;
let mut file = fs::File::create(&geosite_path)?; let mut file = fs::File::create(&geosite_path)?;
let mut reader = response.into_reader(); let mut reader = response.into_reader();
@ -41,6 +41,16 @@ fn geosite_service_creation() {
fn lookup_existing_domain() { fn lookup_existing_domain() {
let service = get_geosite_service().expect("Failed to create service"); let service = get_geosite_service().expect("Failed to create service");
let domain = Domain {
r#type: nsc::geoparsers::v2ray::types::domain::Type::Full as i32,
value: "google.com".to_string(),
attribute: vec![],
};
let result = service.lookup(domain.value.as_str());
assert!(result.is_some(), "Should return GeoSite categs");
println!("{:?}", result);
assert!(!service.is_empty(), "Service should have entries"); assert!(!service.is_empty(), "Service should have entries");
println!("Loaded {} GeoSite entries", service.len()); println!("Loaded {} GeoSite entries", service.len());
} }