Files
gatuna/gatunad/src/upstream.rs
T

99 lines
2.6 KiB
Rust

//! Upstream table: cmdline parsing and MANIFEST entry construction.
use crate::frame::{UpstreamEntry, PROTO_TCP, PROTO_UDP};
/// Read the system hostname via `gethostname(2)`.
pub fn get_hostname() -> String {
let mut buf = [0u8; 256];
let ret = unsafe {
libc::gethostname(buf.as_mut_ptr() as *mut libc::c_char, buf.len())
};
if ret == 0 {
let len = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
String::from_utf8_lossy(&buf[..len]).into_owned()
} else {
String::new()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(dead_code)]
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))
}