Initial version of full UAPI parser

This commit is contained in:
Mathias Hall-Andersen
2019-11-13 15:30:16 +01:00
parent 5b555a2e17
commit a85725eede
4 changed files with 137 additions and 78 deletions

View File

@@ -1,3 +1,7 @@
use std::error::Error;
use std::fmt;
#[derive(Debug)]
pub enum ConfigError { pub enum ConfigError {
NoSuchPeer, NoSuchPeer,
NotListening, NotListening,
@@ -9,13 +13,32 @@ pub enum ConfigError {
InvalidSocketAddr, InvalidSocketAddr,
InvalidKeepaliveInterval, InvalidKeepaliveInterval,
InvalidAllowedIp, InvalidAllowedIp,
InvalidOperation,
LineTooLong,
IOError,
UnsupportedValue, UnsupportedValue,
UnsupportedProtocolVersion, UnsupportedProtocolVersion,
} }
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ConfigError(errno = {})", self.errno())
}
}
impl Error for ConfigError {
fn description(&self) -> &str {
""
}
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
impl ConfigError { impl ConfigError {
fn errno(&self) -> i32 { pub fn errno(&self) -> i32 {
// TODO: obtain the correct error values // TODO: obtain the correct errorno values
match self { match self {
ConfigError::NoSuchPeer => 1, ConfigError::NoSuchPeer => 1,
ConfigError::NotListening => 2, ConfigError::NotListening => 2,
@@ -26,9 +49,12 @@ impl ConfigError {
ConfigError::InvalidSocketAddr => 10, ConfigError::InvalidSocketAddr => 10,
ConfigError::InvalidKeepaliveInterval => 11, ConfigError::InvalidKeepaliveInterval => 11,
ConfigError::InvalidAllowedIp => 12, ConfigError::InvalidAllowedIp => 12,
ConfigError::InvalidOperation => 15,
ConfigError::UnsupportedValue => 7, ConfigError::UnsupportedValue => 7,
ConfigError::LineTooLong => 13,
ConfigError::InvalidKey => 8, ConfigError::InvalidKey => 8,
ConfigError::UnsupportedProtocolVersion => 9, ConfigError::UnsupportedProtocolVersion => 9,
ConfigError::IOError => 14,
} }
} }
} }

View File

@@ -1,54 +1,52 @@
use hex::FromHex; use hex::FromHex;
use subtle::ConstantTimeEq; use subtle::ConstantTimeEq;
use x25519_dalek::{PublicKey, StaticSecret};
use super::{ConfigError, Configuration}; use super::Configuration;
use std::io;
struct Serializer<C: Configuration> { pub fn serialize<C: Configuration, W: io::Write>(writer: &mut W, config: &C) -> io::Result<()> {
config: C, let mut write = |key: &'static str, value: String| {
} debug_assert!(value.is_ascii());
debug_assert!(key.is_ascii());
writer.write(key.as_ref())?;
writer.write(b"=")?;
writer.write(value.as_ref())
};
impl<C: Configuration> Serializer<C> { // serialize interface
fn get(&self) -> Vec<String> { config
let mut peers = self.config.get_peers(); .get_private_key()
let mut lines = Vec::with_capacity(peers.len() * 6 + 5); .map(|sk| write("private_key", hex::encode(sk.to_bytes())));
let mut write = |key, value: String| {
lines.push(String::new() + key + "=" + &value);
};
// serialize interface config
self.config .get_listen_port()
.get_private_key() .map(|port| write("listen_port", port.to_string()));
.map(|sk| write("private_key", hex::encode(sk.to_bytes())));
self.config config
.get_listen_port() .get_fwmark()
.map(|port| write("listen_port", port.to_string())); .map(|fwmark| write("fwmark", fwmark.to_string()));
self.config // serialize all peers
.get_fwmark() let mut peers = config.get_peers();
.map(|fwmark| write("fwmark", fwmark.to_string())); while let Some(p) = peers.pop() {
write("rx_bytes", p.rx_bytes.to_string())?;
// serialize all peers write("tx_bytes", p.tx_bytes.to_string())?;
while let Some(p) = peers.pop() { write(
write("rx_bytes", p.rx_bytes.to_string()); "last_handshake_time_sec",
write("tx_bytes", p.tx_bytes.to_string()); p.last_handshake_time_nsec.to_string(),
write( )?;
"last_handshake_time_sec", write(
p.last_handshake_time_nsec.to_string(), "last_handshake_time_nsec",
); p.last_handshake_time_nsec.to_string(),
write( )?;
"last_handshake_time_nsec", write("public_key", hex::encode(p.public_key.as_bytes()))?;
p.last_handshake_time_nsec.to_string(), if let Some(psk) = p.preshared_key {
); write("preshared_key", hex::encode(psk))?;
write("public_key", hex::encode(p.public_key.as_bytes())); }
p.preshared_key for (ip, cidr) in p.allowed_ips {
.map(|psk| write("preshared_key", hex::encode(psk))); write("allowed_ip", ip.to_string() + "/" + &cidr.to_string())?;
for (ip, cidr) in p.allowed_ips {
write("allowed_ip", ip.to_string() + "/" + &cidr.to_string())
}
} }
lines
} }
Ok(())
} }

View File

@@ -5,45 +5,73 @@ use std::io::{Read, Write};
use super::{ConfigError, Configuration}; use super::{ConfigError, Configuration};
const MAX_LINE_LENGTH: usize = 128; use get::serialize;
use set::LineParser;
struct Parser<C: Configuration, R: Read, W: Write> { const MAX_LINE_LENGTH: usize = 256;
config: C,
reader: R,
writer: W,
}
impl<C: Configuration, R: Read, W: Write> Parser<C, R, W> { pub fn process<R: Read, W: Write, C: Configuration>(reader: &mut R, writer: &mut W, config: &C) {
fn new(&self, reader: R, writer: W, config: C) -> Parser<C, R, W> { fn operation<R: Read, W: Write, C: Configuration>(
Parser { reader: &mut R,
config, writer: &mut W,
reader, config: &C,
writer, ) -> Result<(), ConfigError> {
}
}
fn parse(&mut self) -> Option<()> {
// read string up to maximum length (why is this not in std?) // read string up to maximum length (why is this not in std?)
let mut line = || { fn readline<R: Read>(reader: &mut R) -> Result<String, ConfigError> {
let mut m: [u8; 1] = [0u8]; let mut m: [u8; 1] = [0u8];
let mut l: String = String::with_capacity(MAX_LINE_LENGTH); let mut l: String = String::with_capacity(MAX_LINE_LENGTH);
while let Ok(_) = self.reader.read_exact(&mut m) { while let Ok(_) = reader.read_exact(&mut m) {
let c = m[0] as char; let c = m[0] as char;
if c == '\n' { if c == '\n' {
return Some(l); return Ok(l);
}; };
l.push(c); l.push(c);
if l.len() > MAX_LINE_LENGTH { if l.len() > MAX_LINE_LENGTH {
break; return Err(ConfigError::LineTooLong);
} }
} }
None return Err(ConfigError::IOError);
}
// split into (key, value) pair
fn keypair<'a>(ln: &'a str) -> Result<(&'a str, &'a str), ConfigError> {
let mut split = ln.splitn(2, "=");
match (split.next(), split.next()) {
(Some(key), Some(value)) => Ok((key, value)),
_ => Err(ConfigError::LineTooLong),
}
}; };
match line()?.as_str() { // read operation line
"get=1" => Some(()), match readline(reader)?.as_str() {
"set=1" => Some(()), "get=1" => serialize(writer, config).map_err(|_| ConfigError::IOError),
_ => None, "set=1" => {
let mut parser = LineParser::new(config);
loop {
let ln = readline(reader)?;
if ln == "" {
break Ok(());
};
let (k, v) = keypair(ln.as_str())?;
parser.parse_line(k, v)?;
}
}
_ => Err(ConfigError::InvalidOperation),
} }
} }
// process operation
let res = operation(reader, writer, config);
log::debug!("{:?}", res);
// return errno
let _ = writer.write("errno=".as_ref());
let _ = writer.write(
match res {
Err(e) => e.errno().to_string(),
Ok(()) => "0".to_owned(),
}
.as_ref(),
);
let _ = writer.write("\n\n".as_ref());
} }

