From a85725eede89f8d7fecd10dc0628a01e48cccd7d Mon Sep 17 00:00:00 2001 From: Mathias Hall-Andersen Date: Wed, 13 Nov 2019 15:30:16 +0100 Subject: Initial version of full UAPI parser --- src/configuration/error.rs | 30 +++++++++++++- src/configuration/uapi/get.rs | 94 +++++++++++++++++++++---------------------- src/configuration/uapi/mod.rs | 78 +++++++++++++++++++++++------------ src/configuration/uapi/set.rs | 23 +++++++---- 4 files changed, 142 insertions(+), 83 deletions(-) (limited to 'src') diff --git a/src/configuration/error.rs b/src/configuration/error.rs index be08c9e..b7d7bb0 100644 --- a/src/configuration/error.rs +++ b/src/configuration/error.rs @@ -1,3 +1,7 @@ +use std::error::Error; +use std::fmt; + +#[derive(Debug)] pub enum ConfigError { NoSuchPeer, NotListening, @@ -9,13 +13,32 @@ pub enum ConfigError { InvalidSocketAddr, InvalidKeepaliveInterval, InvalidAllowedIp, + InvalidOperation, + LineTooLong, + IOError, UnsupportedValue, 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 { - fn errno(&self) -> i32 { - // TODO: obtain the correct error values + pub fn errno(&self) -> i32 { + // TODO: obtain the correct errorno values match self { ConfigError::NoSuchPeer => 1, ConfigError::NotListening => 2, @@ -26,9 +49,12 @@ impl ConfigError { ConfigError::InvalidSocketAddr => 10, ConfigError::InvalidKeepaliveInterval => 11, ConfigError::InvalidAllowedIp => 12, + ConfigError::InvalidOperation => 15, ConfigError::UnsupportedValue => 7, + ConfigError::LineTooLong => 13, ConfigError::InvalidKey => 8, ConfigError::UnsupportedProtocolVersion => 9, + ConfigError::IOError => 14, } } } diff --git a/src/configuration/uapi/get.rs b/src/configuration/uapi/get.rs index 99ebbde..9b05421 100644 --- a/src/configuration/uapi/get.rs +++ b/src/configuration/uapi/get.rs @@ -1,54 +1,52 @@ use hex::FromHex; use subtle::ConstantTimeEq; -use x25519_dalek::{PublicKey, StaticSecret}; -use super::{ConfigError, Configuration}; - -struct Serializer { - config: C, -} - -impl Serializer { - fn get(&self) -> Vec { - let mut peers = self.config.get_peers(); - let mut lines = Vec::with_capacity(peers.len() * 6 + 5); - let mut write = |key, value: String| { - lines.push(String::new() + key + "=" + &value); - }; - - // serialize interface - self.config - .get_private_key() - .map(|sk| write("private_key", hex::encode(sk.to_bytes()))); - - self.config - .get_listen_port() - .map(|port| write("listen_port", port.to_string())); - - self.config - .get_fwmark() - .map(|fwmark| write("fwmark", fwmark.to_string())); - - // serialize all peers - while let Some(p) = peers.pop() { - write("rx_bytes", p.rx_bytes.to_string()); - write("tx_bytes", p.tx_bytes.to_string()); - write( - "last_handshake_time_sec", - p.last_handshake_time_nsec.to_string(), - ); - write( - "last_handshake_time_nsec", - p.last_handshake_time_nsec.to_string(), - ); - write("public_key", hex::encode(p.public_key.as_bytes())); - p.preshared_key - .map(|psk| write("preshared_key", hex::encode(psk))); - for (ip, cidr) in p.allowed_ips { - write("allowed_ip", ip.to_string() + "/" + &cidr.to_string()) - } +use super::Configuration; +use std::io; + +pub fn serialize(writer: &mut W, config: &C) -> io::Result<()> { + 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()) + }; + + // serialize interface + config + .get_private_key() + .map(|sk| write("private_key", hex::encode(sk.to_bytes()))); + + config + .get_listen_port() + .map(|port| write("listen_port", port.to_string())); + + config + .get_fwmark() + .map(|fwmark| write("fwmark", fwmark.to_string())); + + // serialize all peers + let mut peers = config.get_peers(); + while let Some(p) = peers.pop() { + write("rx_bytes", p.rx_bytes.to_string())?; + write("tx_bytes", p.tx_bytes.to_string())?; + write( + "last_handshake_time_sec", + p.last_handshake_time_nsec.to_string(), + )?; + write( + "last_handshake_time_nsec", + p.last_handshake_time_nsec.to_string(), + )?; + write("public_key", hex::encode(p.public_key.as_bytes()))?; + if let Some(psk) = p.preshared_key { + write("preshared_key", hex::encode(psk))?; + } + for (ip, cidr) in p.allowed_ips { + write("allowed_ip", ip.to_string() + "/" + &cidr.to_string())?; } - - lines } + + Ok(()) } diff --git a/src/configuration/uapi/mod.rs b/src/configuration/uapi/mod.rs index cba156d..117d970 100644 --- a/src/configuration/uapi/mod.rs +++ b/src/configuration/uapi/mod.rs @@ -5,45 +5,73 @@ use std::io::{Read, Write}; use super::{ConfigError, Configuration}; -const MAX_LINE_LENGTH: usize = 128; +use get::serialize; +use set::LineParser; -struct Parser { - config: C, - reader: R, - writer: W, -} - -impl Parser { - fn new(&self, reader: R, writer: W, config: C) -> Parser { - Parser { - config, - reader, - writer, - } - } +const MAX_LINE_LENGTH: usize = 256; - fn parse(&mut self) -> Option<()> { +pub fn process(reader: &mut R, writer: &mut W, config: &C) { + fn operation( + reader: &mut R, + writer: &mut W, + config: &C, + ) -> Result<(), ConfigError> { // read string up to maximum length (why is this not in std?) - let mut line = || { + fn readline(reader: &mut R) -> Result { let mut m: [u8; 1] = [0u8]; 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; if c == '\n' { - return Some(l); + return Ok(l); }; l.push(c); 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() { - "get=1" => Some(()), - "set=1" => Some(()), - _ => None, + // read operation line + match readline(reader)?.as_str() { + "get=1" => serialize(writer, config).map_err(|_| ConfigError::IOError), + "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()); } diff --git a/src/configuration/uapi/set.rs b/src/configuration/uapi/set.rs index c609d83..4c2c554 100644 --- a/src/configuration/uapi/set.rs +++ b/src/configuration/uapi/set.rs @@ -13,12 +13,19 @@ enum ParserState { Interface, } -struct LineParser { - config: C, +pub struct LineParser<'a, C: Configuration> { + config: &'a C, state: ParserState, } -impl LineParser { +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 { match <[u8; 32]>::from_hex(value) { Ok(pk) => Ok(ParserState::Peer { @@ -29,7 +36,7 @@ impl LineParser { } } - fn parse_line(&mut self, key: &str, value: &str) -> Option { + pub fn parse_line(&mut self, key: &str, value: &str) -> Result<(), ConfigError> { // add the peer if not update_only let flush_peer = |st: ParserState| -> ParserState { match st { @@ -48,7 +55,7 @@ impl LineParser { }; // parse line and update parser state - match self.state { + self.state = match self.state { // configure the interface ParserState::Interface => match key { // opt: set private key @@ -199,8 +206,8 @@ impl LineParser { // unknown key _ => Err(ConfigError::InvalidKey), }, - } - .map(|st| self.state = st) - .err() + }?; + + Ok(()) } } -- cgit v1.2.3-59-g8ed1b