Initial version of full UAPI parser
This commit is contained in:
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user