View File

@@ -13,12 +13,19 @@ enum ParserState {
Interface, Interface,
} }
struct LineParser<C: Configuration> { pub struct LineParser<'a, C: Configuration> {
config: C, config: &'a C,
state: ParserState, state: ParserState,
} }
impl<C: Configuration> LineParser<C> { impl<'a, C: Configuration> LineParser<'a, C> {
pub fn new(config: &'a C) -> LineParser<'a, C> {
LineParser {
config,
state: ParserState::Interface,
}
}
fn new_peer(value: &str) -> Result<ParserState, ConfigError> { fn new_peer(value: &str) -> Result<ParserState, ConfigError> {
match <[u8; 32]>::from_hex(value) { match <[u8; 32]>::from_hex(value) {
Ok(pk) => Ok(ParserState::Peer { Ok(pk) => Ok(ParserState::Peer {
@@ -29,7 +36,7 @@ impl<C: Configuration> LineParser<C> {
} }
} }
fn parse_line(&mut self, key: &str, value: &str) -> Option<ConfigError> { pub fn parse_line(&mut self, key: &str, value: &str) -> Result<(), ConfigError> {
// add the peer if not update_only // add the peer if not update_only
let flush_peer = |st: ParserState| -> ParserState { let flush_peer = |st: ParserState| -> ParserState {
match st { match st {
@@ -48,7 +55,7 @@ impl<C: Configuration> LineParser<C> {
}; };
// parse line and update parser state // parse line and update parser state
match self.state { self.state = match self.state {
// configure the interface // configure the interface
ParserState::Interface => match key { ParserState::Interface => match key {
// opt: set private key // opt: set private key
@@ -199,8 +206,8 @@ impl<C: Configuration> LineParser<C> {
// unknown key // unknown key
_ => Err(ConfigError::InvalidKey), _ => Err(ConfigError::InvalidKey),
}, },
} }?;
.map(|st| self.state = st)
.err() Ok(())
} }
} }