Files
gatuna/gatunad/src/upstream.rs
T
mute 25f00b0deb add gatunad server and winforms client v1 (TCP-only)
Raw-Ethernet tunnel over ethertype 0x6969 to bypass WFP killswitches.
Server (Rust, AF_PACKET + classic BPF) relays TCP to 127.0.0.1 services.
Client (.NET 8 WinForms, SharpPcap/Npcap) discovers upstreams and exposes
local loopback listeners. Wire protocol: 6-byte header, 7 frame types,
MANIFEST with labeled upstreams. UDP types reserved, unimplemented.
2026-08-12 18:20:51 +00:00

84 lines
2.2 KiB
Rust

//! Upstream table: cmdline parsing and MANIFEST entry construction.
use crate::frame::{UpstreamEntry, PROTO_TCP, PROTO_UDP};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Proto {
Tcp,
Udp,
}
impl Proto {
pub fn as_u8(self) -> u8 {
match self {
Proto::Tcp => PROTO_TCP,
Proto::Udp => PROTO_UDP,
}
}
}
#[derive(Clone, Debug)]
pub struct Upstream {
pub id: u8,
pub proto: Proto,
pub port: u16,
pub label: Option<String>,
}
pub struct UpstreamTable(pub Vec<Upstream>);
impl UpstreamTable {
pub fn get(&self, id: u8) -> Option<&Upstream> {
self.0.iter().find(|u| u.id == id)
}
pub fn entries(&self) -> Vec<UpstreamEntry> {
self.0
.iter()
.map(|u| UpstreamEntry {
id: u.id,
proto: u.proto.as_u8(),
port: u.port,
label: u.label.clone(),
})
.collect()
}
}
/// Parse a single `PORT[:label]` argument. v1 only accepts bare integers or
/// `int:label`; an explicit `proto:` prefix is reserved for the UDP extension.
pub fn parse_port_arg(id: u8, s: &str) -> Result<Upstream, String> {
let (port_str, label) = match s.split_once(':') {
Some((p, l)) => (p, Some(l.to_string())),
None => (s, None),
};
let port: u16 = port_str
.parse()
.map_err(|_| format!("invalid port value: {port_str}"))?;
if port == 0 {
return Err(format!("port must be > 0: {s}"));
}
if let Some(l) = &label {
if l.is_empty() {
return Err(format!("empty label: {s}"));
}
if l.len() > 255 {
return Err(format!("label too long (max 255 bytes): {s}"));
}
}
Ok(Upstream { id, proto: Proto::Tcp, port, label })
}
pub fn build_table(args: &[String]) -> Result<UpstreamTable, String> {
if args.is_empty() {
return Err("at least one PORT is required".into());
}
if args.len() > 255 {
return Err("too many upstreams (max 255)".into());
}
let mut v = Vec::with_capacity(args.len());
for (i, a) in args.iter().enumerate() {
let id = (i + 1) as u8;
v.push(parse_port_arg(id, a)?);
}
Ok(UpstreamTable(v))
}